diff --git a/src/components/ui/data-table/DataTableLayout.vue b/src/components/ui/data-table/DataTableLayout.vue index 27197da4..54476fd7 100644 --- a/src/components/ui/data-table/DataTableLayout.vue +++ b/src/components/ui/data-table/DataTableLayout.vue @@ -503,6 +503,19 @@ set: (size) => handlePageSizeChange(size) }); + // When the current pageSize is not in the available pageSizes list + watch( + [pageSizeProxy, () => props.pageSizes], + ([current, sizes]) => { + if (!sizes?.length || sizes.includes(current)) { + return; + } + const nearest = sizes.reduce((prev, s) => (Math.abs(s - current) < Math.abs(prev - current) ? s : prev)); + handlePageSizeChange(nearest); + }, + { immediate: true } + ); + const pageSizeValue = computed({ get: () => String(pageSizeProxy.value), set: (value) => handlePageSizeChange(Number(value)) diff --git a/src/lib/table/__tests__/useVrcxVueTable.test.js b/src/lib/table/__tests__/useVrcxVueTable.test.js index d868641b..71ae5b33 100644 --- a/src/lib/table/__tests__/useVrcxVueTable.test.js +++ b/src/lib/table/__tests__/useVrcxVueTable.test.js @@ -267,4 +267,69 @@ describe('useVrcxVueTable persistence', () => { ); expect(stored?.columnVisibility).toBeUndefined(); }); + + it('persists pageSize to localStorage when pagination changes', async () => { + const { table } = useVrcxVueTable({ + data: [], + columns: makeColumns('name', 'date'), + persistKey: 'test-page-size', + initialPagination: { pageIndex: 0, pageSize: 10 } + }); + + table.setPageSize(25); + + await new Promise((r) => setTimeout(r, 300)); + + const stored = JSON.parse( + localStorage.getItem('vrcx:table:test-page-size') + ); + expect(stored).toBeTruthy(); + expect(stored.pageSize).toBe(25); + }); + + it('restores persisted pageSize on init, overriding initialPagination', () => { + localStorage.setItem( + 'vrcx:table:test-restore-ps', + JSON.stringify({ pageSize: 50 }) + ); + + const { pagination } = useVrcxVueTable({ + data: [], + columns: makeColumns('name', 'date'), + persistKey: 'test-restore-ps', + initialPagination: { pageIndex: 0, pageSize: 10 } + }); + + expect(pagination.value.pageSize).toBe(50); + }); + + it('uses initialPagination.pageSize when no persisted data exists', () => { + const { pagination } = useVrcxVueTable({ + data: [], + columns: makeColumns('name', 'date'), + persistKey: 'test-initial-ps', + initialPagination: { pageIndex: 0, pageSize: 25 } + }); + + expect(pagination.value.pageSize).toBe(25); + }); + + it('does not persist pageSize when persistPageSize is false', async () => { + const { table } = useVrcxVueTable({ + data: [], + columns: makeColumns('name', 'date'), + persistKey: 'test-no-persist-ps', + persistPageSize: false, + initialPagination: { pageIndex: 0, pageSize: 10 } + }); + + table.setPageSize(50); + + await new Promise((r) => setTimeout(r, 300)); + + const stored = JSON.parse( + localStorage.getItem('vrcx:table:test-no-persist-ps') + ); + expect(stored?.pageSize).toBeUndefined(); + }); }); diff --git a/src/lib/table/useVrcxVueTable.js b/src/lib/table/useVrcxVueTable.js index 243c283b..4757df35 100644 --- a/src/lib/table/useVrcxVueTable.js +++ b/src/lib/table/useVrcxVueTable.js @@ -230,6 +230,7 @@ export function useVrcxVueTable(options) { persistSorting = true, persistColumnOrder = true, persistColumnVisibility = true, + persistPageSize = true, persistDebounceMs = 200, tableOptions = {} @@ -296,6 +297,18 @@ export function useVrcxVueTable(options) { columnVisibility.value = persisted.columnVisibility; } + if ( + persisted && + persistPageSize && + typeof persisted.pageSize === 'number' && + persisted.pageSize > 0 + ) { + pagination.value = { + ...pagination.value, + pageSize: persisted.pageSize + }; + } + const state = {}; const handlers = {}; const rowModels = {}; @@ -481,6 +494,17 @@ export function useVrcxVueTable(options) { ); } + if (storageKey && persistPageSize) { + watch( + () => pagination.value.pageSize, + (val) => { + if (typeof val === 'number' && val > 0) { + persistWrite({ pageSize: val }); + } + } + ); + } + return { table, sorting, diff --git a/src/views/Feed/Feed.vue b/src/views/Feed/Feed.vue index 467346fd..358c6b4e 100644 --- a/src/views/Feed/Feed.vue +++ b/src/views/Feed/Feed.vue @@ -120,6 +120,9 @@ const hasDateFilter = computed(() => !!(feedTable.value.dateFrom || feedTable.value.dateTo)); const activeFilterCount = computed(() => (hasDateFilter.value ? 1 : 0)); + /** + * + */ function applyDateFilter() { if (dateRange.value?.start) { const s = dateRange.value.start; @@ -137,6 +140,9 @@ feedTableLookup(); } + /** + * + */ function clearDateFilter() { dateRange.value = undefined; feedTable.value.dateFrom = ''; @@ -155,10 +161,11 @@ }); const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); - const pageSize = computed(() => - feedTable.value.pageSizeLinked ? appearanceSettingsStore.tablePageSize : feedTable.value.pageSize - ); + /** + * + * @param row + */ function getFeedRowId(row) { if (row?.id != null) return `id:${row.id}`; if (row?.rowId != null) return `row:${row.rowId}`; @@ -185,7 +192,7 @@ initialExpanded: {}, initialPagination: { pageIndex: 0, - pageSize: pageSize.value + pageSize: appearanceSettingsStore.tablePageSize }, tableOptions: { autoResetExpanded: false, @@ -200,11 +207,11 @@ }); const handlePageSizeChange = (size) => { - if (feedTable.value.pageSizeLinked) { - appearanceSettingsStore.setTablePageSize(size); - } else { - feedTable.value.pageSize = size; - } + pagination.value = { + ...pagination.value, + pageIndex: 0, + pageSize: size + }; }; const activeFilterSelection = computed(() => { @@ -215,6 +222,10 @@ return filter; }); + /** + * + * @param value + */ function handleFeedFilterChange(value) { const selected = Array.isArray(value) ? value : []; const wasAll = activeFilterSelection.value.includes('All'); @@ -230,16 +241,4 @@ } feedTableLookup(); } - - watch(pageSize, (size) => { - if (pagination.value.pageSize === size) { - return; - } - pagination.value = { - ...pagination.value, - pageIndex: 0, - pageSize: size - }; - table.setPageSize(size); - }); diff --git a/src/views/FriendList/FriendList.vue b/src/views/FriendList/FriendList.vue index 712364ab..6124a3f4 100644 --- a/src/views/FriendList/FriendList.vue +++ b/src/views/FriendList/FriendList.vue @@ -171,7 +171,6 @@ const selectedFriends = ref(new Set()); const friendsListDisplayData = ref([]); const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); - const pageSize = computed(() => appearanceSettingsStore.tablePageSize); const defaultSorting = [{ id: 'friendNumber', desc: true }]; // const initialColumnPinning = { @@ -212,7 +211,7 @@ initialSorting: defaultSorting, initialPagination: { pageIndex: 0, - pageSize: pageSize.value + pageSize: appearanceSettingsStore.tablePageSize } }); @@ -221,7 +220,11 @@ }); const handlePageSizeChange = (size) => { - appearanceSettingsStore.setTablePageSize(size); + pagination.value = { + ...pagination.value, + pageIndex: 0, + pageSize: size + }; }; const handleRowClick = (row) => { @@ -251,18 +254,6 @@ { immediate: true } ); - watch(pageSize, (size) => { - if (pagination.value.pageSize === size) { - return; - } - pagination.value = { - ...pagination.value, - pageIndex: 0, - pageSize: size - }; - table.setPageSize(size); - }); - const route = useRoute(); watch( @@ -280,6 +271,9 @@ } ); + /** + * + */ function friendsListSearchChange() { friendsListLoading.value = true; let query = ''; @@ -333,6 +327,10 @@ }); } + /** + * + * @param id + */ function toggleFriendSelection(id) { if (selectedFriends.value.has(id)) { selectedFriends.value.delete(id); @@ -341,12 +339,18 @@ } } + /** + * + */ function toggleFriendsListBulkUnfriendMode() { if (!friendsListBulkUnfriendMode.value) { selectedFriends.value.clear(); } } + /** + * + */ function showBulkUnfriendSelectionConfirm() { const pending = friendsListDisplayData.value .filter((item) => selectedFriends.value.has(item.id)) @@ -367,6 +371,9 @@ .catch(() => {}); } + /** + * + */ async function bulkUnfriendSelection() { if (!selectedFriends.value.size) return; const selectedFriendsCount = selectedFriends.value.size; @@ -384,6 +391,9 @@ selectedFriends.value.clear(); } + /** + * + */ async function friendsListLoadUsers() { const toFetch = Array.from(friends.value.values()) .filter((ctx) => ctx.ref && !ctx.ref.date_joined) @@ -419,21 +429,35 @@ } } + /** + * + */ function cancelFriendsListLoad() { friendsListLoading.value = false; friendsListLoadDialogVisible.value = false; } + /** + * + * @param val + */ function selectFriendsListRow(val) { if (!val) return; if (!val.id) emit('lookup-user', val); else showUserDialog(val.id); } + /** + * + */ function openChartsTab() { router.push({ name: 'charts' }); } + /** + * + * @param value + */ function handleFriendListFilterChange(value) { friendsListSearchFilters.value = Array.isArray(value) ? value : []; friendsListSearchChange(); diff --git a/src/views/FriendLog/FriendLog.vue b/src/views/FriendLog/FriendLog.vue index 97b12d19..05167d5b 100644 --- a/src/views/FriendLog/FriendLog.vue +++ b/src/views/FriendLog/FriendLog.vue @@ -169,9 +169,6 @@ }); const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); - const pageSize = computed(() => - friendLogTable.value.pageSizeLinked ? appearanceSettingsStore.tablePageSize : friendLogTable.value.pageSize - ); const { table, pagination } = useVrcxVueTable({ persistKey: 'friendLog', @@ -183,7 +180,7 @@ initialSorting: [], initialPagination: { pageIndex: 0, - pageSize: pageSize.value + pageSize: appearanceSettingsStore.tablePageSize }, tableOptions: { autoResetPageIndex: false @@ -195,22 +192,10 @@ }); const handlePageSizeChange = (size) => { - if (friendLogTable.value.pageSizeLinked) { - appearanceSettingsStore.setTablePageSize(size); - } else { - friendLogTable.value.pageSize = size; - } - }; - - watch(pageSize, (size) => { - if (pagination.value.pageSize === size) { - return; - } pagination.value = { ...pagination.value, pageIndex: 0, pageSize: size }; - table.setPageSize(size); - }); + }; diff --git a/src/views/GameLog/GameLog.vue b/src/views/GameLog/GameLog.vue index dbb5507b..a5aeadac 100644 --- a/src/views/GameLog/GameLog.vue +++ b/src/views/GameLog/GameLog.vue @@ -139,9 +139,6 @@ } const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); - const pageSize = computed(() => - gameLogTable.value.pageSizeLinked ? appearanceSettingsStore.tablePageSize : gameLogTable.value.pageSize - ); function getGameLogRowId(row) { if (row?.rowId != null) return `row:${row.rowId}`; @@ -165,7 +162,7 @@ initialSorting: [], initialPagination: { pageIndex: 0, - pageSize: pageSize.value + pageSize: appearanceSettingsStore.tablePageSize }, tableOptions: { autoResetPageIndex: false @@ -179,22 +176,10 @@ }); const handlePageSizeChange = (size) => { - if (gameLogTable.value.pageSizeLinked) { - appearanceSettingsStore.setTablePageSize(size); - } else { - gameLogTable.value.pageSize = size; - } - }; - - watch(pageSize, (size) => { - if (pagination.value.pageSize === size) { - return; - } pagination.value = { ...pagination.value, pageIndex: 0, pageSize: size }; - table.setPageSize(size); - }); + }; diff --git a/src/views/Moderation/Moderation.vue b/src/views/Moderation/Moderation.vue index 63f4e2d6..347c0810 100644 --- a/src/views/Moderation/Moderation.vue +++ b/src/views/Moderation/Moderation.vue @@ -152,11 +152,6 @@ }); const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); - const pageSize = computed(() => - playerModerationTable.value.pageSizeLinked - ? appearanceSettingsStore.tablePageSize - : playerModerationTable.value.pageSize - ); const { table, pagination } = useVrcxVueTable({ persistKey: 'moderation', @@ -168,7 +163,7 @@ initialSorting: [{ id: 'created', desc: true }], initialPagination: { pageIndex: 0, - pageSize: pageSize.value + pageSize: appearanceSettingsStore.tablePageSize } }); @@ -177,24 +172,12 @@ }); const handlePageSizeChange = (size) => { - if (playerModerationTable.value.pageSizeLinked) { - appearanceSettingsStore.setTablePageSize(size); - } else { - playerModerationTable.value.pageSize = size; - } - }; - - watch(pageSize, (size) => { - if (pagination.value.pageSize === size) { - return; - } pagination.value = { ...pagination.value, pageIndex: 0, pageSize: size }; - table.setPageSize(size); - }); + };