nuxt-ui-tools 1.27.5 → 1.29.0
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.json +1 -1
- package/dist/module.mjs +7 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +1 -1
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue +26 -84
- package/dist/runtime/i18n/locales/en.js +5 -0
- package/dist/runtime/i18n/locales/fr.js +5 -0
- package/dist/runtime/i18n/types.d.ts +7 -0
- package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue +3 -3
- package/dist/runtime/media/components/video-player.d.vue.ts +58 -0
- package/dist/runtime/media/components/video-player.vue +155 -0
- package/dist/runtime/media/components/video-player.vue.d.ts +58 -0
- package/dist/runtime/media/index.d.ts +1 -0
- package/dist/runtime/media/index.js +0 -0
- package/dist/runtime/media/types/index.d.ts +1 -0
- package/dist/runtime/media/types/index.js +0 -0
- package/dist/runtime/media/types/media.d.ts +8 -0
- package/dist/runtime/media/types/media.js +0 -0
- package/dist/runtime/table/components/grid/grid-renderer.vue +33 -8
- package/dist/runtime/table/components/table/table-group-header.d.vue.ts +9 -0
- package/dist/runtime/table/components/table/table-group-header.vue +77 -0
- package/dist/runtime/table/components/table/table-group-header.vue.d.ts +9 -0
- package/dist/runtime/table/components/table/table-renderer.vue +12 -7
- package/dist/runtime/table/components/table/table-row.d.vue.ts +4 -1
- package/dist/runtime/table/components/table/table-row.vue +16 -1
- package/dist/runtime/table/components/table/table-row.vue.d.ts +4 -1
- package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +2 -0
- package/dist/runtime/table/components/table/table-selection-control.vue +3 -2
- package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +2 -0
- package/dist/runtime/table/composables/use-table-grid.d.ts +7 -3
- package/dist/runtime/table/composables/use-table-grid.js +27 -2
- package/dist/runtime/table/composables/use-table-groups.d.ts +28 -0
- package/dist/runtime/table/composables/use-table-groups.js +133 -0
- package/dist/runtime/table/composables/use-table-internals.d.ts +42 -0
- package/dist/runtime/table/composables/use-table-internals.js +9 -0
- package/dist/runtime/table/composables/use-table-selection.d.ts +5 -0
- package/dist/runtime/table/composables/use-table-selection.js +13 -3
- package/dist/runtime/table/composables/use-table-tree.js +7 -5
- package/dist/runtime/table/constants/row-motion.d.ts +8 -0
- package/dist/runtime/table/constants/row-motion.js +5 -0
- package/dist/runtime/table/types/groups.d.ts +44 -0
- package/dist/runtime/table/types/groups.js +0 -0
- package/dist/runtime/table/types/index.d.ts +1 -0
- package/dist/runtime/table/types/layout.d.ts +5 -0
- package/dist/runtime/table/utils/groups.d.ts +14 -0
- package/dist/runtime/table/utils/groups.js +55 -0
- package/dist/runtime/table/utils/index.d.ts +1 -0
- package/dist/runtime/table/utils/index.js +1 -0
- package/package.json +16 -1
- /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.d.vue.ts +0 -0
- /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue.d.ts +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
4
|
+
import { useTableInternals } from "../../composables/use-table-internals";
|
|
5
|
+
import TableCell from "./table-cell";
|
|
6
|
+
import TableSelectionControl from "./table-selection-control.vue";
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
group: { type: Object, required: true },
|
|
9
|
+
selectable: { type: Boolean, required: false }
|
|
10
|
+
});
|
|
11
|
+
const { groups, selection } = useTableInternals();
|
|
12
|
+
const { t } = useUiToolsLocale();
|
|
13
|
+
const collapsed = computed(() => groups.isCollapsed(props.group));
|
|
14
|
+
const params = computed(() => ({ rows: props.group.rows, value: props.group.value }));
|
|
15
|
+
const checked = computed(() => selection.getRowsSelectionState({ rowIds: props.group.rowIds }));
|
|
16
|
+
const showCheckbox = computed(() => props.selectable && selection.selectionEnabled.value);
|
|
17
|
+
function renderLabel() {
|
|
18
|
+
const label = groups.config.value?.label;
|
|
19
|
+
if (label) return label(params.value);
|
|
20
|
+
return props.group.value === void 0 ? t("table.groups.empty") : String(props.group.value);
|
|
21
|
+
}
|
|
22
|
+
function renderSummary() {
|
|
23
|
+
const summary = groups.config.value?.summary;
|
|
24
|
+
return summary ? summary(params.value) : props.group.rows.length;
|
|
25
|
+
}
|
|
26
|
+
function toggleSelection() {
|
|
27
|
+
const { rowIds } = props.group;
|
|
28
|
+
if (checked.value === true) selection.unselectRows({ rowIds });
|
|
29
|
+
else selection.selectRows({ rowIds });
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div
|
|
35
|
+
class="nut-dl-group"
|
|
36
|
+
:data-collapsed="collapsed || void 0"
|
|
37
|
+
:data-selectable="showCheckbox || void 0"
|
|
38
|
+
@click="groups.toggle(group)"
|
|
39
|
+
>
|
|
40
|
+
<span v-if="showCheckbox" class="nut-dl-group__check">
|
|
41
|
+
<TableSelectionControl
|
|
42
|
+
:model-value="checked"
|
|
43
|
+
:label="t('table.groups.select')"
|
|
44
|
+
@toggle="toggleSelection"
|
|
45
|
+
/>
|
|
46
|
+
</span>
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
class="nut-dl-group__toggle"
|
|
50
|
+
:aria-expanded="!collapsed"
|
|
51
|
+
:aria-label="t('table.groups.toggle')"
|
|
52
|
+
@click.stop="groups.toggle(group)"
|
|
53
|
+
>
|
|
54
|
+
<svg
|
|
55
|
+
viewBox="0 0 16 16"
|
|
56
|
+
fill="none"
|
|
57
|
+
stroke="currentColor"
|
|
58
|
+
stroke-width="1.75"
|
|
59
|
+
stroke-linecap="round"
|
|
60
|
+
stroke-linejoin="round"
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
>
|
|
63
|
+
<path d="m4 6 4 4 4-4" />
|
|
64
|
+
</svg>
|
|
65
|
+
</button>
|
|
66
|
+
<span class="nut-dl-group__label">
|
|
67
|
+
<TableCell :index="0" :render="renderLabel" :row="group" />
|
|
68
|
+
</span>
|
|
69
|
+
<span class="nut-dl-group__summary">
|
|
70
|
+
<TableCell :index="0" :render="renderSummary" :row="group" />
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
.nut-dl-group{--nut-dl-group-gutter:var(--nut-dl-gutter,0px);box-sizing:border-box;color:var(--ui-text-highlighted);cursor:pointer;font-size:13px;font-weight:600;gap:6px;height:100%;left:0;max-width:100%;min-height:32px;padding-inline:var(--nut-dl-group-gutter);position:sticky;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.nut-dl-group,.nut-dl-group__check{align-items:center;display:inline-flex}.nut-dl-group__check{flex:none;margin-right:-6px;width:calc(var(--nut-dl-select-w, 44px) - var(--nut-dl-group-gutter) + var(--nut-dl-cell-x, 0px))}.nut-dl-group__toggle{background:transparent;border:0;border-radius:calc(var(--ui-radius)*1.5);color:var(--ui-text-muted);cursor:pointer;display:grid;flex:none;height:22px;outline:none;padding:0;place-items:center;width:22px}.nut-dl-group__toggle:hover{background:var(--nut-dl-toggle-hover,var(--ui-bg-accented))}.nut-dl-group__toggle:focus-visible{box-shadow:0 0 0 2px var(--ui-primary)}.nut-dl-group__toggle svg{height:16px;transition:transform .16s ease;width:16px}.nut-dl-group[data-collapsed] .nut-dl-group__toggle svg{transform:rotate(-90deg)}.nut-dl-group__label{min-width:0;overflow:hidden;text-overflow:ellipsis}.nut-dl-group__summary{color:var(--ui-text-muted);flex:none;font-size:12px;font-variant-numeric:tabular-nums;font-weight:500}.nut-dl-row.nut-dl-row--group{background:var(--nut-dl-head-bg)}.nut-dl-row.nut-dl-row--group:hover{background:color-mix(in srgb,var(--nut-dl-head-fg) 7%,var(--nut-dl-head-bg))}.nut-dl-group-cell{background:inherit;border-bottom:1px solid var(--nut-dl-line);height:var(--nut-dl-head-h);padding:0}.nut-dl-grid__group{border-bottom:1px solid var(--ui-border)}@media (prefers-reduced-motion:reduce){.nut-dl-group__toggle svg{transition:none}}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TableGroup } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
group: TableGroup;
|
|
4
|
+
/** Whether the rows beneath it carry checkboxes. */
|
|
5
|
+
selectable?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -6,7 +6,7 @@ import { isFunction, isNumber, isObject } from "../../../shared/utils/predicate"
|
|
|
6
6
|
import { useDataListUi } from "../../composables/use-data-list-ui";
|
|
7
7
|
import { useTableInternals } from "../../composables/use-table-internals";
|
|
8
8
|
import { useTanstackTable } from "../../composables/use-tanstack-table";
|
|
9
|
-
import { mergeDataListUiClass } from "../../utils";
|
|
9
|
+
import { mergeDataListUiClass, tableGroupOf } from "../../utils";
|
|
10
10
|
import {
|
|
11
11
|
ROW_ACTIONS_COLUMN_ID,
|
|
12
12
|
SELECT_COLUMN_ID,
|
|
@@ -54,7 +54,7 @@ const props = defineProps({
|
|
|
54
54
|
ui: { type: Object, required: false }
|
|
55
55
|
});
|
|
56
56
|
const internals = useTableInternals();
|
|
57
|
-
const { tree } = internals;
|
|
57
|
+
const { groups, tree } = internals;
|
|
58
58
|
const dataListUi = useDataListUi();
|
|
59
59
|
const { locale, t } = useUiToolsLocale();
|
|
60
60
|
const rows = computed(
|
|
@@ -89,6 +89,7 @@ const tokenStyle = computed(() => ({
|
|
|
89
89
|
"--nut-dl-gutter": `${gutter.value}px`,
|
|
90
90
|
"--nut-dl-head-h": `${tokens.value.head}px`,
|
|
91
91
|
"--nut-dl-row-h": `${tokens.value.row}px`,
|
|
92
|
+
"--nut-dl-select-w": `${SELECT_COLUMN_WIDTH + gutterExtra.value}px`,
|
|
92
93
|
"--nut-dl-tree-gutter": `${TREE_GUTTER_WIDTH}px`,
|
|
93
94
|
"--nut-dl-tree-toggle": `${TREE_TOGGLE_WIDTH}px`,
|
|
94
95
|
"--nut-dl-tree-gap": `${TREE_GAP}px`,
|
|
@@ -141,11 +142,13 @@ const pinned = computed(() => {
|
|
|
141
142
|
};
|
|
142
143
|
});
|
|
143
144
|
function getRowId(row, index) {
|
|
144
|
-
return internals.selection.getRowId({ index, row });
|
|
145
|
+
return tableGroupOf(row)?.key ?? internals.selection.getRowId({ index, row });
|
|
145
146
|
}
|
|
146
|
-
const sourceRows = computed(
|
|
147
|
-
(
|
|
148
|
-
);
|
|
147
|
+
const sourceRows = computed(() => {
|
|
148
|
+
if (rows.value.length === 0) return rows.value;
|
|
149
|
+
if (tree.enabled.value) return tree.visibleRows.value;
|
|
150
|
+
return groups.enabled.value ? groups.displayRows.value : rows.value;
|
|
151
|
+
});
|
|
149
152
|
const renderRows = computed(() => rowsMounted.value ? sourceRows.value : []);
|
|
150
153
|
const virtualized = computed(
|
|
151
154
|
() => renderRows.value.length >= VIRTUALIZE_ROW_THRESHOLD || renderRows.value.length * columnDefs.value.length >= VIRTUALIZE_CELL_THRESHOLD
|
|
@@ -745,7 +748,9 @@ defineExpose({ resetColumnSizing });
|
|
|
745
748
|
:node="tree.nodeOf(row.id)"
|
|
746
749
|
:expanded="tree.isExpanded(row.id)"
|
|
747
750
|
:checkbox="hasCheckbox(row.id)"
|
|
748
|
-
:motion="tree.motionOf(row.id)"
|
|
751
|
+
:motion="tree.motionOf(row.id) ?? groups.motionOf(row.id)"
|
|
752
|
+
:group="tableGroupOf(row.original)"
|
|
753
|
+
:col-span="columnCount"
|
|
749
754
|
@toggle="tree.toggleRow(row.id)"
|
|
750
755
|
@select="onSelectRow(row.id, $event)"
|
|
751
756
|
/>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* so rows that stay on screen while the virtual window slides keep their cells untouched.
|
|
4
4
|
*/
|
|
5
5
|
import type { HTMLAttributes } from 'vue';
|
|
6
|
-
import type { GenericObject, TableTreeMotion, TableTreeNode } from '../../types/index.js';
|
|
6
|
+
import type { GenericObject, TableGroup, TableTreeMotion, TableTreeNode } from '../../types/index.js';
|
|
7
7
|
import type { tableRowCells } from './table-layout.js';
|
|
8
8
|
type __VLS_Props = {
|
|
9
9
|
rowId: string;
|
|
@@ -20,6 +20,9 @@ type __VLS_Props = {
|
|
|
20
20
|
checkbox?: boolean;
|
|
21
21
|
/** The row is being revealed or concealed by a branch opening or closing. */
|
|
22
22
|
motion?: TableTreeMotion;
|
|
23
|
+
/** Set on a group's header row, which spans the columns instead of filling them. */
|
|
24
|
+
group?: TableGroup;
|
|
25
|
+
colSpan?: number;
|
|
23
26
|
};
|
|
24
27
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
28
|
select: (event: MouseEvent) => any;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import TableCell from "./table-cell";
|
|
3
|
+
import TableGroupHeader from "./table-group-header.vue";
|
|
3
4
|
import TableTreeControl from "./table-tree-control.vue";
|
|
4
5
|
defineProps({
|
|
5
6
|
rowId: { type: String, required: true },
|
|
@@ -12,13 +13,27 @@ defineProps({
|
|
|
12
13
|
node: { type: Object, required: false },
|
|
13
14
|
expanded: { type: Boolean, required: false },
|
|
14
15
|
checkbox: { type: Boolean, required: false },
|
|
15
|
-
motion: { type: Object, required: false }
|
|
16
|
+
motion: { type: Object, required: false },
|
|
17
|
+
group: { type: Object, required: false },
|
|
18
|
+
colSpan: { type: Number, required: false }
|
|
16
19
|
});
|
|
17
20
|
const emit = defineEmits(["toggle", "select"]);
|
|
18
21
|
</script>
|
|
19
22
|
|
|
20
23
|
<template>
|
|
21
24
|
<tr
|
|
25
|
+
v-if="group"
|
|
26
|
+
class="nut-dl-row nut-dl-row--group"
|
|
27
|
+
:class="rowClass"
|
|
28
|
+
:data-index="index"
|
|
29
|
+
:data-row-id="rowId"
|
|
30
|
+
>
|
|
31
|
+
<td :colspan="colSpan" class="nut-dl-group-cell">
|
|
32
|
+
<TableGroupHeader :group="group" selectable />
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
<tr
|
|
36
|
+
v-else
|
|
22
37
|
class="nut-dl-row group/row"
|
|
23
38
|
:class="[
|
|
24
39
|
selected ? 'nut-dl-row--selected' : '',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* so rows that stay on screen while the virtual window slides keep their cells untouched.
|
|
4
4
|
*/
|
|
5
5
|
import type { HTMLAttributes } from 'vue';
|
|
6
|
-
import type { GenericObject, TableTreeMotion, TableTreeNode } from '../../types/index.js';
|
|
6
|
+
import type { GenericObject, TableGroup, TableTreeMotion, TableTreeNode } from '../../types/index.js';
|
|
7
7
|
import type { tableRowCells } from './table-layout.js';
|
|
8
8
|
type __VLS_Props = {
|
|
9
9
|
rowId: string;
|
|
@@ -20,6 +20,9 @@ type __VLS_Props = {
|
|
|
20
20
|
checkbox?: boolean;
|
|
21
21
|
/** The row is being revealed or concealed by a branch opening or closing. */
|
|
22
22
|
motion?: TableTreeMotion;
|
|
23
|
+
/** Set on a group's header row, which spans the columns instead of filling them. */
|
|
24
|
+
group?: TableGroup;
|
|
25
|
+
colSpan?: number;
|
|
23
26
|
};
|
|
24
27
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
28
|
select: (event: MouseEvent) => any;
|
|
@@ -5,6 +5,8 @@ type __VLS_Props = {
|
|
|
5
5
|
row?: GenericObject;
|
|
6
6
|
/** Where the record sits in the rendered rows: it names the record when the schema has no `rowLabel`. */
|
|
7
7
|
index?: number;
|
|
8
|
+
/** Names what it selects when that is neither one record nor every row. */
|
|
9
|
+
label?: string;
|
|
8
10
|
};
|
|
9
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
12
|
toggle: (event: MouseEvent) => any;
|
|
@@ -7,7 +7,8 @@ import { mergeDataListProps, mergeDataListUiClass } from "../../utils";
|
|
|
7
7
|
const props = defineProps({
|
|
8
8
|
modelValue: { type: [Boolean, String], required: true },
|
|
9
9
|
row: { type: Object, required: false },
|
|
10
|
-
index: { type: Number, required: false }
|
|
10
|
+
index: { type: Number, required: false },
|
|
11
|
+
label: { type: String, required: false }
|
|
11
12
|
});
|
|
12
13
|
const emit = defineEmits(["toggle"]);
|
|
13
14
|
const dataListUi = useDataListUi();
|
|
@@ -20,7 +21,7 @@ const areaClass = computed(
|
|
|
20
21
|
)
|
|
21
22
|
);
|
|
22
23
|
const ariaLabel = computed(
|
|
23
|
-
() => props.row ? labels.selectRow({ index: props.index ?? 0, row: props.row }) : labels.selectAll()
|
|
24
|
+
() => props.label ?? (props.row ? labels.selectRow({ index: props.index ?? 0, row: props.row }) : labels.selectAll())
|
|
24
25
|
);
|
|
25
26
|
const checkboxProps = computed(
|
|
26
27
|
() => mergeDataListProps(
|
|
@@ -5,6 +5,8 @@ type __VLS_Props = {
|
|
|
5
5
|
row?: GenericObject;
|
|
6
6
|
/** Where the record sits in the rendered rows: it names the record when the schema has no `rowLabel`. */
|
|
7
7
|
index?: number;
|
|
8
|
+
/** Names what it selects when that is neither one record nor every row. */
|
|
9
|
+
label?: string;
|
|
8
10
|
};
|
|
9
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
12
|
toggle: (event: MouseEvent) => any;
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import type { ComputedRef } from 'vue';
|
|
2
|
-
import type { GenericObject, TableExternalState, TableGridMode, TableSchemaView } from '../types/index.js';
|
|
2
|
+
import type { GenericObject, TableExternalState, TableGridMode, TableGroup, TableSchemaView } from '../types/index.js';
|
|
3
|
+
import type { useTableGroups } from './use-table-groups.js';
|
|
3
4
|
export interface GridRowChunk<TRow = unknown> {
|
|
4
5
|
index: number;
|
|
5
6
|
start: number;
|
|
6
7
|
end: number;
|
|
7
8
|
rows: TRow[];
|
|
9
|
+
/** The group this chunk is the header of; a header holds no rows. */
|
|
10
|
+
group?: TableGroup;
|
|
8
11
|
}
|
|
9
12
|
export interface UseTableGridParams<TRow extends GenericObject = GenericObject> {
|
|
10
13
|
schema: ComputedRef<TableSchemaView>;
|
|
11
14
|
data: ComputedRef<TableExternalState<TRow>>;
|
|
15
|
+
groups: ReturnType<typeof useTableGroups>;
|
|
12
16
|
}
|
|
13
17
|
export declare function useTableGrid<TRow extends GenericObject>(options: UseTableGridParams<TRow>): {
|
|
14
18
|
cardsPerRow: ComputedRef<number>;
|
|
15
19
|
columnCount: ComputedRef<number>;
|
|
16
20
|
itemColumnSpan: ComputedRef<number>;
|
|
17
21
|
mode: ComputedRef<TableGridMode>;
|
|
18
|
-
rowChunks: ComputedRef<GridRowChunk<
|
|
19
|
-
rows: ComputedRef<
|
|
22
|
+
rowChunks: ComputedRef<GridRowChunk<object>[]>;
|
|
23
|
+
rows: ComputedRef<object[]>;
|
|
20
24
|
};
|
|
@@ -19,9 +19,15 @@ export function useTableGrid(options) {
|
|
|
19
19
|
const cardsPerRow = computed(
|
|
20
20
|
() => Math.max(1, Math.floor(columnCount.value / itemColumnSpan.value))
|
|
21
21
|
);
|
|
22
|
-
const rows = computed(
|
|
22
|
+
const rows = computed(
|
|
23
|
+
() => options.groups.enabled.value ? options.groups.orderedRows.value : options.data.value.rows
|
|
24
|
+
);
|
|
23
25
|
const rowChunks = computed(
|
|
24
|
-
() =>
|
|
26
|
+
() => options.groups.enabled.value ? chunkGroups({
|
|
27
|
+
cardsPerRow: cardsPerRow.value,
|
|
28
|
+
groups: options.groups.groups.value,
|
|
29
|
+
isCollapsed: options.groups.isCollapsed
|
|
30
|
+
}) : chunkRows({
|
|
25
31
|
cardsPerRow: cardsPerRow.value,
|
|
26
32
|
rows: rows.value
|
|
27
33
|
})
|
|
@@ -48,6 +54,25 @@ function resolveResponsiveGridNumber(value) {
|
|
|
48
54
|
function isResponsiveGridValueResolver(value) {
|
|
49
55
|
return isFunction(value);
|
|
50
56
|
}
|
|
57
|
+
function chunkGroups(options) {
|
|
58
|
+
const chunks = [];
|
|
59
|
+
let offset = 0;
|
|
60
|
+
for (const group of options.groups) {
|
|
61
|
+
chunks.push({ end: offset, group, index: chunks.length, rows: [], start: offset });
|
|
62
|
+
if (!options.isCollapsed(group)) {
|
|
63
|
+
for (const chunk of chunkRows({ cardsPerRow: options.cardsPerRow, rows: group.rows })) {
|
|
64
|
+
chunks.push({
|
|
65
|
+
...chunk,
|
|
66
|
+
end: offset + chunk.end,
|
|
67
|
+
index: chunks.length,
|
|
68
|
+
start: offset + chunk.start
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
offset += group.rows.length;
|
|
73
|
+
}
|
|
74
|
+
return chunks;
|
|
75
|
+
}
|
|
51
76
|
function chunkRows(options) {
|
|
52
77
|
return Array.from(
|
|
53
78
|
{ length: Math.ceil(options.rows.length / options.cardsPerRow) },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
import type { TableGroup, TableSchemaView, TableTreeMotion } from '../types/index.js';
|
|
3
|
+
import type { UseTableDataReturn } from './use-table-data.js';
|
|
4
|
+
import type { useTableLayout } from './use-table-layout.js';
|
|
5
|
+
export interface UseTableGroupsParams {
|
|
6
|
+
schema: ComputedRef<TableSchemaView>;
|
|
7
|
+
layout: ReturnType<typeof useTableLayout>;
|
|
8
|
+
queryContent: Pick<UseTableDataReturn, 'data'>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Owns row groups: which groups the loaded rows form, which are folded, and the motion of folding
|
|
12
|
+
* one. The data stays flat; `displayRows` adds the header rows to what the table renders.
|
|
13
|
+
*
|
|
14
|
+
* Fold state is the schema's `collapsed` plus the groups the reader toggled, kept per table so a
|
|
15
|
+
* trip to a record and back finds the list as it was left. A table with `table.tree` has its own
|
|
16
|
+
* hierarchy and is not grouped.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useTableGroups(params: UseTableGroupsParams): {
|
|
19
|
+
config: ComputedRef<import("../types/index.js").TableGroupBySchema<object> | undefined>;
|
|
20
|
+
displayRows: ComputedRef<object[]>;
|
|
21
|
+
enabled: ComputedRef<boolean>;
|
|
22
|
+
groups: ComputedRef<TableGroup<object>[]>;
|
|
23
|
+
isCollapsed: (group: TableGroup) => boolean;
|
|
24
|
+
motionOf: (rowId: string) => TableTreeMotion | undefined;
|
|
25
|
+
orderedRows: ComputedRef<object[]>;
|
|
26
|
+
toggle: (group: TableGroup) => void;
|
|
27
|
+
visibleRowIds: ComputedRef<string[]>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { usePreferredReducedMotion } from "@vueuse/core";
|
|
2
|
+
import { computed, getCurrentInstance, onMounted, onScopeDispose, ref, shallowRef } from "vue";
|
|
3
|
+
import {
|
|
4
|
+
ROW_CONCEAL_MS,
|
|
5
|
+
ROW_MOTION_LIMIT,
|
|
6
|
+
ROW_REVEAL_MS,
|
|
7
|
+
ROW_STAGGER_LIMIT,
|
|
8
|
+
ROW_STAGGER_MS
|
|
9
|
+
} from "../constants/row-motion.js";
|
|
10
|
+
import {
|
|
11
|
+
buildTableGroups,
|
|
12
|
+
parseStoredGroupKeys,
|
|
13
|
+
tableGroupRow,
|
|
14
|
+
tableGroupsStorageKey
|
|
15
|
+
} from "../utils/groups.js";
|
|
16
|
+
import { resolveTableRowId } from "../utils/rows.js";
|
|
17
|
+
export function useTableGroups(params) {
|
|
18
|
+
const reducedMotion = usePreferredReducedMotion();
|
|
19
|
+
const config = computed(() => {
|
|
20
|
+
const { grid, table } = params.schema.value;
|
|
21
|
+
if (params.layout.activeLayout.value === "grid") return grid?.groupBy;
|
|
22
|
+
return table?.tree ? void 0 : table?.groupBy;
|
|
23
|
+
});
|
|
24
|
+
const enabled = computed(() => Boolean(config.value));
|
|
25
|
+
const groups = computed(() => {
|
|
26
|
+
const groupBy = config.value;
|
|
27
|
+
if (!groupBy) return [];
|
|
28
|
+
return buildTableGroups({
|
|
29
|
+
getRowId: (row, index) => String(resolveTableRowId({ index, row, rowKey: params.schema.value.rowKey })),
|
|
30
|
+
groupBy,
|
|
31
|
+
rows: params.queryContent.data.value.rows
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
const toggled = ref(/* @__PURE__ */ new Set());
|
|
35
|
+
const persists = computed(() => params.schema.value.persistence?.preferences !== false);
|
|
36
|
+
function storage() {
|
|
37
|
+
try {
|
|
38
|
+
return typeof window === "undefined" || !persists.value ? null : window.localStorage;
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function restore() {
|
|
44
|
+
try {
|
|
45
|
+
const raw = storage()?.getItem(tableGroupsStorageKey(params.schema.value.tableKey)) ?? null;
|
|
46
|
+
toggled.value = new Set(parseStoredGroupKeys(raw));
|
|
47
|
+
} catch {
|
|
48
|
+
toggled.value = /* @__PURE__ */ new Set();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function remember() {
|
|
52
|
+
const key = tableGroupsStorageKey(params.schema.value.tableKey);
|
|
53
|
+
try {
|
|
54
|
+
if (toggled.value.size) storage()?.setItem(key, JSON.stringify([...toggled.value]));
|
|
55
|
+
else storage()?.removeItem(key);
|
|
56
|
+
} catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (getCurrentInstance()) onMounted(restore);
|
|
61
|
+
else restore();
|
|
62
|
+
function startsCollapsed(group) {
|
|
63
|
+
const collapsed = config.value?.collapsed;
|
|
64
|
+
return typeof collapsed === "function" ? collapsed({ rows: group.rows, value: group.value }) : Boolean(collapsed);
|
|
65
|
+
}
|
|
66
|
+
function isCollapsed(group) {
|
|
67
|
+
return startsCollapsed(group) !== toggled.value.has(group.key);
|
|
68
|
+
}
|
|
69
|
+
function flip(group) {
|
|
70
|
+
const next = new Set(toggled.value);
|
|
71
|
+
if (!next.delete(group.key)) next.add(group.key);
|
|
72
|
+
toggled.value = next;
|
|
73
|
+
remember();
|
|
74
|
+
}
|
|
75
|
+
const motion = shallowRef(null);
|
|
76
|
+
const closing = ref(null);
|
|
77
|
+
let timer;
|
|
78
|
+
function settle() {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
closing.value = null;
|
|
81
|
+
motion.value = null;
|
|
82
|
+
}
|
|
83
|
+
function toggle(group) {
|
|
84
|
+
settle();
|
|
85
|
+
const opening = isCollapsed(group);
|
|
86
|
+
flip(group);
|
|
87
|
+
if (reducedMotion.value === "reduce" || params.layout.activeLayout.value === "grid") return;
|
|
88
|
+
const moving = group.rowIds.slice(0, ROW_MOTION_LIMIT);
|
|
89
|
+
motion.value = new Map(
|
|
90
|
+
moving.map((rowId, order) => [
|
|
91
|
+
rowId,
|
|
92
|
+
{ kind: opening ? "enter" : "leave", order: Math.min(order, ROW_STAGGER_LIMIT) }
|
|
93
|
+
])
|
|
94
|
+
);
|
|
95
|
+
if (opening) {
|
|
96
|
+
timer = setTimeout(
|
|
97
|
+
settle,
|
|
98
|
+
ROW_REVEAL_MS + Math.min(moving.length, ROW_STAGGER_LIMIT) * ROW_STAGGER_MS
|
|
99
|
+
);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
closing.value = group.key;
|
|
103
|
+
timer = setTimeout(settle, ROW_CONCEAL_MS);
|
|
104
|
+
}
|
|
105
|
+
onScopeDispose(() => clearTimeout(timer));
|
|
106
|
+
function motionOf(rowId) {
|
|
107
|
+
return motion.value?.get(rowId);
|
|
108
|
+
}
|
|
109
|
+
function rowsListed(group) {
|
|
110
|
+
return !isCollapsed(group) || closing.value === group.key;
|
|
111
|
+
}
|
|
112
|
+
const displayRows = computed(
|
|
113
|
+
() => groups.value.flatMap((group) => [
|
|
114
|
+
tableGroupRow(group),
|
|
115
|
+
...rowsListed(group) ? group.rows : []
|
|
116
|
+
])
|
|
117
|
+
);
|
|
118
|
+
const orderedRows = computed(() => groups.value.flatMap((group) => group.rows));
|
|
119
|
+
const visibleRowIds = computed(
|
|
120
|
+
() => groups.value.flatMap((group) => isCollapsed(group) ? [] : group.rowIds)
|
|
121
|
+
);
|
|
122
|
+
return {
|
|
123
|
+
config,
|
|
124
|
+
displayRows,
|
|
125
|
+
enabled,
|
|
126
|
+
groups,
|
|
127
|
+
isCollapsed,
|
|
128
|
+
motionOf,
|
|
129
|
+
orderedRows,
|
|
130
|
+
toggle,
|
|
131
|
+
visibleRowIds
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -228,6 +228,17 @@ declare function createTableInternals<TSchema>(options: {
|
|
|
228
228
|
rowChunks: import("vue").ComputedRef<import("./use-table-grid.js").GridRowChunk<object>[]>;
|
|
229
229
|
rows: import("vue").ComputedRef<object[]>;
|
|
230
230
|
};
|
|
231
|
+
groups: {
|
|
232
|
+
config: import("vue").ComputedRef<import("../types/index.js").TableGroupBySchema<object> | undefined>;
|
|
233
|
+
displayRows: import("vue").ComputedRef<object[]>;
|
|
234
|
+
enabled: import("vue").ComputedRef<boolean>;
|
|
235
|
+
groups: import("vue").ComputedRef<import("../types/index.js").TableGroup<object>[]>;
|
|
236
|
+
isCollapsed: (group: import("../types/index.js").TableGroup) => boolean;
|
|
237
|
+
motionOf: (rowId: string) => import("../types/index.js").TableTreeMotion | undefined;
|
|
238
|
+
orderedRows: import("vue").ComputedRef<object[]>;
|
|
239
|
+
toggle: (group: import("../types/index.js").TableGroup) => void;
|
|
240
|
+
visibleRowIds: import("vue").ComputedRef<string[]>;
|
|
241
|
+
};
|
|
231
242
|
layout: {
|
|
232
243
|
activeLayout: import("vue").WritableComputedRef<import("../types/index.js").TableLayout, import("../types/index.js").TableLayout>;
|
|
233
244
|
gridEnabled: import("vue").ComputedRef<boolean>;
|
|
@@ -345,6 +356,9 @@ declare function createTableInternals<TSchema>(options: {
|
|
|
345
356
|
row: import("../types/index.js").GenericObject;
|
|
346
357
|
index?: number;
|
|
347
358
|
}) => string;
|
|
359
|
+
getRowsSelectionState: (params: {
|
|
360
|
+
rowIds: string[];
|
|
361
|
+
}) => boolean | "indeterminate";
|
|
348
362
|
isRowSelected: (params: {
|
|
349
363
|
rowId: string;
|
|
350
364
|
}) => boolean;
|
|
@@ -851,6 +865,17 @@ declare function useProvideTableInternals<TSchema>(options: {
|
|
|
851
865
|
rowChunks: import("vue").ComputedRef<import("./use-table-grid.js").GridRowChunk<object>[]>;
|
|
852
866
|
rows: import("vue").ComputedRef<object[]>;
|
|
853
867
|
};
|
|
868
|
+
groups: {
|
|
869
|
+
config: import("vue").ComputedRef<import("../types/index.js").TableGroupBySchema<object> | undefined>;
|
|
870
|
+
displayRows: import("vue").ComputedRef<object[]>;
|
|
871
|
+
enabled: import("vue").ComputedRef<boolean>;
|
|
872
|
+
groups: import("vue").ComputedRef<import("../types/index.js").TableGroup<object>[]>;
|
|
873
|
+
isCollapsed: (group: import("../types/index.js").TableGroup) => boolean;
|
|
874
|
+
motionOf: (rowId: string) => import("../types/index.js").TableTreeMotion | undefined;
|
|
875
|
+
orderedRows: import("vue").ComputedRef<object[]>;
|
|
876
|
+
toggle: (group: import("../types/index.js").TableGroup) => void;
|
|
877
|
+
visibleRowIds: import("vue").ComputedRef<string[]>;
|
|
878
|
+
};
|
|
854
879
|
layout: {
|
|
855
880
|
activeLayout: import("vue").WritableComputedRef<import("../types/index.js").TableLayout, import("../types/index.js").TableLayout>;
|
|
856
881
|
gridEnabled: import("vue").ComputedRef<boolean>;
|
|
@@ -968,6 +993,9 @@ declare function useProvideTableInternals<TSchema>(options: {
|
|
|
968
993
|
row: import("../types/index.js").GenericObject;
|
|
969
994
|
index?: number;
|
|
970
995
|
}) => string;
|
|
996
|
+
getRowsSelectionState: (params: {
|
|
997
|
+
rowIds: string[];
|
|
998
|
+
}) => boolean | "indeterminate";
|
|
971
999
|
isRowSelected: (params: {
|
|
972
1000
|
rowId: string;
|
|
973
1001
|
}) => boolean;
|
|
@@ -1471,6 +1499,17 @@ declare function useTableInternals(): {
|
|
|
1471
1499
|
rowChunks: import("vue").ComputedRef<import("./use-table-grid.js").GridRowChunk<object>[]>;
|
|
1472
1500
|
rows: import("vue").ComputedRef<object[]>;
|
|
1473
1501
|
};
|
|
1502
|
+
groups: {
|
|
1503
|
+
config: import("vue").ComputedRef<import("../types/index.js").TableGroupBySchema<object> | undefined>;
|
|
1504
|
+
displayRows: import("vue").ComputedRef<object[]>;
|
|
1505
|
+
enabled: import("vue").ComputedRef<boolean>;
|
|
1506
|
+
groups: import("vue").ComputedRef<import("../types/index.js").TableGroup<object>[]>;
|
|
1507
|
+
isCollapsed: (group: import("../types/index.js").TableGroup) => boolean;
|
|
1508
|
+
motionOf: (rowId: string) => import("../types/index.js").TableTreeMotion | undefined;
|
|
1509
|
+
orderedRows: import("vue").ComputedRef<object[]>;
|
|
1510
|
+
toggle: (group: import("../types/index.js").TableGroup) => void;
|
|
1511
|
+
visibleRowIds: import("vue").ComputedRef<string[]>;
|
|
1512
|
+
};
|
|
1474
1513
|
layout: {
|
|
1475
1514
|
activeLayout: import("vue").WritableComputedRef<import("../types/index.js").TableLayout, import("../types/index.js").TableLayout>;
|
|
1476
1515
|
gridEnabled: import("vue").ComputedRef<boolean>;
|
|
@@ -1588,6 +1627,9 @@ declare function useTableInternals(): {
|
|
|
1588
1627
|
row: import("../types/index.js").GenericObject;
|
|
1589
1628
|
index?: number;
|
|
1590
1629
|
}) => string;
|
|
1630
|
+
getRowsSelectionState: (params: {
|
|
1631
|
+
rowIds: string[];
|
|
1632
|
+
}) => boolean | "indeterminate";
|
|
1591
1633
|
isRowSelected: (params: {
|
|
1592
1634
|
rowId: string;
|
|
1593
1635
|
}) => boolean;
|
|
@@ -8,6 +8,7 @@ import { useTableData } from "./use-table-data.js";
|
|
|
8
8
|
import { useTableFilterPresentation } from "./use-table-filter-presentation.js";
|
|
9
9
|
import { useTableFilters } from "./use-table-filters.js";
|
|
10
10
|
import { useTableGrid } from "./use-table-grid.js";
|
|
11
|
+
import { useTableGroups } from "./use-table-groups.js";
|
|
11
12
|
import { useTableLayout } from "./use-table-layout.js";
|
|
12
13
|
import { useTablePagination } from "./use-table-pagination.js";
|
|
13
14
|
import { useTableRowSummary } from "./use-table-row-summary.js";
|
|
@@ -35,7 +36,13 @@ function createTableInternals(options) {
|
|
|
35
36
|
queryContent,
|
|
36
37
|
schema
|
|
37
38
|
});
|
|
39
|
+
const groups = useTableGroups({
|
|
40
|
+
layout,
|
|
41
|
+
queryContent,
|
|
42
|
+
schema
|
|
43
|
+
});
|
|
38
44
|
const selection = useTableSelection({
|
|
45
|
+
groups,
|
|
39
46
|
queryContent,
|
|
40
47
|
schema,
|
|
41
48
|
tree
|
|
@@ -60,6 +67,7 @@ function createTableInternals(options) {
|
|
|
60
67
|
});
|
|
61
68
|
const grid = useTableGrid({
|
|
62
69
|
data: queryContent.data,
|
|
70
|
+
groups,
|
|
63
71
|
schema
|
|
64
72
|
});
|
|
65
73
|
const tableColumns = useTableColumns({
|
|
@@ -112,6 +120,7 @@ function createTableInternals(options) {
|
|
|
112
120
|
filterPresentation,
|
|
113
121
|
filters,
|
|
114
122
|
grid,
|
|
123
|
+
groups,
|
|
115
124
|
layout,
|
|
116
125
|
pagination,
|
|
117
126
|
queryContent,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { ComputedRef } from 'vue';
|
|
2
2
|
import type { GenericObject, TableSchemaView } from '../types/index.js';
|
|
3
3
|
import type { useTableData } from './use-table-data.js';
|
|
4
|
+
import type { useTableGroups } from './use-table-groups.js';
|
|
4
5
|
import type { useTableTree } from './use-table-tree.js';
|
|
5
6
|
export interface UseTableSelectionParams {
|
|
6
7
|
schema: ComputedRef<TableSchemaView>;
|
|
7
8
|
queryContent: ReturnType<typeof useTableData>;
|
|
8
9
|
tree: ReturnType<typeof useTableTree>;
|
|
10
|
+
groups: ReturnType<typeof useTableGroups>;
|
|
9
11
|
}
|
|
10
12
|
export declare function useTableSelection(options: UseTableSelectionParams): {
|
|
11
13
|
allSelected: ComputedRef<boolean>;
|
|
@@ -15,6 +17,9 @@ export declare function useTableSelection(options: UseTableSelectionParams): {
|
|
|
15
17
|
row: GenericObject;
|
|
16
18
|
index?: number;
|
|
17
19
|
}) => string;
|
|
20
|
+
getRowsSelectionState: (params: {
|
|
21
|
+
rowIds: string[];
|
|
22
|
+
}) => boolean | "indeterminate";
|
|
18
23
|
isRowSelected: (params: {
|
|
19
24
|
rowId: string;
|
|
20
25
|
}) => boolean;
|