@antelopejs/dms 0.7.8 → 0.7.10
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 +45 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/implementations/dms/api-base-url.d.ts +1 -0
- package/dist/implementations/dms/api-base-url.js +6 -0
- package/dist/implementations/dms/api-base-url.js.map +1 -0
- package/dist/implementations/dms/frontend-modules.js +7 -6
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/page.js +8 -2
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/test/attachment-host/capabilities.d.ts +25 -0
- package/dist/test/attachment-host/capabilities.js +53 -1
- package/dist/test/attachment-host/capabilities.js.map +1 -1
- package/dist/test/integration/table-view-capabilities.test.js +13 -0
- package/dist/test/integration/table-view-capabilities.test.js.map +1 -1
- package/dist/test/unit/api-base-url.test.d.ts +1 -0
- package/dist/test/unit/api-base-url.test.js +31 -0
- package/dist/test/unit/api-base-url.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js +106 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -0
- package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/block-parity-options.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/block-parity-options.test.js +46 -0
- package/dist/test/unit/interfaces/dms-base/block-parity-options.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js +26 -0
- package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js +10 -0
- package/dist/test/unit/interfaces/dms-base/data-types/field-types.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/guard.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/guard.test.js +53 -0
- package/dist/test/unit/interfaces/dms-base/guard.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/layout-header-actions.test.js +23 -0
- package/dist/test/unit/interfaces/dms-base/layout-header-actions.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/page-live.test.js +55 -0
- package/dist/test/unit/interfaces/dms-base/page-live.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/period-selector-catalog.test.js +17 -0
- package/dist/test/unit/interfaces/dms-base/period-selector-catalog.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js +8 -1
- package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js +28 -0
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js.map +1 -1
- package/frontend-vue/layers/dms-core/app/build/composables/user/preferencesCookie.ts +149 -0
- package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +321 -0
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +4 -3
- package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +4 -3
- package/frontend-vue/layers/dms-core/app/composables/period/types.ts +3 -1
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +9 -2
- package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +48 -16
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +21 -4
- package/frontend-vue/layers/dms-core/app/composables/user/usePreferences.ts +81 -49
- package/frontend-vue/layers/dms-core/app/types/confirm-dialog.ts +35 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +2 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +2 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +10 -1
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +32 -1
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +13 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +27 -3
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/rowSidePanel.ts +16 -0
- package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +75 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +8 -1
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +13 -2
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +7 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +7 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +7 -4
- package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +59 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +5 -1
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +5 -1
- package/frontend-vue/layers/dms-ui/app/build/components/activity-feed/activityFeedDays.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/build/components/blocks/BlockInlineStatus.vue +67 -0
- package/frontend-vue/layers/dms-ui/app/build/components/blocks/BlockStatus.vue +60 -20
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +15 -8
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +14 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +29 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +76 -28
- package/frontend-vue/layers/dms-ui/app/build/composables/blocks/useBlockItems.ts +26 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/blocks/useGuard.ts +115 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +58 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/secondaryTarget.ts +86 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/useActionConfirm.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +6 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/liveCadence.ts +42 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/liveHolds.ts +66 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +42 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/liveStorage.ts +42 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/missedTally.ts +65 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/pageLiveController.ts +247 -88
- package/frontend-vue/layers/dms-ui/app/build/composables/live/useLivePeriodSpan.ts +33 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +79 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparisonControl.ts +70 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +55 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +41 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +22 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodLocation.ts +13 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +27 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/clearTableFilters.ts +18 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/usePersistedPagination.ts +19 -26
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +66 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +7 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +65 -11
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTablePreferences.ts +149 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +39 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +16 -11
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +39 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +29 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +18 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/sortableColumns.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/fetchNotFound.ts +79 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +46 -1
- package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +5 -4
- package/frontend-vue/layers/dms-ui/app/components/activity/ActivityItem.vue +4 -0
- package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +40 -2
- package/frontend-vue/layers/dms-ui/app/components/blocks/CodeBlock.vue +11 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/KeyValueListBlock.vue +12 -2
- package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +129 -4
- package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +6 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +33 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/ChoiceCards.vue +22 -4
- package/frontend-vue/layers/dms-ui/app/components/guard/Guard.vue +74 -0
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +12 -17
- package/frontend-vue/layers/dms-ui/app/components/list-row/ListRow.vue +15 -0
- package/frontend-vue/layers/dms-ui/app/components/meter/MeterBlock.vue +12 -15
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +104 -16
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +113 -99
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +11 -1
- package/frontend-vue/layers/dms-ui/app/composables/chart/types.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +11 -1
- package/frontend-vue/layers/dms-ui/app/composables/confirm/types.ts +15 -1
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +86 -10
- package/frontend-vue/layers/dms-ui/app/composables/table-view/shortcuts/tableViewShiftF.ts +20 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +12 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +8 -1
- package/frontend-vue/layers/dms-ui/app/composables/table-view/useTableViewEmptyState.ts +55 -0
- package/frontend-vue/layers/dms-ui/app/types/live.ts +47 -1
- package/frontend-vue/layers/dms-ui/app/types/row-action.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/utils/componentId.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/utils/rowClickAction.ts +12 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +13 -4
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +12 -3
- package/frontend-vue/tests/activity-feed-fill-height.test.ts +1 -0
- package/frontend-vue/tests/activity-feed-head.test.ts +140 -0
- package/frontend-vue/tests/block-not-found.test.ts +220 -0
- package/frontend-vue/tests/choice-cards-columns.test.ts +60 -0
- package/frontend-vue/tests/component-id.test.ts +18 -0
- package/frontend-vue/tests/confirm-secondary-action.test.ts +269 -0
- package/frontend-vue/tests/guard.test.ts +242 -0
- package/frontend-vue/tests/header-action-page-query.test.ts +286 -0
- package/frontend-vue/tests/live-toggle.test.ts +87 -1
- package/frontend-vue/tests/page-layout-registration.test.ts +1 -0
- package/frontend-vue/tests/page-live-parity.test.ts +367 -0
- package/frontend-vue/tests/page-realtime-remount.test.ts +185 -0
- package/frontend-vue/tests/period-selector-parity.test.ts +138 -0
- package/frontend-vue/tests/period-selector-url.test.ts +84 -6
- package/frontend-vue/tests/period-url-query.test.ts +52 -0
- package/frontend-vue/tests/preferences-cookie-size.test.ts +311 -0
- package/frontend-vue/tests/row-side-panel.test.ts +109 -10
- package/frontend-vue/tests/table-actions-search-label.test.ts +57 -1
- package/frontend-vue/tests/table-clear-filters.test.ts +99 -1
- package/frontend-vue/tests/table-density-menu.test.ts +4 -3
- package/frontend-vue/tests/table-live-hold.test.ts +95 -5
- package/frontend-vue/tests/table-row-action-labels.test.ts +181 -0
- package/frontend-vue/tests/table-row-hover.test.ts +32 -0
- package/frontend-vue/tests/table-source-period-scope.test.ts +31 -3
- package/frontend-vue/tests/table-view-hidden-filters-page.test.ts +14 -17
- package/frontend-vue/tests/table-view-pinned-rows.test.ts +143 -0
- package/frontend-vue/tests/table-view-sortable-columns.test.ts +45 -0
- package/frontend-vue/tests/table-view-views.test.ts +56 -15
- package/frontend-vue/tests/top-list-card-value-suffix.test.ts +107 -0
- package/frontend-vue/tests/vue-inventory.test.ts +1 -1
- package/package.json +2 -2
- package/skills/dms/SKILL.md +9 -3
- package/skills/dms-module/SKILL.md +5 -0
- package/skills/dms-pages/REFERENCE.md +23 -2
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
defaultSortingState,
|
|
71
71
|
resolveDefaultSortConfig,
|
|
72
72
|
sanitizeSorting,
|
|
73
|
+
sortingOrDefault,
|
|
73
74
|
sortableColumnIds,
|
|
74
75
|
sortValueKind,
|
|
75
76
|
} from "../../build/composables/table-view/utils/sortableColumns";
|
|
@@ -95,8 +96,12 @@ import {
|
|
|
95
96
|
staticQuickFilterItems,
|
|
96
97
|
} from "../../build/composables/table-view/utils/quickFilters";
|
|
97
98
|
import { useNavBadges } from "../../build/composables/navigation/useNavBadges";
|
|
98
|
-
import {
|
|
99
|
+
import {
|
|
100
|
+
resolveRowClickAction,
|
|
101
|
+
rowCustomActions,
|
|
102
|
+
} from "../../utils/rowClickAction";
|
|
99
103
|
import { usePermissionPreview } from "#dms-core/app/build/composables/auth/usePermissionPreview";
|
|
104
|
+
import type { RowActionRule } from "#dms-core/app/types/row-action";
|
|
100
105
|
|
|
101
106
|
import UTable from "../../build/components/table/Table.vue";
|
|
102
107
|
import ExpandedRowDetail from "../../build/components/table-view/ExpandedRowDetail.vue";
|
|
@@ -111,7 +116,9 @@ import type {
|
|
|
111
116
|
} from "../../build/components/table/Table.vue";
|
|
112
117
|
import {
|
|
113
118
|
defineAsyncComponent,
|
|
119
|
+
useTemplateRef,
|
|
114
120
|
type Component,
|
|
121
|
+
type ComponentPublicInstance,
|
|
115
122
|
type WatchOptions,
|
|
116
123
|
type WatchSource,
|
|
117
124
|
} from "vue";
|
|
@@ -121,9 +128,13 @@ import { useGroupedRows } from "../../build/composables/table-view/useGroupedRow
|
|
|
121
128
|
import { useTableFooter } from "../../build/composables/table-view/useTableFooter";
|
|
122
129
|
import { useAccumulatedPages } from "../../build/composables/table-view/useAccumulatedPages";
|
|
123
130
|
import { usePersistedPagination } from "../../build/composables/table-view/usePersistedPagination";
|
|
131
|
+
import { useTablePreferences } from "../../build/composables/table-view/useTablePreferences";
|
|
132
|
+
import type { Ref } from "vue";
|
|
133
|
+
import type { TableField } from "#dms-core/app/build/composables/user/tablePreferences";
|
|
124
134
|
import { useTableRows } from "../../build/composables/table-view/useTableRows";
|
|
125
135
|
import { useRefreshTriggers } from "../../build/composables/blocks/useRefreshTriggers";
|
|
126
136
|
import { useTableLiveHold } from "../../build/composables/table-view/useTableLiveHold";
|
|
137
|
+
import type { TableViewLiveHoldOptions } from "../../types/live";
|
|
127
138
|
import { usePageLive } from "../../composables/live/usePageLive";
|
|
128
139
|
import LiveHoldBanner from "../../build/components/table-view/LiveHoldBanner.vue";
|
|
129
140
|
import { useTableReorder } from "../../build/composables/table-view/useTableReorder";
|
|
@@ -166,6 +177,9 @@ interface RealtimePresenceActor {
|
|
|
166
177
|
|
|
167
178
|
type RealtimePresenceMap = Record<string, RealtimePresenceActor[]>;
|
|
168
179
|
|
|
180
|
+
/** A kept field of the table, its state, and its default. */
|
|
181
|
+
type TableStatePreference = [TableField, Ref<unknown>, unknown];
|
|
182
|
+
|
|
169
183
|
// The backend options of a table view, declared here as well so the
|
|
170
184
|
// component's runtime props never depend on resolving the imported config.
|
|
171
185
|
interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
@@ -173,6 +187,8 @@ interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
|
173
187
|
expandable?: TableViewExpandableConfig;
|
|
174
188
|
/** Placeholder of the search field. */
|
|
175
189
|
searchPlaceholder?: string;
|
|
190
|
+
/** Key opening the search (`defineShortcuts` notation); `shift_f` else. */
|
|
191
|
+
searchShortcut?: string;
|
|
176
192
|
/** One-click dropdown filters of the toolbar. */
|
|
177
193
|
quickFilters?: TableViewQuickFilter[];
|
|
178
194
|
/** Rows per page while the user picked none. */
|
|
@@ -183,6 +199,8 @@ interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
|
183
199
|
source?: TableViewSourceConfig;
|
|
184
200
|
/** Rows ordered by hand on a number column (backend `reorder`). */
|
|
185
201
|
reorder?: TableViewReorderConfig;
|
|
202
|
+
/** Rows listed first whatever the sort (backend `pinnedRows`). */
|
|
203
|
+
pinnedRows?: RowActionRule;
|
|
186
204
|
/** Footer texts and figures: row count, hint, summaries and legend. */
|
|
187
205
|
footer?: TableViewFooter;
|
|
188
206
|
/** What the empty body says, per reason it is empty. */
|
|
@@ -196,7 +214,7 @@ interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
|
196
214
|
/** Topics whose events make the table read its rows again. */
|
|
197
215
|
realtimeTopic?: string | string[];
|
|
198
216
|
/** Keeps the rows while the live page is paused (backend `liveHold`). */
|
|
199
|
-
liveHold?: boolean;
|
|
217
|
+
liveHold?: boolean | TableViewLiveHoldOptions;
|
|
200
218
|
}
|
|
201
219
|
|
|
202
220
|
const props = defineProps<TableViewProps<T>>();
|
|
@@ -227,14 +245,10 @@ const {
|
|
|
227
245
|
defaultDisplay,
|
|
228
246
|
} = useTableViewConfig<T>(props);
|
|
229
247
|
|
|
230
|
-
const
|
|
248
|
+
const tablePreferences = useTablePreferences(componentId, pageId);
|
|
231
249
|
const { sendComponentEvent } = useComponentEvent(componentId);
|
|
232
250
|
const { processI18n } = useTranslation();
|
|
233
251
|
|
|
234
|
-
const getTablePreferenceKey = (suffix: string) => {
|
|
235
|
-
return `tables.${componentId}.${pageId}.${suffix}`;
|
|
236
|
-
};
|
|
237
|
-
|
|
238
252
|
const kanbanEligibleColumns = allColumns.filter(isEligibleKanbanColumn);
|
|
239
253
|
const kanbanGroupByOptions = kanbanEligibleColumns.map((column) => ({
|
|
240
254
|
label: processI18n(column.header),
|
|
@@ -257,8 +271,8 @@ const groupedOptions = optionsForDisplay(GROUPED_DISPLAY_ID) as
|
|
|
257
271
|
|
|
258
272
|
// Stored under the legacy "viewMode" key (free migration of "table"/"kanban");
|
|
259
273
|
// clamped to the offered set so a stale/removed id never renders an un-offered display.
|
|
260
|
-
const persistedDisplay =
|
|
261
|
-
|
|
274
|
+
const persistedDisplay = tablePreferences.get<string>(
|
|
275
|
+
"viewMode",
|
|
262
276
|
defaultDisplay,
|
|
263
277
|
);
|
|
264
278
|
// A default display that draws its whole interface (no table chrome, hence no
|
|
@@ -335,30 +349,24 @@ const activeDisplayComponent = computed<Component | string | undefined>(() => {
|
|
|
335
349
|
return STATIC_DISPLAY_COMPONENTS[id];
|
|
336
350
|
});
|
|
337
351
|
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
getTablePreferenceKey("kanbanGroupBy"),
|
|
341
|
-
kanbanOptions?.groupByField ?? "",
|
|
342
|
-
),
|
|
343
|
-
);
|
|
352
|
+
const defaultKanbanGroupBy = kanbanOptions?.groupByField ?? "";
|
|
353
|
+
const kanbanGroupBy = ref<string>(defaultKanbanGroupBy);
|
|
344
354
|
// A saved preference may point to a column that no longer exists or is no
|
|
345
355
|
// longer eligible after a backend config change.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
|
|
356
|
+
tablePreferences.restore(
|
|
357
|
+
"kanbanGroupBy",
|
|
358
|
+
kanbanGroupBy,
|
|
359
|
+
(field) =>
|
|
360
|
+
!kanbanOptions ||
|
|
361
|
+
kanbanEligibleColumns.some((c) => c.accessorKey === field),
|
|
362
|
+
);
|
|
352
363
|
|
|
353
364
|
const DEFAULT_DENSITY = "default";
|
|
354
365
|
// The module's density is the default; the user's pick in the ⋯ menu is kept
|
|
355
366
|
// with the rest of the table's state.
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
props.density ?? DEFAULT_DENSITY,
|
|
360
|
-
),
|
|
361
|
-
);
|
|
367
|
+
const defaultDensity = props.density ?? DEFAULT_DENSITY;
|
|
368
|
+
const density = ref<"default" | "compact">(defaultDensity);
|
|
369
|
+
tablePreferences.restore("density", density);
|
|
362
370
|
|
|
363
371
|
const DEFAULT_PAGE_SIZE = 10;
|
|
364
372
|
const DEFAULT_PAGINATION: PaginationState = {
|
|
@@ -403,8 +411,7 @@ const routeParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
|
403
411
|
// A hidden filter's value changed with the URL, in place or through a
|
|
404
412
|
// remount: the page reached among the previous rows lists from page 1.
|
|
405
413
|
const paginationState = usePersistedPagination({
|
|
406
|
-
preferences:
|
|
407
|
-
preferenceKey: getTablePreferenceKey,
|
|
414
|
+
preferences: tablePreferences,
|
|
408
415
|
defaults: DEFAULT_PAGINATION,
|
|
409
416
|
scope: () => [queryParamHiddenFilters.value, routeParamHiddenFilters.value],
|
|
410
417
|
});
|
|
@@ -446,18 +453,14 @@ const groupedGroupByOptions = groupedOptions
|
|
|
446
453
|
value: column.accessorKey,
|
|
447
454
|
}))
|
|
448
455
|
: [];
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
456
|
+
const defaultGroupedGroupBy = groupedOptions?.groupByField ?? "";
|
|
457
|
+
const groupedGroupBy = ref<string>(defaultGroupedGroupBy);
|
|
458
|
+
tablePreferences.restore(
|
|
459
|
+
"groupedGroupBy",
|
|
460
|
+
groupedGroupBy,
|
|
461
|
+
(field) =>
|
|
462
|
+
!groupedOptions || groupedGroupByOptions.some((o) => o.value === field),
|
|
454
463
|
);
|
|
455
|
-
if (
|
|
456
|
-
groupedOptions &&
|
|
457
|
-
!groupedGroupByOptions.some((o) => o.value === groupedGroupBy.value)
|
|
458
|
-
) {
|
|
459
|
-
groupedGroupBy.value = groupedOptions.groupByField;
|
|
460
|
-
}
|
|
461
464
|
const activeGroupedOptions = computed<TableViewGroupedConfig | undefined>(
|
|
462
465
|
() =>
|
|
463
466
|
groupedOptions &&
|
|
@@ -484,19 +487,14 @@ const defaultSortKind = tableDefaultSort
|
|
|
484
487
|
)
|
|
485
488
|
: undefined;
|
|
486
489
|
const sorting = ref<SortingState>(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
getTablePreferenceKey("sorting"),
|
|
490
|
-
defaultSortState,
|
|
491
|
-
),
|
|
490
|
+
sortingOrDefault(
|
|
491
|
+
tablePreferences.get<SortingState>("sorting", defaultSortState),
|
|
492
492
|
declaredSortableIds,
|
|
493
|
+
defaultSortState,
|
|
493
494
|
),
|
|
494
495
|
);
|
|
495
496
|
|
|
496
|
-
const savedFilters =
|
|
497
|
-
getTablePreferenceKey("columnFilters"),
|
|
498
|
-
[],
|
|
499
|
-
);
|
|
497
|
+
const savedFilters = tablePreferences.get<TableFilter[]>("columnFilters", []);
|
|
500
498
|
|
|
501
499
|
const buildPinnedFilters = (): TableFilter[] =>
|
|
502
500
|
(defaultFilters || []).map((def) => {
|
|
@@ -518,26 +516,17 @@ const initialFilters = [...buildPinnedFilters(), ...manualFilters];
|
|
|
518
516
|
|
|
519
517
|
const columnFilters = ref<TableFilter[]>(initialFilters);
|
|
520
518
|
const columnVisibility = ref<VisibilityState>(
|
|
521
|
-
|
|
522
|
-
getTablePreferenceKey("columnVisibility"),
|
|
523
|
-
initialVisibility,
|
|
524
|
-
),
|
|
525
|
-
);
|
|
526
|
-
const columnSizing = ref<ColumnSizingState>(
|
|
527
|
-
getPreference<ColumnSizingState>(getTablePreferenceKey("columnSizing"), {}),
|
|
528
|
-
);
|
|
529
|
-
const columnOrder = ref<string[]>(
|
|
530
|
-
getPreference<string[]>(getTablePreferenceKey("columnOrder"), []),
|
|
519
|
+
tablePreferences.get<VisibilityState>("columnVisibility", initialVisibility),
|
|
531
520
|
);
|
|
521
|
+
const columnSizing = ref<ColumnSizingState>({});
|
|
522
|
+
tablePreferences.restore("columnSizing", columnSizing);
|
|
523
|
+
const columnOrder = ref<string[]>([]);
|
|
524
|
+
tablePreferences.restore("columnOrder", columnOrder, Array.isArray);
|
|
532
525
|
const hasEmptyDefaultFilter = (defaultFilters || []).some(
|
|
533
526
|
(f) => f.value === undefined || f.value === null || f.value === "",
|
|
534
527
|
);
|
|
535
|
-
const filtersRowOpen = ref<boolean>(
|
|
536
|
-
|
|
537
|
-
getTablePreferenceKey("filtersOpen"),
|
|
538
|
-
hasEmptyDefaultFilter,
|
|
539
|
-
),
|
|
540
|
-
);
|
|
528
|
+
const filtersRowOpen = ref<boolean>(hasEmptyDefaultFilter);
|
|
529
|
+
tablePreferences.restore("filtersOpen", filtersRowOpen);
|
|
541
530
|
const globalFilter = shallowRef<string>();
|
|
542
531
|
const globalFilterDebounced = refDebounced(
|
|
543
532
|
globalFilter,
|
|
@@ -592,7 +581,7 @@ const {
|
|
|
592
581
|
density,
|
|
593
582
|
pagination: paginationState,
|
|
594
583
|
},
|
|
595
|
-
|
|
584
|
+
preferences: tablePreferences,
|
|
596
585
|
urlScope,
|
|
597
586
|
});
|
|
598
587
|
|
|
@@ -604,8 +593,7 @@ const TAB_URL_KEY = "tab";
|
|
|
604
593
|
const urlTab = readTableUrlKey(useDmsRoute().query, urlScope, TAB_URL_KEY);
|
|
605
594
|
// A tab named by the URL wins over the one kept from the last visit.
|
|
606
595
|
const activeTabId = ref<string>(
|
|
607
|
-
urlTab.value ??
|
|
608
|
-
getPreference<string>(getTablePreferenceKey("activeTab"), ALL_TAB_ID),
|
|
596
|
+
urlTab.value ?? tablePreferences.get<string>("activeTab", ALL_TAB_ID),
|
|
609
597
|
);
|
|
610
598
|
|
|
611
599
|
interface ResolvedTab {
|
|
@@ -674,6 +662,8 @@ const tableRows = useTableRows<T>({
|
|
|
674
662
|
routeParams: props.routeParams,
|
|
675
663
|
routeQuery: route.query as Record<string, unknown>,
|
|
676
664
|
}),
|
|
665
|
+
// Groups keep their rows together: the grouped display pins nothing.
|
|
666
|
+
pinnedRows: () => (isGroupedDisplay.value ? undefined : props.pinnedRows),
|
|
677
667
|
});
|
|
678
668
|
// Quick filters: dropdowns of the toolbar over a column's values (a select's
|
|
679
669
|
// items, a boolean, the rows a relation points to), writing that column's
|
|
@@ -819,12 +809,26 @@ const archiveQuery = computed(() =>
|
|
|
819
809
|
archiveMode ? { showArchived: showArchived.value } : {},
|
|
820
810
|
);
|
|
821
811
|
|
|
812
|
+
// The row a side panel has open, which the grid highlights.
|
|
813
|
+
const rowSidePanel = useRowSidePanelContent();
|
|
814
|
+
const rowSidePanelOwner = rowSidePanelOwnerKey(pageId!, componentId!);
|
|
815
|
+
const openRowId = computed(() =>
|
|
816
|
+
rowSidePanel.value?.ownerKey === rowSidePanelOwner
|
|
817
|
+
? rowSidePanel.value.rowKey
|
|
818
|
+
: undefined,
|
|
819
|
+
);
|
|
820
|
+
|
|
822
821
|
// The gate is set up with the other refresh triggers below; the hold only
|
|
823
822
|
// reads it once the table is on screen.
|
|
823
|
+
const tableRoot = useTemplateRef<ComponentPublicInstance>("tableRoot");
|
|
824
824
|
const liveHold = useTableLiveHold({
|
|
825
|
-
|
|
825
|
+
liveHold: props.liveHold,
|
|
826
826
|
isSource: tableRows.isSource,
|
|
827
827
|
gate: () => liveGate,
|
|
828
|
+
signals: {
|
|
829
|
+
isRowOpen: () => openRowId.value !== undefined,
|
|
830
|
+
tableElement: () => tableRoot.value?.$el ?? null,
|
|
831
|
+
},
|
|
828
832
|
});
|
|
829
833
|
|
|
830
834
|
const { data, status, error, refresh } = await useTableListData<T>({
|
|
@@ -878,18 +882,27 @@ watch(listLoadError, (message) => {
|
|
|
878
882
|
});
|
|
879
883
|
|
|
880
884
|
// Whatever sets the sort (a header, the sort menu, a pasted table config), the
|
|
881
|
-
// route only ever receives a sort it accepts
|
|
885
|
+
// route only ever receives a sort it accepts, and a cleared sort goes back to
|
|
886
|
+
// the default one rather than being remembered empty. A new order lists from page 1:
|
|
882
887
|
// the page reached in the previous order shows unrelated rows in the new one.
|
|
883
888
|
// Synchronous, so the list is queried once, with both changes.
|
|
884
889
|
watch(
|
|
885
890
|
sorting,
|
|
886
891
|
(next, previous) => {
|
|
887
|
-
const sanitized =
|
|
892
|
+
const sanitized = sortingOrDefault(
|
|
893
|
+
next,
|
|
894
|
+
sortableIds.value,
|
|
895
|
+
defaultSortState,
|
|
896
|
+
);
|
|
888
897
|
if (!isSameSorting(sanitized, next)) {
|
|
889
898
|
sorting.value = sanitized;
|
|
890
899
|
return;
|
|
891
900
|
}
|
|
892
|
-
const before =
|
|
901
|
+
const before = sortingOrDefault(
|
|
902
|
+
previous,
|
|
903
|
+
sortableIds.value,
|
|
904
|
+
defaultSortState,
|
|
905
|
+
);
|
|
893
906
|
if (
|
|
894
907
|
!isSameSorting(sanitized, before) &&
|
|
895
908
|
paginationState.value.pageIndex !== 0
|
|
@@ -1235,6 +1248,7 @@ const {
|
|
|
1235
1248
|
handleCustomButton,
|
|
1236
1249
|
handleCustomRowAction,
|
|
1237
1250
|
handleBulkCustomAction,
|
|
1251
|
+
handleMissingRow,
|
|
1238
1252
|
runConfirmedBuiltIn,
|
|
1239
1253
|
} = useTableRowActions<T>({
|
|
1240
1254
|
api: $authFetch,
|
|
@@ -1665,25 +1679,17 @@ const openRecordFromUrl = async () => {
|
|
|
1665
1679
|
const id = deepLinkAction ? readRecordId(route.query, urlScope) : undefined;
|
|
1666
1680
|
if (!deepLinkAction || !id) return;
|
|
1667
1681
|
await until(isListLoading).toBe(false);
|
|
1668
|
-
const row = await resolveDeepLinkedRow(id, {
|
|
1682
|
+
const { row, isMissing } = await resolveDeepLinkedRow(id, {
|
|
1669
1683
|
listed: shownResults.value ?? undefined,
|
|
1670
1684
|
idOf: getRowId,
|
|
1671
1685
|
getRow: tableRows.getRow,
|
|
1672
1686
|
});
|
|
1673
1687
|
if (row) handleCustomRowAction(deepLinkAction, row as Data);
|
|
1688
|
+
else if (isMissing) handleMissingRow(deepLinkAction, id);
|
|
1674
1689
|
};
|
|
1675
1690
|
|
|
1676
1691
|
const selectedIds = computed(() => selectedRowIds(rowSelect.value));
|
|
1677
1692
|
|
|
1678
|
-
// The row a side panel has open, which the grid highlights.
|
|
1679
|
-
const rowSidePanel = useRowSidePanelContent();
|
|
1680
|
-
const rowSidePanelOwner = rowSidePanelOwnerKey(pageId!, componentId!);
|
|
1681
|
-
const openRowId = computed(() =>
|
|
1682
|
-
rowSidePanel.value?.ownerKey === rowSidePanelOwner
|
|
1683
|
-
? rowSidePanel.value.rowKey
|
|
1684
|
-
: undefined,
|
|
1685
|
-
);
|
|
1686
|
-
|
|
1687
1693
|
const isRowActionEnabled = (
|
|
1688
1694
|
config: boolean | RowActionConfig | undefined,
|
|
1689
1695
|
item: T,
|
|
@@ -1778,6 +1784,11 @@ const displayRowActions = {
|
|
|
1778
1784
|
isRowActionEnabled(tableProps.value.rowActions?.edit, item),
|
|
1779
1785
|
canDeleteRow: (item: T) =>
|
|
1780
1786
|
isRowActionEnabled(tableProps.value.rowActions?.delete, item),
|
|
1787
|
+
customFor: (item: T) =>
|
|
1788
|
+
rowCustomActions(
|
|
1789
|
+
tableProps.value.rowActions?.custom,
|
|
1790
|
+
item as Record<string, unknown>,
|
|
1791
|
+
),
|
|
1781
1792
|
};
|
|
1782
1793
|
|
|
1783
1794
|
// The TanStack instance is only available as a #body slot prop; captured here so
|
|
@@ -1914,24 +1925,25 @@ defineShortcuts(
|
|
|
1914
1925
|
}),
|
|
1915
1926
|
);
|
|
1916
1927
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1928
|
+
// Each kept field with its default: a value back to it is forgotten.
|
|
1929
|
+
const tableStatePreferences: TableStatePreference[] = [
|
|
1930
|
+
["sorting", sorting, defaultSortState],
|
|
1931
|
+
["columnFilters", columnFilters, []],
|
|
1932
|
+
["columnVisibility", columnVisibility, initialVisibility],
|
|
1933
|
+
["filtersOpen", filtersRowOpen, hasEmptyDefaultFilter],
|
|
1934
|
+
["activeTab", activeTabId, ALL_TAB_ID],
|
|
1935
|
+
["viewMode", activeDisplayId, defaultDisplay],
|
|
1936
|
+
["columnOrder", columnOrder, []],
|
|
1937
|
+
["kanbanGroupBy", kanbanGroupBy, defaultKanbanGroupBy],
|
|
1938
|
+
["groupedGroupBy", groupedGroupBy, defaultGroupedGroupBy],
|
|
1939
|
+
["density", density, defaultDensity],
|
|
1940
|
+
];
|
|
1941
|
+
|
|
1942
|
+
tableStatePreferences.forEach(([field, state, defaultValue]) => {
|
|
1931
1943
|
watch(
|
|
1932
|
-
|
|
1944
|
+
state,
|
|
1933
1945
|
(newValue) => {
|
|
1934
|
-
|
|
1946
|
+
tablePreferences.set(field, newValue, defaultValue);
|
|
1935
1947
|
},
|
|
1936
1948
|
{ deep: true },
|
|
1937
1949
|
);
|
|
@@ -1943,7 +1955,7 @@ const COLUMN_SIZING_PERSIST_DEBOUNCE_MS = 500;
|
|
|
1943
1955
|
let pendingColumnSizingWrite = false;
|
|
1944
1956
|
|
|
1945
1957
|
const persistColumnSizing = () => {
|
|
1946
|
-
|
|
1958
|
+
tablePreferences.set("columnSizing", columnSizing.value, {});
|
|
1947
1959
|
pendingColumnSizingWrite = false;
|
|
1948
1960
|
};
|
|
1949
1961
|
|
|
@@ -2022,6 +2034,7 @@ onMounted(() => {
|
|
|
2022
2034
|
|
|
2023
2035
|
<template>
|
|
2024
2036
|
<UTable
|
|
2037
|
+
ref="tableRoot"
|
|
2025
2038
|
v-bind="tableProps"
|
|
2026
2039
|
v-model:pagination="paginationState"
|
|
2027
2040
|
v-model:global-filter="globalFilter"
|
|
@@ -2045,6 +2058,7 @@ onMounted(() => {
|
|
|
2045
2058
|
:reorder="reorderState"
|
|
2046
2059
|
:views-placement="viewsLayout === 'menu' ? 'header' : 'band'"
|
|
2047
2060
|
:search-placeholder="props.searchPlaceholder"
|
|
2061
|
+
:search-shortcut="props.searchShortcut"
|
|
2048
2062
|
:quick-filters="resolvedQuickFilters"
|
|
2049
2063
|
:footer="resolvedFooter"
|
|
2050
2064
|
:empty-states="props.emptyStates"
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ValuePrecision,
|
|
23
23
|
} from "../../composables/chart/types";
|
|
24
24
|
import type { DefaultComponentProps } from "../../../../dms-core/app/types/component";
|
|
25
|
+
import { useComposedText } from "../../../../dms-core/app/composables/translation/useComposedText";
|
|
25
26
|
|
|
26
27
|
interface Props extends DefaultComponentProps {
|
|
27
28
|
title: string;
|
|
@@ -32,6 +33,8 @@ interface Props extends DefaultComponentProps {
|
|
|
32
33
|
valueFormat?: ValueFormat;
|
|
33
34
|
currencyCode?: string;
|
|
34
35
|
valuePrecision?: ValuePrecision;
|
|
36
|
+
/** Unit after each formatted value, space included (" ms"); `$` key or literal. */
|
|
37
|
+
valueSuffix?: string;
|
|
35
38
|
showRank?: boolean;
|
|
36
39
|
highlightTopN?: number;
|
|
37
40
|
rankColor?: UiColor;
|
|
@@ -100,6 +103,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
100
103
|
|
|
101
104
|
const { locale, t } = useI18n();
|
|
102
105
|
const { processI18n } = useTranslation();
|
|
106
|
+
const { processText } = useComposedText();
|
|
103
107
|
useComponentEvent(props.componentId);
|
|
104
108
|
const { state: watchState } = useWatch(
|
|
105
109
|
props.watchActions || [],
|
|
@@ -167,14 +171,20 @@ function barClass(index: number): string {
|
|
|
167
171
|
return `${rankFillClass.value} ${QUIET_BAR_OPACITY_CLASS}`;
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
const valueSuffixText = computed(() =>
|
|
175
|
+
props.valueSuffix ? processI18n(props.valueSuffix) : "",
|
|
176
|
+
);
|
|
177
|
+
|
|
170
178
|
function formattedValue(item: TopListItem): string {
|
|
171
|
-
return
|
|
179
|
+
if (item.valueLabel) return processText(item.valueLabel);
|
|
180
|
+
const formatted = formatValue(
|
|
172
181
|
item.value,
|
|
173
182
|
props.valueFormat,
|
|
174
183
|
locale.value,
|
|
175
184
|
props.currencyCode,
|
|
176
185
|
props.valuePrecision,
|
|
177
186
|
);
|
|
187
|
+
return `${formatted}${valueSuffixText.value}`;
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
function sparklineAccentFor(item: TopListItem): string {
|
|
@@ -106,6 +106,8 @@ export interface TopListItem {
|
|
|
106
106
|
title: BlockText;
|
|
107
107
|
description?: BlockText;
|
|
108
108
|
value: number;
|
|
109
|
+
/** The value as written, in place of the formatted one. */
|
|
110
|
+
valueLabel?: BlockText;
|
|
109
111
|
delta?: number | null;
|
|
110
112
|
sparkline?: number[];
|
|
111
113
|
icon?: string;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
resolveUrlVariables,
|
|
17
17
|
} from "../../build/utils/urlVariables";
|
|
18
18
|
import { useRefreshTriggers } from "../../build/composables/blocks/useRefreshTriggers";
|
|
19
|
+
import { readFetchNotFound } from "../../build/utils/fetchNotFound";
|
|
19
20
|
|
|
20
21
|
export interface UseChartFetchOptions<T> {
|
|
21
22
|
/**
|
|
@@ -41,6 +42,10 @@ export interface UseChartFetchReturn<T> {
|
|
|
41
42
|
* (period, watched state, realtime event) until the new answer lands.
|
|
42
43
|
*/
|
|
43
44
|
isLoading: Ref<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Why the last request failed. A 404 also clears `data`: read it with
|
|
47
|
+
* `readFetchNotFound` to draw a not-found state rather than a load error.
|
|
48
|
+
*/
|
|
44
49
|
error: Ref<unknown>;
|
|
45
50
|
refresh: () => Promise<void>;
|
|
46
51
|
}
|
|
@@ -111,7 +116,12 @@ async function runFetch<T>(
|
|
|
111
116
|
data.value = response;
|
|
112
117
|
} catch (err) {
|
|
113
118
|
if (runner.activeAbort !== controller) return;
|
|
114
|
-
if ((err as { name?: string })?.name
|
|
119
|
+
if ((err as { name?: string })?.name === "AbortError") return;
|
|
120
|
+
error.value = err;
|
|
121
|
+
// A 404 says what the block showed is gone (another tab deleted it, the
|
|
122
|
+
// URL now names another record): its last values must not stay on screen
|
|
123
|
+
// as if they still belonged to it. Any other failure keeps them.
|
|
124
|
+
if (readFetchNotFound(err)) data.value = null;
|
|
115
125
|
} finally {
|
|
116
126
|
// A request a newer one superseded leaves the loading flag to that one:
|
|
117
127
|
// clearing it here would show the empty state while the newer answer is
|
|
@@ -24,7 +24,14 @@ export type ConfirmValues = Record<string, unknown>;
|
|
|
24
24
|
* whose texts are already resolved, plus what only a caller in the browser
|
|
25
25
|
* can give (render functions, handlers).
|
|
26
26
|
*/
|
|
27
|
-
export interface ConfirmOptions
|
|
27
|
+
export interface ConfirmOptions
|
|
28
|
+
extends Omit<ConfirmDialog, "params" | "icon" | "secondaryAction"> {
|
|
29
|
+
/**
|
|
30
|
+
* A button next to cancel and confirm that leaves the dialog open (a
|
|
31
|
+
* download before removing): its `run` is awaited, the button loading,
|
|
32
|
+
* and a rejection is shown in the dialog's alert.
|
|
33
|
+
*/
|
|
34
|
+
secondaryAction?: ConfirmSecondaryAction;
|
|
28
35
|
/**
|
|
29
36
|
* The button focused on open (`"cancel"` for a dialog whose safe answer
|
|
30
37
|
* is to stay); left out, the dialog's first focusable element.
|
|
@@ -63,6 +70,13 @@ export interface ConfirmOptions extends Omit<ConfirmDialog, "params" | "icon"> {
|
|
|
63
70
|
) => Promise<void | boolean | ConfirmPartialOutcome>;
|
|
64
71
|
}
|
|
65
72
|
|
|
73
|
+
/** A secondary button of the confirm modal: its text resolved, its handler. */
|
|
74
|
+
export interface ConfirmSecondaryAction {
|
|
75
|
+
label: string;
|
|
76
|
+
icon?: string;
|
|
77
|
+
run: () => Promise<void> | void;
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
/** A summary shown in the confirm modal's alert: a title and its detail. */
|
|
67
81
|
export interface ConfirmNotice {
|
|
68
82
|
title: string;
|