@antelopejs/dms 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/implementations/dms-base/export-jobs.d.ts +4 -1
- package/dist/implementations/dms-base/export-jobs.js +10 -6
- package/dist/implementations/dms-base/export-jobs.js.map +1 -1
- package/dist/implementations/dms-base/search-route.js +20 -11
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/test/attachment-host/capabilities.d.ts +2 -0
- package/dist/test/attachment-host/capabilities.js +11 -0
- package/dist/test/attachment-host/capabilities.js.map +1 -1
- package/dist/test/integration/table-view-capabilities.test.js +23 -5
- package/dist/test/integration/table-view-capabilities.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js +29 -0
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js +14 -0
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.js +49 -0
- package/dist/test/unit/interfaces/dms-base/export-jobs-binding.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.js +105 -0
- package/dist/test/unit/interfaces/dms-base/table-view-hidden-columns.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js +16 -0
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js.map +1 -1
- package/dist/test/unit/request-tenant-guards.test.js +38 -0
- package/dist/test/unit/request-tenant-guards.test.js.map +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/accounts.vue +9 -3
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +6 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +6 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/CommandPaletteAssistantAnswer.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +2 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageSkeleton.vue +13 -4
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RolePreviewBar.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +5 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSummaryRow.vue +4 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/RoleEditor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityEmail.vue +4 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityPassword.vue +4 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +19 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/theme/AppearanceDeveloper.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/theme/CssVariablesList.vue +6 -3
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +20 -5
- package/frontend-vue/layers/dms-ui/app/build/components/chart/internal/ChartSkeleton.vue +4 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +10 -2
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +7 -2
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +87 -40
- package/frontend-vue/layers/dms-ui/app/build/components/skeleton/RowSkeleton.vue +8 -1
- package/frontend-vue/layers/dms-ui/app/build/components/stat/StatCell.vue +54 -13
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +9 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +22 -40
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +22 -5
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +49 -9
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/ExpandedRowDetail.vue +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useReorderHandles.ts +93 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableReorder.ts +8 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +20 -2
- package/frontend-vue/layers/dms-ui/app/build/utils/characterCount.ts +8 -0
- package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/CodeBlock.vue +3 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +13 -5
- package/frontend-vue/layers/dms-ui/app/components/card/NavCard.vue +7 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +10 -3
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +8 -0
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +4 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +5 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +27 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +24 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/RichText.vue +13 -4
- package/frontend-vue/layers/dms-ui/app/components/form/components/Textarea.vue +27 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +8 -4
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/components/meter/MeterBlock.vue +15 -4
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +69 -42
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +15 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +13 -3
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +8 -2
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +45 -9
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/composables/tab/types/props.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +51 -0
- package/frontend-vue/tests/confirm-dialog-fields.test.ts +12 -0
- package/frontend-vue/tests/drawer-target-direction.test.ts +1 -0
- package/frontend-vue/tests/field-suffix-count.test.ts +120 -0
- package/frontend-vue/tests/form-section-nav-groups.test.ts +116 -0
- package/frontend-vue/tests/nav-card-badge.test.ts +5 -1
- package/frontend-vue/tests/nuxt-ui-aria-labels.test.ts +58 -0
- package/frontend-vue/tests/page-fill-height.test.ts +2 -2
- package/frontend-vue/tests/save-bar.test.ts +49 -0
- package/frontend-vue/tests/stat-group-detail-lines.test.ts +63 -0
- package/frontend-vue/tests/tab-fit.test.ts +99 -0
- package/frontend-vue/tests/table-cell-sublines.test.ts +15 -0
- package/frontend-vue/tests/table-cell-wrap.test.ts +27 -3
- package/frontend-vue/tests/table-row-action-errors.test.ts +26 -0
- package/frontend-vue/tests/table-row-hover.test.ts +3 -1
- package/frontend-vue/tests/table-scroll-area.test.ts +3 -1
- package/frontend-vue/tests/table-view-card-props.test.ts +8 -2
- package/frontend-vue/tests/table-view-cards-reorder.test.ts +250 -0
- package/frontend-vue/tests/table-view-reorder.test.ts +2 -2
- package/frontend-vue/tests/top-list-card-actions.test.ts +91 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +14 -1
|
@@ -419,12 +419,27 @@ const summaryCards = computed<StatGroupItem[]>(() =>
|
|
|
419
419
|
class="flex min-h-[228px] flex-col gap-3 p-4"
|
|
420
420
|
>
|
|
421
421
|
<div class="flex items-start justify-between">
|
|
422
|
-
<USkeleton
|
|
423
|
-
|
|
422
|
+
<USkeleton
|
|
423
|
+
:aria-label="t('dms.a11y.loading')"
|
|
424
|
+
class="size-9 rounded-[10px]"
|
|
425
|
+
/>
|
|
426
|
+
<USkeleton
|
|
427
|
+
:aria-label="t('dms.a11y.loading')"
|
|
428
|
+
class="h-5 w-12 rounded-full"
|
|
429
|
+
/>
|
|
424
430
|
</div>
|
|
425
|
-
<USkeleton
|
|
426
|
-
|
|
427
|
-
|
|
431
|
+
<USkeleton
|
|
432
|
+
:aria-label="t('dms.a11y.loading')"
|
|
433
|
+
class="h-2.5 w-4/5"
|
|
434
|
+
/>
|
|
435
|
+
<USkeleton
|
|
436
|
+
:aria-label="t('dms.a11y.loading')"
|
|
437
|
+
class="h-2.5 w-3/5"
|
|
438
|
+
/>
|
|
439
|
+
<USkeleton
|
|
440
|
+
:aria-label="t('dms.a11y.loading')"
|
|
441
|
+
class="mt-auto h-4 w-2/5"
|
|
442
|
+
/>
|
|
428
443
|
</DmsCard>
|
|
429
444
|
</div>
|
|
430
445
|
|
|
@@ -22,6 +22,8 @@ const PERCENT_UNIT = "%";
|
|
|
22
22
|
const RING_SIZE = "min(78%, 16rem)";
|
|
23
23
|
const RING_HOLE_MASK =
|
|
24
24
|
"radial-gradient(circle, transparent 54%, #000 calc(54% + 1px))";
|
|
25
|
+
|
|
26
|
+
const { t } = useI18n();
|
|
25
27
|
</script>
|
|
26
28
|
|
|
27
29
|
<template>
|
|
@@ -32,6 +34,7 @@ const RING_HOLE_MASK =
|
|
|
32
34
|
aria-busy="true"
|
|
33
35
|
>
|
|
34
36
|
<USkeleton
|
|
37
|
+
:aria-label="t('dms.a11y.loading')"
|
|
35
38
|
class="aspect-square rounded-full"
|
|
36
39
|
:style="{
|
|
37
40
|
height: RING_SIZE,
|
|
@@ -47,6 +50,7 @@ const RING_HOLE_MASK =
|
|
|
47
50
|
aria-busy="true"
|
|
48
51
|
>
|
|
49
52
|
<USkeleton
|
|
53
|
+
:aria-label="t('dms.a11y.loading')"
|
|
50
54
|
v-for="(barHeight, index) in GHOST_BAR_HEIGHTS"
|
|
51
55
|
:key="index"
|
|
52
56
|
class="flex-1 rounded-t-[4px] rounded-b-none"
|
|
@@ -115,6 +115,8 @@ function onContentPending() {
|
|
|
115
115
|
function onContentResolve() {
|
|
116
116
|
isContentLoading.value = false;
|
|
117
117
|
}
|
|
118
|
+
|
|
119
|
+
const { t } = useI18n();
|
|
118
120
|
</script>
|
|
119
121
|
|
|
120
122
|
<template>
|
|
@@ -191,8 +193,14 @@ function onContentResolve() {
|
|
|
191
193
|
:key="field"
|
|
192
194
|
class="flex flex-col gap-2"
|
|
193
195
|
>
|
|
194
|
-
<USkeleton
|
|
195
|
-
|
|
196
|
+
<USkeleton
|
|
197
|
+
:aria-label="t('dms.a11y.loading')"
|
|
198
|
+
class="h-3 w-24"
|
|
199
|
+
/>
|
|
200
|
+
<USkeleton
|
|
201
|
+
:aria-label="t('dms.a11y.loading')"
|
|
202
|
+
class="h-8 w-full"
|
|
203
|
+
/>
|
|
196
204
|
</div>
|
|
197
205
|
</div>
|
|
198
206
|
</template>
|
|
@@ -137,6 +137,8 @@ function onContentPending() {
|
|
|
137
137
|
function onContentResolve() {
|
|
138
138
|
isContentLoading.value = false;
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
const { t } = useI18n();
|
|
140
142
|
</script>
|
|
141
143
|
|
|
142
144
|
<template>
|
|
@@ -205,8 +207,11 @@ function onContentResolve() {
|
|
|
205
207
|
:key="field"
|
|
206
208
|
class="flex flex-col gap-2"
|
|
207
209
|
>
|
|
208
|
-
<USkeleton class="h-3 w-24" />
|
|
209
|
-
<USkeleton
|
|
210
|
+
<USkeleton :aria-label="t('dms.a11y.loading')" class="h-3 w-24" />
|
|
211
|
+
<USkeleton
|
|
212
|
+
:aria-label="t('dms.a11y.loading')"
|
|
213
|
+
class="h-8 w-full"
|
|
214
|
+
/>
|
|
210
215
|
</div>
|
|
211
216
|
</div>
|
|
212
217
|
</template>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { computed, useId } from "vue";
|
|
3
|
+
import {
|
|
4
|
+
type FormSectionLayout,
|
|
5
|
+
type FormSectionNavGroup,
|
|
6
|
+
type FormSectionState,
|
|
7
|
+
groupSectionsForNav,
|
|
5
8
|
} from "../../composables/form/formSections";
|
|
9
|
+
import DmsEyebrow from "../../../components/section-header/Eyebrow.vue";
|
|
6
10
|
|
|
7
11
|
type FormSectionNavVariant = "list" | "chips";
|
|
8
12
|
|
|
@@ -21,6 +25,18 @@ const emit = defineEmits<{ select: [sectionId: string] }>();
|
|
|
21
25
|
const { t } = useI18n();
|
|
22
26
|
const { processI18n } = useTranslation();
|
|
23
27
|
|
|
28
|
+
// The side list reads its sections in titled runs (`navGroup`); the row of
|
|
29
|
+
// chips has no room for headings and lists them all in one run.
|
|
30
|
+
const GROUPS_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
31
|
+
list: "flex flex-col gap-4",
|
|
32
|
+
chips: "",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const GROUP_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
36
|
+
list: "grid gap-1",
|
|
37
|
+
chips: "contents",
|
|
38
|
+
};
|
|
39
|
+
|
|
24
40
|
const VARIANT_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
25
41
|
list: "flex flex-col gap-0.5",
|
|
26
42
|
chips: "flex gap-1.5 overflow-x-auto pb-1",
|
|
@@ -37,6 +53,17 @@ const ACTIVE_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
|
37
53
|
chips: "border-accented bg-elevated text-highlighted",
|
|
38
54
|
};
|
|
39
55
|
|
|
56
|
+
const groupIdPrefix = useId();
|
|
57
|
+
const groups = computed<FormSectionNavGroup[]>(() =>
|
|
58
|
+
props.variant === "list"
|
|
59
|
+
? groupSectionsForNav(props.sections)
|
|
60
|
+
: [{ sections: props.sections }],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
function groupHeadingId(index: number): string {
|
|
64
|
+
return `${groupIdPrefix}-group-${index}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
function stateLabel(state: FormSectionState | undefined): string | undefined {
|
|
41
68
|
if (state?.invalidCount) {
|
|
42
69
|
const count = state.invalidCount;
|
|
@@ -52,43 +79,63 @@ function onSelect(event: MouseEvent, sectionId: string): void {
|
|
|
52
79
|
</script>
|
|
53
80
|
|
|
54
81
|
<template>
|
|
55
|
-
<nav
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
<nav
|
|
83
|
+
:aria-label="t('dms.form.sections.nav')"
|
|
84
|
+
:class="GROUPS_CLASSES[props.variant]"
|
|
85
|
+
>
|
|
86
|
+
<div
|
|
87
|
+
v-for="(group, groupIndex) in groups"
|
|
88
|
+
:key="group.sections[0]?.id ?? groupIndex"
|
|
89
|
+
:class="GROUP_CLASSES[props.variant]"
|
|
90
|
+
>
|
|
91
|
+
<DmsEyebrow
|
|
92
|
+
v-if="group.label"
|
|
93
|
+
:id="groupHeadingId(groupIndex)"
|
|
94
|
+
:label="processI18n(group.label)"
|
|
95
|
+
class="px-2.5"
|
|
96
|
+
/>
|
|
97
|
+
<ul
|
|
98
|
+
:class="VARIANT_CLASSES[props.variant]"
|
|
99
|
+
:aria-labelledby="group.label ? groupHeadingId(groupIndex) : undefined"
|
|
100
|
+
>
|
|
101
|
+
<li v-for="section in group.sections" :key="section.id">
|
|
102
|
+
<a
|
|
103
|
+
:href="`#${props.anchorId(section.id)}`"
|
|
104
|
+
:class="[
|
|
105
|
+
ITEM_CLASSES[props.variant],
|
|
106
|
+
section.id === props.activeId && ACTIVE_CLASSES[props.variant],
|
|
107
|
+
]"
|
|
108
|
+
:aria-current="
|
|
109
|
+
section.id === props.activeId ? 'location' : undefined
|
|
110
|
+
"
|
|
111
|
+
@click="onSelect($event, section.id)"
|
|
78
112
|
>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class="
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
<UIcon
|
|
114
|
+
v-if="section.icon && props.variant === 'list'"
|
|
115
|
+
:name="section.icon"
|
|
116
|
+
class="size-4 shrink-0"
|
|
117
|
+
/>
|
|
118
|
+
<span class="truncate">{{ processI18n(section.label) }}</span>
|
|
119
|
+
<!-- Read by its sr-only label below, not twice. -->
|
|
120
|
+
<span
|
|
121
|
+
v-if="props.states[section.id]?.invalidCount"
|
|
122
|
+
class="text-error ms-auto inline-flex items-center gap-1 font-mono text-[11px]"
|
|
123
|
+
aria-hidden="true"
|
|
124
|
+
>
|
|
125
|
+
<span class="bg-error size-1.5 rounded-full" aria-hidden="true" />
|
|
126
|
+
{{ props.states[section.id]?.invalidCount }}
|
|
127
|
+
</span>
|
|
128
|
+
<span
|
|
129
|
+
v-else-if="props.states[section.id]?.dirty"
|
|
130
|
+
class="bg-warning ms-auto size-1.5 shrink-0 rounded-full"
|
|
131
|
+
aria-hidden="true"
|
|
132
|
+
/>
|
|
133
|
+
<span v-if="stateLabel(props.states[section.id])" class="sr-only">
|
|
134
|
+
{{ stateLabel(props.states[section.id]) }}
|
|
135
|
+
</span>
|
|
136
|
+
</a>
|
|
137
|
+
</li>
|
|
138
|
+
</ul>
|
|
139
|
+
</div>
|
|
93
140
|
</nav>
|
|
94
141
|
</template>
|
|
@@ -18,6 +18,8 @@ const props = withDefaults(defineProps<RowSkeletonProps>(), {
|
|
|
18
18
|
linesClass: "space-y-1.5",
|
|
19
19
|
isCentered: false,
|
|
20
20
|
});
|
|
21
|
+
|
|
22
|
+
const { t } = useI18n();
|
|
21
23
|
</script>
|
|
22
24
|
|
|
23
25
|
<template>
|
|
@@ -26,9 +28,14 @@ const props = withDefaults(defineProps<RowSkeletonProps>(), {
|
|
|
26
28
|
:class="props.isCentered ? 'items-center' : 'items-start'"
|
|
27
29
|
aria-hidden="true"
|
|
28
30
|
>
|
|
29
|
-
<USkeleton
|
|
31
|
+
<USkeleton
|
|
32
|
+
:aria-label="t('dms.a11y.loading')"
|
|
33
|
+
class="shrink-0"
|
|
34
|
+
:class="props.well"
|
|
35
|
+
/>
|
|
30
36
|
<div class="min-w-0 flex-1" :class="props.linesClass">
|
|
31
37
|
<USkeleton
|
|
38
|
+
:aria-label="t('dms.a11y.loading')"
|
|
32
39
|
v-for="(line, index) in props.lines"
|
|
33
40
|
:key="index"
|
|
34
41
|
:class="line"
|
|
@@ -26,7 +26,10 @@ interface StatCellProps {
|
|
|
26
26
|
/** Icon of the well; the cell has no well without one. */
|
|
27
27
|
icon?: string;
|
|
28
28
|
tone?: IconWellTone;
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The line under the value; a longer one wraps to a second line before its
|
|
31
|
+
* ellipsis, the row taking the height of its tallest cell.
|
|
32
|
+
*/
|
|
30
33
|
detail?: string;
|
|
31
34
|
/** Tone of the detail line (`neutral` = muted text). */
|
|
32
35
|
detailTone?: Tone;
|
|
@@ -91,6 +94,8 @@ const cellClass = computed(() => {
|
|
|
91
94
|
];
|
|
92
95
|
});
|
|
93
96
|
const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
97
|
+
|
|
98
|
+
const { t } = useI18n();
|
|
94
99
|
</script>
|
|
95
100
|
|
|
96
101
|
<template>
|
|
@@ -101,6 +106,7 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
101
106
|
>
|
|
102
107
|
<template v-if="isMd">
|
|
103
108
|
<USkeleton
|
|
109
|
+
:aria-label="t('dms.a11y.loading')"
|
|
104
110
|
v-if="props.placeholder || (props.loading && props.icon)"
|
|
105
111
|
class="size-10 shrink-0 rounded-[10px]"
|
|
106
112
|
/>
|
|
@@ -113,9 +119,18 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
113
119
|
<!-- A placeholder keeps the loaded lines' boxes (eyebrow 13px, value
|
|
114
120
|
24px, detail 16px), so the card keeps its height when it lands. -->
|
|
115
121
|
<div v-if="props.placeholder" class="grid flex-1 gap-[3px]">
|
|
116
|
-
<USkeleton
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
<USkeleton
|
|
123
|
+
:aria-label="t('dms.a11y.loading')"
|
|
124
|
+
class="my-[1.5px] h-2.5 w-20"
|
|
125
|
+
/>
|
|
126
|
+
<USkeleton
|
|
127
|
+
:aria-label="t('dms.a11y.loading')"
|
|
128
|
+
class="my-px h-[22px] w-10"
|
|
129
|
+
/>
|
|
130
|
+
<USkeleton
|
|
131
|
+
:aria-label="t('dms.a11y.loading')"
|
|
132
|
+
class="my-0.5 h-3 w-24"
|
|
133
|
+
/>
|
|
119
134
|
</div>
|
|
120
135
|
<div v-else class="grid min-w-0 flex-1 gap-[3px]">
|
|
121
136
|
<DmsEyebrow
|
|
@@ -124,7 +139,11 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
124
139
|
:class="wraps"
|
|
125
140
|
:label="props.eyebrow"
|
|
126
141
|
/>
|
|
127
|
-
<USkeleton
|
|
142
|
+
<USkeleton
|
|
143
|
+
:aria-label="t('dms.a11y.loading')"
|
|
144
|
+
v-if="props.loading"
|
|
145
|
+
class="h-[22px] w-16"
|
|
146
|
+
/>
|
|
128
147
|
<p
|
|
129
148
|
v-else
|
|
130
149
|
class="text-highlighted text-[22px] leading-[1.1] font-[650] tracking-[-0.035em] tabular-nums transition-opacity"
|
|
@@ -134,8 +153,8 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
134
153
|
</p>
|
|
135
154
|
<p
|
|
136
155
|
v-if="props.detail && !props.loading"
|
|
137
|
-
class="
|
|
138
|
-
:class="
|
|
156
|
+
class="line-clamp-2 text-xs [overflow-wrap:anywhere]"
|
|
157
|
+
:class="detailClass"
|
|
139
158
|
>
|
|
140
159
|
{{ props.detail }}
|
|
141
160
|
</p>
|
|
@@ -145,6 +164,7 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
145
164
|
|
|
146
165
|
<template v-else>
|
|
147
166
|
<USkeleton
|
|
167
|
+
:aria-label="t('dms.a11y.loading')"
|
|
148
168
|
v-if="props.placeholder || (props.loading && props.icon)"
|
|
149
169
|
class="size-8 shrink-0 rounded-[9px]"
|
|
150
170
|
/>
|
|
@@ -157,13 +177,26 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
157
177
|
<!-- Each placeholder line keeps the loaded line's box (eyebrow 11px,
|
|
158
178
|
value 19px, detail 16px), so the group keeps its height. -->
|
|
159
179
|
<div v-if="props.placeholder" class="grid flex-1 gap-0.5">
|
|
160
|
-
<USkeleton
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
<USkeleton
|
|
181
|
+
:aria-label="t('dms.a11y.loading')"
|
|
182
|
+
class="my-px h-[9px] w-16"
|
|
183
|
+
/>
|
|
184
|
+
<USkeleton
|
|
185
|
+
:aria-label="t('dms.a11y.loading')"
|
|
186
|
+
class="my-[2.5px] h-3.5 w-24"
|
|
187
|
+
/>
|
|
188
|
+
<USkeleton
|
|
189
|
+
:aria-label="t('dms.a11y.loading')"
|
|
190
|
+
class="my-0.5 h-3 w-20"
|
|
191
|
+
/>
|
|
163
192
|
</div>
|
|
164
193
|
<span v-else class="grid min-w-0 gap-0.5">
|
|
165
194
|
<DmsEyebrow as="span" size="xs" :label="props.eyebrow" />
|
|
166
|
-
<USkeleton
|
|
195
|
+
<USkeleton
|
|
196
|
+
:aria-label="t('dms.a11y.loading')"
|
|
197
|
+
v-if="props.loading"
|
|
198
|
+
class="my-0.5 h-3.5 w-24"
|
|
199
|
+
/>
|
|
167
200
|
<b
|
|
168
201
|
v-else
|
|
169
202
|
class="text-highlighted text-sm leading-[1.35] font-semibold whitespace-nowrap tabular-nums transition-opacity"
|
|
@@ -172,8 +205,16 @@ const detailClass = computed(() => DMS_TONE_TEXT[props.detailTone]);
|
|
|
172
205
|
<slot name="value">{{ props.value }}</slot>
|
|
173
206
|
</b>
|
|
174
207
|
<template v-if="props.detail">
|
|
175
|
-
<USkeleton
|
|
176
|
-
|
|
208
|
+
<USkeleton
|
|
209
|
+
:aria-label="t('dms.a11y.loading')"
|
|
210
|
+
v-if="props.loading"
|
|
211
|
+
class="h-3 w-20"
|
|
212
|
+
/>
|
|
213
|
+
<small
|
|
214
|
+
v-else
|
|
215
|
+
class="line-clamp-2 text-xs [overflow-wrap:anywhere]"
|
|
216
|
+
:class="detailClass"
|
|
217
|
+
>
|
|
177
218
|
{{ props.detail }}
|
|
178
219
|
</small>
|
|
179
220
|
</template>
|
|
@@ -130,8 +130,14 @@ const uiTablePagination = computed(() => uiTablePaginationVariant());
|
|
|
130
130
|
aria-hidden="true"
|
|
131
131
|
:class="[uiTablePagination.root(), uiTablePagination.placeholderRoot()]"
|
|
132
132
|
>
|
|
133
|
-
<USkeleton
|
|
134
|
-
|
|
133
|
+
<USkeleton
|
|
134
|
+
:aria-label="t('dms.a11y.loading')"
|
|
135
|
+
:class="uiTablePagination.placeholderCount()"
|
|
136
|
+
/>
|
|
137
|
+
<USkeleton
|
|
138
|
+
:aria-label="t('dms.a11y.loading')"
|
|
139
|
+
:class="uiTablePagination.placeholderPager()"
|
|
140
|
+
/>
|
|
135
141
|
</div>
|
|
136
142
|
<nav
|
|
137
143
|
v-else-if="rowCount"
|
|
@@ -183,6 +189,7 @@ const uiTablePagination = computed(() => uiTablePaginationVariant());
|
|
|
183
189
|
</span>
|
|
184
190
|
{{ summary.label }}
|
|
185
191
|
<USkeleton
|
|
192
|
+
:aria-label="t('dms.a11y.loading')"
|
|
186
193
|
v-if="summary.value === undefined"
|
|
187
194
|
aria-hidden="true"
|
|
188
195
|
:class="uiTablePagination.summaryPlaceholder()"
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import type {
|
|
30
30
|
CustomButton,
|
|
31
31
|
TableViewDisplayCapabilities,
|
|
32
|
+
TableViewDisplayReorder,
|
|
32
33
|
TableViewEmptyStatesConfig,
|
|
33
34
|
} from "../../../composables/table-view/types";
|
|
34
35
|
import type {
|
|
@@ -44,6 +45,10 @@ import type { ResolvedQuickFilter } from "../../composables/table-view/utils/qui
|
|
|
44
45
|
import type { ClearableTableFilters } from "../../composables/table/utils/clearTableFilters";
|
|
45
46
|
import type { EventHookOn } from "@vueuse/core";
|
|
46
47
|
import type { VNodeChild } from "vue";
|
|
48
|
+
import {
|
|
49
|
+
REORDER_HANDLE_CLASS,
|
|
50
|
+
useReorderHandles,
|
|
51
|
+
} from "../../composables/table-view/useReorderHandles";
|
|
47
52
|
|
|
48
53
|
export interface Data {
|
|
49
54
|
[key: string]: unknown;
|
|
@@ -82,12 +87,7 @@ export interface TableAccumulation {
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
/** Rows ordered by hand (backend `reorder`): a handle moves a row. */
|
|
85
|
-
export
|
|
86
|
-
/** Off while a search, a filter or a tab narrows the rows. */
|
|
87
|
-
enabled: boolean;
|
|
88
|
-
/** Moves the row at `from` to `to`, within the page. */
|
|
89
|
-
move: (from: number, to: number) => void;
|
|
90
|
-
}
|
|
90
|
+
export type TableReorder = TableViewDisplayReorder;
|
|
91
91
|
|
|
92
92
|
/** Where a table draws its views (see `TableProps.viewsPlacement`). */
|
|
93
93
|
export type TableViewsPlacement = "band" | "header";
|
|
@@ -531,12 +531,15 @@ const theme = tv({
|
|
|
531
531
|
handleHeadCell: `${HEADER_MATCH_BG} border-b-default w-8 border-b p-0`,
|
|
532
532
|
handleCell:
|
|
533
533
|
"border-b-muted w-8 border-b ps-2 pe-0 in-[tr:last-child]:border-b-0",
|
|
534
|
-
handle:
|
|
535
|
-
"inline-flex size-6 cursor-grab items-center justify-center rounded text-dimmed hover:text-highlighted focus-visible:outline-2 focus-visible:outline-(--dms-accent-line) disabled:cursor-not-allowed disabled:opacity-40 [&>svg]:size-4",
|
|
534
|
+
handle: REORDER_HANDLE_CLASS,
|
|
536
535
|
},
|
|
537
536
|
variants: {
|
|
537
|
+
// A wrapped cell grows its row past the 44px band: it keeps the band's
|
|
538
|
+
// breathing room above and below its lines instead of touching the
|
|
539
|
+
// hairlines (one line still fits the band, so its row keeps its height).
|
|
538
540
|
cellWrap: {
|
|
539
541
|
true: {
|
|
542
|
+
rowCell: "py-2.5",
|
|
540
543
|
rowSpan: "line-clamp-none whitespace-normal [overflow-wrap:anywhere]",
|
|
541
544
|
},
|
|
542
545
|
},
|
|
@@ -809,23 +812,10 @@ const leadingCellCount = computed(
|
|
|
809
812
|
() => (hasPresenceRail.value ? 1 : 0) + (props.reorder ? 1 : 0),
|
|
810
813
|
);
|
|
811
814
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const from = draggedRowIndex.value;
|
|
817
|
-
draggedRowIndex.value = undefined;
|
|
818
|
-
if (from !== undefined && props.reorder?.enabled) {
|
|
819
|
-
props.reorder.move(from, index);
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
const stepRow = (index: number, step: number) => {
|
|
823
|
-
const target = index + step;
|
|
824
|
-
const count = table.getRowModel().rows.length;
|
|
825
|
-
if (props.reorder?.enabled && target >= 0 && target < count) {
|
|
826
|
-
props.reorder.move(index, target);
|
|
827
|
-
}
|
|
828
|
-
};
|
|
815
|
+
const reorderHandles = useReorderHandles({
|
|
816
|
+
reorder: () => props.reorder,
|
|
817
|
+
count: () => table.getRowModel().rows.length,
|
|
818
|
+
});
|
|
829
819
|
|
|
830
820
|
// Pinned columns stick only while they cover at most 60% of the visible scroll
|
|
831
821
|
// area: on a phone, labelled row actions or a wide pinned set would otherwise
|
|
@@ -1263,6 +1253,7 @@ defineShortcuts({
|
|
|
1263
1253
|
{{ caption }}
|
|
1264
1254
|
</span>
|
|
1265
1255
|
<USkeleton
|
|
1256
|
+
:aria-label="t('dms.a11y.loading')"
|
|
1266
1257
|
v-if="isFirstPageLoading"
|
|
1267
1258
|
aria-hidden="true"
|
|
1268
1259
|
:class="uiTable.captionCountPlaceholder()"
|
|
@@ -1497,8 +1488,8 @@ defineShortcuts({
|
|
|
1497
1488
|
@dblclick="handleRowDoubleClick(row.original)"
|
|
1498
1489
|
@mouseenter="handleRowHover(row)"
|
|
1499
1490
|
@mouseleave="handleRowLeave"
|
|
1500
|
-
@dragover="
|
|
1501
|
-
@drop="
|
|
1491
|
+
@dragover="reorderHandles.allowDrop"
|
|
1492
|
+
@drop="reorderHandles.drop(row.index)"
|
|
1502
1493
|
>
|
|
1503
1494
|
<td
|
|
1504
1495
|
v-if="hasPresenceRail"
|
|
@@ -1515,20 +1506,8 @@ defineShortcuts({
|
|
|
1515
1506
|
/>
|
|
1516
1507
|
<td v-if="reorder" :class="uiTable.handleCell()">
|
|
1517
1508
|
<button
|
|
1518
|
-
|
|
1519
|
-
:draggable="reorder.enabled"
|
|
1520
|
-
:disabled="!reorder.enabled"
|
|
1521
|
-
:aria-label="t('dms.table.reorder.move')"
|
|
1522
|
-
:title="
|
|
1523
|
-
reorder.enabled
|
|
1524
|
-
? t('dms.table.reorder.move')
|
|
1525
|
-
: t('dms.table.reorder.disabled')
|
|
1526
|
-
"
|
|
1509
|
+
v-bind="reorderHandles.handleFor(row.index)"
|
|
1527
1510
|
:class="uiTable.handle()"
|
|
1528
|
-
@dragstart="draggedRowIndex = row.index"
|
|
1529
|
-
@dragend="draggedRowIndex = undefined"
|
|
1530
|
-
@keydown.up.prevent="stepRow(row.index, -1)"
|
|
1531
|
-
@keydown.down.prevent="stepRow(row.index, 1)"
|
|
1532
1511
|
>
|
|
1533
1512
|
<UIcon name="i-ph-dots-six-vertical" />
|
|
1534
1513
|
</button>
|
|
@@ -1542,6 +1521,8 @@ defineShortcuts({
|
|
|
1542
1521
|
pinned: getPinnedVariant(cell.column),
|
|
1543
1522
|
loading,
|
|
1544
1523
|
rowClickable: isRowClickable(row.original),
|
|
1524
|
+
cellWrap: (cell.column.columnDef as TableColumn<T>)
|
|
1525
|
+
.cellWrap,
|
|
1545
1526
|
})
|
|
1546
1527
|
"
|
|
1547
1528
|
:style="{
|
|
@@ -1616,6 +1597,7 @@ defineShortcuts({
|
|
|
1616
1597
|
:class="uiTable.rowCell()"
|
|
1617
1598
|
>
|
|
1618
1599
|
<USkeleton
|
|
1600
|
+
:aria-label="t('dms.a11y.loading')"
|
|
1619
1601
|
v-if="column.columnDef.meta"
|
|
1620
1602
|
:class="uiTable.skeletonCell()"
|
|
1621
1603
|
:style="{ width: skeletonWidth(rowIndex, columnIndex) }"
|
|
@@ -21,6 +21,8 @@ const MAX_CARD_COUNT = 12;
|
|
|
21
21
|
const cardCount = computed(() =>
|
|
22
22
|
Math.max(1, Math.min(props.count, MAX_CARD_COUNT)),
|
|
23
23
|
);
|
|
24
|
+
|
|
25
|
+
const { t } = useI18n();
|
|
24
26
|
</script>
|
|
25
27
|
|
|
26
28
|
<template>
|
|
@@ -34,13 +36,22 @@ const cardCount = computed(() =>
|
|
|
34
36
|
class="border-default @container rounded-[10px] border bg-(--ui-bg) p-3.5 text-[12.5px]"
|
|
35
37
|
>
|
|
36
38
|
<div class="flex items-center gap-2.5">
|
|
37
|
-
<USkeleton
|
|
39
|
+
<USkeleton
|
|
40
|
+
:aria-label="t('dms.a11y.loading')"
|
|
41
|
+
class="size-7 shrink-0 rounded-[7px]"
|
|
42
|
+
/>
|
|
38
43
|
<div class="min-w-0 flex-1">
|
|
39
44
|
<div class="text-[13px]">
|
|
40
|
-
<USkeleton
|
|
45
|
+
<USkeleton
|
|
46
|
+
:aria-label="t('dms.a11y.loading')"
|
|
47
|
+
class="inline-block h-3 w-3/5 align-middle"
|
|
48
|
+
/>
|
|
41
49
|
</div>
|
|
42
50
|
<div class="font-mono text-[11px]">
|
|
43
|
-
<USkeleton
|
|
51
|
+
<USkeleton
|
|
52
|
+
:aria-label="t('dms.a11y.loading')"
|
|
53
|
+
class="inline-block h-2 w-2/5 align-middle"
|
|
54
|
+
/>
|
|
44
55
|
</div>
|
|
45
56
|
</div>
|
|
46
57
|
</div>
|
|
@@ -49,10 +60,16 @@ const cardCount = computed(() =>
|
|
|
49
60
|
>
|
|
50
61
|
<div v-for="field in props.fields" :key="field" class="min-w-0">
|
|
51
62
|
<div :class="['font-mono', props.labelClass]">
|
|
52
|
-
<USkeleton
|
|
63
|
+
<USkeleton
|
|
64
|
+
:aria-label="t('dms.a11y.loading')"
|
|
65
|
+
class="inline-block h-2 w-1/2 align-middle"
|
|
66
|
+
/>
|
|
53
67
|
</div>
|
|
54
68
|
<div class="mt-0.5">
|
|
55
|
-
<USkeleton
|
|
69
|
+
<USkeleton
|
|
70
|
+
:aria-label="t('dms.a11y.loading')"
|
|
71
|
+
class="inline-block h-2.5 w-4/5 align-middle"
|
|
72
|
+
/>
|
|
56
73
|
</div>
|
|
57
74
|
</div>
|
|
58
75
|
</div>
|