mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-19 14:53:50 +02:00
feat: draggable datatable tablehead
This commit is contained in:
@@ -2,6 +2,10 @@ import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { useVrcxVueTable } from '../useVrcxVueTable';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {...any} ids
|
||||
*/
|
||||
function makeColumns(...ids) {
|
||||
return ids.map((id) => ({ id, header: id, accessorKey: id }));
|
||||
}
|
||||
@@ -123,4 +127,75 @@ describe('useVrcxVueTable persistence', () => {
|
||||
|
||||
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';
|
||||
import { computed, ref, unref, watch } from 'vue';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str
|
||||
*/
|
||||
function safeJsonParse(str) {
|
||||
if (!str) {
|
||||
return null;
|
||||
@@ -20,6 +24,11 @@ function safeJsonParse(str) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn
|
||||
* @param wait
|
||||
*/
|
||||
function debounce(fn, wait) {
|
||||
let t = 0;
|
||||
return (...args) => {
|
||||
@@ -30,6 +39,11 @@ function debounce(fn, wait) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sizing
|
||||
* @param columns
|
||||
*/
|
||||
function filterSizingByColumns(sizing, columns) {
|
||||
if (!sizing || typeof sizing !== 'object') {
|
||||
return {};
|
||||
@@ -44,6 +58,11 @@ function filterSizingByColumns(sizing, columns) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sorting
|
||||
* @param columns
|
||||
*/
|
||||
function filterSortingByColumns(sorting, columns) {
|
||||
if (!Array.isArray(sorting)) {
|
||||
return [];
|
||||
@@ -52,10 +71,31 @@ function filterSortingByColumns(sorting, columns) {
|
||||
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) {
|
||||
return col?.id ?? col?.accessorKey ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param columns
|
||||
*/
|
||||
function findStretchColumnId(columns) {
|
||||
if (!Array.isArray(columns)) {
|
||||
return null;
|
||||
@@ -68,16 +108,32 @@ function findStretchColumnId(columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param updaterOrValue
|
||||
* @param targetRef
|
||||
*/
|
||||
function setRef(updaterOrValue, targetRef) {
|
||||
targetRef.value = isFunction(updaterOrValue)
|
||||
? updaterOrValue(targetRef.value)
|
||||
: updaterOrValue;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param func
|
||||
*/
|
||||
function resolveMaybeGetter(func) {
|
||||
return typeof func === 'function' ? func() : unref(func);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param columns
|
||||
* @param enabled
|
||||
* @param spacerId
|
||||
* @param stretchAfterId
|
||||
*/
|
||||
function withSpacerColumn(columns, enabled, spacerId, stretchAfterId) {
|
||||
if (!enabled) {
|
||||
return columns;
|
||||
@@ -116,6 +172,10 @@ function withSpacerColumn(columns, enabled, spacerId, stretchAfterId) {
|
||||
return [...columns, spacerColumn];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
export function useVrcxVueTable(options) {
|
||||
const {
|
||||
getRowId,
|
||||
@@ -137,12 +197,16 @@ export function useVrcxVueTable(options) {
|
||||
columnResizeMode = 'onChange',
|
||||
initialColumnSizing,
|
||||
|
||||
enableColumnReorder = true,
|
||||
initialColumnOrder,
|
||||
|
||||
fillRemainingSpace = true,
|
||||
spacerColumnId = '__spacer',
|
||||
|
||||
persistKey,
|
||||
persistColumnSizing = true,
|
||||
persistSorting = true,
|
||||
persistColumnOrder = true,
|
||||
persistDebounceMs = 200,
|
||||
|
||||
tableOptions = {}
|
||||
@@ -157,9 +221,13 @@ export function useVrcxVueTable(options) {
|
||||
const pagination = ref(initialPagination ?? { pageIndex: 0, pageSize: 50 });
|
||||
const columnPinning = ref(initialColumnPinning ?? { left: [], right: [] });
|
||||
const columnSizing = ref(initialColumnSizing ?? {});
|
||||
const columnOrder = ref(initialColumnOrder ?? []);
|
||||
|
||||
const storageKey = persistKey ? `vrcx:table:${persistKey}` : null;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function readPersisted() {
|
||||
if (!storageKey) {
|
||||
return null;
|
||||
@@ -167,6 +235,10 @@ export function useVrcxVueTable(options) {
|
||||
return safeJsonParse(localStorage.getItem(storageKey));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param patch
|
||||
*/
|
||||
function writePersisted(patch) {
|
||||
if (!storageKey) {
|
||||
return;
|
||||
@@ -188,11 +260,28 @@ export function useVrcxVueTable(options) {
|
||||
columnSizing.value = persisted.columnSizing;
|
||||
}
|
||||
|
||||
if (
|
||||
persisted &&
|
||||
persistColumnOrder &&
|
||||
Array.isArray(persisted.columnOrder)
|
||||
) {
|
||||
columnOrder.value = persisted.columnOrder;
|
||||
}
|
||||
|
||||
const state = {};
|
||||
const handlers = {};
|
||||
const rowModels = {};
|
||||
const extra = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param enabled
|
||||
* @param key
|
||||
* @param r
|
||||
* @param onChangeKey
|
||||
* @param rowModelPart
|
||||
* @param extraPart
|
||||
*/
|
||||
function register(enabled, key, r, onChangeKey, rowModelPart, extraPart) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
@@ -241,6 +330,13 @@ export function useVrcxVueTable(options) {
|
||||
{ enableColumnResizing: true, columnResizeMode }
|
||||
);
|
||||
|
||||
register(
|
||||
enableColumnReorder,
|
||||
'columnOrder',
|
||||
columnOrder,
|
||||
'onColumnOrderChange'
|
||||
);
|
||||
|
||||
if (enableFiltering) {
|
||||
Object.assign(rowModels, {
|
||||
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 {
|
||||
table,
|
||||
sorting,
|
||||
pagination,
|
||||
expanded,
|
||||
columnPinning,
|
||||
columnSizing
|
||||
columnSizing,
|
||||
columnOrder
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user