@coveord/plasma-mantine 55.5.1 → 55.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +3 -3
- package/.turbo/turbo-test.log +49 -49
- package/dist/.tsbuildinfo +1 -1
- package/dist/cjs/components/table/index.d.ts +1 -1
- package/dist/cjs/components/table/index.d.ts.map +1 -1
- package/dist/cjs/components/table/index.js.map +1 -1
- package/dist/cjs/components/table/use-table.d.ts.map +1 -1
- package/dist/cjs/components/table/use-table.js +177 -162
- package/dist/cjs/components/table/use-table.js.map +1 -1
- package/dist/cjs/components/table/use-url-synced-state.d.ts +8 -4
- package/dist/cjs/components/table/use-url-synced-state.d.ts.map +1 -1
- package/dist/cjs/components/table/use-url-synced-state.js +94 -67
- package/dist/cjs/components/table/use-url-synced-state.js.map +1 -1
- package/dist/esm/components/table/index.d.ts +1 -1
- package/dist/esm/components/table/index.d.ts.map +1 -1
- package/dist/esm/components/table/index.js.map +1 -1
- package/dist/esm/components/table/use-table.d.ts.map +1 -1
- package/dist/esm/components/table/use-table.js +116 -95
- package/dist/esm/components/table/use-table.js.map +1 -1
- package/dist/esm/components/table/use-url-synced-state.d.ts +8 -4
- package/dist/esm/components/table/use-url-synced-state.d.ts.map +1 -1
- package/dist/esm/components/table/use-url-synced-state.js +59 -44
- package/dist/esm/components/table/use-url-synced-state.js.map +1 -1
- package/package.json +1 -1
- package/src/components/table/__tests__/use-url-synced-state.unit.spec.ts +2 -1
- package/src/components/table/index.ts +1 -1
- package/src/components/table/use-table.ts +109 -79
- package/src/components/table/use-url-synced-state.ts +79 -64
|
@@ -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 * 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
|
+
{"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, UseUrlSyncedStateOptions} 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\nconst serialization = <K extends keyof TableState>(\n input: Pick<UseUrlSyncedStateOptions<TableState[K]>, 'serializer' | 'deserializer'>,\n) => Object.freeze(input);\n\nconst PAGINATION_SERIALIZATION = serialization<'pagination'>({\n serializer: ({pageIndex, pageSize}) => [\n ['page', (pageIndex + 1).toString()],\n ['pageSize', pageSize.toString()],\n ],\n deserializer: (params, initialState) =>\n defaultsDeep(\n {\n pageIndex: params.get('page') ? Math.max(1, parseInt(params.get('page'), 10)) - 1 : undefined,\n pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined,\n },\n initialState,\n ),\n});\n\nconst SORTING_SERIALIZATION = serialization<'sorting'>({\n serializer: (sorting) => [['sortBy', sorting.map(({id, desc}) => `${id}.${desc ? 'desc' : 'asc'}`).join(',')]],\n deserializer: (params, initialState) => {\n if (!params.has('sortBy')) {\n return initialState;\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});\n\nconst GLOBAL_FILTER_SERIALIZATION = serialization<'globalFilter'>({\n serializer: (filter) => [['filter', filter]],\n deserializer: (params, initialState) => params.get('filter') ?? initialState,\n});\n\nconst PREDICATES_SERIALIZATION = serialization<'predicates'>({\n serializer: (predicates) => Object.entries(predicates),\n deserializer: (params, initialState) =>\n Object.keys(initialState).reduce(\n (acc, predicateKey) => {\n acc[predicateKey] = params.get(predicateKey) ?? initialState[predicateKey];\n return acc;\n },\n {} as TableState['predicates'],\n ),\n});\n\nconst LAYOUT_SERIALIZATION = serialization<'layout'>({\n serializer: (_layout) => [['layout', _layout]],\n deserializer: (params, initialState) => params.get('layout') ?? initialState,\n});\n\nconst DATE_RANGE_SERIALIZATION = serialization<'dateRange'>({\n serializer: ([from, to]) => [\n ['from', from?.toISOString() ?? '', true],\n ['to', to?.toISOString() ?? '', true],\n ],\n deserializer: (params, initial) => [\n params.get('from') ? new Date(params.get('from') as string) : initial[0],\n params.get('to') ? new Date(params.get('to') as string) : initial[1],\n ],\n});\n\nconst COLUMN_VISIBILITY_SERIALIZATION = serialization<'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, initial) => {\n if (!params.has('show') && !params.has('hide')) {\n return initial;\n }\n const visible = params.get('show')?.split(',') ?? [];\n const invisible = params.get('hide')?.split(',') ?? [];\n const columns = {} as TableState['columnVisibility'];\n visible.forEach((column) => {\n columns[column] = true;\n });\n invisible.forEach((column) => {\n columns[column] = false;\n });\n return columns;\n },\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 // (Optionally) synced with url\n const [pagination, setPagination] = useUrlSyncedState<TableState<TData>['pagination']>({\n ...PAGINATION_SERIALIZATION,\n initialState: initialState.pagination,\n sync,\n });\n const [sorting, setSorting] = useUrlSyncedState<TableState<TData>['sorting']>({\n ...SORTING_SERIALIZATION,\n initialState: initialState.sorting,\n sync,\n });\n const [globalFilter, setGlobalFilter] = useUrlSyncedState<TableState<TData>['globalFilter']>({\n ...GLOBAL_FILTER_SERIALIZATION,\n initialState: initialState.globalFilter,\n sync,\n });\n const [predicates, setPredicates] = useUrlSyncedState<TableState<TData>['predicates']>({\n ...PREDICATES_SERIALIZATION,\n initialState: initialState.predicates,\n sync,\n });\n const [layout, setLayout] = useUrlSyncedState<TableState<TData>['layout']>({\n ...LAYOUT_SERIALIZATION,\n initialState: initialState.layout,\n sync,\n });\n const [dateRange, setDateRange] = useUrlSyncedState<TableState<TData>['dateRange']>({\n ...DATE_RANGE_SERIALIZATION,\n initialState: initialState.dateRange,\n sync,\n });\n const [columnVisibility, setColumnVisibility] = useUrlSyncedState<TableState<TData>['columnVisibility']>({\n ...COLUMN_VISIBILITY_SERIALIZATION,\n initialState: initialState.columnVisibility,\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","serialization","input","Object","freeze","PAGINATION_SERIALIZATION","serializer","toString","deserializer","params","initialState","get","Math","max","parseInt","undefined","SORTING_SERIALIZATION","map","id","desc","join","has","sorts","split","sort","order","GLOBAL_FILTER_SERIALIZATION","filter","PREDICATES_SERIALIZATION","entries","keys","reduce","acc","predicateKey","LAYOUT_SERIALIZATION","_layout","DATE_RANGE_SERIALIZATION","from","to","toISOString","initial","Date","COLUMN_VISIBILITY_SERIALIZATION","columns","visible","columnName","invisible","forEach","column","useTable","userOptions","options","sync","setPagination","setSorting","setGlobalFilter","setPredicates","setLayout","setDateRange","setColumnVisibility","_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":"AAAA,SAAQA,YAAY,QAAO,iBAAiB;AAE5C,OAAOC,kBAAkB,sBAAsB;AAC/C,SAAkCC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AAE/E,SAAQC,iBAAiB,QAAiC,yBAAyB;AA8LnF,MAAMC,iBAAkC;IACpCC,oBAAoB;IACpBC,yBAAyB;IACzBC,gBAAgB;IAChBC,aAAa;AACjB;AAEA,MAAMC,eAAoC;IACtCC,YAAY;QACRC,WAAW;QACXC,UAAU;IACd;IACAC,cAAc;IACdC,SAAS,EAAE;IACXC,cAAc;IACdC,YAAY,CAAC;IACbC,QAAQ;IACRC,WAAW;QAAC;QAAM;KAAK;IACvBC,cAAc,CAAC;IACfC,kBAAkB,CAAC;AACvB;AAEA,MAAMC,gBAAgB,CAClBC,QACCC,OAAOC,MAAM,CAACF;AAEnB,MAAMG,2BAA2BJ,cAA4B;IACzDK,YAAY,CAAC,EAACf,SAAS,EAAEC,QAAQ,EAAC,GAAK;YACnC;gBAAC;gBAASD,CAAAA,YAAY,CAAA,EAAGgB,QAAQ;aAAG;YACpC;gBAAC;gBAAYf,SAASe,QAAQ;aAAG;SACpC;IACDC,cAAc,CAACC,QAAQC,eACnB/B,aACI;YACIY,WAAWkB,OAAOE,GAAG,CAAC,UAAUC,KAAKC,GAAG,CAAC,GAAGC,SAASL,OAAOE,GAAG,CAAC,SAAS,OAAO,IAAII;YACpFvB,UAAUiB,OAAOE,GAAG,CAAC,cAAcG,SAASL,OAAOE,GAAG,CAAC,aAAa,MAAMI;QAC9E,GACAL;AAEZ;AAEA,MAAMM,wBAAwBf,cAAyB;IACnDK,YAAY,CAACZ,UAAY;YAAC;gBAAC;gBAAUA,QAAQuB,GAAG,CAAC,CAAC,EAACC,EAAE,EAAEC,IAAI,EAAC,GAAK,GAAGD,GAAG,CAAC,EAAEC,OAAO,SAAS,OAAO,EAAEC,IAAI,CAAC;aAAK;SAAC;IAC9GZ,cAAc,CAACC,QAAQC;QACnB,IAAI,CAACD,OAAOY,GAAG,CAAC,WAAW;YACvB,OAAOX;QACX;QACA,MAAMY,QAAQb,OAAOE,GAAG,CAAC,WAAWY,MAAM,QAAQ,EAAE;QACpD,OAAOD,MAAML,GAAG,CAAC,CAACO;YACd,MAAM,CAACN,IAAIO,MAAM,GAAGD,KAAKD,KAAK,CAAC;YAC/B,OAAO;gBAACL;gBAAIC,MAAMM,UAAU;YAAM;QACtC;IACJ;AACJ;AAEA,MAAMC,8BAA8BzB,cAA8B;IAC9DK,YAAY,CAACqB,SAAW;YAAC;gBAAC;gBAAUA;aAAO;SAAC;IAC5CnB,cAAc,CAACC,QAAQC,eAAiBD,OAAOE,GAAG,CAAC,aAAaD;AACpE;AAEA,MAAMkB,2BAA2B3B,cAA4B;IACzDK,YAAY,CAACV,aAAeO,OAAO0B,OAAO,CAACjC;IAC3CY,cAAc,CAACC,QAAQC,eACnBP,OAAO2B,IAAI,CAACpB,cAAcqB,MAAM,CAC5B,CAACC,KAAKC;YACFD,GAAG,CAACC,aAAa,GAAGxB,OAAOE,GAAG,CAACsB,iBAAiBvB,YAAY,CAACuB,aAAa;YAC1E,OAAOD;QACX,GACA,CAAC;AAEb;AAEA,MAAME,uBAAuBjC,cAAwB;IACjDK,YAAY,CAAC6B,UAAY;YAAC;gBAAC;gBAAUA;aAAQ;SAAC;IAC9C3B,cAAc,CAACC,QAAQC,eAAiBD,OAAOE,GAAG,CAAC,aAAaD;AACpE;AAEA,MAAM0B,2BAA2BnC,cAA2B;IACxDK,YAAY,CAAC,CAAC+B,MAAMC,GAAG,GAAK;YACxB;gBAAC;gBAAQD,MAAME,iBAAiB;gBAAI;aAAK;YACzC;gBAAC;gBAAMD,IAAIC,iBAAiB;gBAAI;aAAK;SACxC;IACD/B,cAAc,CAACC,QAAQ+B,UAAY;YAC/B/B,OAAOE,GAAG,CAAC,UAAU,IAAI8B,KAAKhC,OAAOE,GAAG,CAAC,WAAqB6B,OAAO,CAAC,EAAE;YACxE/B,OAAOE,GAAG,CAAC,QAAQ,IAAI8B,KAAKhC,OAAOE,GAAG,CAAC,SAAmB6B,OAAO,CAAC,EAAE;SACvE;AACL;AAEA,MAAME,kCAAkCzC,cAAkC;IACtEK,YAAY,CAACqC,UAAY;YACrB;gBACI;gBACAxC,OAAO0B,OAAO,CAACc,SACVhB,MAAM,CAAC,CAAC,GAAGiB,QAAQ,GAAKA,YAAY,MACpC3B,GAAG,CAAC,CAAC,CAAC4B,WAAW,GAAKA,YACtBzB,IAAI,CAAC;aACb;YACD;gBACI;gBACAjB,OAAO0B,OAAO,CAACc,SACVhB,MAAM,CAAC,CAAC,GAAGiB,QAAQ,GAAKA,YAAY,OACpC3B,GAAG,CAAC,CAAC,CAAC4B,WAAW,GAAKA,YACtBzB,IAAI,CAAC;aACb;SACJ;IACDZ,cAAc,CAACC,QAAQ+B;QACnB,IAAI,CAAC/B,OAAOY,GAAG,CAAC,WAAW,CAACZ,OAAOY,GAAG,CAAC,SAAS;YAC5C,OAAOmB;QACX;QACA,MAAMI,UAAUnC,OAAOE,GAAG,CAAC,SAASY,MAAM,QAAQ,EAAE;QACpD,MAAMuB,YAAYrC,OAAOE,GAAG,CAAC,SAASY,MAAM,QAAQ,EAAE;QACtD,MAAMoB,UAAU,CAAC;QACjBC,QAAQG,OAAO,CAAC,CAACC;YACbL,OAAO,CAACK,OAAO,GAAG;QACtB;QACAF,UAAUC,OAAO,CAAC,CAACC;YACfL,OAAO,CAACK,OAAO,GAAG;QACtB;QACA,OAAOL;IACX;AACJ;AAEA,OAAO,MAAMM,WAAW,CAAQC,cAAsC,CAAC,CAAC;IACpE,MAAMC,UAAUxE,aAAa,CAAC,GAAGuE,aAAalE;IAC9C,MAAM0B,eAAe/B,aAAa,CAAC,GAAGwE,QAAQzC,YAAY,EAAErB;IAC5D;;;KAGC,GACD,MAAM+D,OAAO,CAAC,CAACD,QAAQ/D,WAAW;IAElC,+BAA+B;IAC/B,MAAM,CAACE,YAAY+D,cAAc,GAAGtE,kBAAmD;QACnF,GAAGsB,wBAAwB;QAC3BK,cAAcA,aAAapB,UAAU;QACrC8D;IACJ;IACA,MAAM,CAAC1D,SAAS4D,WAAW,GAAGvE,kBAAgD;QAC1E,GAAGiC,qBAAqB;QACxBN,cAAcA,aAAahB,OAAO;QAClC0D;IACJ;IACA,MAAM,CAACzD,cAAc4D,gBAAgB,GAAGxE,kBAAqD;QACzF,GAAG2C,2BAA2B;QAC9BhB,cAAcA,aAAaf,YAAY;QACvCyD;IACJ;IACA,MAAM,CAACxD,YAAY4D,cAAc,GAAGzE,kBAAmD;QACnF,GAAG6C,wBAAwB;QAC3BlB,cAAcA,aAAad,UAAU;QACrCwD;IACJ;IACA,MAAM,CAACvD,QAAQ4D,UAAU,GAAG1E,kBAA+C;QACvE,GAAGmD,oBAAoB;QACvBxB,cAAcA,aAAab,MAAM;QACjCuD;IACJ;IACA,MAAM,CAACtD,WAAW4D,aAAa,GAAG3E,kBAAkD;QAChF,GAAGqD,wBAAwB;QAC3B1B,cAAcA,aAAaZ,SAAS;QACpCsD;IACJ;IACA,MAAM,CAACpD,kBAAkB2D,oBAAoB,GAAG5E,kBAAyD;QACrG,GAAG2D,+BAA+B;QAClChC,cAAcA,aAAaV,gBAAgB;QAC3CoD;IACJ;IAEA,WAAW;IACX,MAAM,CAAC3D,cAAcmE,iBAAiB,GAAG9E,SAA4C4B,aAAajB,YAAY;IAC9G,MAAM,CAACoE,wBAAwBC,0BAA0B,GAAGhF,SACxD4B,aAAajB,YAAY;IAE7B,MAAM,CAACsE,UAAUC,YAAY,GAAGlF,SAAwC4B,aAAaqD,QAAQ;IAC7F,MAAM,CAAChE,cAAckE,gBAAgB,GAAGnF,SAA4C4B,aAAaX,YAAY;IAE7G,MAAMmE,aACF,CAAC,CAACvE,gBACFQ,OAAO2B,IAAI,CAAClC,YAAYuE,IAAI,CAAC,CAACC,YAAc,CAAC,CAACxE,UAAU,CAACwE,UAAU,KACnE,CAAC,CAACtE,WAAW,CAAC,EAAE,IAChB,CAAC,CAACA,WAAW,CAAC,EAAE;IAEpB,MAAMuE,WAAWR,2BAA2B;IAE5C,MAAMS,kBAA2C1F,YAC7C,CAAC2F;QACGX,iBAAiB,CAACY;YACd,MAAMC,kBAAkBF,mBAAmBG,WAAWH,QAAQC,OAAOD;YACrE,IAAI,CAACL,YAAY;gBACbJ,0BAA0BW;YAC9B;YACA,OAAOA;QACX;IACJ,GACA;QAACP;KAAW;IAGhB,MAAMS,eAAe/F,YAAY;QAC7B4E,cAAc9C,aAAad,UAAU;QACrC2D,gBAAgB;IACpB,GAAG,EAAE;IAEL,MAAMqB,oBAAoBhG,YAAY;QAClCqF,gBAAgB,CAAC;IACrB,GAAG,EAAE;IAEL,MAAMY,kBAAkBjG,YAAY,IAAMuB,OAAO2E,MAAM,CAAC/E,eAAe;QAACA;KAAa;IAErF,MAAMgF,iBAAiB,IAAMF,iBAAiB,CAAC,EAAE,IAAI;IAErDnG,aAAa;QACT,IAAI,CAACyE,QAAQjE,uBAAuB,EAAE;YAClC0F;QACJ;IACJ,GAAG;QAACjF;QAAcL;QAAYI;QAASI;QAAWF;KAAW;IAE7D,MAAMoF,QAAQnG,QACV,IAAO,CAAA;YACHS;YACAG;YACAC;YACAC;YACAoE;YACAnE;YACAC;YACAC;YACAC;YACAC;QACJ,CAAA,GACA;QACIV;QACAG;QACAC;QACAC;QACAoE;QACAnE;QACAC;QACAC;QACAC;QACAC;KACH;IAGL,OAAO;QACHgF;QACA3B;QACAiB;QACAhB;QACAC;QACAS;QACAR;QACAC;QACAC;QACAO;QACAN;QACAO;QACAG;QACAM;QACAC;QACAC;QACAE;QACAE,qBAAqB9B,QAAQlE,kBAAkB;QAC/CiG,oBAAoB/B,QAAQhE,cAAc;QAC1CgG,0BAA0BhC,QAAQjE,uBAAuB;IAC7D;AACJ,EAAE"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* A search param entry defines the
|
|
3
|
+
* A search param entry defines the encoded value of a search parameter as `[key, value, alwaysEmit?]`.
|
|
4
|
+
* The third entry is an optional boolean that defaults to `false`.
|
|
5
|
+
* Setting `alwaysEmit` to `true` means any non-nullish value is always written to the search params,
|
|
6
|
+
* even if it matches the initial value. It is also written on initialization.
|
|
4
7
|
*/
|
|
5
|
-
export type SearchParamEntry = [string, string | null | undefined];
|
|
8
|
+
export type SearchParamEntry = [string, string | null | undefined, boolean?];
|
|
6
9
|
export interface UseUrlSyncedStateOptions<T> {
|
|
7
10
|
/**
|
|
8
11
|
* The initial state to use, if there would be no search params to deserialize from.
|
|
9
12
|
* These values are also treated as defaults, and if the current state matches the initialState,
|
|
10
13
|
* no value will be written to the search params.
|
|
11
14
|
*/
|
|
12
|
-
initialState: T
|
|
15
|
+
initialState: T | (() => T);
|
|
13
16
|
/**
|
|
14
17
|
* The serializer function is used to determine how the state is translated to url search parameters.
|
|
15
18
|
* Called each time the state changes.
|
|
@@ -26,10 +29,11 @@ export interface UseUrlSyncedStateOptions<T> {
|
|
|
26
29
|
* May return a partial state, values that are not deserialed are taken from the `initialState`.
|
|
27
30
|
* Called only once when initializing the state.
|
|
28
31
|
* @param params All the search parameters of the current url.
|
|
32
|
+
* @param initialState The initialState, can be used to take defaults from.
|
|
29
33
|
* @returns The initial state based on the current url.
|
|
30
34
|
* @example (params) => params.get('filter') ?? '',
|
|
31
35
|
*/
|
|
32
|
-
deserializer: (params: URLSearchParams) => T;
|
|
36
|
+
deserializer: (params: URLSearchParams, initialState: T) => T;
|
|
33
37
|
/**
|
|
34
38
|
* Whether the state should be synced with the url, defaults to `true`.
|
|
35
39
|
* When set to `false`, the hook behaves just like a regular `useState` hook from react.
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;AAyC7E,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACvC;;;;OAIG;IACH,YAAY,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5B;;;;;;;;;OASG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,CAAC,KAAK,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC1D;;;;;;;;OAQG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;IAC9D;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAKD,eAAO,MAAM,iBAAiB,GAAI,CAAC,WAAW,wBAAwB,CAAC,CAAC,CAAC,8CA+CxE,CAAC"}
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Get the index of the ? in a URL that denotes the start of the "search".
|
|
4
|
+
* Performs a nested search for '#/', to detect hash router urls and take the params of the hash in that case.
|
|
4
5
|
*
|
|
5
|
-
* @param
|
|
6
|
-
* @
|
|
7
|
-
|
|
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
|
-
}
|
|
15
|
-
}
|
|
16
|
-
};
|
|
6
|
+
* @param url The URL to search.
|
|
7
|
+
* @returns The location of the question mark, or `-1` if not found.
|
|
8
|
+
*/ const indexOfSearch = (url)=>url.indexOf('?', url.indexOf('#/') + 1);
|
|
17
9
|
/**
|
|
18
10
|
* Read the **current** search params from `window.location`, with support for detecting React's HashRouter.
|
|
19
11
|
* Also returns a method that will yield the href (string) value, after any changes made on the params object.
|
|
@@ -21,45 +13,68 @@ import { useMemo, useState } from 'react';
|
|
|
21
13
|
* @returns The `URLSearchParams` instance, and a function that can be used to get an updated href.
|
|
22
14
|
*/ const getSearchParams = ()=>{
|
|
23
15
|
const href = window.location.href;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
const searchStart = indexOfSearch(href);
|
|
17
|
+
return new URLSearchParams(searchStart < 0 ? undefined : href.substring(searchStart));
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Apply the search params to the current location, using `replaceState` (no navigation history).
|
|
21
|
+
* Note that only parameters in the `params` argument will be set, any other current params will be removed.
|
|
22
|
+
*
|
|
23
|
+
* @param params The parameters to apply.
|
|
24
|
+
*/ const applySearchParams = (params)=>{
|
|
25
|
+
const currentHref = window.location.href;
|
|
26
|
+
const index = indexOfSearch(currentHref);
|
|
27
|
+
let nextHref = index < 0 ? currentHref : currentHref.substring(0, index);
|
|
28
|
+
if (params.size > 0) {
|
|
29
|
+
nextHref = nextHref.concat('?', params.toString());
|
|
30
|
+
}
|
|
31
|
+
if (nextHref !== currentHref) {
|
|
32
|
+
window.history.replaceState(null, '', nextHref);
|
|
33
|
+
}
|
|
37
34
|
};
|
|
35
|
+
const getInitialState = (options)=>options.initialState instanceof Function ? options.initialState() : options.initialState;
|
|
38
36
|
export const useUrlSyncedState = (options)=>{
|
|
39
37
|
const sync = options.sync !== false;
|
|
40
|
-
const
|
|
41
|
-
options
|
|
42
|
-
options.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Capture the initial state as a map, to compare values and not set them if they match.
|
|
38
|
+
const [state, setState] = useState(()=>{
|
|
39
|
+
const initialState = getInitialState(options);
|
|
40
|
+
return sync ? options.deserializer(getSearchParams(), initialState) : initialState;
|
|
41
|
+
});
|
|
42
|
+
// Capture the initial state as a map (first render only!), to compare values and see if they should be set to the params.
|
|
46
43
|
const initialStateSerialized = useMemo(()=>{
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
const stateMap = new Map();
|
|
45
|
+
let initialize = null;
|
|
46
|
+
for (const [key, value, alwaysEmit] of options.serializer(getInitialState(options))){
|
|
47
|
+
stateMap.set(key, value);
|
|
48
|
+
if (alwaysEmit && value) {
|
|
49
|
+
initialize ?? (initialize = getSearchParams());
|
|
50
|
+
initialize.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (initialize) {
|
|
54
|
+
applySearchParams(initialize);
|
|
55
|
+
}
|
|
56
|
+
return stateMap;
|
|
57
|
+
}, []);
|
|
58
|
+
const enhancedSetState = useMemo(()=>{
|
|
59
|
+
if (!sync) {
|
|
60
|
+
return setState;
|
|
61
|
+
}
|
|
62
|
+
return (updater)=>{
|
|
55
63
|
setState((old)=>{
|
|
56
|
-
const [search, getUrl] = getSearchParams();
|
|
57
64
|
const newValue = updater instanceof Function ? updater(old) : updater;
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
const search = getSearchParams();
|
|
66
|
+
for (const [key, value, alwaysEmit] of options.serializer(newValue)){
|
|
67
|
+
if (value && (alwaysEmit || !Object.is(initialStateSerialized.get(key), value))) {
|
|
68
|
+
search.set(key, value);
|
|
69
|
+
} else {
|
|
70
|
+
search.delete(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
applySearchParams(search);
|
|
60
74
|
return newValue;
|
|
61
75
|
});
|
|
62
|
-
}
|
|
76
|
+
};
|
|
77
|
+
}, [
|
|
63
78
|
sync
|
|
64
79
|
]);
|
|
65
80
|
return [
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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 encoded value of a search parameter as `[key, value, alwaysEmit?]`.\n * The third entry is an optional boolean that defaults to `false`.\n * Setting `alwaysEmit` to `true` means any non-nullish value is always written to the search params,\n * even if it matches the initial value. It is also written on initialization.\n */\nexport type SearchParamEntry = [string, string | null | undefined, boolean?];\n\n/**\n * Get the index of the ? in a URL that denotes the start of the \"search\".\n * Performs a nested search for '#/', to detect hash router urls and take the params of the hash in that case.\n *\n * @param url The URL to search.\n * @returns The location of the question mark, or `-1` if not found.\n */\nconst indexOfSearch = (url: string): number => url.indexOf('?', url.indexOf('#/') + 1);\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 => {\n const href = window.location.href;\n const searchStart = indexOfSearch(href);\n return new URLSearchParams(searchStart < 0 ? undefined : href.substring(searchStart));\n};\n\n/**\n * Apply the search params to the current location, using `replaceState` (no navigation history).\n * Note that only parameters in the `params` argument will be set, any other current params will be removed.\n *\n * @param params The parameters to apply.\n */\nconst applySearchParams = (params: URLSearchParams): void => {\n const currentHref = window.location.href;\n const index = indexOfSearch(currentHref);\n let nextHref = index < 0 ? currentHref : currentHref.substring(0, index);\n if (params.size > 0) {\n nextHref = nextHref.concat('?', params.toString());\n }\n if (nextHref !== currentHref) {\n window.history.replaceState(null, '', nextHref);\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 | (() => 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 * @param initialState The initialState, can be used to take defaults from.\n * @returns The initial state based on the current url.\n * @example (params) => params.get('filter') ?? '',\n */\n deserializer: (params: URLSearchParams, initialState: T) => 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\nconst getInitialState = <T>(options: UseUrlSyncedStateOptions<T>): T =>\n options.initialState instanceof Function ? options.initialState() : options.initialState;\n\nexport const useUrlSyncedState = <T>(options: UseUrlSyncedStateOptions<T>) => {\n const sync = options.sync !== false;\n const [state, setState] = useState<T>(() => {\n const initialState = getInitialState(options);\n return sync ? options.deserializer(getSearchParams(), initialState) : initialState;\n });\n // Capture the initial state as a map (first render only!), to compare values and see if they should be set to the params.\n const initialStateSerialized = useMemo(() => {\n const stateMap = new Map<string, string>();\n let initialize: URLSearchParams | null = null;\n for (const [key, value, alwaysEmit] of options.serializer(getInitialState(options))) {\n stateMap.set(key, value);\n if (alwaysEmit && value) {\n initialize ??= getSearchParams();\n initialize.set(key, value);\n }\n }\n if (initialize) {\n applySearchParams(initialize);\n }\n return stateMap;\n }, []);\n\n const enhancedSetState = useMemo<Dispatch<SetStateAction<T>>>(() => {\n if (!sync) {\n return setState;\n }\n return (updater: SetStateAction<T>) => {\n setState((old) => {\n const newValue = updater instanceof Function ? updater(old) : updater;\n\n const search = getSearchParams();\n for (const [key, value, alwaysEmit] of options.serializer(newValue)) {\n if (value && (alwaysEmit || !Object.is(initialStateSerialized.get(key), value))) {\n search.set(key, value);\n } else {\n search.delete(key);\n }\n }\n applySearchParams(search);\n\n return newValue;\n });\n };\n }, [sync]);\n\n return [state, enhancedSetState] as const;\n};\n"],"names":["useMemo","useState","indexOfSearch","url","indexOf","getSearchParams","href","window","location","searchStart","URLSearchParams","undefined","substring","applySearchParams","params","currentHref","index","nextHref","size","concat","toString","history","replaceState","getInitialState","options","initialState","Function","useUrlSyncedState","sync","state","setState","deserializer","initialStateSerialized","stateMap","Map","initialize","key","value","alwaysEmit","serializer","set","enhancedSetState","updater","old","newValue","search","Object","is","get","delete"],"mappings":"AAAA,SAAkCA,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AAUlE;;;;;;CAMC,GACD,MAAMC,gBAAgB,CAACC,MAAwBA,IAAIC,OAAO,CAAC,KAAKD,IAAIC,OAAO,CAAC,QAAQ;AAEpF;;;;;CAKC,GACD,MAAMC,kBAAkB;IACpB,MAAMC,OAAOC,OAAOC,QAAQ,CAACF,IAAI;IACjC,MAAMG,cAAcP,cAAcI;IAClC,OAAO,IAAII,gBAAgBD,cAAc,IAAIE,YAAYL,KAAKM,SAAS,CAACH;AAC5E;AAEA;;;;;CAKC,GACD,MAAMI,oBAAoB,CAACC;IACvB,MAAMC,cAAcR,OAAOC,QAAQ,CAACF,IAAI;IACxC,MAAMU,QAAQd,cAAca;IAC5B,IAAIE,WAAWD,QAAQ,IAAID,cAAcA,YAAYH,SAAS,CAAC,GAAGI;IAClE,IAAIF,OAAOI,IAAI,GAAG,GAAG;QACjBD,WAAWA,SAASE,MAAM,CAAC,KAAKL,OAAOM,QAAQ;IACnD;IACA,IAAIH,aAAaF,aAAa;QAC1BR,OAAOc,OAAO,CAACC,YAAY,CAAC,MAAM,IAAIL;IAC1C;AACJ;AAqCA,MAAMM,kBAAkB,CAAIC,UACxBA,QAAQC,YAAY,YAAYC,WAAWF,QAAQC,YAAY,KAAKD,QAAQC,YAAY;AAE5F,OAAO,MAAME,oBAAoB,CAAIH;IACjC,MAAMI,OAAOJ,QAAQI,IAAI,KAAK;IAC9B,MAAM,CAACC,OAAOC,SAAS,GAAG7B,SAAY;QAClC,MAAMwB,eAAeF,gBAAgBC;QACrC,OAAOI,OAAOJ,QAAQO,YAAY,CAAC1B,mBAAmBoB,gBAAgBA;IAC1E;IACA,0HAA0H;IAC1H,MAAMO,yBAAyBhC,QAAQ;QACnC,MAAMiC,WAAW,IAAIC;QACrB,IAAIC,aAAqC;QACzC,KAAK,MAAM,CAACC,KAAKC,OAAOC,WAAW,IAAId,QAAQe,UAAU,CAAChB,gBAAgBC,UAAW;YACjFS,SAASO,GAAG,CAACJ,KAAKC;YAClB,IAAIC,cAAcD,OAAO;gBACrBF,eAAAA,aAAe9B;gBACf8B,WAAWK,GAAG,CAACJ,KAAKC;YACxB;QACJ;QACA,IAAIF,YAAY;YACZtB,kBAAkBsB;QACtB;QACA,OAAOF;IACX,GAAG,EAAE;IAEL,MAAMQ,mBAAmBzC,QAAqC;QAC1D,IAAI,CAAC4B,MAAM;YACP,OAAOE;QACX;QACA,OAAO,CAACY;YACJZ,SAAS,CAACa;gBACN,MAAMC,WAAWF,mBAAmBhB,WAAWgB,QAAQC,OAAOD;gBAE9D,MAAMG,SAASxC;gBACf,KAAK,MAAM,CAAC+B,KAAKC,OAAOC,WAAW,IAAId,QAAQe,UAAU,CAACK,UAAW;oBACjE,IAAIP,SAAUC,CAAAA,cAAc,CAACQ,OAAOC,EAAE,CAACf,uBAAuBgB,GAAG,CAACZ,MAAMC,MAAK,GAAI;wBAC7EQ,OAAOL,GAAG,CAACJ,KAAKC;oBACpB,OAAO;wBACHQ,OAAOI,MAAM,CAACb;oBAClB;gBACJ;gBACAvB,kBAAkBgC;gBAElB,OAAOD;YACX;QACJ;IACJ,GAAG;QAAChB;KAAK;IAET,OAAO;QAACC;QAAOY;KAAiB;AACpC,EAAE"}
|
package/package.json
CHANGED
|
@@ -43,7 +43,8 @@ describe('useUrlSyncedState', () => {
|
|
|
43
43
|
useUrlSyncedState({
|
|
44
44
|
initialState: true,
|
|
45
45
|
serializer: (state) => [['key', state ? 'true' : 'false']],
|
|
46
|
-
deserializer: (params) =>
|
|
46
|
+
deserializer: (params, initialState) =>
|
|
47
|
+
params.has('key') ? params.get('key') === 'true' : initialState,
|
|
47
48
|
sync: true,
|
|
48
49
|
}),
|
|
49
50
|
);
|
|
@@ -4,4 +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';
|
|
7
|
+
export {useUrlSyncedState, type UseUrlSyncedStateOptions, type SearchParamEntry} from './use-url-synced-state';
|
|
@@ -3,7 +3,7 @@ import {type ExpandedState, type PaginationState, type SortingState} from '@tans
|
|
|
3
3
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
4
4
|
import {Dispatch, SetStateAction, useCallback, useMemo, useState} from 'react';
|
|
5
5
|
import {type DateRangePickerValue} from '../date-range-picker';
|
|
6
|
-
import {useUrlSyncedState} from './use-url-synced-state';
|
|
6
|
+
import {useUrlSyncedState, UseUrlSyncedStateOptions} from './use-url-synced-state';
|
|
7
7
|
|
|
8
8
|
// Create a deeply optional version of another type
|
|
9
9
|
type DeepPartial<T> = {
|
|
@@ -215,6 +215,106 @@ const defaultState: Partial<TableState> = {
|
|
|
215
215
|
columnVisibility: {},
|
|
216
216
|
};
|
|
217
217
|
|
|
218
|
+
const serialization = <K extends keyof TableState>(
|
|
219
|
+
input: Pick<UseUrlSyncedStateOptions<TableState[K]>, 'serializer' | 'deserializer'>,
|
|
220
|
+
) => Object.freeze(input);
|
|
221
|
+
|
|
222
|
+
const PAGINATION_SERIALIZATION = serialization<'pagination'>({
|
|
223
|
+
serializer: ({pageIndex, pageSize}) => [
|
|
224
|
+
['page', (pageIndex + 1).toString()],
|
|
225
|
+
['pageSize', pageSize.toString()],
|
|
226
|
+
],
|
|
227
|
+
deserializer: (params, initialState) =>
|
|
228
|
+
defaultsDeep(
|
|
229
|
+
{
|
|
230
|
+
pageIndex: params.get('page') ? Math.max(1, parseInt(params.get('page'), 10)) - 1 : undefined,
|
|
231
|
+
pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined,
|
|
232
|
+
},
|
|
233
|
+
initialState,
|
|
234
|
+
),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const SORTING_SERIALIZATION = serialization<'sorting'>({
|
|
238
|
+
serializer: (sorting) => [['sortBy', sorting.map(({id, desc}) => `${id}.${desc ? 'desc' : 'asc'}`).join(',')]],
|
|
239
|
+
deserializer: (params, initialState) => {
|
|
240
|
+
if (!params.has('sortBy')) {
|
|
241
|
+
return initialState;
|
|
242
|
+
}
|
|
243
|
+
const sorts = params.get('sortBy')?.split(',') ?? [];
|
|
244
|
+
return sorts.map((sort) => {
|
|
245
|
+
const [id, order] = sort.split('.');
|
|
246
|
+
return {id, desc: order === 'desc'};
|
|
247
|
+
});
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const GLOBAL_FILTER_SERIALIZATION = serialization<'globalFilter'>({
|
|
252
|
+
serializer: (filter) => [['filter', filter]],
|
|
253
|
+
deserializer: (params, initialState) => params.get('filter') ?? initialState,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const PREDICATES_SERIALIZATION = serialization<'predicates'>({
|
|
257
|
+
serializer: (predicates) => Object.entries(predicates),
|
|
258
|
+
deserializer: (params, initialState) =>
|
|
259
|
+
Object.keys(initialState).reduce(
|
|
260
|
+
(acc, predicateKey) => {
|
|
261
|
+
acc[predicateKey] = params.get(predicateKey) ?? initialState[predicateKey];
|
|
262
|
+
return acc;
|
|
263
|
+
},
|
|
264
|
+
{} as TableState['predicates'],
|
|
265
|
+
),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const LAYOUT_SERIALIZATION = serialization<'layout'>({
|
|
269
|
+
serializer: (_layout) => [['layout', _layout]],
|
|
270
|
+
deserializer: (params, initialState) => params.get('layout') ?? initialState,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const DATE_RANGE_SERIALIZATION = serialization<'dateRange'>({
|
|
274
|
+
serializer: ([from, to]) => [
|
|
275
|
+
['from', from?.toISOString() ?? '', true],
|
|
276
|
+
['to', to?.toISOString() ?? '', true],
|
|
277
|
+
],
|
|
278
|
+
deserializer: (params, initial) => [
|
|
279
|
+
params.get('from') ? new Date(params.get('from') as string) : initial[0],
|
|
280
|
+
params.get('to') ? new Date(params.get('to') as string) : initial[1],
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const COLUMN_VISIBILITY_SERIALIZATION = serialization<'columnVisibility'>({
|
|
285
|
+
serializer: (columns) => [
|
|
286
|
+
[
|
|
287
|
+
'show',
|
|
288
|
+
Object.entries(columns)
|
|
289
|
+
.filter(([, visible]) => visible === true)
|
|
290
|
+
.map(([columnName]) => columnName)
|
|
291
|
+
.join(','),
|
|
292
|
+
],
|
|
293
|
+
[
|
|
294
|
+
'hide',
|
|
295
|
+
Object.entries(columns)
|
|
296
|
+
.filter(([, visible]) => visible === false)
|
|
297
|
+
.map(([columnName]) => columnName)
|
|
298
|
+
.join(','),
|
|
299
|
+
],
|
|
300
|
+
],
|
|
301
|
+
deserializer: (params, initial) => {
|
|
302
|
+
if (!params.has('show') && !params.has('hide')) {
|
|
303
|
+
return initial;
|
|
304
|
+
}
|
|
305
|
+
const visible = params.get('show')?.split(',') ?? [];
|
|
306
|
+
const invisible = params.get('hide')?.split(',') ?? [];
|
|
307
|
+
const columns = {} as TableState['columnVisibility'];
|
|
308
|
+
visible.forEach((column) => {
|
|
309
|
+
columns[column] = true;
|
|
310
|
+
});
|
|
311
|
+
invisible.forEach((column) => {
|
|
312
|
+
columns[column] = false;
|
|
313
|
+
});
|
|
314
|
+
return columns;
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
218
318
|
export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): TableStore<TData> => {
|
|
219
319
|
const options = defaultsDeep({}, userOptions, defaultOptions) as UseTableOptions<TData>;
|
|
220
320
|
const initialState = defaultsDeep({}, options.initialState, defaultState) as TableState<TData>;
|
|
@@ -224,110 +324,40 @@ export const useTable = <TData>(userOptions: UseTableOptions<TData> = {}): Table
|
|
|
224
324
|
*/
|
|
225
325
|
const sync = !!options.syncWithUrl;
|
|
226
326
|
|
|
227
|
-
// synced with url
|
|
327
|
+
// (Optionally) synced with url
|
|
228
328
|
const [pagination, setPagination] = useUrlSyncedState<TableState<TData>['pagination']>({
|
|
329
|
+
...PAGINATION_SERIALIZATION,
|
|
229
330
|
initialState: initialState.pagination,
|
|
230
|
-
serializer: ({pageIndex, pageSize}) => [
|
|
231
|
-
['page', (pageIndex + 1).toString()],
|
|
232
|
-
['pageSize', pageSize.toString()],
|
|
233
|
-
],
|
|
234
|
-
deserializer: (params) =>
|
|
235
|
-
defaultsDeep(
|
|
236
|
-
{
|
|
237
|
-
pageIndex: params.get('page') ? parseInt(params.get('page'), 10) - 1 : undefined,
|
|
238
|
-
pageSize: params.get('pageSize') ? parseInt(params.get('pageSize'), 10) : undefined,
|
|
239
|
-
},
|
|
240
|
-
initialState.pagination,
|
|
241
|
-
),
|
|
242
331
|
sync,
|
|
243
332
|
});
|
|
244
333
|
const [sorting, setSorting] = useUrlSyncedState<TableState<TData>['sorting']>({
|
|
334
|
+
...SORTING_SERIALIZATION,
|
|
245
335
|
initialState: initialState.sorting,
|
|
246
|
-
serializer: (_sorting) => [
|
|
247
|
-
['sortBy', _sorting.map(({id, desc}) => `${id}.${desc ? 'desc' : 'asc'}`).join(',')],
|
|
248
|
-
],
|
|
249
|
-
deserializer: (params) => {
|
|
250
|
-
if (!params.has('sortBy')) {
|
|
251
|
-
return initialState.sorting;
|
|
252
|
-
}
|
|
253
|
-
const sorts = params.get('sortBy')?.split(',') ?? [];
|
|
254
|
-
return sorts.map((sort) => {
|
|
255
|
-
const [id, order] = sort.split('.');
|
|
256
|
-
return {id, desc: order === 'desc'};
|
|
257
|
-
});
|
|
258
|
-
},
|
|
259
336
|
sync,
|
|
260
337
|
});
|
|
261
338
|
const [globalFilter, setGlobalFilter] = useUrlSyncedState<TableState<TData>['globalFilter']>({
|
|
339
|
+
...GLOBAL_FILTER_SERIALIZATION,
|
|
262
340
|
initialState: initialState.globalFilter,
|
|
263
|
-
serializer: (filter) => [['filter', filter]],
|
|
264
|
-
deserializer: (params) => params.get('filter') ?? initialState.globalFilter,
|
|
265
341
|
sync,
|
|
266
342
|
});
|
|
267
343
|
const [predicates, setPredicates] = useUrlSyncedState<TableState<TData>['predicates']>({
|
|
344
|
+
...PREDICATES_SERIALIZATION,
|
|
268
345
|
initialState: initialState.predicates,
|
|
269
|
-
serializer: (_predicates) => Object.entries(_predicates).map(([key, value]) => [key, value]),
|
|
270
|
-
deserializer: (params) =>
|
|
271
|
-
Object.keys(initialState.predicates).reduce(
|
|
272
|
-
(acc, predicateKey) => {
|
|
273
|
-
acc[predicateKey] = params.get(predicateKey) ?? initialState.predicates[predicateKey];
|
|
274
|
-
return acc;
|
|
275
|
-
},
|
|
276
|
-
{} as TableState<TData>['predicates'],
|
|
277
|
-
),
|
|
278
346
|
sync,
|
|
279
347
|
});
|
|
280
348
|
const [layout, setLayout] = useUrlSyncedState<TableState<TData>['layout']>({
|
|
349
|
+
...LAYOUT_SERIALIZATION,
|
|
281
350
|
initialState: initialState.layout,
|
|
282
|
-
serializer: (_layout) => [['layout', _layout]],
|
|
283
|
-
deserializer: (params) => params.get('layout') ?? initialState.layout,
|
|
284
351
|
sync,
|
|
285
352
|
});
|
|
286
353
|
const [dateRange, setDateRange] = useUrlSyncedState<TableState<TData>['dateRange']>({
|
|
354
|
+
...DATE_RANGE_SERIALIZATION,
|
|
287
355
|
initialState: initialState.dateRange,
|
|
288
|
-
serializer: ([from, to]) => [
|
|
289
|
-
['from', from?.toISOString() ?? ''],
|
|
290
|
-
['to', to?.toISOString() ?? ''],
|
|
291
|
-
],
|
|
292
|
-
deserializer: (params) => [
|
|
293
|
-
params.get('from') ? new Date(params.get('from') as string) : initialState.dateRange[0],
|
|
294
|
-
params.get('to') ? new Date(params.get('to') as string) : initialState.dateRange[1],
|
|
295
|
-
],
|
|
296
356
|
sync,
|
|
297
357
|
});
|
|
298
358
|
const [columnVisibility, setColumnVisibility] = useUrlSyncedState<TableState<TData>['columnVisibility']>({
|
|
359
|
+
...COLUMN_VISIBILITY_SERIALIZATION,
|
|
299
360
|
initialState: initialState.columnVisibility,
|
|
300
|
-
serializer: (columns) => [
|
|
301
|
-
[
|
|
302
|
-
'show',
|
|
303
|
-
Object.entries(columns)
|
|
304
|
-
.filter(([, visible]) => visible === true)
|
|
305
|
-
.map(([columnName]) => columnName)
|
|
306
|
-
.join(','),
|
|
307
|
-
],
|
|
308
|
-
[
|
|
309
|
-
'hide',
|
|
310
|
-
Object.entries(columns)
|
|
311
|
-
.filter(([, visible]) => visible === false)
|
|
312
|
-
.map(([columnName]) => columnName)
|
|
313
|
-
.join(','),
|
|
314
|
-
],
|
|
315
|
-
],
|
|
316
|
-
deserializer: (params) => {
|
|
317
|
-
if (!params.has('show') && !params.has('hide')) {
|
|
318
|
-
return initialState.columnVisibility;
|
|
319
|
-
}
|
|
320
|
-
const visible = params.get('show')?.split(',') ?? [];
|
|
321
|
-
const invisible = params.get('hide')?.split(',') ?? [];
|
|
322
|
-
const columns = {} as TableState<TData>['columnVisibility'];
|
|
323
|
-
visible.forEach((column) => {
|
|
324
|
-
columns[column] = true;
|
|
325
|
-
});
|
|
326
|
-
invisible.forEach((column) => {
|
|
327
|
-
columns[column] = false;
|
|
328
|
-
});
|
|
329
|
-
return columns;
|
|
330
|
-
},
|
|
331
361
|
sync,
|
|
332
362
|
});
|
|
333
363
|
|