@coveord/plasma-mantine 55.4.0 → 55.5.1

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.
Files changed (37) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/.turbo/turbo-test.log +51 -46
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/cjs/components/table/index.d.ts +1 -0
  5. package/dist/cjs/components/table/index.d.ts.map +1 -1
  6. package/dist/cjs/components/table/index.js +4 -0
  7. package/dist/cjs/components/table/index.js.map +1 -1
  8. package/dist/cjs/components/table/table-predicate/TablePredicate.d.ts.map +1 -1
  9. package/dist/cjs/components/table/table-predicate/TablePredicate.js +4 -1
  10. package/dist/cjs/components/table/table-predicate/TablePredicate.js.map +1 -1
  11. package/dist/cjs/components/table/use-table.d.ts.map +1 -1
  12. package/dist/cjs/components/table/use-table.js +11 -7
  13. package/dist/cjs/components/table/use-table.js.map +1 -1
  14. package/dist/cjs/components/table/use-url-synced-state.d.ts +18 -7
  15. package/dist/cjs/components/table/use-url-synced-state.d.ts.map +1 -1
  16. package/dist/cjs/components/table/use-url-synced-state.js +81 -30
  17. package/dist/cjs/components/table/use-url-synced-state.js.map +1 -1
  18. package/dist/esm/components/table/index.d.ts +1 -0
  19. package/dist/esm/components/table/index.d.ts.map +1 -1
  20. package/dist/esm/components/table/index.js +1 -0
  21. package/dist/esm/components/table/index.js.map +1 -1
  22. package/dist/esm/components/table/table-predicate/TablePredicate.d.ts.map +1 -1
  23. package/dist/esm/components/table/table-predicate/TablePredicate.js +3 -0
  24. package/dist/esm/components/table/table-predicate/TablePredicate.js.map +1 -1
  25. package/dist/esm/components/table/use-table.d.ts.map +1 -1
  26. package/dist/esm/components/table/use-table.js +11 -7
  27. package/dist/esm/components/table/use-table.js.map +1 -1
  28. package/dist/esm/components/table/use-url-synced-state.d.ts +18 -7
  29. package/dist/esm/components/table/use-url-synced-state.d.ts.map +1 -1
  30. package/dist/esm/components/table/use-url-synced-state.js +57 -27
  31. package/dist/esm/components/table/use-url-synced-state.js.map +1 -1
  32. package/package.json +2 -2
  33. package/src/components/table/__tests__/use-url-synced-state.unit.spec.ts +74 -21
  34. package/src/components/table/index.ts +1 -0
  35. package/src/components/table/table-predicate/TablePredicate.tsx +1 -0
  36. package/src/components/table/use-table.ts +12 -7
  37. package/src/components/table/use-url-synced-state.ts +89 -39
@@ -1 +1 @@
1
- {"version":3,"file":"use-table.d.ts","sourceRoot":"","sources":["../../../../src/components/table/use-table.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-table.d.ts","sourceRoot":"","sources":["../../../../src/components/table/use-table.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,10 @@ const defaultState = {
28
28
  export const useTable = (userOptions = {})=>{
29
29
  const options = defaultsDeep({}, userOptions, defaultOptions);
30
30
  const initialState = defaultsDeep({}, options.initialState, defaultState);
31
+ /**
32
+ * The `useUrlSyncedState` hook defaults to synchronize, but the table wants to default to not synchronize,
33
+ * so always pass the sync option as a resolved boolean value.
34
+ */ const sync = !!options.syncWithUrl;
31
35
  // synced with url
32
36
  const [pagination, setPagination] = useUrlSyncedState({
33
37
  initialState: initialState.pagination,
@@ -45,7 +49,7 @@ export const useTable = (userOptions = {})=>{
45
49
  pageIndex: params.get('page') ? parseInt(params.get('page'), 10) - 1 : undefined,
46
50
  pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined
47
51
  }, initialState.pagination),
48
- sync: options.syncWithUrl
52
+ sync
49
53
  });
50
54
  const [sorting, setSorting] = useUrlSyncedState({
51
55
  initialState: initialState.sorting,
@@ -68,7 +72,7 @@ export const useTable = (userOptions = {})=>{
68
72
  };
69
73
  });
70
74
  },
71
- sync: options.syncWithUrl
75
+ sync
72
76
  });
73
77
  const [globalFilter, setGlobalFilter] = useUrlSyncedState({
74
78
  initialState: initialState.globalFilter,
@@ -79,7 +83,7 @@ export const useTable = (userOptions = {})=>{
79
83
  ]
80
84
  ],
81
85
  deserializer: (params)=>params.get('filter') ?? initialState.globalFilter,
82
- sync: options.syncWithUrl
86
+ sync
83
87
  });
84
88
  const [predicates, setPredicates] = useUrlSyncedState({
85
89
  initialState: initialState.predicates,
@@ -91,7 +95,7 @@ export const useTable = (userOptions = {})=>{
91
95
  acc[predicateKey] = params.get(predicateKey) ?? initialState.predicates[predicateKey];
92
96
  return acc;
93
97
  }, {}),
94
- sync: options.syncWithUrl
98
+ sync
95
99
  });
96
100
  const [layout, setLayout] = useUrlSyncedState({
97
101
  initialState: initialState.layout,
@@ -102,7 +106,7 @@ export const useTable = (userOptions = {})=>{
102
106
  ]
103
107
  ],
104
108
  deserializer: (params)=>params.get('layout') ?? initialState.layout,
105
- sync: options.syncWithUrl
109
+ sync
106
110
  });
107
111
  const [dateRange, setDateRange] = useUrlSyncedState({
108
112
  initialState: initialState.dateRange,
@@ -120,7 +124,7 @@ export const useTable = (userOptions = {})=>{
120
124
  params.get('from') ? new Date(params.get('from')) : initialState.dateRange[0],
121
125
  params.get('to') ? new Date(params.get('to')) : initialState.dateRange[1]
122
126
  ],
123
- sync: options.syncWithUrl
127
+ sync
124
128
  });
125
129
  const [columnVisibility, setColumnVisibility] = useUrlSyncedState({
126
130
  initialState: initialState.columnVisibility,
@@ -149,7 +153,7 @@ export const useTable = (userOptions = {})=>{
149
153
  });
150
154
  return columns;
151
155
  },
152
- sync: options.syncWithUrl
156
+ sync
153
157
  });
154
158
  // unsynced
155
159
  const [totalEntries, _setTotalEntries] = useState(initialState.totalEntries);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/table/use-table.ts"],"sourcesContent":["import {useDidUpdate} from '@mantine/hooks';\nimport {type ExpandedState, type PaginationState, type SortingState} from '@tanstack/table-core';\nimport defaultsDeep from 'lodash.defaultsdeep';\nimport {Dispatch, SetStateAction, useCallback, useMemo, useState} from 'react';\nimport {type DateRangePickerValue} from '../date-range-picker';\nimport {useUrlSyncedState} from './use-url-synced-state';\n\n// Create a deeply optional version of another type\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\nexport interface TableState<TData = unknown> {\n /**\n * Current pagination state\n *\n * @default { pageIndex: 0, pageSize: 50 }\n */\n pagination: PaginationState;\n /**\n * Total number of entries in the table.\n * This number is used to calculate the number of pages in the pagination.\n * When null, the number of pages is calculated using the current data length.\n *\n * @default null\n */\n totalEntries: number | null;\n /**\n * Current sorting state\n *\n * @default []\n */\n sorting: SortingState;\n /**\n * Current global filter value\n *\n * @default ''\n */\n globalFilter: string;\n\n /**\n * Current expanded state\n *\n * @default {}\n */\n expanded: ExpandedState;\n /**\n * Predicates and their current value\n *\n * @default {}\n */\n predicates: Record<string, string>;\n /**\n * Layout currently selected. When null, the first layout is used.\n *\n * @default null\n */\n layout: string | null;\n /**\n * Currently selected date range\n *\n * @default [null, null]\n */\n dateRange: DateRangePickerValue;\n /**\n * Currently selected rows\n *\n * @default {}\n */\n rowSelection: Record<string, TData>;\n /**\n * Columns that are currently visible\n *\n * @default {}\n */\n columnVisibility: Record<string, boolean>;\n}\n\nexport interface TableStore<TData = unknown> {\n /**\n * Current state of the table.\n */\n state: TableState<TData>;\n /**\n * Allows to change the pagination state.\n */\n setPagination: Dispatch<SetStateAction<TableState<TData>['pagination']>>;\n /**\n * Allows to change the total number of entries.\n */\n setTotalEntries: Dispatch<SetStateAction<TableState<TData>['totalEntries']>>;\n /**\n * Allows to change the sorting state.\n */\n setSorting: Dispatch<SetStateAction<TableState<TData>['sorting']>>;\n /**\n * Allows to change the global filter value.\n */\n setGlobalFilter: Dispatch<SetStateAction<TableState<TData>['globalFilter']>>;\n /**\n * Allows to change the rows expanded state.\n */\n setExpanded: Dispatch<SetStateAction<TableState<TData>['expanded']>>;\n /**\n * Allows to change the predicates values.\n */\n setPredicates: Dispatch<SetStateAction<TableState<TData>['predicates']>>;\n /**\n * Allows to change the selected layout.\n */\n setLayout: Dispatch<SetStateAction<TableState<TData>['layout']>>;\n /**\n * Allows to change the selected date range.\n */\n setDateRange: Dispatch<SetStateAction<TableState<TData>['dateRange']>>;\n /**\n * Allows to change the current row selection.\n */\n setRowSelection: Dispatch<SetStateAction<TableState<TData>['rowSelection']>>;\n /**\n * Allows to change the visible columns.\n */\n setColumnVisibility: Dispatch<SetStateAction<TableState<TData>['columnVisibility']>>;\n /**\n * Whether the table is currently filtered.\n */\n isFiltered: boolean;\n /**\n * Whether the table has data when unfiltered.\n *\n * This is derived from the totalEntries so make sure you set that number correctly, even if you're using a client side table.\n */\n isVacant: boolean;\n /**\n * Clear currently applied filters.\n */\n clearFilters: () => void;\n /**\n * Deselects all currently selected rows.\n */\n clearRowSelection: () => void;\n /**\n * Get currently selected rows.\n */\n getSelectedRows: () => TData[];\n /**\n * Get currently selected row\n */\n getSelectedRow: () => TData | null;\n /**\n * Whether the user can select multiple rows at the same time.\n */\n multiRowSelectionEnabled: boolean;\n /**\n * Whether rows can be selected.\n */\n rowSelectionEnabled: boolean;\n /**\n * Whether row selection is forced.\n */\n rowSelectionForced: boolean;\n}\n\nexport interface UseTableOptions<TData = unknown> {\n /**\n * Initial state of the table.\n */\n initialState?: DeepPartial<TableState<TData>>;\n /**\n * Whether rows can be selected.\n *\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Whether multiple rows can be selected at the same time.\n *\n * @default false\n */\n enableMultiRowSelection?: boolean;\n /**\n * Forces the user to always have one row selected.\n * When activating that setting, a good practice is to have a row already selected in the initial state.\n *\n * @default false\n */\n forceSelection?: boolean;\n /**\n * Whether to sync the table state with the URL.\n *\n * @default false\n */\n syncWithUrl?: boolean;\n}\n\nconst defaultOptions: UseTableOptions = {\n enableRowSelection: true,\n enableMultiRowSelection: false,\n forceSelection: false,\n syncWithUrl: false,\n};\n\nconst defaultState: Partial<TableState> = {\n pagination: {\n pageIndex: 0,\n pageSize: 50,\n },\n totalEntries: null,\n sorting: [],\n globalFilter: '',\n predicates: {},\n layout: null,\n dateRange: [null, null],\n rowSelection: {},\n columnVisibility: {},\n};\n\nexport const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): TableStore<TData> => {\n const options = defaultsDeep({}, userOptions, defaultOptions) as UseTableOptions<TData>;\n const initialState = defaultsDeep({}, options.initialState, defaultState) as TableState<TData>;\n\n // synced with url\n const [pagination, setPagination] = useUrlSyncedState<TableState<TData>['pagination']>({\n initialState: initialState.pagination,\n serializer: ({pageIndex, pageSize}) => [\n ['page', (pageIndex + 1).toString()],\n ['pageSize', pageSize.toString()],\n ],\n deserializer: (params) =>\n defaultsDeep(\n {\n pageIndex: params.get('page') ? parseInt(params.get('page'), 10) - 1 : undefined,\n pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined,\n },\n initialState.pagination,\n ),\n sync: options.syncWithUrl,\n });\n const [sorting, setSorting] = useUrlSyncedState<TableState<TData>['sorting']>({\n initialState: initialState.sorting,\n serializer: (_sorting) => [\n ['sortBy', _sorting.map(({id, desc}) => `${id}.${desc ? 'desc' : 'asc'}`).join(',')],\n ],\n deserializer: (params) => {\n if (!params.has('sortBy')) {\n return initialState.sorting;\n }\n const sorts = params.get('sortBy')?.split(',') ?? [];\n return sorts.map((sort) => {\n const [id, order] = sort.split('.');\n return {id, desc: order === 'desc'};\n });\n },\n sync: options.syncWithUrl,\n });\n const [globalFilter, setGlobalFilter] = useUrlSyncedState<TableState<TData>['globalFilter']>({\n initialState: initialState.globalFilter,\n serializer: (filter) => [['filter', filter]],\n deserializer: (params) => params.get('filter') ?? initialState.globalFilter,\n sync: options.syncWithUrl,\n });\n const [predicates, setPredicates] = useUrlSyncedState<TableState<TData>['predicates']>({\n initialState: initialState.predicates,\n serializer: (_predicates) => Object.entries(_predicates).map(([key, value]) => [key, value]),\n deserializer: (params) =>\n Object.keys(initialState.predicates).reduce(\n (acc, predicateKey) => {\n acc[predicateKey] = params.get(predicateKey) ?? initialState.predicates[predicateKey];\n return acc;\n },\n {} as TableState<TData>['predicates'],\n ),\n sync: options.syncWithUrl,\n });\n const [layout, setLayout] = useUrlSyncedState<TableState<TData>['layout']>({\n initialState: initialState.layout,\n serializer: (_layout) => [['layout', _layout]],\n deserializer: (params) => params.get('layout') ?? initialState.layout,\n sync: options.syncWithUrl,\n });\n const [dateRange, setDateRange] = useUrlSyncedState<TableState<TData>['dateRange']>({\n initialState: initialState.dateRange,\n serializer: ([from, to]) => [\n ['from', from?.toISOString() ?? ''],\n ['to', to?.toISOString() ?? ''],\n ],\n deserializer: (params) => [\n params.get('from') ? new Date(params.get('from') as string) : initialState.dateRange[0],\n params.get('to') ? new Date(params.get('to') as string) : initialState.dateRange[1],\n ],\n sync: options.syncWithUrl,\n });\n const [columnVisibility, setColumnVisibility] = useUrlSyncedState<TableState<TData>['columnVisibility']>({\n initialState: initialState.columnVisibility,\n serializer: (columns) => [\n [\n 'show',\n Object.entries(columns)\n .filter(([, visible]) => visible === true)\n .map(([columnName]) => columnName)\n .join(','),\n ],\n [\n 'hide',\n Object.entries(columns)\n .filter(([, visible]) => visible === false)\n .map(([columnName]) => columnName)\n .join(','),\n ],\n ],\n deserializer: (params) => {\n if (!params.has('show') && !params.has('hide')) {\n return initialState.columnVisibility;\n }\n const visible = params.get('show')?.split(',') ?? [];\n const invisible = params.get('hide')?.split(',') ?? [];\n const columns = {} as TableState<TData>['columnVisibility'];\n visible.forEach((column) => {\n columns[column] = true;\n });\n invisible.forEach((column) => {\n columns[column] = false;\n });\n return columns;\n },\n sync: options.syncWithUrl,\n });\n\n // unsynced\n const [totalEntries, _setTotalEntries] = useState<TableState<TData>['totalEntries']>(initialState.totalEntries);\n const [unfilteredTotalEntries, setUnfilteredTotalEntries] = useState<TableState<TData>['totalEntries']>(\n initialState.totalEntries,\n );\n const [expanded, setExpanded] = useState<TableState<TData>['expanded']>(initialState.expanded);\n const [rowSelection, setRowSelection] = useState<TableState<TData>['rowSelection']>(initialState.rowSelection);\n\n const isFiltered =\n !!globalFilter ||\n Object.keys(predicates).some((predicate) => !!predicates[predicate]) ||\n !!dateRange?.[0] ||\n !!dateRange?.[1];\n\n const isVacant = unfilteredTotalEntries === 0;\n\n const setTotalEntries: typeof _setTotalEntries = useCallback(\n (updater) => {\n _setTotalEntries((old) => {\n const newTotalEntries = updater instanceof Function ? updater(old) : updater;\n if (!isFiltered) {\n setUnfilteredTotalEntries(newTotalEntries);\n }\n return newTotalEntries;\n });\n },\n [isFiltered],\n );\n\n const clearFilters = useCallback(() => {\n setPredicates(initialState.predicates);\n setGlobalFilter('');\n }, []);\n\n const clearRowSelection = useCallback(() => {\n setRowSelection({});\n }, []);\n\n const getSelectedRows = useCallback(() => Object.values(rowSelection), [rowSelection]);\n\n const getSelectedRow = () => getSelectedRows()[0] ?? null;\n\n useDidUpdate(() => {\n if (!options.enableMultiRowSelection) {\n clearRowSelection();\n }\n }, [globalFilter, pagination, sorting, dateRange, predicates]);\n\n const state = useMemo(\n () => ({\n pagination,\n totalEntries,\n sorting,\n globalFilter,\n expanded,\n predicates,\n layout,\n dateRange,\n rowSelection,\n columnVisibility,\n }),\n [\n pagination,\n totalEntries,\n sorting,\n globalFilter,\n expanded,\n predicates,\n layout,\n dateRange,\n rowSelection,\n columnVisibility,\n ],\n );\n\n return {\n state,\n setPagination,\n setTotalEntries,\n setSorting,\n setGlobalFilter,\n setExpanded,\n setPredicates,\n setLayout,\n setDateRange,\n setRowSelection,\n setColumnVisibility,\n isFiltered,\n isVacant,\n clearFilters,\n clearRowSelection,\n getSelectedRows,\n getSelectedRow,\n rowSelectionEnabled: options.enableRowSelection,\n rowSelectionForced: options.forceSelection,\n multiRowSelectionEnabled: options.enableMultiRowSelection,\n };\n};\n"],"names":["useDidUpdate","defaultsDeep","useCallback","useMemo","useState","useUrlSyncedState","defaultOptions","enableRowSelection","enableMultiRowSelection","forceSelection","syncWithUrl","defaultState","pagination","pageIndex","pageSize","totalEntries","sorting","globalFilter","predicates","layout","dateRange","rowSelection","columnVisibility","useTable","userOptions","options","initialState","setPagination","serializer","toString","deserializer","params","get","parseInt","undefined","sync","setSorting","_sorting","map","id","desc","join","has","sorts","split","sort","order","setGlobalFilter","filter","setPredicates","_predicates","Object","entries","key","value","keys","reduce","acc","predicateKey","setLayout","_layout","setDateRange","from","to","toISOString","Date","setColumnVisibility","columns","visible","columnName","invisible","forEach","column","_setTotalEntries","unfilteredTotalEntries","setUnfilteredTotalEntries","expanded","setExpanded","setRowSelection","isFiltered","some","predicate","isVacant","setTotalEntries","updater","old","newTotalEntries","Function","clearFilters","clearRowSelection","getSelectedRows","values","getSelectedRow","state","rowSelectionEnabled","rowSelectionForced","multiRowSelectionEnabled"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/components/table/use-table.ts"],"sourcesContent":["import {useDidUpdate} from '@mantine/hooks';\nimport {type ExpandedState, type PaginationState, type SortingState} from '@tanstack/table-core';\nimport defaultsDeep from 'lodash.defaultsdeep';\nimport {Dispatch, SetStateAction, useCallback, useMemo, useState} from 'react';\nimport {type DateRangePickerValue} from '../date-range-picker';\nimport {useUrlSyncedState} from './use-url-synced-state';\n\n// Create a deeply optional version of another type\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\nexport interface TableState<TData = unknown> {\n /**\n * Current pagination state\n *\n * @default { pageIndex: 0, pageSize: 50 }\n */\n pagination: PaginationState;\n /**\n * Total number of entries in the table.\n * This number is used to calculate the number of pages in the pagination.\n * When null, the number of pages is calculated using the current data length.\n *\n * @default null\n */\n totalEntries: number | null;\n /**\n * Current sorting state\n *\n * @default []\n */\n sorting: SortingState;\n /**\n * Current global filter value\n *\n * @default ''\n */\n globalFilter: string;\n\n /**\n * Current expanded state\n *\n * @default {}\n */\n expanded: ExpandedState;\n /**\n * Predicates and their current value\n *\n * @default {}\n */\n predicates: Record<string, string>;\n /**\n * Layout currently selected. When null, the first layout is used.\n *\n * @default null\n */\n layout: string | null;\n /**\n * Currently selected date range\n *\n * @default [null, null]\n */\n dateRange: DateRangePickerValue;\n /**\n * Currently selected rows\n *\n * @default {}\n */\n rowSelection: Record<string, TData>;\n /**\n * Columns that are currently visible\n *\n * @default {}\n */\n columnVisibility: Record<string, boolean>;\n}\n\nexport interface TableStore<TData = unknown> {\n /**\n * Current state of the table.\n */\n state: TableState<TData>;\n /**\n * Allows to change the pagination state.\n */\n setPagination: Dispatch<SetStateAction<TableState<TData>['pagination']>>;\n /**\n * Allows to change the total number of entries.\n */\n setTotalEntries: Dispatch<SetStateAction<TableState<TData>['totalEntries']>>;\n /**\n * Allows to change the sorting state.\n */\n setSorting: Dispatch<SetStateAction<TableState<TData>['sorting']>>;\n /**\n * Allows to change the global filter value.\n */\n setGlobalFilter: Dispatch<SetStateAction<TableState<TData>['globalFilter']>>;\n /**\n * Allows to change the rows expanded state.\n */\n setExpanded: Dispatch<SetStateAction<TableState<TData>['expanded']>>;\n /**\n * Allows to change the predicates values.\n */\n setPredicates: Dispatch<SetStateAction<TableState<TData>['predicates']>>;\n /**\n * Allows to change the selected layout.\n */\n setLayout: Dispatch<SetStateAction<TableState<TData>['layout']>>;\n /**\n * Allows to change the selected date range.\n */\n setDateRange: Dispatch<SetStateAction<TableState<TData>['dateRange']>>;\n /**\n * Allows to change the current row selection.\n */\n setRowSelection: Dispatch<SetStateAction<TableState<TData>['rowSelection']>>;\n /**\n * Allows to change the visible columns.\n */\n setColumnVisibility: Dispatch<SetStateAction<TableState<TData>['columnVisibility']>>;\n /**\n * Whether the table is currently filtered.\n */\n isFiltered: boolean;\n /**\n * Whether the table has data when unfiltered.\n *\n * This is derived from the totalEntries so make sure you set that number correctly, even if you're using a client side table.\n */\n isVacant: boolean;\n /**\n * Clear currently applied filters.\n */\n clearFilters: () => void;\n /**\n * Deselects all currently selected rows.\n */\n clearRowSelection: () => void;\n /**\n * Get currently selected rows.\n */\n getSelectedRows: () => TData[];\n /**\n * Get currently selected row\n */\n getSelectedRow: () => TData | null;\n /**\n * Whether the user can select multiple rows at the same time.\n */\n multiRowSelectionEnabled: boolean;\n /**\n * Whether rows can be selected.\n */\n rowSelectionEnabled: boolean;\n /**\n * Whether row selection is forced.\n */\n rowSelectionForced: boolean;\n}\n\nexport interface UseTableOptions<TData = unknown> {\n /**\n * Initial state of the table.\n */\n initialState?: DeepPartial<TableState<TData>>;\n /**\n * Whether rows can be selected.\n *\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Whether multiple rows can be selected at the same time.\n *\n * @default false\n */\n enableMultiRowSelection?: boolean;\n /**\n * Forces the user to always have one row selected.\n * When activating that setting, a good practice is to have a row already selected in the initial state.\n *\n * @default false\n */\n forceSelection?: boolean;\n /**\n * Whether to sync the table state with the URL.\n *\n * @default false\n */\n syncWithUrl?: boolean;\n}\n\nconst defaultOptions: UseTableOptions = {\n enableRowSelection: true,\n enableMultiRowSelection: false,\n forceSelection: false,\n syncWithUrl: false,\n};\n\nconst defaultState: Partial<TableState> = {\n pagination: {\n pageIndex: 0,\n pageSize: 50,\n },\n totalEntries: null,\n sorting: [],\n globalFilter: '',\n predicates: {},\n layout: null,\n dateRange: [null, null],\n rowSelection: {},\n columnVisibility: {},\n};\n\nexport const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): TableStore<TData> => {\n const options = defaultsDeep({}, userOptions, defaultOptions) as UseTableOptions<TData>;\n const initialState = defaultsDeep({}, options.initialState, defaultState) as TableState<TData>;\n /**\n * The `useUrlSyncedState` hook defaults to synchronize, but the table wants to default to not synchronize,\n * so always pass the sync option as a resolved boolean value.\n */\n const sync = !!options.syncWithUrl;\n\n // synced with url\n const [pagination, setPagination] = useUrlSyncedState<TableState<TData>['pagination']>({\n initialState: initialState.pagination,\n serializer: ({pageIndex, pageSize}) => [\n ['page', (pageIndex + 1).toString()],\n ['pageSize', pageSize.toString()],\n ],\n deserializer: (params) =>\n defaultsDeep(\n {\n pageIndex: params.get('page') ? parseInt(params.get('page'), 10) - 1 : undefined,\n pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined,\n },\n initialState.pagination,\n ),\n sync,\n });\n const [sorting, setSorting] = useUrlSyncedState<TableState<TData>['sorting']>({\n initialState: initialState.sorting,\n serializer: (_sorting) => [\n ['sortBy', _sorting.map(({id, desc}) => `${id}.${desc ? 'desc' : 'asc'}`).join(',')],\n ],\n deserializer: (params) => {\n if (!params.has('sortBy')) {\n return initialState.sorting;\n }\n const sorts = params.get('sortBy')?.split(',') ?? [];\n return sorts.map((sort) => {\n const [id, order] = sort.split('.');\n return {id, desc: order === 'desc'};\n });\n },\n sync,\n });\n const [globalFilter, setGlobalFilter] = useUrlSyncedState<TableState<TData>['globalFilter']>({\n initialState: initialState.globalFilter,\n serializer: (filter) => [['filter', filter]],\n deserializer: (params) => params.get('filter') ?? initialState.globalFilter,\n sync,\n });\n const [predicates, setPredicates] = useUrlSyncedState<TableState<TData>['predicates']>({\n initialState: initialState.predicates,\n serializer: (_predicates) => Object.entries(_predicates).map(([key, value]) => [key, value]),\n deserializer: (params) =>\n Object.keys(initialState.predicates).reduce(\n (acc, predicateKey) => {\n acc[predicateKey] = params.get(predicateKey) ?? initialState.predicates[predicateKey];\n return acc;\n },\n {} as TableState<TData>['predicates'],\n ),\n sync,\n });\n const [layout, setLayout] = useUrlSyncedState<TableState<TData>['layout']>({\n initialState: initialState.layout,\n serializer: (_layout) => [['layout', _layout]],\n deserializer: (params) => params.get('layout') ?? initialState.layout,\n sync,\n });\n const [dateRange, setDateRange] = useUrlSyncedState<TableState<TData>['dateRange']>({\n initialState: initialState.dateRange,\n serializer: ([from, to]) => [\n ['from', from?.toISOString() ?? ''],\n ['to', to?.toISOString() ?? ''],\n ],\n deserializer: (params) => [\n params.get('from') ? new Date(params.get('from') as string) : initialState.dateRange[0],\n params.get('to') ? new Date(params.get('to') as string) : initialState.dateRange[1],\n ],\n sync,\n });\n const [columnVisibility, setColumnVisibility] = useUrlSyncedState<TableState<TData>['columnVisibility']>({\n initialState: initialState.columnVisibility,\n serializer: (columns) => [\n [\n 'show',\n Object.entries(columns)\n .filter(([, visible]) => visible === true)\n .map(([columnName]) => columnName)\n .join(','),\n ],\n [\n 'hide',\n Object.entries(columns)\n .filter(([, visible]) => visible === false)\n .map(([columnName]) => columnName)\n .join(','),\n ],\n ],\n deserializer: (params) => {\n if (!params.has('show') && !params.has('hide')) {\n return initialState.columnVisibility;\n }\n const visible = params.get('show')?.split(',') ?? [];\n const invisible = params.get('hide')?.split(',') ?? [];\n const columns = {} as TableState<TData>['columnVisibility'];\n visible.forEach((column) => {\n columns[column] = true;\n });\n invisible.forEach((column) => {\n columns[column] = false;\n });\n return columns;\n },\n sync,\n });\n\n // unsynced\n const [totalEntries, _setTotalEntries] = useState<TableState<TData>['totalEntries']>(initialState.totalEntries);\n const [unfilteredTotalEntries, setUnfilteredTotalEntries] = useState<TableState<TData>['totalEntries']>(\n initialState.totalEntries,\n );\n const [expanded, setExpanded] = useState<TableState<TData>['expanded']>(initialState.expanded);\n const [rowSelection, setRowSelection] = useState<TableState<TData>['rowSelection']>(initialState.rowSelection);\n\n const isFiltered =\n !!globalFilter ||\n Object.keys(predicates).some((predicate) => !!predicates[predicate]) ||\n !!dateRange?.[0] ||\n !!dateRange?.[1];\n\n const isVacant = unfilteredTotalEntries === 0;\n\n const setTotalEntries: typeof _setTotalEntries = useCallback(\n (updater) => {\n _setTotalEntries((old) => {\n const newTotalEntries = updater instanceof Function ? updater(old) : updater;\n if (!isFiltered) {\n setUnfilteredTotalEntries(newTotalEntries);\n }\n return newTotalEntries;\n });\n },\n [isFiltered],\n );\n\n const clearFilters = useCallback(() => {\n setPredicates(initialState.predicates);\n setGlobalFilter('');\n }, []);\n\n const clearRowSelection = useCallback(() => {\n setRowSelection({});\n }, []);\n\n const getSelectedRows = useCallback(() => Object.values(rowSelection), [rowSelection]);\n\n const getSelectedRow = () => getSelectedRows()[0] ?? null;\n\n useDidUpdate(() => {\n if (!options.enableMultiRowSelection) {\n clearRowSelection();\n }\n }, [globalFilter, pagination, sorting, dateRange, predicates]);\n\n const state = useMemo(\n () => ({\n pagination,\n totalEntries,\n sorting,\n globalFilter,\n expanded,\n predicates,\n layout,\n dateRange,\n rowSelection,\n columnVisibility,\n }),\n [\n pagination,\n totalEntries,\n sorting,\n globalFilter,\n expanded,\n predicates,\n layout,\n dateRange,\n rowSelection,\n columnVisibility,\n ],\n );\n\n return {\n state,\n setPagination,\n setTotalEntries,\n setSorting,\n setGlobalFilter,\n setExpanded,\n setPredicates,\n setLayout,\n setDateRange,\n setRowSelection,\n setColumnVisibility,\n isFiltered,\n isVacant,\n clearFilters,\n clearRowSelection,\n getSelectedRows,\n getSelectedRow,\n rowSelectionEnabled: options.enableRowSelection,\n rowSelectionForced: options.forceSelection,\n multiRowSelectionEnabled: options.enableMultiRowSelection,\n };\n};\n"],"names":["useDidUpdate","defaultsDeep","useCallback","useMemo","useState","useUrlSyncedState","defaultOptions","enableRowSelection","enableMultiRowSelection","forceSelection","syncWithUrl","defaultState","pagination","pageIndex","pageSize","totalEntries","sorting","globalFilter","predicates","layout","dateRange","rowSelection","columnVisibility","useTable","userOptions","options","initialState","sync","setPagination","serializer","toString","deserializer","params","get","parseInt","undefined","setSorting","_sorting","map","id","desc","join","has","sorts","split","sort","order","setGlobalFilter","filter","setPredicates","_predicates","Object","entries","key","value","keys","reduce","acc","predicateKey","setLayout","_layout","setDateRange","from","to","toISOString","Date","setColumnVisibility","columns","visible","columnName","invisible","forEach","column","_setTotalEntries","unfilteredTotalEntries","setUnfilteredTotalEntries","expanded","setExpanded","setRowSelection","isFiltered","some","predicate","isVacant","setTotalEntries","updater","old","newTotalEntries","Function","clearFilters","clearRowSelection","getSelectedRows","values","getSelectedRow","state","rowSelectionEnabled","rowSelectionForced","multiRowSelectionEnabled"],"mappings":"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"}
@@ -1,18 +1,29 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ /**
3
+ * A search param entry defines the key (index 0) and value (index 1) of a search parameter.
4
+ */
5
+ export type SearchParamEntry = [string, string | null | undefined];
1
6
  export interface UseUrlSyncedStateOptions<T> {
2
7
  /**
3
- * The initial state
8
+ * The initial state to use, if there would be no search params to deserialize from.
9
+ * These values are also treated as defaults, and if the current state matches the initialState,
10
+ * no value will be written to the search params.
4
11
  */
5
- initialState: T;
12
+ initialState: T extends object ? Readonly<T> : T;
6
13
  /**
7
14
  * The serializer function is used to determine how the state is translated to url search parameters.
8
15
  * Called each time the state changes.
16
+ * Note that the serializer should always return entries for keys it controls, also if the current value is "unset" (`null` or empty).
17
+ * This ensures params get removed from the search when they are being unset.
18
+ *
9
19
  * @param stateValue The new state value to serialize.
10
- * @returns A list of [key, value] to set as url search parameters.
20
+ * @returns An iterable of `[key, value]` to set as url search parameters.
11
21
  * @example (filterValue) => [['filter', filterValue]] // ?filter=filterValue
12
22
  */
13
- serializer: (stateValue: T) => Array<[string, string]>;
23
+ serializer: (stateValue: T) => Iterable<SearchParamEntry>;
14
24
  /**
15
25
  * The deserializer function is used to determine how the url parameters influence the initial state.
26
+ * May return a partial state, values that are not deserialed are taken from the `initialState`.
16
27
  * Called only once when initializing the state.
17
28
  * @param params All the search parameters of the current url.
18
29
  * @returns The initial state based on the current url.
@@ -20,10 +31,10 @@ export interface UseUrlSyncedStateOptions<T> {
20
31
  */
21
32
  deserializer: (params: URLSearchParams) => T;
22
33
  /**
23
- * Whether the state should be synced with the url.
24
- * When set to false, the hook behaves just like a regular useState hook from react.
34
+ * Whether the state should be synced with the url, defaults to `true`.
35
+ * When set to `false`, the hook behaves just like a regular `useState` hook from react.
25
36
  */
26
37
  sync?: boolean;
27
38
  }
28
- export declare const useUrlSyncedState: <T>({ initialState, serializer, deserializer, sync }: UseUrlSyncedStateOptions<T>) => readonly [T, import("react").Dispatch<import("react").SetStateAction<T>>];
39
+ export declare const useUrlSyncedState: <T>(options: UseUrlSyncedStateOptions<T>) => readonly [T, Dispatch<SetStateAction<T>>];
29
40
  //# sourceMappingURL=use-url-synced-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-url-synced-state.d.ts","sourceRoot":"","sources":["../../../../src/components/table/use-url-synced-state.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACvC;;OAEG;IACH,YAAY,EAAE,CAAC,CAAC;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,CAAC,CAAC;IAC7C;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,GAAI,CAAC,oDAAkD,wBAAwB,CAAC,CAAC,CAAC,8EAwB/G,CAAC"}
1
+ {"version":3,"file":"use-url-synced-state.d.ts","sourceRoot":"","sources":["../../../../src/components/table/use-url-synced-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAoB,MAAM,OAAO,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AA8CnE,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACvC;;;;OAIG;IACH,YAAY,EAAE,CAAC,SAAS,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACjD;;;;;;;;;OASG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,CAAC,KAAK,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC1D;;;;;;;OAOG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,CAAC,CAAC;IAC7C;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,GAAI,CAAC,WAAW,wBAAwB,CAAC,CAAC,CAAC,8CAkCxE,CAAC"}
@@ -1,35 +1,65 @@
1
1
  import { useMemo, useState } from 'react';
2
- const setSearchParam = (key, value, initial)=>{
3
- const url = new URL(window.location.href);
4
- if (value === '' || value === initial) {
5
- url.searchParams.delete(key);
6
- } else {
7
- url.searchParams.set(key, value);
2
+ /**
3
+ * Iterates over the `SearchParamEntry` values, and applies them to `target`, optionally filtering values.
4
+ *
5
+ * @param target The target to write values to, can be a Map (for the initial values) or `URLSearchParams`.
6
+ * @param entries The entries to apply (as returned by the serializer).
7
+ * @param filter Optional filter that allows to treat non-empty values as empty (e.g. to not set default values).
8
+ */ const applyValues = (target, entries, filter)=>{
9
+ for (const entry of entries){
10
+ if (entry[1] && filter(entry)) {
11
+ target.set(entry[0], entry[1]);
12
+ } else {
13
+ target.delete(entry[0]);
14
+ }
8
15
  }
9
- window.history.replaceState(null, '', url.toString());
10
- };
11
- const getSearchParams = ()=>{
12
- const url = new URL(window.location.href);
13
- return url.searchParams;
14
16
  };
15
- export const useUrlSyncedState = ({ initialState, serializer, deserializer, sync })=>{
16
- const [state, setState] = useState(sync ? deserializer(getSearchParams()) : initialState);
17
- const enhancedSetState = useMemo(()=>{
18
- if (sync) {
19
- const initialSerialized = serializer(initialState).reduce((acc, [key, value])=>{
20
- acc[key] = value;
21
- return acc;
22
- }, {});
23
- return (updater)=>{
24
- setState((old)=>{
25
- const newValue = updater instanceof Function ? updater(old) : updater;
26
- serializer(newValue).forEach(([key, value])=>setSearchParam(key, value, initialSerialized[key]));
27
- return newValue;
28
- });
29
- };
17
+ /**
18
+ * Read the **current** search params from `window.location`, with support for detecting React's HashRouter.
19
+ * Also returns a method that will yield the href (string) value, after any changes made on the params object.
20
+ *
21
+ * @returns The `URLSearchParams` instance, and a function that can be used to get an updated href.
22
+ */ const getSearchParams = ()=>{
23
+ const href = window.location.href;
24
+ // Search for '#/' to detect hash router urls
25
+ const searchStart = href.indexOf('?', href.indexOf('#/') + 1);
26
+ const params = new URLSearchParams(searchStart < 0 ? '' : href.substring(searchStart));
27
+ return [
28
+ params,
29
+ ()=>{
30
+ let result = searchStart < 0 ? href : href.substring(0, searchStart);
31
+ if (params.size > 0) {
32
+ result = result.concat('?', params.toString());
33
+ }
34
+ return result;
30
35
  }
31
- return setState;
36
+ ];
37
+ };
38
+ export const useUrlSyncedState = (options)=>{
39
+ const sync = options.sync !== false;
40
+ const initialState = useMemo(()=>sync ? options.deserializer(getSearchParams()[0]) : options.initialState, [
41
+ options.initialState,
42
+ options.sync
43
+ ]);
44
+ const [state, setState] = useState(initialState);
45
+ // Capture the initial state as a map, to compare values and not set them if they match.
46
+ const initialStateSerialized = useMemo(()=>{
47
+ const v = new Map();
48
+ applyValues(v, options.serializer(options.initialState), (_)=>true);
49
+ return v;
32
50
  }, [
51
+ initialState,
52
+ options.serializer
53
+ ]);
54
+ const enhancedSetState = useMemo(()=>sync ? (updater)=>{
55
+ setState((old)=>{
56
+ const [search, getUrl] = getSearchParams();
57
+ const newValue = updater instanceof Function ? updater(old) : updater;
58
+ applyValues(search, options.serializer(newValue), ([key, value])=>initialStateSerialized.get(key) !== value);
59
+ window.history.replaceState(null, '', getUrl());
60
+ return newValue;
61
+ });
62
+ } : setState, [
33
63
  sync
34
64
  ]);
35
65
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/table/use-url-synced-state.ts"],"sourcesContent":["import {useMemo, useState} from 'react';\n\nconst setSearchParam = (key: string, value: string, initial: string) => {\n const url = new URL(window.location.href);\n if (value === '' || value === initial) {\n url.searchParams.delete(key);\n } else {\n url.searchParams.set(key, value);\n }\n window.history.replaceState(null, '', url.toString());\n};\n\nconst getSearchParams = (): URLSearchParams => {\n const url = new URL(window.location.href);\n return url.searchParams;\n};\n\nexport interface UseUrlSyncedStateOptions<T> {\n /**\n * The initial state\n */\n initialState: T;\n /**\n * The serializer function is used to determine how the state is translated to url search parameters.\n * Called each time the state changes.\n * @param stateValue The new state value to serialize.\n * @returns A list of [key, value] to set as url search parameters.\n * @example (filterValue) => [['filter', filterValue]] // ?filter=filterValue\n */\n serializer: (stateValue: T) => Array<[string, string]>;\n /**\n * The deserializer function is used to determine how the url parameters influence the initial state.\n * Called only once when initializing the state.\n * @param params All the search parameters of the current url.\n * @returns The initial state based on the current url.\n * @example (params) => params.get('filter') ?? '',\n */\n deserializer: (params: URLSearchParams) => T;\n /**\n * Whether the state should be synced with the url.\n * When set to false, the hook behaves just like a regular useState hook from react.\n */\n sync?: boolean;\n}\n\nexport const useUrlSyncedState = <T>({initialState, serializer, deserializer, sync}: UseUrlSyncedStateOptions<T>) => {\n const [state, setState] = useState<T>(sync ? deserializer(getSearchParams()) : initialState);\n const enhancedSetState = useMemo(() => {\n if (sync) {\n const initialSerialized = serializer(initialState).reduce(\n (acc, [key, value]) => {\n acc[key] = value;\n return acc;\n },\n {} as Record<string, string>,\n );\n return (updater: T | ((old: T) => T)) => {\n setState((old) => {\n const newValue = updater instanceof Function ? updater(old) : updater;\n serializer(newValue).forEach(([key, value]) => setSearchParam(key, value, initialSerialized[key]));\n return newValue;\n });\n };\n }\n\n return setState;\n }, [sync]);\n\n return [state, enhancedSetState] as const;\n};\n"],"names":["useMemo","useState","setSearchParam","key","value","initial","url","URL","window","location","href","searchParams","delete","set","history","replaceState","toString","getSearchParams","useUrlSyncedState","initialState","serializer","deserializer","sync","state","setState","enhancedSetState","initialSerialized","reduce","acc","updater","old","newValue","Function","forEach"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AAExC,MAAMC,iBAAiB,CAACC,KAAaC,OAAeC;IAChD,MAAMC,MAAM,IAAIC,IAAIC,OAAOC,QAAQ,CAACC,IAAI;IACxC,IAAIN,UAAU,MAAMA,UAAUC,SAAS;QACnCC,IAAIK,YAAY,CAACC,MAAM,CAACT;IAC5B,OAAO;QACHG,IAAIK,YAAY,CAACE,GAAG,CAACV,KAAKC;IAC9B;IACAI,OAAOM,OAAO,CAACC,YAAY,CAAC,MAAM,IAAIT,IAAIU,QAAQ;AACtD;AAEA,MAAMC,kBAAkB;IACpB,MAAMX,MAAM,IAAIC,IAAIC,OAAOC,QAAQ,CAACC,IAAI;IACxC,OAAOJ,IAAIK,YAAY;AAC3B;AA8BA,OAAO,MAAMO,oBAAoB,CAAI,EAACC,YAAY,EAAEC,UAAU,EAAEC,YAAY,EAAEC,IAAI,EAA8B;IAC5G,MAAM,CAACC,OAAOC,SAAS,GAAGvB,SAAYqB,OAAOD,aAAaJ,qBAAqBE;IAC/E,MAAMM,mBAAmBzB,QAAQ;QAC7B,IAAIsB,MAAM;YACN,MAAMI,oBAAoBN,WAAWD,cAAcQ,MAAM,CACrD,CAACC,KAAK,CAACzB,KAAKC,MAAM;gBACdwB,GAAG,CAACzB,IAAI,GAAGC;gBACX,OAAOwB;YACX,GACA,CAAC;YAEL,OAAO,CAACC;gBACJL,SAAS,CAACM;oBACN,MAAMC,WAAWF,mBAAmBG,WAAWH,QAAQC,OAAOD;oBAC9DT,WAAWW,UAAUE,OAAO,CAAC,CAAC,CAAC9B,KAAKC,MAAM,GAAKF,eAAeC,KAAKC,OAAOsB,iBAAiB,CAACvB,IAAI;oBAChG,OAAO4B;gBACX;YACJ;QACJ;QAEA,OAAOP;IACX,GAAG;QAACF;KAAK;IAET,OAAO;QAACC;QAAOE;KAAiB;AACpC,EAAE"}
1
+ {"version":3,"sources":["../../../../src/components/table/use-url-synced-state.ts"],"sourcesContent":["import {Dispatch, SetStateAction, useMemo, useState} from 'react';\n\n/**\n * A search param entry defines the key (index 0) and value (index 1) of a search parameter.\n */\nexport type SearchParamEntry = [string, string | null | undefined];\n\n/**\n * Iterates over the `SearchParamEntry` values, and applies them to `target`, optionally filtering values.\n *\n * @param target The target to write values to, can be a Map (for the initial values) or `URLSearchParams`.\n * @param entries The entries to apply (as returned by the serializer).\n * @param filter Optional filter that allows to treat non-empty values as empty (e.g. to not set default values).\n */\nconst applyValues = (\n target: Map<string, string> | URLSearchParams,\n entries: Iterable<SearchParamEntry>,\n filter: (entry: Readonly<SearchParamEntry>) => boolean,\n): void => {\n for (const entry of entries) {\n if (entry[1] && filter(entry)) {\n target.set(entry[0], entry[1]);\n } else {\n target.delete(entry[0]);\n }\n }\n};\n\n/**\n * Read the **current** search params from `window.location`, with support for detecting React's HashRouter.\n * Also returns a method that will yield the href (string) value, after any changes made on the params object.\n *\n * @returns The `URLSearchParams` instance, and a function that can be used to get an updated href.\n */\nconst getSearchParams = (): [URLSearchParams, () => string] => {\n const href = window.location.href;\n // Search for '#/' to detect hash router urls\n const searchStart = href.indexOf('?', href.indexOf('#/') + 1);\n const params = new URLSearchParams(searchStart < 0 ? '' : href.substring(searchStart));\n return [\n params,\n () => {\n let result = searchStart < 0 ? href : href.substring(0, searchStart);\n if (params.size > 0) {\n result = result.concat('?', params.toString());\n }\n return result;\n },\n ];\n};\n\nexport interface UseUrlSyncedStateOptions<T> {\n /**\n * The initial state to use, if there would be no search params to deserialize from.\n * These values are also treated as defaults, and if the current state matches the initialState,\n * no value will be written to the search params.\n */\n initialState: T extends object ? Readonly<T> : T;\n /**\n * The serializer function is used to determine how the state is translated to url search parameters.\n * Called each time the state changes.\n * Note that the serializer should always return entries for keys it controls, also if the current value is \"unset\" (`null` or empty).\n * This ensures params get removed from the search when they are being unset.\n *\n * @param stateValue The new state value to serialize.\n * @returns An iterable of `[key, value]` to set as url search parameters.\n * @example (filterValue) => [['filter', filterValue]] // ?filter=filterValue\n */\n serializer: (stateValue: T) => Iterable<SearchParamEntry>;\n /**\n * The deserializer function is used to determine how the url parameters influence the initial state.\n * May return a partial state, values that are not deserialed are taken from the `initialState`.\n * Called only once when initializing the state.\n * @param params All the search parameters of the current url.\n * @returns The initial state based on the current url.\n * @example (params) => params.get('filter') ?? '',\n */\n deserializer: (params: URLSearchParams) => T;\n /**\n * Whether the state should be synced with the url, defaults to `true`.\n * When set to `false`, the hook behaves just like a regular `useState` hook from react.\n */\n sync?: boolean;\n}\n\nexport const useUrlSyncedState = <T>(options: UseUrlSyncedStateOptions<T>) => {\n const sync = options.sync !== false;\n const initialState = useMemo(\n () => (sync ? options.deserializer(getSearchParams()[0]) : options.initialState),\n [options.initialState, options.sync],\n );\n const [state, setState] = useState<T>(initialState);\n // Capture the initial state as a map, to compare values and not set them if they match.\n const initialStateSerialized = useMemo(() => {\n const v = new Map<string, string>();\n applyValues(v, options.serializer(options.initialState), (_) => true);\n return v;\n }, [initialState, options.serializer]);\n const enhancedSetState = useMemo<Dispatch<SetStateAction<T>>>(\n () =>\n sync\n ? (updater: SetStateAction<T>) => {\n setState((old) => {\n const [search, getUrl] = getSearchParams();\n const newValue = updater instanceof Function ? updater(old) : updater;\n applyValues(\n search,\n options.serializer(newValue),\n ([key, value]) => initialStateSerialized.get(key) !== value,\n );\n window.history.replaceState(null, '', getUrl());\n return newValue;\n });\n }\n : setState,\n [sync],\n );\n\n return [state, enhancedSetState] as const;\n};\n"],"names":["useMemo","useState","applyValues","target","entries","filter","entry","set","delete","getSearchParams","href","window","location","searchStart","indexOf","params","URLSearchParams","substring","result","size","concat","toString","useUrlSyncedState","options","sync","initialState","deserializer","state","setState","initialStateSerialized","v","Map","serializer","_","enhancedSetState","updater","old","search","getUrl","newValue","Function","key","value","get","history","replaceState"],"mappings":"AAAA,SAAkCA,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AAOlE;;;;;;CAMC,GACD,MAAMC,cAAc,CAChBC,QACAC,SACAC;IAEA,KAAK,MAAMC,SAASF,QAAS;QACzB,IAAIE,KAAK,CAAC,EAAE,IAAID,OAAOC,QAAQ;YAC3BH,OAAOI,GAAG,CAACD,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE;QACjC,OAAO;YACHH,OAAOK,MAAM,CAACF,KAAK,CAAC,EAAE;QAC1B;IACJ;AACJ;AAEA;;;;;CAKC,GACD,MAAMG,kBAAkB;IACpB,MAAMC,OAAOC,OAAOC,QAAQ,CAACF,IAAI;IACjC,6CAA6C;IAC7C,MAAMG,cAAcH,KAAKI,OAAO,CAAC,KAAKJ,KAAKI,OAAO,CAAC,QAAQ;IAC3D,MAAMC,SAAS,IAAIC,gBAAgBH,cAAc,IAAI,KAAKH,KAAKO,SAAS,CAACJ;IACzE,OAAO;QACHE;QACA;YACI,IAAIG,SAASL,cAAc,IAAIH,OAAOA,KAAKO,SAAS,CAAC,GAAGJ;YACxD,IAAIE,OAAOI,IAAI,GAAG,GAAG;gBACjBD,SAASA,OAAOE,MAAM,CAAC,KAAKL,OAAOM,QAAQ;YAC/C;YACA,OAAOH;QACX;KACH;AACL;AAoCA,OAAO,MAAMI,oBAAoB,CAAIC;IACjC,MAAMC,OAAOD,QAAQC,IAAI,KAAK;IAC9B,MAAMC,eAAezB,QACjB,IAAOwB,OAAOD,QAAQG,YAAY,CAACjB,iBAAiB,CAAC,EAAE,IAAIc,QAAQE,YAAY,EAC/E;QAACF,QAAQE,YAAY;QAAEF,QAAQC,IAAI;KAAC;IAExC,MAAM,CAACG,OAAOC,SAAS,GAAG3B,SAAYwB;IACtC,wFAAwF;IACxF,MAAMI,yBAAyB7B,QAAQ;QACnC,MAAM8B,IAAI,IAAIC;QACd7B,YAAY4B,GAAGP,QAAQS,UAAU,CAACT,QAAQE,YAAY,GAAG,CAACQ,IAAM;QAChE,OAAOH;IACX,GAAG;QAACL;QAAcF,QAAQS,UAAU;KAAC;IACrC,MAAME,mBAAmBlC,QACrB,IACIwB,OACM,CAACW;YACGP,SAAS,CAACQ;gBACN,MAAM,CAACC,QAAQC,OAAO,GAAG7B;gBACzB,MAAM8B,WAAWJ,mBAAmBK,WAAWL,QAAQC,OAAOD;gBAC9DjC,YACImC,QACAd,QAAQS,UAAU,CAACO,WACnB,CAAC,CAACE,KAAKC,MAAM,GAAKb,uBAAuBc,GAAG,CAACF,SAASC;gBAE1D/B,OAAOiC,OAAO,CAACC,YAAY,CAAC,MAAM,IAAIP;gBACtC,OAAOC;YACX;QACJ,IACAX,UACV;QAACJ;KAAK;IAGV,OAAO;QAACG;QAAOO;KAAiB;AACpC,EAAE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coveord/plasma-mantine",
3
- "version": "55.4.0",
3
+ "version": "55.5.1",
4
4
  "description": "A Plasma flavoured Mantine theme",
5
5
  "keywords": [
6
6
  "plasma",
@@ -66,7 +66,7 @@
66
66
  "@types/react-dom": "18.3.1",
67
67
  "embla-carousel-react": "7.1.0",
68
68
  "identity-obj-proxy": "3.0.0",
69
- "jsdom": "25.0.1",
69
+ "jsdom": "26.0.0",
70
70
  "postcss": "8.4.49",
71
71
  "postcss-preset-mantine": "^1.11.0",
72
72
  "postcss-simple-vars": "^7.0.1",
@@ -12,7 +12,6 @@ describe('useUrlSyncedState', () => {
12
12
  initialState: '',
13
13
  serializer: (state) => [['key', state]],
14
14
  deserializer: (params) => params.get('key') ?? '',
15
- sync: true,
16
15
  }),
17
16
  );
18
17
  act(() => result.current[1]('value'));
@@ -23,34 +22,34 @@ describe('useUrlSyncedState', () => {
23
22
  it('allows to serialize the state value into multiple parameters', () => {
24
23
  const {result} = renderHook(() =>
25
24
  useUrlSyncedState({
26
- initialState: {key1: '', key2: ''},
27
- serializer: (state) => [
28
- ['key1', state.key1],
29
- ['key2', state.key2],
30
- ],
31
- deserializer: (params) => ({
32
- key1: params.get('key1') ?? '',
33
- key2: params.get('key2') ?? '',
34
- }),
35
- sync: true,
25
+ initialState: new Date(),
26
+ serializer: (state) => {
27
+ const iso = state.toISOString();
28
+ return [
29
+ ['date', iso.substring(0, 10)],
30
+ ['time', iso.substring(11, 24)],
31
+ ];
32
+ },
33
+ deserializer: (params) =>
34
+ new Date(`${params.get('date') ?? '2025-01-01'}T${params.get('time') ?? '00:00:00.000Z'}`),
36
35
  }),
37
36
  );
38
- act(() => result.current[1]({key1: 'value1', key2: 'value2'}));
39
- expect(window.location.search).toBe('?key1=value1&key2=value2');
37
+ act(() => result.current[1](new Date(Date.UTC(2025, 0, 31, 12, 34, 56, 789))));
38
+ expect(window.location.search).toBe('?date=2025-01-31&time=12%3A34%3A56.789Z');
40
39
  });
41
40
 
42
41
  it('removes the parameter from the url if the state serializes to the same value as the initial state', () => {
43
42
  const {result} = renderHook(() =>
44
43
  useUrlSyncedState({
45
- initialState: 'initial',
46
- serializer: (state) => [['key', state]],
47
- deserializer: (params) => params.get('key') ?? '',
44
+ initialState: true,
45
+ serializer: (state) => [['key', state ? 'true' : 'false']],
46
+ deserializer: (params) => params.get('key') === 'true',
48
47
  sync: true,
49
48
  }),
50
49
  );
51
- act(() => result.current[1]('value'));
52
- expect(window.location.search).toBe('?key=value');
53
- act(() => result.current[1]('initial'));
50
+ act(() => result.current[1](false));
51
+ expect(window.location.search).toBe('?key=false');
52
+ act(() => result.current[1](true));
54
53
  expect(window.location.search).toBe('');
55
54
  });
56
55
 
@@ -60,7 +59,6 @@ describe('useUrlSyncedState', () => {
60
59
  initialState: 'initial',
61
60
  serializer: (state) => [['key', state]],
62
61
  deserializer: (params) => params.get('key') ?? '',
63
- sync: true,
64
62
  }),
65
63
  );
66
64
  act(() => result.current[1]('value'));
@@ -91,7 +89,6 @@ describe('useUrlSyncedState', () => {
91
89
  initialState: 'initial',
92
90
  serializer: (state) => [['key', state]],
93
91
  deserializer: (params) => params.get('key') ?? '',
94
- sync: true,
95
92
  }),
96
93
  );
97
94
  expect(result.current[0]).toBe('value');
@@ -110,4 +107,60 @@ describe('useUrlSyncedState', () => {
110
107
  );
111
108
  expect(result.current[0]).toBe('initial');
112
109
  });
110
+
111
+ describe('with hash router urls', () => {
112
+ it('reads values from the hash parameters', () => {
113
+ window.history.replaceState(null, '', '?key=unexpected#/hash/route?key=value');
114
+
115
+ const {result} = renderHook(() =>
116
+ useUrlSyncedState({
117
+ initialState: 'initial',
118
+ serializer: (state) => [['key', state]],
119
+ deserializer: (params) => params.get('key') ?? '',
120
+ }),
121
+ );
122
+ expect(result.current[0]).toBe('value');
123
+ });
124
+
125
+ it('serializes the state values to the hash route parameters', () => {
126
+ window.history.replaceState(null, '', '?a=untouched#/hash/route');
127
+
128
+ const {result} = renderHook(() =>
129
+ useUrlSyncedState({
130
+ initialState: {a: null, b: null},
131
+ serializer: (state) => [
132
+ ['a', state.a],
133
+ ['b', state.b],
134
+ ],
135
+ deserializer: (params) => ({a: params.get('a') ?? '', b: params.get('b')}),
136
+ }),
137
+ );
138
+ act(() => result.current[1]({a: 'test', b: 'state'}));
139
+ expect(result.current[0]).toStrictEqual({a: 'test', b: 'state'});
140
+ expect(window.location.search).toBe('?a=untouched');
141
+ expect(window.location.hash).toBe('#/hash/route?a=test&b=state');
142
+ });
143
+
144
+ it('removes the state values from the hash route parameters', () => {
145
+ window.history.replaceState(null, '', '?a=untouched&b=part-of-search#/hash/route?a=1&b=2');
146
+
147
+ const {result} = renderHook(() =>
148
+ useUrlSyncedState<{a: number | null; b: number}>({
149
+ initialState: {a: 13, b: 37},
150
+ serializer: (state) => [
151
+ ['a', state.a?.toString()],
152
+ ['b', state.b?.toString()],
153
+ ],
154
+ deserializer: (params) => ({
155
+ a: Number.parseInt(params.get('a') ?? '0', 10),
156
+ b: Number.parseInt(params.get('b') ?? '0', 10),
157
+ }),
158
+ }),
159
+ );
160
+ act(() => result.current[1]({a: null, b: 37}));
161
+ expect(result.current[0]).toStrictEqual({a: null, b: 37});
162
+ expect(window.location.search).toBe('?a=untouched&b=part-of-search');
163
+ expect(window.location.hash).toBe('#/hash/route');
164
+ });
165
+ });
113
166
  });
@@ -4,3 +4,4 @@ export {type TablePredicateProps} from './table-predicate/TablePredicate';
4
4
  export {type TableAction, type TableLayout, type TableLayoutProps, type TableProps} from './Table.types';
5
5
  export {useTableContext} from './TableContext';
6
6
  export {useTable, type TableState, type TableStore, type UseTableOptions} from './use-table';
7
+ export {useUrlSyncedState} from './use-url-synced-state';
@@ -76,6 +76,7 @@ export const TablePredicate: FunctionComponent<TablePredicateProps> = factory<Ta
76
76
  aria-label={label ?? id}
77
77
  searchable={data.length > 7}
78
78
  renderOption={renderOption}
79
+ scrollAreaProps={{type: 'always'}}
79
80
  {...getStyles('predicateSelect', stylesApiProps)}
80
81
  />
81
82
  </Group>
@@ -218,6 +218,11 @@ const defaultState: Partial<TableState> = {
218
218
  export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): TableStore<TData> => {
219
219
  const options = defaultsDeep({}, userOptions, defaultOptions) as UseTableOptions<TData>;
220
220
  const initialState = defaultsDeep({}, options.initialState, defaultState) as TableState<TData>;
221
+ /**
222
+ * The `useUrlSyncedState` hook defaults to synchronize, but the table wants to default to not synchronize,
223
+ * so always pass the sync option as a resolved boolean value.
224
+ */
225
+ const sync = !!options.syncWithUrl;
221
226
 
222
227
  // synced with url
223
228
  const [pagination, setPagination] = useUrlSyncedState<TableState<TData>['pagination']>({
@@ -234,7 +239,7 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
234
239
  },
235
240
  initialState.pagination,
236
241
  ),
237
- sync: options.syncWithUrl,
242
+ sync,
238
243
  });
239
244
  const [sorting, setSorting] = useUrlSyncedState<TableState<TData>['sorting']>({
240
245
  initialState: initialState.sorting,
@@ -251,13 +256,13 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
251
256
  return {id, desc: order === 'desc'};
252
257
  });
253
258
  },
254
- sync: options.syncWithUrl,
259
+ sync,
255
260
  });
256
261
  const [globalFilter, setGlobalFilter] = useUrlSyncedState<TableState<TData>['globalFilter']>({
257
262
  initialState: initialState.globalFilter,
258
263
  serializer: (filter) => [['filter', filter]],
259
264
  deserializer: (params) => params.get('filter') ?? initialState.globalFilter,
260
- sync: options.syncWithUrl,
265
+ sync,
261
266
  });
262
267
  const [predicates, setPredicates] = useUrlSyncedState<TableState<TData>['predicates']>({
263
268
  initialState: initialState.predicates,
@@ -270,13 +275,13 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
270
275
  },
271
276
  {} as TableState<TData>['predicates'],
272
277
  ),
273
- sync: options.syncWithUrl,
278
+ sync,
274
279
  });
275
280
  const [layout, setLayout] = useUrlSyncedState<TableState<TData>['layout']>({
276
281
  initialState: initialState.layout,
277
282
  serializer: (_layout) => [['layout', _layout]],
278
283
  deserializer: (params) => params.get('layout') ?? initialState.layout,
279
- sync: options.syncWithUrl,
284
+ sync,
280
285
  });
281
286
  const [dateRange, setDateRange] = useUrlSyncedState<TableState<TData>['dateRange']>({
282
287
  initialState: initialState.dateRange,
@@ -288,7 +293,7 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
288
293
  params.get('from') ? new Date(params.get('from') as string) : initialState.dateRange[0],
289
294
  params.get('to') ? new Date(params.get('to') as string) : initialState.dateRange[1],
290
295
  ],
291
- sync: options.syncWithUrl,
296
+ sync,
292
297
  });
293
298
  const [columnVisibility, setColumnVisibility] = useUrlSyncedState<TableState<TData>['columnVisibility']>({
294
299
  initialState: initialState.columnVisibility,
@@ -323,7 +328,7 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
323
328
  });
324
329
  return columns;
325
330
  },
326
- sync: options.syncWithUrl,
331
+ sync,
327
332
  });
328
333
 
329
334
  // unsynced