@revenexx/studio-shared 0.3.1 → 0.3.3
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/dist/module.d.mts +4 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +9 -0
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.d.vue.ts +15 -0
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue +39 -0
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue.d.ts +15 -0
- package/dist/runtime/components/Dashboard/DashboardControls.d.vue.ts +57 -0
- package/dist/runtime/components/Dashboard/DashboardControls.vue +209 -0
- package/dist/runtime/components/Dashboard/DashboardControls.vue.d.ts +57 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.d.vue.ts +16 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.vue +87 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.vue.d.ts +16 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.d.vue.ts +18 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.vue +61 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.vue.d.ts +18 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.d.vue.ts +14 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue +100 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue.d.ts +14 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.d.vue.ts +27 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.vue +155 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.vue.d.ts +27 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.d.vue.ts +59 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.vue +79 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.vue.d.ts +59 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.d.vue.ts +42 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue +153 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue.d.ts +42 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.d.vue.ts +27 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue +80 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue.d.ts +27 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.d.vue.ts +6 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.vue +30 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.vue.d.ts +6 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.d.vue.ts +8 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.vue +13 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.vue.d.ts +8 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.d.vue.ts +23 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.vue +140 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.vue.d.ts +23 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.d.vue.ts +22 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.vue +124 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.vue.d.ts +22 -0
- package/dist/runtime/components/DataTable/DataTable.d.vue.ts +39 -3
- package/dist/runtime/components/DataTable/DataTable.vue +306 -32
- package/dist/runtime/components/DataTable/DataTable.vue.d.ts +39 -3
- package/dist/runtime/components/DataTable/FacetChips.d.vue.ts +16 -0
- package/dist/runtime/components/DataTable/FacetChips.vue +72 -0
- package/dist/runtime/components/DataTable/FacetChips.vue.d.ts +16 -0
- package/dist/runtime/components/DataTable/FacetPanel.d.vue.ts +20 -0
- package/dist/runtime/components/DataTable/FacetPanel.vue +359 -0
- package/dist/runtime/components/DataTable/FacetPanel.vue.d.ts +20 -0
- package/dist/runtime/components/DataTable/SavedViewsMenu.d.vue.ts +5 -1
- package/dist/runtime/components/DataTable/SavedViewsMenu.vue +83 -63
- package/dist/runtime/components/DataTable/SavedViewsMenu.vue.d.ts +5 -1
- package/dist/runtime/components/DataTable/SortMenu.d.vue.ts +5 -1
- package/dist/runtime/components/DataTable/SortMenu.vue +40 -12
- package/dist/runtime/components/DataTable/SortMenu.vue.d.ts +5 -1
- package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +2 -2
- package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +2 -2
- package/dist/runtime/components/Media/MediaUploader.d.vue.ts +2 -2
- package/dist/runtime/components/Media/MediaUploader.vue.d.ts +2 -2
- package/dist/runtime/components/Shared/SortHeader.vue +6 -2
- package/dist/runtime/components/Shared/TabBar.d.vue.ts +23 -7
- package/dist/runtime/components/Shared/TabBar.vue +3 -2
- package/dist/runtime/components/Shared/TabBar.vue.d.ts +23 -7
- package/dist/runtime/composables/useDashboards.d.ts +27 -0
- package/dist/runtime/composables/useDashboards.js +143 -0
- package/dist/runtime/composables/useSavedViews.d.ts +44 -0
- package/dist/runtime/composables/useSavedViews.js +44 -0
- package/dist/runtime/composables/useStickyListView.d.ts +60 -0
- package/dist/runtime/composables/useStickyListView.js +32 -0
- package/dist/runtime/lib/canonicalJson.d.ts +43 -0
- package/dist/runtime/lib/canonicalJson.js +38 -0
- package/dist/runtime/lib/dashboardIcons.d.ts +4 -0
- package/dist/runtime/lib/dashboardIcons.js +50 -0
- package/dist/runtime/lib/facets.d.ts +200 -0
- package/dist/runtime/lib/facets.js +171 -0
- package/dist/runtime/lib/gridTracks.d.ts +55 -0
- package/dist/runtime/lib/gridTracks.js +29 -0
- package/dist/runtime/lib/widgetRegistry.d.ts +124 -0
- package/dist/runtime/lib/widgetRegistry.js +66 -0
- package/dist/runtime/types/dashboard.d.ts +95 -0
- package/dist/runtime/types/dashboard.js +2 -0
- package/dist/types.d.mts +8 -0
- package/package.json +4 -3
|
@@ -19,7 +19,10 @@ import {
|
|
|
19
19
|
import { orderColumnsBy } from "../../lib/columnOrder";
|
|
20
20
|
import { initiallyHidden, visibleColumnsOf } from "../../lib/columnVisibility";
|
|
21
21
|
import { countByValue, matchesFilter } from "../../lib/dataTableFilters";
|
|
22
|
+
import { gridTemplateColumns } from "../../lib/gridTracks";
|
|
22
23
|
import SortHeader from "../Shared/SortHeader.vue";
|
|
24
|
+
import FacetChips from "./FacetChips.vue";
|
|
25
|
+
import FacetPanel from "./FacetPanel.vue";
|
|
23
26
|
import FilterMenu from "./FilterMenu.vue";
|
|
24
27
|
import SavedViewsMenu from "./SavedViewsMenu.vue";
|
|
25
28
|
import SortMenu from "./SortMenu.vue";
|
|
@@ -31,10 +34,12 @@ const props = defineProps({
|
|
|
31
34
|
columnsMenu: { type: Boolean, required: false, default: true },
|
|
32
35
|
filter: { type: [Boolean, Object], required: false, default: false },
|
|
33
36
|
filterTabs: { type: [Object, Array], required: false },
|
|
37
|
+
facets: { type: Array, required: false, default: () => [] },
|
|
38
|
+
chipFacetLabels: { type: Boolean, required: false, default: false },
|
|
34
39
|
savedViews: { type: [Object, null], required: false, default: null },
|
|
35
40
|
viewModes: { type: [Array, null], required: false, default: null },
|
|
36
41
|
rail: { type: [Boolean, Object], required: false, default: false },
|
|
37
|
-
scrollBody: { type: [Boolean, Object], required: false },
|
|
42
|
+
scrollBody: { type: [Boolean, String, Object], required: false },
|
|
38
43
|
pagination: { type: [Boolean, Object], required: false, default: true },
|
|
39
44
|
defaultSort: { type: Object, required: false },
|
|
40
45
|
serverMode: { type: Boolean, required: false, default: false },
|
|
@@ -56,6 +61,8 @@ const sort = defineModel("sort", { type: Object, ...{
|
|
|
56
61
|
const page = defineModel("page", { type: Number, ...{ default: 0 } });
|
|
57
62
|
const viewMode = defineModel("viewMode", { type: String, ...{ default: "list" } });
|
|
58
63
|
const railOpen = defineModel("railOpen", { type: Boolean, ...{ default: true } });
|
|
64
|
+
const facetSelection = defineModel("facetSelection", { type: Object, ...{ default: () => ({}) } });
|
|
65
|
+
const panelFacets = computed(() => props.facets);
|
|
59
66
|
if (props.defaultSort && sort.value.column == null) {
|
|
60
67
|
sort.value = { column: props.defaultSort.column, direction: props.defaultSort.direction };
|
|
61
68
|
}
|
|
@@ -224,23 +231,30 @@ function toggleSort(column) {
|
|
|
224
231
|
}
|
|
225
232
|
sort.value = { column: null, direction: null };
|
|
226
233
|
}
|
|
234
|
+
function ariaSortFor(col) {
|
|
235
|
+
if (!col.sortable) return void 0;
|
|
236
|
+
if (sort.value.column !== col.key) return "none";
|
|
237
|
+
return sort.value.direction === "desc" ? "descending" : "ascending";
|
|
238
|
+
}
|
|
227
239
|
function rowKeyFor(row) {
|
|
228
240
|
if (typeof props.rowKey === "function") return props.rowKey(row);
|
|
229
241
|
return row[props.rowKey];
|
|
230
242
|
}
|
|
231
|
-
const gridTemplate = computed(() => {
|
|
232
|
-
|
|
233
|
-
return `grid-template-columns: ${tracks.join(" ")};`;
|
|
234
|
-
});
|
|
243
|
+
const gridTemplate = computed(() => `grid-template-columns: ${gridTemplateColumns(visibleColumns.value.map((c) => c.width))};`);
|
|
244
|
+
const SUBGRID_ROW = "grid-column: 1 / -1; grid-template-columns: subgrid;";
|
|
235
245
|
const placeholder = computed(
|
|
236
246
|
() => typeof props.search === "object" ? props.search.placeholder ?? t("shared.datatable.toolbar.search.placeholder.default") : t("shared.datatable.toolbar.search.placeholder.default")
|
|
237
247
|
);
|
|
248
|
+
const searchLabel = computed(() => {
|
|
249
|
+
const configured = typeof props.search === "object" ? props.search.label ?? props.search.placeholder : void 0;
|
|
250
|
+
return configured ?? t("shared.datatable.toolbar.search.aria.label");
|
|
251
|
+
});
|
|
238
252
|
const slots = useSlots();
|
|
239
253
|
const showToolbar = computed(
|
|
240
254
|
() => props.search || props.columnsMenu || props.filter || !!props.filterTabs || !!props.savedViews || !!props.viewModes?.length || !!props.rail || !!slots.actions || !!slots.scope || !!slots["toolbar-left"] || !!slots["toolbar-right"]
|
|
241
255
|
);
|
|
242
256
|
const railLabel = computed(
|
|
243
|
-
() => (typeof props.rail === "object" ? props.rail.label : void 0) ??
|
|
257
|
+
() => (typeof props.rail === "object" ? props.rail.label : void 0) ?? null
|
|
244
258
|
);
|
|
245
259
|
const showTable = computed(() => !props.viewModes?.length || viewMode.value === "list");
|
|
246
260
|
const sortableColumns = computed(() => props.columns.filter((c) => c.sortable));
|
|
@@ -248,8 +262,121 @@ const showSortMenu = computed(() => !showTable.value && sortableColumns.value.le
|
|
|
248
262
|
function cellIconFor(col) {
|
|
249
263
|
return col.cellIcon ?? null;
|
|
250
264
|
}
|
|
265
|
+
const scrollEl = ref(null);
|
|
266
|
+
const footerEl = ref(null);
|
|
267
|
+
const fitHeight = ref(null);
|
|
268
|
+
const MIN_FIT_PX = 220;
|
|
269
|
+
const BOTTOM_GAP_PX = 16;
|
|
270
|
+
function scrollParent(el) {
|
|
271
|
+
let node = el.parentElement;
|
|
272
|
+
while (node && node !== document.body) {
|
|
273
|
+
const overflow = getComputedStyle(node).overflowY;
|
|
274
|
+
if (overflow === "auto" || overflow === "scroll") return node;
|
|
275
|
+
node = node.parentElement;
|
|
276
|
+
}
|
|
277
|
+
return document.documentElement;
|
|
278
|
+
}
|
|
279
|
+
function measureFit() {
|
|
280
|
+
if (props.scrollBody !== "fit" || !import.meta.client) return;
|
|
281
|
+
const el = scrollEl.value;
|
|
282
|
+
if (!el) return;
|
|
283
|
+
const container = scrollParent(el);
|
|
284
|
+
const offset = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop;
|
|
285
|
+
const reserve = (footerEl.value?.offsetHeight ?? 0) + BOTTOM_GAP_PX;
|
|
286
|
+
const overflow = Math.max(container.scrollHeight - container.clientHeight, 0);
|
|
287
|
+
const available = Math.max(
|
|
288
|
+
Math.floor(container.clientHeight - offset - reserve - overflow) - 1,
|
|
289
|
+
MIN_FIT_PX
|
|
290
|
+
);
|
|
291
|
+
const current = fitHeight.value ? Number.parseInt(fitHeight.value, 10) : null;
|
|
292
|
+
if (current !== null && Math.abs(current - available) < 8) return;
|
|
293
|
+
fitHeight.value = `${available}px`;
|
|
294
|
+
}
|
|
295
|
+
if (import.meta.client) {
|
|
296
|
+
let frame = 0;
|
|
297
|
+
let timers = [];
|
|
298
|
+
const schedule = () => {
|
|
299
|
+
if (frame) return;
|
|
300
|
+
frame = requestAnimationFrame(() => {
|
|
301
|
+
frame = 0;
|
|
302
|
+
measureFit();
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
onMounted(() => {
|
|
306
|
+
measureFit();
|
|
307
|
+
window.addEventListener("resize", schedule);
|
|
308
|
+
timers = [setTimeout(schedule, 250), setTimeout(schedule, 1200)];
|
|
309
|
+
});
|
|
310
|
+
onBeforeUnmount(() => {
|
|
311
|
+
window.removeEventListener("resize", schedule);
|
|
312
|
+
for (const timer of timers) clearTimeout(timer);
|
|
313
|
+
timers = [];
|
|
314
|
+
if (frame) cancelAnimationFrame(frame);
|
|
315
|
+
});
|
|
316
|
+
watch(() => [props.data.length, props.loading], () => nextTick(() => {
|
|
317
|
+
schedule();
|
|
318
|
+
syncThumb();
|
|
319
|
+
}));
|
|
320
|
+
onMounted(() => {
|
|
321
|
+
nextTick(syncThumb);
|
|
322
|
+
window.addEventListener("resize", syncThumb);
|
|
323
|
+
});
|
|
324
|
+
onBeforeUnmount(() => window.removeEventListener("resize", syncThumb));
|
|
325
|
+
watch(fitHeight, () => nextTick(syncThumb));
|
|
326
|
+
}
|
|
327
|
+
const headerRowEl = ref(null);
|
|
328
|
+
const headerHeight = ref(0);
|
|
329
|
+
const rowsOverflow = ref(false);
|
|
330
|
+
const thumbHeight = ref(0);
|
|
331
|
+
const thumbOffset = ref(0);
|
|
332
|
+
const MIN_THUMB_PX = 32;
|
|
333
|
+
const drawnBarSupported = import.meta.client && typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("selector(::-webkit-scrollbar)");
|
|
334
|
+
function syncThumb() {
|
|
335
|
+
const el = scrollEl.value;
|
|
336
|
+
if (!props.scrollBody || !el) return;
|
|
337
|
+
headerHeight.value = headerRowEl.value?.offsetHeight ?? 0;
|
|
338
|
+
const trackHeight = el.clientHeight - headerHeight.value;
|
|
339
|
+
const scrollable = el.scrollHeight - el.clientHeight;
|
|
340
|
+
rowsOverflow.value = drawnBarSupported && scrollable > 1 && trackHeight > MIN_THUMB_PX;
|
|
341
|
+
if (!rowsOverflow.value) return;
|
|
342
|
+
const ratio = el.clientHeight / el.scrollHeight;
|
|
343
|
+
const height = Math.max(Math.round(trackHeight * ratio), MIN_THUMB_PX);
|
|
344
|
+
thumbHeight.value = height;
|
|
345
|
+
thumbOffset.value = Math.round(el.scrollTop / scrollable * (trackHeight - height));
|
|
346
|
+
}
|
|
347
|
+
function onRowsScroll() {
|
|
348
|
+
syncThumb();
|
|
349
|
+
}
|
|
350
|
+
function startThumbDrag(event) {
|
|
351
|
+
const el = scrollEl.value;
|
|
352
|
+
if (!el) return;
|
|
353
|
+
event.preventDefault();
|
|
354
|
+
const startY = event.clientY;
|
|
355
|
+
const startScroll = el.scrollTop;
|
|
356
|
+
const trackHeight = el.clientHeight - headerHeight.value;
|
|
357
|
+
const scrollable = el.scrollHeight - el.clientHeight;
|
|
358
|
+
const travel = trackHeight - thumbHeight.value;
|
|
359
|
+
if (travel <= 0) return;
|
|
360
|
+
const target = event.target;
|
|
361
|
+
target.setPointerCapture(event.pointerId);
|
|
362
|
+
const move = (e) => {
|
|
363
|
+
el.scrollTop = startScroll + (e.clientY - startY) / travel * scrollable;
|
|
364
|
+
};
|
|
365
|
+
const up = (e) => {
|
|
366
|
+
target.releasePointerCapture(e.pointerId);
|
|
367
|
+
target.removeEventListener("pointermove", move);
|
|
368
|
+
target.removeEventListener("pointerup", up);
|
|
369
|
+
target.removeEventListener("pointercancel", up);
|
|
370
|
+
};
|
|
371
|
+
target.addEventListener("pointermove", move);
|
|
372
|
+
target.addEventListener("pointerup", up);
|
|
373
|
+
target.addEventListener("pointercancel", up);
|
|
374
|
+
}
|
|
251
375
|
const scrollStyle = computed(() => {
|
|
252
376
|
if (!props.scrollBody) return void 0;
|
|
377
|
+
if (props.scrollBody === "fit") {
|
|
378
|
+
return fitHeight.value ? { maxHeight: fitHeight.value } : void 0;
|
|
379
|
+
}
|
|
253
380
|
const configured = typeof props.scrollBody === "object" ? props.scrollBody.maxHeight : void 0;
|
|
254
381
|
return { maxHeight: configured ?? "calc(100vh - 22rem)" };
|
|
255
382
|
});
|
|
@@ -320,13 +447,16 @@ function defaultCell(col, row) {
|
|
|
320
447
|
variant="outline"
|
|
321
448
|
size="sm"
|
|
322
449
|
class="h-7 shrink-0 gap-1.5 text-sm"
|
|
323
|
-
:class="
|
|
450
|
+
:class="[
|
|
451
|
+
railOpen ? 'border-primary/30 text-primary' : '',
|
|
452
|
+
railLabel ? '' : 'w-7 px-0 justify-center'
|
|
453
|
+
]"
|
|
324
454
|
:aria-pressed="railOpen"
|
|
325
|
-
:aria-label="railOpen ? t('shared.datatable.toolbar.rail.aria.hide', { label: railLabel }) : t('shared.datatable.toolbar.rail.aria.show', { label: railLabel })"
|
|
455
|
+
:aria-label="railLabel ? railOpen ? t('shared.datatable.toolbar.rail.aria.hide', { label: railLabel }) : t('shared.datatable.toolbar.rail.aria.show', { label: railLabel }) : railOpen ? t('shared.datatable.toolbar.rail.aria.hideUnnamed') : t('shared.datatable.toolbar.rail.aria.showUnnamed')"
|
|
326
456
|
@click="railOpen = !railOpen"
|
|
327
457
|
>
|
|
328
458
|
<SidebarMinimalistic :size="14" />
|
|
329
|
-
{{ railLabel }}
|
|
459
|
+
<template v-if="railLabel">{{ railLabel }}</template>
|
|
330
460
|
</Button>
|
|
331
461
|
<!-- Shrinks (never wraps the row) once the toolbar carries scope /
|
|
332
462
|
view-mode / saved-view controls next to it.
|
|
@@ -338,6 +468,7 @@ function defaultCell(col, row) {
|
|
|
338
468
|
v-if="search"
|
|
339
469
|
v-model="query"
|
|
340
470
|
:placeholder="placeholder"
|
|
471
|
+
:label="searchLabel"
|
|
341
472
|
class="w-[21rem] min-w-40 max-w-full shrink"
|
|
342
473
|
>
|
|
343
474
|
<template #icon>
|
|
@@ -374,8 +505,20 @@ function defaultCell(col, row) {
|
|
|
374
505
|
<Filter :size="14" weight="BoldDuotone" />
|
|
375
506
|
</template>
|
|
376
507
|
</FilterPanel>
|
|
508
|
+
<!--
|
|
509
|
+
The one filter control, for a surface that owns its own query.
|
|
510
|
+
|
|
511
|
+
Drawn in the same slot as the other two so a surface never shows two
|
|
512
|
+
filter triggers: `facets` and `filter` are alternatives, and
|
|
513
|
+
`filterTabs` is the in-memory path this replaces.
|
|
514
|
+
-->
|
|
515
|
+
<FacetPanel
|
|
516
|
+
v-if="panelFacets.length"
|
|
517
|
+
v-model="facetSelection"
|
|
518
|
+
:facets="facets"
|
|
519
|
+
/>
|
|
377
520
|
<FilterMenu
|
|
378
|
-
v-if="filter"
|
|
521
|
+
v-if="filter && !panelFacets.length"
|
|
379
522
|
:width="typeof filter === 'object' ? filter.width : void 0"
|
|
380
523
|
:active="typeof filter === 'object' ? filter.active : void 0"
|
|
381
524
|
:count="typeof filter === 'object' ? filter.count : void 0"
|
|
@@ -412,23 +555,51 @@ function defaultCell(col, row) {
|
|
|
412
555
|
<EyeClosed :size="16" weight="Linear" />
|
|
413
556
|
</template>
|
|
414
557
|
</ColumnPicker>
|
|
558
|
+
<!--
|
|
559
|
+
Saved views, beside the columns picker rather than out past the market
|
|
560
|
+
scope and the view-mode switch.
|
|
561
|
+
|
|
562
|
+
It belongs with what it saves. Filters, sort and columns are the three
|
|
563
|
+
things a view is made of, so the control that names one sits at the end
|
|
564
|
+
of that group — not after the market scope, which is not part of a
|
|
565
|
+
view, or the view-mode switch, which is about what you are looking at.
|
|
566
|
+
-->
|
|
567
|
+
<SavedViewsMenu v-if="savedViews" :config="savedViews" />
|
|
415
568
|
<!-- Market scope — the surface passes the shared MarketScopeToggle in;
|
|
416
569
|
the row position is fixed here. -->
|
|
417
570
|
<slot name="scope" />
|
|
418
571
|
<!-- The editor's device switch, which is the right shape wherever the
|
|
419
572
|
choice changes what you are LOOKING AT rather than which rows. -->
|
|
420
573
|
<SegmentedToggle v-if="viewModes?.length" v-model="viewMode" variant="inset">
|
|
574
|
+
<!--
|
|
575
|
+
Violet on hover, and violet while it is the one in effect — the
|
|
576
|
+
same language the filter trigger, the column picker and the saved
|
|
577
|
+
views speak two controls to the left. The track's own variant only
|
|
578
|
+
lifts the active segment onto a white pill, which says WHICH is
|
|
579
|
+
picked but not that the row is a set of live controls.
|
|
580
|
+
|
|
581
|
+
The hover is written against `data-[state=off]` and marked
|
|
582
|
+
important because the design system's own toggle already ships
|
|
583
|
+
`data-[state=off]:hover:text-foreground`. A plain `hover:` loses to
|
|
584
|
+
it on specificity, and matching the specificity alone would leave
|
|
585
|
+
the winner to whatever order Tailwind happened to emit the two
|
|
586
|
+
utilities in — a coin toss that reads as a bug the day it lands the
|
|
587
|
+
other way. Delete the `!` if the toggle's own hover ever goes violet.
|
|
588
|
+
|
|
589
|
+
`aria-label` because these are icon-only: the mode still has to
|
|
590
|
+
announce itself.
|
|
591
|
+
-->
|
|
421
592
|
<SegmentedToggleItem
|
|
422
593
|
v-for="m in viewModes"
|
|
423
594
|
:key="m.value"
|
|
424
595
|
:value="m.value"
|
|
425
596
|
:aria-label="m.label"
|
|
597
|
+
class="transition-colors data-[state=on]:text-primary data-[state=off]:hover:text-primary!"
|
|
426
598
|
>
|
|
427
599
|
<component :is="m.icon" v-if="m.icon" :size="14" weight="Bold" />
|
|
428
600
|
<template v-if="!m.icon">{{ m.label }}</template>
|
|
429
601
|
</SegmentedToggleItem>
|
|
430
602
|
</SegmentedToggle>
|
|
431
|
-
<SavedViewsMenu v-if="savedViews" :config="savedViews" />
|
|
432
603
|
<slot name="toolbar-left" />
|
|
433
604
|
</div>
|
|
434
605
|
<div class="flex items-center gap-2 ml-auto shrink-0">
|
|
@@ -437,29 +608,88 @@ function defaultCell(col, row) {
|
|
|
437
608
|
</div>
|
|
438
609
|
</div>
|
|
439
610
|
|
|
611
|
+
<!--
|
|
612
|
+
What is filtering right now. Not optional beside a paned panel: with it
|
|
613
|
+
closed, a count on an icon says that something narrows the list and never
|
|
614
|
+
what.
|
|
615
|
+
-->
|
|
616
|
+
<FacetChips
|
|
617
|
+
v-if="facets.length"
|
|
618
|
+
v-model="facetSelection"
|
|
619
|
+
:facets="facets"
|
|
620
|
+
:show-facet="chipFacetLabels"
|
|
621
|
+
/>
|
|
622
|
+
|
|
440
623
|
<!-- Alternate view mode (gallery, board, …) — same toolbar, same footer. -->
|
|
441
624
|
<div v-if="!showTable" class="border-t border-black/[0.04] bg-white/55">
|
|
442
625
|
<slot name="view" :rows="displayedRows" :loading="loading" />
|
|
443
626
|
</div>
|
|
444
627
|
|
|
445
|
-
<!--
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
628
|
+
<!-- Two boxes: one that scrolls, and the table itself.
|
|
629
|
+
|
|
630
|
+
The table is ONE GRID and the rows are subgrids of it (`SUBGRID_ROW`),
|
|
631
|
+
which is what keeps a heading over its own column. It used to be a grid
|
|
632
|
+
per row, and a content-sized track then measured itself separately in
|
|
633
|
+
each one — see `SUBGRID_ROW` for what that did to every list with row
|
|
634
|
+
actions.
|
|
635
|
+
|
|
636
|
+
The scrolling is the outer box's job, and it has to be, because the
|
|
637
|
+
grid carries `min-w-min`: a box that grows to its own content cannot
|
|
638
|
+
also be the thing that scrolls it. The panel around them clips
|
|
639
|
+
(`overflow-hidden`, for the rounded corners), so without this a table
|
|
640
|
+
whose declared widths outgrow the panel simply lost the excess.
|
|
641
|
+
|
|
642
|
+
The inner element used to be `display: contents` when scrolling was
|
|
643
|
+
off, which made it vanish from layout — and a box that is not in the
|
|
644
|
+
layout can be neither a grid nor a scroller.
|
|
645
|
+
|
|
646
|
+
IT IS ALSO THE TABLE. The box that holds the header and the rows and
|
|
647
|
+
nothing else is the only element in this component that can carry the
|
|
648
|
+
role: the root has the toolbar and the footer in it, and a `table` may
|
|
649
|
+
own rows and rowgroups only. Which is why everything below is a `row`
|
|
650
|
+
or is hidden — an empty state or a skeleton left as a bare `div` here
|
|
651
|
+
would be a child the role does not allow, and the whole table's
|
|
652
|
+
semantics are then whatever the browser decides to salvage.
|
|
653
|
+
|
|
654
|
+
`table` and not `grid`. `grid` is the interactive one: it promises
|
|
655
|
+
arrow keys move a focus between cells, and nothing here implements
|
|
656
|
+
that. A static table read with a screen reader's own table commands is
|
|
657
|
+
what these lists actually are, and both roles announce the column
|
|
658
|
+
heading with the cell, which is the thing that was missing. -->
|
|
659
|
+
<div :class="scrollBody ? 'relative flex min-h-0 flex-col' : ''">
|
|
449
660
|
<div
|
|
450
|
-
|
|
661
|
+
ref="scrollEl"
|
|
662
|
+
:class="scrollBody ? 'rx-rows min-h-0 overflow-auto' : 'overflow-x-auto'"
|
|
451
663
|
:style="scrollStyle"
|
|
664
|
+
@scroll="onRowsScroll"
|
|
452
665
|
>
|
|
666
|
+
<div
|
|
667
|
+
:role="showTable ? 'table' : void 0"
|
|
668
|
+
:aria-busy="showTable && loading ? 'true' : void 0"
|
|
669
|
+
:class="showTable ? 'grid min-w-min gap-x-2' : ''"
|
|
670
|
+
:style="showTable ? gridTemplate : void 0"
|
|
671
|
+
>
|
|
453
672
|
<!-- Header row -------------------------------------------------- -->
|
|
454
673
|
<div
|
|
455
674
|
v-if="showTable"
|
|
456
|
-
|
|
675
|
+
ref="headerRowEl"
|
|
676
|
+
role="row"
|
|
677
|
+
class="grid items-center gap-x-2 border-t rounded-t-lg border-gray-200 bg-gray-100 px-4 py-2.5 text-sm font-medium uppercase tracking-normal text-muted-foreground"
|
|
457
678
|
:class="scrollBody ? 'sticky top-0 z-10' : ''"
|
|
458
|
-
:style="
|
|
679
|
+
:style="SUBGRID_ROW"
|
|
459
680
|
>
|
|
681
|
+
<!-- `columnheader` is what names every cell underneath it, so this is
|
|
682
|
+
the half of the fix that does the work. `aria-sort` goes with it:
|
|
683
|
+
a heading that carries a sort button and says nothing about the
|
|
684
|
+
current direction is a control whose state is visible only to
|
|
685
|
+
people who can see the arrow. -->
|
|
460
686
|
<div
|
|
461
687
|
v-for="col in visibleColumns"
|
|
462
688
|
:key="col.key"
|
|
689
|
+
role="columnheader"
|
|
690
|
+
:aria-sort="ariaSortFor(col)"
|
|
691
|
+
:title="col.label || void 0"
|
|
692
|
+
class="min-w-0"
|
|
463
693
|
:class="col.alignRight ? 'text-right' : ''"
|
|
464
694
|
>
|
|
465
695
|
<!-- `#header-<key>` lets a page take over a header cell (e.g. a
|
|
@@ -480,30 +710,40 @@ function defaultCell(col, row) {
|
|
|
480
710
|
the row's uppercase. Without it a column reads "LAST CHECK"
|
|
481
711
|
beside "Channel" and "Provider" for no reason a reader can see —
|
|
482
712
|
the header shouts wherever a column happens not to be sortable. -->
|
|
483
|
-
<span v-else class="normal-case">{{ col.label }}</span>
|
|
713
|
+
<span v-else class="block truncate normal-case">{{ col.label }}</span>
|
|
484
714
|
</slot>
|
|
485
715
|
</div>
|
|
486
716
|
</div>
|
|
487
717
|
|
|
488
718
|
<!-- Body --------------------------------------------------------- -->
|
|
489
719
|
<template v-for="entry in showTable ? renderRows : []" :key="entry.key">
|
|
490
|
-
<!-- Group header (groupBy mode)
|
|
720
|
+
<!-- Group header (groupBy mode) — a row of one cell that spans the lot.
|
|
721
|
+
The flex moves one element in, onto the cell: a `row` may hold
|
|
722
|
+
cells and the label and the count are neither. -->
|
|
491
723
|
<div
|
|
492
724
|
v-if="entry.kind === 'header'"
|
|
493
|
-
|
|
725
|
+
role="row"
|
|
726
|
+
class="col-[1/-1] border-t border-black/[0.04] bg-gray-100/70 px-4 py-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground"
|
|
494
727
|
>
|
|
495
|
-
|
|
496
|
-
|
|
728
|
+
<!-- Spans every column like the rows do, so a table wide enough to
|
|
729
|
+
scroll does not show this band stopping short of the rows it
|
|
730
|
+
groups. No subgrid: it holds one cell, not a set of columns. -->
|
|
731
|
+
<div role="cell" :aria-colspan="visibleColumns.length" class="flex items-center gap-2">
|
|
732
|
+
{{ entry.label }}
|
|
733
|
+
<span class="tabular-nums normal-case text-muted-foreground/70">{{ entry.count }}</span>
|
|
734
|
+
</div>
|
|
497
735
|
</div>
|
|
498
736
|
<div
|
|
499
737
|
v-else
|
|
500
|
-
|
|
501
|
-
|
|
738
|
+
role="row"
|
|
739
|
+
class="group grid items-center gap-x-2 border-t border-black/[0.04] px-4 py-3 cursor-pointer bg-gray-50 transition-colors"
|
|
740
|
+
:style="SUBGRID_ROW"
|
|
502
741
|
@click="emit('rowClick', entry.row)"
|
|
503
742
|
>
|
|
504
743
|
<div
|
|
505
744
|
v-for="col in visibleColumns"
|
|
506
745
|
:key="col.key"
|
|
746
|
+
role="cell"
|
|
507
747
|
class="min-w-0"
|
|
508
748
|
:class="col.alignRight ? 'text-right' : ''"
|
|
509
749
|
>
|
|
@@ -527,13 +767,18 @@ function defaultCell(col, row) {
|
|
|
527
767
|
</div>
|
|
528
768
|
</template>
|
|
529
769
|
|
|
530
|
-
<!-- Loading skeleton (only when there are no rows yet) -----------
|
|
770
|
+
<!-- Loading skeleton (only when there are no rows yet) -----------
|
|
771
|
+
Hidden rather than given roles. Six rows of pulsing grey carry no
|
|
772
|
+
information to read out, and announcing them as six empty rows would
|
|
773
|
+
be worse than silence; the table says it is `aria-busy` instead,
|
|
774
|
+
which is the same news in one word. -->
|
|
531
775
|
<template v-if="showTable && loading && !displayedRows.length">
|
|
532
776
|
<div
|
|
533
777
|
v-for="n in 6"
|
|
534
778
|
:key="`sk-${n}`"
|
|
535
|
-
|
|
536
|
-
|
|
779
|
+
aria-hidden="true"
|
|
780
|
+
class="grid items-center gap-x-2 border-t border-black/[0.04] bg-gray-50 px-4 py-3"
|
|
781
|
+
:style="SUBGRID_ROW"
|
|
537
782
|
>
|
|
538
783
|
<div
|
|
539
784
|
v-for="col in visibleColumns"
|
|
@@ -551,18 +796,43 @@ function defaultCell(col, row) {
|
|
|
551
796
|
<!-- Empty -------------------------------------------------------- -->
|
|
552
797
|
<div
|
|
553
798
|
v-if="showTable && !displayedRows.length && !loading"
|
|
554
|
-
|
|
799
|
+
role="row"
|
|
800
|
+
class="col-[1/-1] px-3 py-8 text-center text-sm text-muted-foreground"
|
|
555
801
|
>
|
|
556
|
-
<
|
|
557
|
-
|
|
558
|
-
|
|
802
|
+
<div role="cell" :aria-colspan="visibleColumns.length">
|
|
803
|
+
<slot name="empty">
|
|
804
|
+
{{ emptyText ?? t("shared.datatable.empty.default") }}
|
|
805
|
+
</slot>
|
|
806
|
+
</div>
|
|
559
807
|
</div>
|
|
560
808
|
|
|
809
|
+
</div>
|
|
810
|
+
</div>
|
|
811
|
+
|
|
812
|
+
<!-- The rows' scrollbar ---------------------------------------- -->
|
|
813
|
+
<!-- Drawn, not borrowed. A browser's own bar belongs to the scrolling
|
|
814
|
+
box, and the column head is inside that box — it has to be, because
|
|
815
|
+
the head and the rows are one grid and a `max-content` track resolves
|
|
816
|
+
per grid (PE-610). So the native bar runs beside the head, which is
|
|
817
|
+
not where a bar over the ROWS belongs. This one starts under the
|
|
818
|
+
head, ends at the footer, and drives the same native scrolling. -->
|
|
819
|
+
<div
|
|
820
|
+
v-if="scrollBody && rowsOverflow"
|
|
821
|
+
class="pointer-events-none absolute right-0 z-20 w-3.5"
|
|
822
|
+
:style="{ top: `${headerHeight}px`, bottom: '0px' }"
|
|
823
|
+
>
|
|
824
|
+
<div
|
|
825
|
+
class="rx-thumb pointer-events-auto absolute right-[3px] w-2 cursor-default rounded-full bg-[rgb(11_5_34_/_0.18)] transition-colors hover:bg-[rgb(11_5_34_/_0.32)]"
|
|
826
|
+
:style="{ height: `${thumbHeight}px`, transform: `translateY(${thumbOffset}px)` }"
|
|
827
|
+
@pointerdown="startThumbDrag"
|
|
828
|
+
/>
|
|
829
|
+
</div>
|
|
561
830
|
</div>
|
|
562
831
|
|
|
563
832
|
<!-- Footer ------------------------------------------------------- -->
|
|
564
833
|
<div
|
|
565
834
|
v-if="showFooter"
|
|
835
|
+
ref="footerEl"
|
|
566
836
|
class="flex items-center justify-between gap-2 border-t border-gray-200 bg-white/55 backdrop-blur-md px-4 py-3 text-sm text-muted-foreground/60"
|
|
567
837
|
>
|
|
568
838
|
<div class="flex items-center gap-2.5">
|
|
@@ -607,3 +877,7 @@ function defaultCell(col, row) {
|
|
|
607
877
|
</div>
|
|
608
878
|
</div>
|
|
609
879
|
</template>
|
|
880
|
+
|
|
881
|
+
<style scoped>
|
|
882
|
+
.rx-rows::-webkit-scrollbar{height:10px;width:0}.rx-rows::-webkit-scrollbar-track{background:transparent}.rx-rows::-webkit-scrollbar-thumb{background:rgba(11,5,34,.18);background-clip:padding-box;border:3px solid transparent;border-radius:9999px}.rx-thumb{min-height:32px}
|
|
883
|
+
</style>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Facet, FacetSelection } from '../../lib/facets.js';
|
|
1
2
|
import type { DataTableColumn, DataTableFilter, DataTableRail, DataTableSavedViews, DataTableState, DataTableViewMode, RowKey, SortState } from './types.js';
|
|
2
3
|
declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
4
|
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
@@ -5,14 +6,18 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
5
6
|
readonly "onUpdate:page"?: ((value: number) => any) | undefined;
|
|
6
7
|
readonly "onUpdate:viewMode"?: ((value: string) => any) | undefined;
|
|
7
8
|
readonly "onUpdate:railOpen"?: ((value: boolean) => any) | undefined;
|
|
9
|
+
readonly "onUpdate:facetSelection"?: ((value: FacetSelection) => any) | undefined;
|
|
8
10
|
readonly onRowClick?: ((row: T) => any) | undefined;
|
|
9
11
|
readonly onStateChange?: ((state: DataTableState) => any) | undefined;
|
|
10
|
-
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:sort" | "onUpdate:page" | "onUpdate:viewMode" | "onUpdate:railOpen" | "onRowClick" | "onStateChange"> & ({
|
|
12
|
+
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:sort" | "onUpdate:page" | "onUpdate:viewMode" | "onUpdate:railOpen" | "onUpdate:facetSelection" | "onRowClick" | "onStateChange"> & ({
|
|
11
13
|
data: T[];
|
|
12
14
|
columns: DataTableColumn<T>[];
|
|
13
15
|
rowKey: RowKey<T>;
|
|
16
|
+
/** `label` is the field's accessible name; it defaults to the placeholder
|
|
17
|
+
* where the surface wrote one (see `searchLabel`). */
|
|
14
18
|
search?: boolean | {
|
|
15
19
|
placeholder?: string;
|
|
20
|
+
label?: string;
|
|
16
21
|
};
|
|
17
22
|
columnsMenu?: boolean;
|
|
18
23
|
/** Mount the surface's own filter panel in the toolbar's Filter popover
|
|
@@ -25,6 +30,25 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
25
30
|
/** Built-in status/dimension filter pills (static mode). One dimension, or
|
|
26
31
|
* several (each renders as its own sub-menu). */
|
|
27
32
|
filterTabs?: DataTableFilter<T> | DataTableFilter<T>[];
|
|
33
|
+
/**
|
|
34
|
+
* The surface's declared dimensions, as one facet model (PE-6xx).
|
|
35
|
+
*
|
|
36
|
+
* The THIRD filter path, and the one the others are being folded into. It
|
|
37
|
+
* differs from `filterTabs` in who does the work: those are filtered in
|
|
38
|
+
* memory here, which a server-paged list cannot use — a facet count taken
|
|
39
|
+
* over one page of fifty rows out of twenty-three thousand is a number
|
|
40
|
+
* nobody can reconcile with the table under it. Here the surface owns the
|
|
41
|
+
* query and the counts, and this component only draws the control and hands
|
|
42
|
+
* back a committed selection (`v-model:facet-selection`).
|
|
43
|
+
*
|
|
44
|
+
* EVERY declared dimension lives in this one panel, committed together, and
|
|
45
|
+
* reads back in one chip strip under the toolbar. There is deliberately no
|
|
46
|
+
* second placement: a tree pinned in a left column beside a panel is two
|
|
47
|
+
* places to look and two commit rules for one question.
|
|
48
|
+
*/
|
|
49
|
+
facets?: Facet[];
|
|
50
|
+
/** Say which facet each chip belongs to (see `FacetChips.showFacet`). */
|
|
51
|
+
chipFacetLabels?: boolean;
|
|
28
52
|
/** Saved views control — the surface supplies snapshot/apply, the table
|
|
29
53
|
* owns the control, the storage and the dialogs. */
|
|
30
54
|
savedViews?: DataTableSavedViews | null;
|
|
@@ -49,8 +73,13 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
49
73
|
* sitting above it. That is not a detail: a scrollbar takes width from the
|
|
50
74
|
* scrolling box, so a header outside it stays wider than the rows and every
|
|
51
75
|
* column drifts by however wide the scrollbar is.
|
|
76
|
+
*
|
|
77
|
+
* `'fit'` measures instead of guessing: the rows get whatever height is left
|
|
78
|
+
* in the surrounding scroll container, so the table ends where the page ends
|
|
79
|
+
* and nothing below it is pushed out of sight. A short list still ends after
|
|
80
|
+
* its last row — the measurement is a MAXIMUM, not a height.
|
|
52
81
|
*/
|
|
53
|
-
scrollBody?: boolean | {
|
|
82
|
+
scrollBody?: boolean | "fit" | {
|
|
54
83
|
maxHeight?: string;
|
|
55
84
|
};
|
|
56
85
|
pagination?: boolean | {
|
|
@@ -71,6 +100,13 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
71
100
|
page?: number;
|
|
72
101
|
viewMode?: string;
|
|
73
102
|
railOpen?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* The committed facet selection. Two-way so a chip removed under the toolbar,
|
|
105
|
+
* a facet picked in the surface's pinned rail and the panel's own Apply all
|
|
106
|
+
* write to one place — and so the surface can seed it from a saved view or a
|
|
107
|
+
* URL without this component holding a second copy to drift from.
|
|
108
|
+
*/
|
|
109
|
+
facetSelection?: FacetSelection;
|
|
74
110
|
}) & Partial<{}>> & import("vue").PublicProps;
|
|
75
111
|
expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
|
|
76
112
|
attrs: any;
|
|
@@ -98,7 +134,7 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
98
134
|
} & {
|
|
99
135
|
empty?: (props: {}) => any;
|
|
100
136
|
};
|
|
101
|
-
emit: (((evt: "rowClick", row: T) => void) & ((evt: "stateChange", state: DataTableState) => void)) & (((evt: "update:sort", value: SortState) => void) & ((evt: "update:page", value: number) => void) & ((evt: "update:viewMode", value: string) => void) & ((evt: "update:railOpen", value: boolean) => void));
|
|
137
|
+
emit: (((evt: "rowClick", row: T) => void) & ((evt: "stateChange", state: DataTableState) => void)) & (((evt: "update:sort", value: SortState) => void) & ((evt: "update:page", value: number) => void) & ((evt: "update:viewMode", value: string) => void) & ((evt: "update:railOpen", value: boolean) => void) & ((evt: "update:facetSelection", value: FacetSelection) => void));
|
|
102
138
|
}>) => import("vue").VNode & {
|
|
103
139
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
104
140
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Facet, FacetSelection } from '../../lib/facets.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
facets: Facet[];
|
|
4
|
+
modelValue: FacetSelection;
|
|
5
|
+
/**
|
|
6
|
+
* Say which facet each chip belongs to. On where "Draft" could be a status or
|
|
7
|
+
* a lifecycle; off where the values speak for themselves.
|
|
8
|
+
*/
|
|
9
|
+
showFacet?: boolean;
|
|
10
|
+
};
|
|
11
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
12
|
+
"update:modelValue": (value: FacetSelection) => any;
|
|
13
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
14
|
+
"onUpdate:modelValue"?: ((value: FacetSelection) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
export default _default;
|