mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
feat: draggable datatable tablehead
This commit is contained in:
@@ -12,23 +12,57 @@
|
|||||||
</colgroup>
|
</colgroup>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
|
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
|
||||||
<TableHead
|
<template v-if="enableColumnReorder">
|
||||||
v-for="header in headerGroup.headers"
|
<DragDropProvider @dragEnd="onHeaderDragEnd">
|
||||||
:key="header.id"
|
<template v-for="(header, hIdx) in headerGroup.headers" :key="header.id">
|
||||||
:class="getHeaderClass(header)"
|
<SortableTableHead
|
||||||
:style="getPinnedStyle(header.column)">
|
v-if="isReorderable(header)"
|
||||||
<template v-if="!header.isPlaceholder">
|
:header="header"
|
||||||
<FlexRender :render="header.column.columnDef.header" :props="header.getContext()" />
|
:index="reorderableIndex(headerGroup.headers, hIdx)"
|
||||||
<div
|
:header-class="getHeaderClass(header)"
|
||||||
v-if="header.column.getCanResize?.()"
|
:pinned-style="getPinnedStyle(header.column)" />
|
||||||
class="absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none select-none opacity-0 transition-opacity group-hover:opacity-100"
|
<TableHead
|
||||||
@mousedown.stop="header.getResizeHandler?.()($event)"
|
v-else
|
||||||
@touchstart.stop="header.getResizeHandler?.()($event)">
|
:class="getHeaderClass(header)"
|
||||||
|
:style="getPinnedStyle(header.column)">
|
||||||
|
<template v-if="!header.isPlaceholder">
|
||||||
|
<FlexRender
|
||||||
|
:render="header.column.columnDef.header"
|
||||||
|
:props="header.getContext()" />
|
||||||
|
<div
|
||||||
|
v-if="header.column.getCanResize?.()"
|
||||||
|
class="absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none select-none opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
@mousedown.stop="header.getResizeHandler?.()($event)"
|
||||||
|
@touchstart.stop="header.getResizeHandler?.()($event)">
|
||||||
|
<div
|
||||||
|
class="absolute right-0 top-0 h-full w-px bg-border dark:bg-border dark:brightness-[2]" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TableHead>
|
||||||
|
</template>
|
||||||
|
</DragDropProvider>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<TableHead
|
||||||
|
v-for="header in headerGroup.headers"
|
||||||
|
:key="header.id"
|
||||||
|
:class="getHeaderClass(header)"
|
||||||
|
:style="getPinnedStyle(header.column)">
|
||||||
|
<template v-if="!header.isPlaceholder">
|
||||||
|
<FlexRender
|
||||||
|
:render="header.column.columnDef.header"
|
||||||
|
:props="header.getContext()" />
|
||||||
<div
|
<div
|
||||||
class="absolute right-0 top-0 h-full w-px bg-border dark:bg-border dark:brightness-[2]" />
|
v-if="header.column.getCanResize?.()"
|
||||||
</div>
|
class="absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none select-none opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
</template>
|
@mousedown.stop="header.getResizeHandler?.()($event)"
|
||||||
</TableHead>
|
@touchstart.stop="header.getResizeHandler?.()($event)">
|
||||||
|
<div
|
||||||
|
class="absolute right-0 top-0 h-full w-px bg-border dark:bg-border dark:brightness-[2]" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TableHead>
|
||||||
|
</template>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -144,8 +178,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
import { DragDropProvider } from '@dnd-kit/vue';
|
||||||
import { FlexRender } from '@tanstack/vue-table';
|
import { FlexRender } from '@tanstack/vue-table';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
import { isSortable } from '@dnd-kit/vue/sortable';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useAppearanceSettingsStore } from '@/stores/';
|
import { useAppearanceSettingsStore } from '@/stores/';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -163,6 +199,7 @@
|
|||||||
import { ContextMenu, ContextMenuTrigger } from '../context-menu';
|
import { ContextMenu, ContextMenuTrigger } from '../context-menu';
|
||||||
|
|
||||||
import DataTableEmpty from './DataTableEmpty.vue';
|
import DataTableEmpty from './DataTableEmpty.vue';
|
||||||
|
import SortableTableHead from './SortableTableHead.vue';
|
||||||
|
|
||||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||||
const { isDataTableStriped } = storeToRefs(appearanceSettingsStore);
|
const { isDataTableStriped } = storeToRefs(appearanceSettingsStore);
|
||||||
@@ -215,6 +252,10 @@
|
|||||||
rowClass: {
|
rowClass: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
enableColumnReorder: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -289,6 +330,65 @@
|
|||||||
return !!col?.columnDef?.meta?.stretch;
|
return !!col?.columnDef?.meta?.stretch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isReorderable = (header) => {
|
||||||
|
const col = header?.column;
|
||||||
|
if (!col) return false;
|
||||||
|
if (isSpacer(col)) return false;
|
||||||
|
if (getPinnedState(col)) return false;
|
||||||
|
if (col.columnDef?.meta?.disableReorder) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reorderableIndex = (headers, actualIndex) => {
|
||||||
|
let sortableIdx = 0;
|
||||||
|
for (let i = 0; i < actualIndex; i++) {
|
||||||
|
if (isReorderable(headers[i])) {
|
||||||
|
sortableIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sortableIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onHeaderDragEnd = (event) => {
|
||||||
|
if (event.canceled) return;
|
||||||
|
const { source } = event.operation;
|
||||||
|
if (!isSortable(source)) return;
|
||||||
|
|
||||||
|
const { initialIndex, index } = source;
|
||||||
|
if (initialIndex === index) return;
|
||||||
|
|
||||||
|
const allColumns = props.table.getVisibleLeafColumns?.() ?? [];
|
||||||
|
const reorderableIds = allColumns
|
||||||
|
.filter((col) => {
|
||||||
|
if (isSpacer(col)) return false;
|
||||||
|
if (getPinnedState(col)) return false;
|
||||||
|
if (col.columnDef?.meta?.disableReorder) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((col) => col.id);
|
||||||
|
|
||||||
|
const newOrder = [...reorderableIds];
|
||||||
|
const [moved] = newOrder.splice(initialIndex, 1);
|
||||||
|
newOrder.splice(index, 0, moved);
|
||||||
|
|
||||||
|
const fixedBefore = [];
|
||||||
|
const fixedAfter = [];
|
||||||
|
for (const col of allColumns) {
|
||||||
|
if (!reorderableIds.includes(col.id)) {
|
||||||
|
const colIdx = allColumns.indexOf(col);
|
||||||
|
const firstReorderableIdx = allColumns.findIndex((c) => reorderableIds.includes(c.id));
|
||||||
|
if (colIdx < firstReorderableIdx || firstReorderableIdx === -1) {
|
||||||
|
fixedBefore.push(col.id);
|
||||||
|
} else {
|
||||||
|
fixedAfter.push(col.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullOrder = [...fixedBefore, ...newOrder, ...fixedAfter];
|
||||||
|
props.table.setColumnOrder(fullOrder);
|
||||||
|
};
|
||||||
|
|
||||||
const getColStyle = (col) => {
|
const getColStyle = (col) => {
|
||||||
if (isSpacer(col)) return { width: '0px' };
|
if (isSpacer(col)) return { width: '0px' };
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { FlexRender } from '@tanstack/vue-table';
|
||||||
|
import { GripVertical } from 'lucide-vue-next';
|
||||||
|
import { useSortable } from '@dnd-kit/vue/sortable';
|
||||||
|
|
||||||
|
import { TableHead } from '../table';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
header: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
headerClass: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
pinnedStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = ref(null);
|
||||||
|
|
||||||
|
const { isDragSource } = useSortable({
|
||||||
|
id: computed(() => props.header.id),
|
||||||
|
index: computed(() => props.index),
|
||||||
|
element,
|
||||||
|
disabled: computed(() => props.disabled)
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TableHead
|
||||||
|
ref="element"
|
||||||
|
:class="[headerClass, isDragSource && 'opacity-50', !disabled && 'cursor-grab active:cursor-grabbing']"
|
||||||
|
:style="pinnedStyle">
|
||||||
|
<template v-if="!header.isPlaceholder">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<GripVertical
|
||||||
|
v-if="!disabled"
|
||||||
|
class="size-3.5 shrink-0 text-muted-foreground/50 -ml-1 mr-0.5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||||
|
<FlexRender :render="header.column.columnDef.header" :props="header.getContext()" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="header.column.getCanResize?.()"
|
||||||
|
class="absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none select-none opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
@mousedown.stop="header.getResizeHandler?.()($event)"
|
||||||
|
@touchstart.stop="header.getResizeHandler?.()($event)">
|
||||||
|
<div class="absolute right-0 top-0 h-full w-px bg-border dark:bg-border dark:brightness-[2]" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TableHead>
|
||||||
|
</template>
|
||||||
@@ -2,6 +2,10 @@ import { beforeEach, describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
import { useVrcxVueTable } from '../useVrcxVueTable';
|
import { useVrcxVueTable } from '../useVrcxVueTable';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {...any} ids
|
||||||
|
*/
|
||||||
function makeColumns(...ids) {
|
function makeColumns(...ids) {
|
||||||
return ids.map((id) => ({ id, header: id, accessorKey: id }));
|
return ids.map((id) => ({ id, header: id, accessorKey: id }));
|
||||||
}
|
}
|
||||||
@@ -123,4 +127,75 @@ describe('useVrcxVueTable persistence', () => {
|
|||||||
|
|
||||||
expect(sorting.value).toEqual([{ id: 'date', desc: true }]);
|
expect(sorting.value).toEqual([{ id: 'date', desc: true }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('persists columnOrder to localStorage when order changes', async () => {
|
||||||
|
const { columnOrder } = useVrcxVueTable({
|
||||||
|
data: [],
|
||||||
|
columns: makeColumns('name', 'date', 'status'),
|
||||||
|
persistKey: 'test-col-order'
|
||||||
|
});
|
||||||
|
|
||||||
|
columnOrder.value = ['date', 'name', 'status'];
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
|
||||||
|
const stored = JSON.parse(
|
||||||
|
localStorage.getItem('vrcx:table:test-col-order')
|
||||||
|
);
|
||||||
|
expect(stored).toBeTruthy();
|
||||||
|
expect(stored.columnOrder).toEqual(['date', 'name', 'status']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('restores persisted columnOrder on init', () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'vrcx:table:test-restore-order',
|
||||||
|
JSON.stringify({ columnOrder: ['status', 'name', 'date'] })
|
||||||
|
);
|
||||||
|
|
||||||
|
const { columnOrder } = useVrcxVueTable({
|
||||||
|
data: [],
|
||||||
|
columns: makeColumns('name', 'date', 'status'),
|
||||||
|
persistKey: 'test-restore-order'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(columnOrder.value).toEqual(['status', 'name', 'date']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters stale columnOrder entries on persist', async () => {
|
||||||
|
const { columnOrder } = useVrcxVueTable({
|
||||||
|
data: [],
|
||||||
|
columns: makeColumns('name', 'date'),
|
||||||
|
persistKey: 'test-stale-order'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Include a column ID that doesn't exist
|
||||||
|
columnOrder.value = ['removed_col', 'date', 'name'];
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
|
||||||
|
const stored = JSON.parse(
|
||||||
|
localStorage.getItem('vrcx:table:test-stale-order')
|
||||||
|
);
|
||||||
|
expect(stored).toBeTruthy();
|
||||||
|
// 'removed_col' should be filtered out
|
||||||
|
expect(stored.columnOrder).toEqual(['date', 'name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not persist columnOrder when persistColumnOrder is false', async () => {
|
||||||
|
const { columnOrder } = useVrcxVueTable({
|
||||||
|
data: [],
|
||||||
|
columns: makeColumns('name', 'date'),
|
||||||
|
persistKey: 'test-no-persist-order',
|
||||||
|
persistColumnOrder: false
|
||||||
|
});
|
||||||
|
|
||||||
|
columnOrder.value = ['date', 'name'];
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
|
||||||
|
const stored = JSON.parse(
|
||||||
|
localStorage.getItem('vrcx:table:test-no-persist-order')
|
||||||
|
);
|
||||||
|
expect(stored?.columnOrder).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import {
|
|||||||
} from '@tanstack/vue-table';
|
} from '@tanstack/vue-table';
|
||||||
import { computed, ref, unref, watch } from 'vue';
|
import { computed, ref, unref, watch } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
function safeJsonParse(str) {
|
function safeJsonParse(str) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return null;
|
return null;
|
||||||
@@ -20,6 +24,11 @@ function safeJsonParse(str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param fn
|
||||||
|
* @param wait
|
||||||
|
*/
|
||||||
function debounce(fn, wait) {
|
function debounce(fn, wait) {
|
||||||
let t = 0;
|
let t = 0;
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
@@ -30,6 +39,11 @@ function debounce(fn, wait) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param sizing
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
function filterSizingByColumns(sizing, columns) {
|
function filterSizingByColumns(sizing, columns) {
|
||||||
if (!sizing || typeof sizing !== 'object') {
|
if (!sizing || typeof sizing !== 'object') {
|
||||||
return {};
|
return {};
|
||||||
@@ -44,6 +58,11 @@ function filterSizingByColumns(sizing, columns) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param sorting
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
function filterSortingByColumns(sorting, columns) {
|
function filterSortingByColumns(sorting, columns) {
|
||||||
if (!Array.isArray(sorting)) {
|
if (!Array.isArray(sorting)) {
|
||||||
return [];
|
return [];
|
||||||
@@ -52,10 +71,31 @@ function filterSortingByColumns(sorting, columns) {
|
|||||||
return sorting.filter((s) => s && ids.has(s.id));
|
return sorting.filter((s) => s && ids.has(s.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param order
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
|
function filterOrderByColumns(order, columns) {
|
||||||
|
if (!Array.isArray(order)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const ids = new Set((columns ?? []).map((c) => c?.id).filter(Boolean));
|
||||||
|
return order.filter((id) => ids.has(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param col
|
||||||
|
*/
|
||||||
function getColumnId(col) {
|
function getColumnId(col) {
|
||||||
return col?.id ?? col?.accessorKey ?? null;
|
return col?.id ?? col?.accessorKey ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
function findStretchColumnId(columns) {
|
function findStretchColumnId(columns) {
|
||||||
if (!Array.isArray(columns)) {
|
if (!Array.isArray(columns)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -68,16 +108,32 @@ function findStretchColumnId(columns) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param updaterOrValue
|
||||||
|
* @param targetRef
|
||||||
|
*/
|
||||||
function setRef(updaterOrValue, targetRef) {
|
function setRef(updaterOrValue, targetRef) {
|
||||||
targetRef.value = isFunction(updaterOrValue)
|
targetRef.value = isFunction(updaterOrValue)
|
||||||
? updaterOrValue(targetRef.value)
|
? updaterOrValue(targetRef.value)
|
||||||
: updaterOrValue;
|
: updaterOrValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param func
|
||||||
|
*/
|
||||||
function resolveMaybeGetter(func) {
|
function resolveMaybeGetter(func) {
|
||||||
return typeof func === 'function' ? func() : unref(func);
|
return typeof func === 'function' ? func() : unref(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
* @param enabled
|
||||||
|
* @param spacerId
|
||||||
|
* @param stretchAfterId
|
||||||
|
*/
|
||||||
function withSpacerColumn(columns, enabled, spacerId, stretchAfterId) {
|
function withSpacerColumn(columns, enabled, spacerId, stretchAfterId) {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return columns;
|
return columns;
|
||||||
@@ -116,6 +172,10 @@ function withSpacerColumn(columns, enabled, spacerId, stretchAfterId) {
|
|||||||
return [...columns, spacerColumn];
|
return [...columns, spacerColumn];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
export function useVrcxVueTable(options) {
|
export function useVrcxVueTable(options) {
|
||||||
const {
|
const {
|
||||||
getRowId,
|
getRowId,
|
||||||
@@ -137,12 +197,16 @@ export function useVrcxVueTable(options) {
|
|||||||
columnResizeMode = 'onChange',
|
columnResizeMode = 'onChange',
|
||||||
initialColumnSizing,
|
initialColumnSizing,
|
||||||
|
|
||||||
|
enableColumnReorder = true,
|
||||||
|
initialColumnOrder,
|
||||||
|
|
||||||
fillRemainingSpace = true,
|
fillRemainingSpace = true,
|
||||||
spacerColumnId = '__spacer',
|
spacerColumnId = '__spacer',
|
||||||
|
|
||||||
persistKey,
|
persistKey,
|
||||||
persistColumnSizing = true,
|
persistColumnSizing = true,
|
||||||
persistSorting = true,
|
persistSorting = true,
|
||||||
|
persistColumnOrder = true,
|
||||||
persistDebounceMs = 200,
|
persistDebounceMs = 200,
|
||||||
|
|
||||||
tableOptions = {}
|
tableOptions = {}
|
||||||
@@ -157,9 +221,13 @@ export function useVrcxVueTable(options) {
|
|||||||
const pagination = ref(initialPagination ?? { pageIndex: 0, pageSize: 50 });
|
const pagination = ref(initialPagination ?? { pageIndex: 0, pageSize: 50 });
|
||||||
const columnPinning = ref(initialColumnPinning ?? { left: [], right: [] });
|
const columnPinning = ref(initialColumnPinning ?? { left: [], right: [] });
|
||||||
const columnSizing = ref(initialColumnSizing ?? {});
|
const columnSizing = ref(initialColumnSizing ?? {});
|
||||||
|
const columnOrder = ref(initialColumnOrder ?? []);
|
||||||
|
|
||||||
const storageKey = persistKey ? `vrcx:table:${persistKey}` : null;
|
const storageKey = persistKey ? `vrcx:table:${persistKey}` : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
function readPersisted() {
|
function readPersisted() {
|
||||||
if (!storageKey) {
|
if (!storageKey) {
|
||||||
return null;
|
return null;
|
||||||
@@ -167,6 +235,10 @@ export function useVrcxVueTable(options) {
|
|||||||
return safeJsonParse(localStorage.getItem(storageKey));
|
return safeJsonParse(localStorage.getItem(storageKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param patch
|
||||||
|
*/
|
||||||
function writePersisted(patch) {
|
function writePersisted(patch) {
|
||||||
if (!storageKey) {
|
if (!storageKey) {
|
||||||
return;
|
return;
|
||||||
@@ -188,11 +260,28 @@ export function useVrcxVueTable(options) {
|
|||||||
columnSizing.value = persisted.columnSizing;
|
columnSizing.value = persisted.columnSizing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
persisted &&
|
||||||
|
persistColumnOrder &&
|
||||||
|
Array.isArray(persisted.columnOrder)
|
||||||
|
) {
|
||||||
|
columnOrder.value = persisted.columnOrder;
|
||||||
|
}
|
||||||
|
|
||||||
const state = {};
|
const state = {};
|
||||||
const handlers = {};
|
const handlers = {};
|
||||||
const rowModels = {};
|
const rowModels = {};
|
||||||
const extra = {};
|
const extra = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param enabled
|
||||||
|
* @param key
|
||||||
|
* @param r
|
||||||
|
* @param onChangeKey
|
||||||
|
* @param rowModelPart
|
||||||
|
* @param extraPart
|
||||||
|
*/
|
||||||
function register(enabled, key, r, onChangeKey, rowModelPart, extraPart) {
|
function register(enabled, key, r, onChangeKey, rowModelPart, extraPart) {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
@@ -241,6 +330,13 @@ export function useVrcxVueTable(options) {
|
|||||||
{ enableColumnResizing: true, columnResizeMode }
|
{ enableColumnResizing: true, columnResizeMode }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
register(
|
||||||
|
enableColumnReorder,
|
||||||
|
'columnOrder',
|
||||||
|
columnOrder,
|
||||||
|
'onColumnOrderChange'
|
||||||
|
);
|
||||||
|
|
||||||
if (enableFiltering) {
|
if (enableFiltering) {
|
||||||
Object.assign(rowModels, {
|
Object.assign(rowModels, {
|
||||||
getFilteredRowModel: getFilteredRowModel()
|
getFilteredRowModel: getFilteredRowModel()
|
||||||
@@ -324,12 +420,26 @@ export function useVrcxVueTable(options) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (storageKey && persistColumnOrder) {
|
||||||
|
watch(
|
||||||
|
columnOrder,
|
||||||
|
(val) => {
|
||||||
|
const cols = table.getAllLeafColumns?.() ?? [];
|
||||||
|
persistWrite({
|
||||||
|
columnOrder: filterOrderByColumns(val, cols)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
table,
|
table,
|
||||||
sorting,
|
sorting,
|
||||||
pagination,
|
pagination,
|
||||||
expanded,
|
expanded,
|
||||||
columnPinning,
|
columnPinning,
|
||||||
columnSizing
|
columnSizing,
|
||||||
|
columnOrder
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user