update
This commit is contained in:
@@ -47,6 +47,6 @@ const props = withDefaults(
|
||||
}
|
||||
|
||||
.ui-data-row.is-disabled {
|
||||
color: var(--ui-control-disabled-foreground);
|
||||
color: var(--ui-unavailable-foreground);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,8 +12,17 @@ const props = withDefaults(
|
||||
disabled?: boolean
|
||||
expandedLabel?: string
|
||||
collapsedLabel?: string
|
||||
compact?: boolean
|
||||
showState?: boolean
|
||||
indicatorPosition?: 'start' | 'end'
|
||||
}>(),
|
||||
{ expandedLabel: '已展开', collapsedLabel: '未展开' },
|
||||
{
|
||||
expandedLabel: '已展开',
|
||||
collapsedLabel: '未展开',
|
||||
compact: false,
|
||||
showState: true,
|
||||
indicatorPosition: 'start',
|
||||
},
|
||||
)
|
||||
|
||||
const open = defineModel<boolean>('open', { default: true })
|
||||
@@ -23,12 +32,21 @@ const open = defineModel<boolean>('open', { default: true })
|
||||
<CollapsibleRoot
|
||||
v-model:open="open"
|
||||
:disabled="disabled"
|
||||
:class="cn('ui-data-section', props.class)"
|
||||
:class="
|
||||
cn(
|
||||
'ui-data-section',
|
||||
compact && 'ui-data-section--compact',
|
||||
indicatorPosition === 'end' && 'ui-data-section--indicator-end',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<CollapsibleTrigger class="ui-data-section__trigger">
|
||||
<img :src="open ? expandedIcon : collapsedIcon" alt="" aria-hidden="true" />
|
||||
<span class="ui-data-section__title">{{ title }}</span>
|
||||
<span class="ui-data-section__state">{{ open ? expandedLabel : collapsedLabel }}</span>
|
||||
<span v-if="showState" class="ui-data-section__state">
|
||||
{{ open ? expandedLabel : collapsedLabel }}
|
||||
</span>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent class="ui-data-section__content">
|
||||
<slot />
|
||||
@@ -99,4 +117,12 @@ const open = defineModel<boolean>('open', { default: true })
|
||||
.ui-data-section__content {
|
||||
padding: var(--ui-disclosure-content-padding-block) var(--ui-field-padding-inline);
|
||||
}
|
||||
|
||||
.ui-data-section--compact .ui-data-section__trigger {
|
||||
min-height: var(--ui-control-height-compact);
|
||||
}
|
||||
|
||||
.ui-data-section--indicator-end .ui-data-section__trigger > img {
|
||||
order: 3;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -207,4 +207,11 @@ function fastChange(direction: 1 | -1) {
|
||||
.ui-number-field__fast-step button + button span {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.ui-number-field {
|
||||
height: 50px;
|
||||
grid-template-columns: minmax(0, 1fr) 25px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -66,7 +66,7 @@ const page = defineModel<number>('page', { default: 1 })
|
||||
border: 1px solid var(--ui-pagination-border);
|
||||
outline: none;
|
||||
background: var(--ui-pagination-background);
|
||||
color: var(--ui-control-disabled-foreground);
|
||||
color: var(--ui-muted-foreground);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ const page = defineModel<number>('page', { default: 1 })
|
||||
}
|
||||
|
||||
.ui-pagination__button:disabled {
|
||||
color: var(--ui-control-disabled-foreground);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,10 @@ const visibleMarkers = computed(() => {
|
||||
|
||||
<template>
|
||||
<ProgressRoot v-bind="delegatedProps" :class="cn('ui-progress', props.class)">
|
||||
<ProgressIndicator class="ui-progress__indicator" :style="{ width: `${percentage}%` }" />
|
||||
<ProgressIndicator
|
||||
class="ui-progress__indicator"
|
||||
:style="{ transform: `scaleX(${percentage / 100})` }"
|
||||
/>
|
||||
<span
|
||||
v-for="milestone in visibleMarkers"
|
||||
:key="milestone"
|
||||
@@ -52,10 +55,12 @@ const visibleMarkers = computed(() => {
|
||||
}
|
||||
|
||||
.ui-progress__indicator {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--ui-progress-fill);
|
||||
box-shadow: inset 0 0 0 1px var(--ui-progress-fill-inset);
|
||||
transition: width 180ms ease-out;
|
||||
transform-origin: left center;
|
||||
transition: transform 180ms ease-out;
|
||||
}
|
||||
|
||||
.ui-progress__marker {
|
||||
@@ -68,4 +73,10 @@ const visibleMarkers = computed(() => {
|
||||
border-left: 4px solid transparent;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ui-progress__indicator {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,7 +26,13 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
|
||||
<template>
|
||||
<RatingRoot v-bind="forwarded" :class="cn('ui-rating', props.class)" :aria-label="props.label">
|
||||
<RatingItem v-for="item in props.length" :key="item" :item="item" class="ui-rating__item">
|
||||
<RatingItem
|
||||
v-for="item in props.length"
|
||||
:key="item"
|
||||
:item="item"
|
||||
class="ui-rating__item"
|
||||
:aria-label="`${props.label}:${item} / ${props.length}`"
|
||||
>
|
||||
<img :src="emptyStar" alt="" />
|
||||
<RatingItemIndicator :step="item" class="ui-rating__indicator">
|
||||
<img :src="activeStar" alt="" />
|
||||
|
||||
@@ -31,7 +31,7 @@ function selectRow(row: UiShopItem) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('ui-shop-list', props.class)" role="listbox" aria-label="物品列表">
|
||||
<div :class="cn('ui-shop-list', props.class)" role="grid" aria-label="物品列表">
|
||||
<div class="ui-shop-list__surface">
|
||||
<div
|
||||
v-for="(row, index) in rows"
|
||||
@@ -42,7 +42,7 @@ function selectRow(row: UiShopItem) {
|
||||
'is-selected': selected === row.id,
|
||||
'is-disabled': row.disabled,
|
||||
}"
|
||||
role="option"
|
||||
role="row"
|
||||
:aria-selected="selected === row.id"
|
||||
:aria-disabled="row.disabled || undefined"
|
||||
:tabindex="row.disabled ? -1 : 0"
|
||||
@@ -51,11 +51,11 @@ function selectRow(row: UiShopItem) {
|
||||
@keydown.space.prevent="selectRow(row)"
|
||||
>
|
||||
<i class="ui-shop-row__frame" aria-hidden="true" />
|
||||
<div class="ui-shop-row__icon"><img :src="row.image" alt="" /></div>
|
||||
<div>{{ row.reinforce }}</div>
|
||||
<div>{{ row.amplify }}</div>
|
||||
<div :class="row.rarity && `quality-${row.rarity}`">{{ row.name }}</div>
|
||||
<div>
|
||||
<div class="ui-shop-row__icon" role="gridcell"><img :src="row.image" alt="" /></div>
|
||||
<div role="gridcell">{{ row.reinforce }}</div>
|
||||
<div role="gridcell">{{ row.amplify }}</div>
|
||||
<div role="gridcell" :class="row.rarity && `quality-${row.rarity}`">{{ row.name }}</div>
|
||||
<div role="gridcell">
|
||||
<button
|
||||
type="button"
|
||||
class="ui-shop-row__inspect"
|
||||
@@ -65,8 +65,10 @@ function selectRow(row: UiShopItem) {
|
||||
<img :src="inspectIcon" alt="" />
|
||||
</button>
|
||||
</div>
|
||||
<div :class="row.statusTone && `status-${row.statusTone}`">{{ row.status }}</div>
|
||||
<div class="ui-shop-row__owner">{{ row.owner }}</div>
|
||||
<div role="gridcell" :class="row.statusTone && `status-${row.statusTone}`">
|
||||
{{ row.status }}
|
||||
</div>
|
||||
<div class="ui-shop-row__owner" role="gridcell">{{ row.owner }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -305,6 +307,6 @@ function selectRow(row: UiShopItem) {
|
||||
color: var(--ui-success);
|
||||
}
|
||||
.status-danger {
|
||||
color: var(--ui-destructive);
|
||||
color: var(--ui-danger-foreground);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<TabsListProps & { class?: HTMLAttributes['class']; marker?: 'arrow' | 'line' }>(),
|
||||
{ marker: 'arrow' },
|
||||
{ marker: 'line' },
|
||||
)
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _class, marker: _marker, ...delegated } = props
|
||||
@@ -22,6 +22,12 @@ const delegatedProps = computed(() => {
|
||||
const listElement = ref<HTMLElement | ComponentPublicInstance>()
|
||||
const markerLeft = ref('0px')
|
||||
const markerReady = ref(false)
|
||||
const itemWidth = ref('auto')
|
||||
const groupWidth = ref('max-content')
|
||||
const geometryStyle = computed(() => ({
|
||||
'--ui-tab-item-width': itemWidth.value,
|
||||
'--ui-tabs-group-width': groupWidth.value,
|
||||
}))
|
||||
let resizeObserver: ResizeObserver | undefined
|
||||
let mutationObserver: MutationObserver | undefined
|
||||
|
||||
@@ -42,19 +48,60 @@ function positionMarker(animate = true) {
|
||||
requestAnimationFrame(() => (markerReady.value = true))
|
||||
}
|
||||
|
||||
function equalizeItems() {
|
||||
const list = getListElement()
|
||||
const triggers = list ? [...list.querySelectorAll<HTMLElement>('[role="tab"]')] : []
|
||||
if (!list || !triggers.length) return
|
||||
|
||||
const labels = triggers.map(
|
||||
(trigger) => trigger.querySelector<HTMLElement>('.ui-tabs-trigger__label') ?? trigger,
|
||||
)
|
||||
const widestLabel = Math.max(
|
||||
0,
|
||||
...labels.map((label) => Math.max(label.scrollWidth, label.getBoundingClientRect().width)),
|
||||
)
|
||||
const listStyle = getComputedStyle(list)
|
||||
const triggerStyle = getComputedStyle(triggers[0])
|
||||
const groupPadding =
|
||||
Number.parseFloat(listStyle.paddingLeft) + Number.parseFloat(listStyle.paddingRight)
|
||||
const itemPadding =
|
||||
Number.parseFloat(triggerStyle.paddingLeft) + Number.parseFloat(triggerStyle.paddingRight)
|
||||
const parentWidth = list.parentElement?.clientWidth ?? list.clientWidth
|
||||
const availableWidth = Math.max(0, parentWidth - groupPadding)
|
||||
if (!availableWidth) return
|
||||
|
||||
const equalizedItemWidth = Math.min(
|
||||
Math.ceil(widestLabel + itemPadding),
|
||||
availableWidth / triggers.length,
|
||||
)
|
||||
itemWidth.value = `${equalizedItemWidth}px`
|
||||
groupWidth.value = `${equalizedItemWidth * triggers.length + groupPadding}px`
|
||||
}
|
||||
|
||||
async function updateGeometry(animate = true) {
|
||||
equalizeItems()
|
||||
await nextTick()
|
||||
positionMarker(animate)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
positionMarker(false)
|
||||
document.fonts?.ready.then(() => positionMarker(false))
|
||||
resizeObserver = new ResizeObserver(() => positionMarker(false))
|
||||
await updateGeometry(false)
|
||||
document.fonts?.ready.then(() => updateGeometry(false))
|
||||
resizeObserver = new ResizeObserver(() => updateGeometry(false))
|
||||
const list = getListElement()
|
||||
if (list) resizeObserver.observe(list)
|
||||
mutationObserver = new MutationObserver(() => positionMarker())
|
||||
if (list) {
|
||||
resizeObserver.observe(list)
|
||||
if (list.parentElement) resizeObserver.observe(list.parentElement)
|
||||
}
|
||||
mutationObserver = new MutationObserver(() => updateGeometry())
|
||||
if (list) {
|
||||
mutationObserver.observe(list, {
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['data-state'],
|
||||
childList: true,
|
||||
characterData: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -66,7 +113,12 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabsList ref="listElement" v-bind="delegatedProps" :class="cn('ui-tabs-list', props.class)">
|
||||
<TabsList
|
||||
ref="listElement"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('ui-tabs-list', props.class)"
|
||||
:style="geometryStyle"
|
||||
>
|
||||
<slot />
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@@ -84,19 +136,22 @@ onBeforeUnmount(() => {
|
||||
.ui-tabs-list {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
width: var(--ui-tabs-group-width, max-content);
|
||||
max-width: 100%;
|
||||
height: var(--ui-tabs-height);
|
||||
align-items: center;
|
||||
padding: 5px 7px;
|
||||
justify-content: flex-start;
|
||||
padding: 4px 6px;
|
||||
background: var(--ui-tab-list-background);
|
||||
}
|
||||
|
||||
.ui-tabs-list::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 35px;
|
||||
left: 7px;
|
||||
right: 6px;
|
||||
top: 28px;
|
||||
left: 6px;
|
||||
height: 1px;
|
||||
background: var(--ui-tab-divider);
|
||||
}
|
||||
|
||||
@@ -23,18 +23,20 @@ const delegatedProps = computed(() => {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
box-sizing: border-box;
|
||||
width: var(--ui-tab-item-width, auto);
|
||||
height: var(--ui-tab-height);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
flex: 1 1 0;
|
||||
padding: 0 12px;
|
||||
flex: 0 0 var(--ui-tab-item-width, auto);
|
||||
padding: 0 10px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
background: var(--ui-tab-background);
|
||||
color: var(--ui-tab-foreground);
|
||||
font:
|
||||
16px / var(--ui-control-line-height) 'DCalc',
|
||||
14px / var(--ui-control-line-height) 'DCalc',
|
||||
SimSun,
|
||||
sans-serif;
|
||||
text-shadow: 1px 1px 0 var(--ui-dialog-titlebar-text-shadow);
|
||||
@@ -44,10 +46,10 @@ const delegatedProps = computed(() => {
|
||||
.ui-tabs-trigger:not(:last-of-type)::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 19px;
|
||||
height: 15px;
|
||||
background: linear-gradient(180deg, transparent, var(--ui-tab-separator) 22% 78%, transparent);
|
||||
}
|
||||
|
||||
@@ -72,7 +74,11 @@ const delegatedProps = computed(() => {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
line-height: var(--ui-control-line-height);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transform: translateY(var(--ui-control-text-offset-y));
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user