diff --git a/CHANGELOG.md b/CHANGELOG.md index 53c3817..b351741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- added the `usePersistentReactDataTableState` hook which saves it's own state into the local storage +- added `initialAfterSearchFilter` property to give the possebility, to set a different initial after filter search (fall back is still `initialColumnFilters`) + ### Changed - migrated to eslint 9 flat config diff --git a/package.json b/package.json index ee5d65e..2ee9872 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "^8.0.0", + "@neolution-ch/javascript-utils": "^2.2.0", "@neolution-ch/react-pattern-ui": "^5.3.0", "@tanstack/react-table": "^8.12.0", "@tanstack/react-virtual": "^3.13.12", diff --git a/src/index.ts b/src/index.ts index 0b01578..86d326e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export * from "./lib/translations/translations"; export * from "./lib/useReactDataTableState/useReactDataTableState"; export * from "./lib/useReactDataTableState/useReactDataTableStateProps"; +export * from "./lib/useReactDataTableState/usePersistentReactDataTableState"; export * from "./lib/useReactDataTable/useReactDataTable"; export * from "./lib/useReactDataTable/useReactDataTableProps"; export * from "./lib/useReactDataTable/useReactDataTableResult"; diff --git a/src/lib/useReactDataTableState/usePersistentReactDataTableState.ts b/src/lib/useReactDataTableState/usePersistentReactDataTableState.ts new file mode 100644 index 0000000..2418b8f --- /dev/null +++ b/src/lib/useReactDataTableState/usePersistentReactDataTableState.ts @@ -0,0 +1,126 @@ +import { getLocalStorageItem, setLocalStorageItem } from "@neolution-ch/javascript-utils"; +import { OptionalNullable } from "../types/NullableTypes"; +import { useReactDataTableStateProps } from "./useReactDataTableStateProps"; +import { useReactDataTableStateResult } from "./useReactDataTableStateResult"; +import { useReactDataTableState } from "./useReactDataTableState"; +import { FilterModel } from "../types/TableState"; +import { useCallback } from "react"; + +/** + * A custom hook that will initialize all the state needed for the react data table and will persist it to local storage + * @param props The properties to configure the initial state + * @returns the state and the setters + */ +const usePersistentReactDataTableState = >( + props: OptionalNullable> & { localStorageKey: string }, +): useReactDataTableStateResult => { + const { + initialColumnFilters, + initialSorting, + initialPagination, + initialRowSelection, + initialExpanded, + initialColumnPinning, + initialAfterSearchFilter, + localStorageKey, + } = props as useReactDataTableStateProps & { localStorageKey: string }; + + const { + pagination, + setPagination, + columnFilters, + columnPinning, + expanded, + rowSelection, + sorting, + setColumnFilters, + afterSearchFilter, + setAfterSearchFilter, + setColumnPinning, + setExpanded, + setRowSelection, + setSorting, + } = useReactDataTableState({ + initialColumnPinning: + getLocalStorageItem["initialColumnPinning"]>(`${localStorageKey}_columnPinning`) ?? + initialColumnPinning, + initialExpanded: + getLocalStorageItem["initialExpanded"]>(`${localStorageKey}_expanded`) ?? initialExpanded, + initialPagination: + getLocalStorageItem["initialPagination"]>(`${localStorageKey}_pagination`) ?? + initialPagination, + initialRowSelection: + getLocalStorageItem["initialRowSelection"]>(`${localStorageKey}_rowSelection`) ?? + initialRowSelection, + initialSorting: + getLocalStorageItem["initialSorting"]>(`${localStorageKey}_sorting`) ?? initialSorting, + initialColumnFilters: + getLocalStorageItem["initialColumnFilters"]>(`${localStorageKey}_columnFilters`) ?? + initialColumnFilters, + initialAfterSearchFilter: + getLocalStorageItem["initialAfterSearchFilter"]>( + `${localStorageKey}_afterSearchFilter`, + ) ?? initialAfterSearchFilter, + } as useReactDataTableStateProps as OptionalNullable>); + + return { + pagination, + setPagination: useCallback( + (newPagination) => { + setPagination(newPagination); + setLocalStorageItem(`${props.localStorageKey}_pagination`, newPagination); + }, + [props.localStorageKey, setPagination], + ), + columnFilters, + setColumnFilters: useCallback( + (newColumnFilters) => { + setColumnFilters(newColumnFilters); + setLocalStorageItem(`${props.localStorageKey}_columnFilters`, newColumnFilters); + }, + [props.localStorageKey, setColumnFilters], + ), + afterSearchFilter, + setAfterSearchFilter: useCallback( + (newAfterSearchFilter) => { + setAfterSearchFilter(newAfterSearchFilter); + setLocalStorageItem(`${props.localStorageKey}_afterSearchFilter`, newAfterSearchFilter); + }, + [props.localStorageKey, setAfterSearchFilter], + ), + columnPinning, + setColumnPinning: useCallback( + (newColumnPinning) => { + setColumnPinning(newColumnPinning); + setLocalStorageItem(`${props.localStorageKey}_columnPinning`, newColumnPinning); + }, + [props.localStorageKey, setColumnPinning], + ), + expanded, + setExpanded: useCallback( + (newExpanded) => { + setExpanded(newExpanded); + setLocalStorageItem(`${props.localStorageKey}_expanded`, newExpanded); + }, + [props.localStorageKey, setExpanded], + ), + rowSelection, + setRowSelection: useCallback( + (newRowSelection) => { + setRowSelection(newRowSelection); + setLocalStorageItem(`${props.localStorageKey}_rowSelection`, newRowSelection); + }, + [props.localStorageKey, setRowSelection], + ), + sorting, + setSorting: useCallback( + (newSorting) => { + setSorting(newSorting); + setLocalStorageItem(`${props.localStorageKey}_sorting`, newSorting); + }, + [props.localStorageKey, setSorting], + ), + }; +}; + +export { usePersistentReactDataTableState }; diff --git a/src/lib/useReactDataTableState/useReactDataTableState.ts b/src/lib/useReactDataTableState/useReactDataTableState.ts index 375d5db..e4558a1 100644 --- a/src/lib/useReactDataTableState/useReactDataTableState.ts +++ b/src/lib/useReactDataTableState/useReactDataTableState.ts @@ -14,11 +14,18 @@ import { OptionalNullable } from "../types/NullableTypes"; const useReactDataTableState = >( props: OptionalNullable>, ): useReactDataTableStateResult => { - const { initialColumnFilters, initialSorting, initialPagination, initialRowSelection, initialExpanded, initialColumnPinning } = - props as useReactDataTableStateProps; + const { + initialColumnFilters, + initialSorting, + initialPagination, + initialRowSelection, + initialExpanded, + initialColumnPinning, + initialAfterSearchFilter, + } = props as useReactDataTableStateProps; const [columnFilters, setColumnFilters] = useState((initialColumnFilters ?? {}) as TFilter); - const [afterSearchFilter, setAfterSearchFilter] = useState((initialColumnFilters ?? {}) as TFilter); + const [afterSearchFilter, setAfterSearchFilter] = useState((initialAfterSearchFilter ?? initialColumnFilters ?? {}) as TFilter); const [sorting, setSorting] = useState | undefined>(initialSorting); const [rowSelection, setRowSelection] = useState(initialRowSelection ?? ({} as RowSelectionState)); const [expanded, setExpanded] = useState(initialExpanded ?? ({} as ExpandedState)); diff --git a/src/lib/useReactDataTableState/useReactDataTableStateProps.ts b/src/lib/useReactDataTableState/useReactDataTableStateProps.ts index 75f41f8..fa1deae 100644 --- a/src/lib/useReactDataTableState/useReactDataTableStateProps.ts +++ b/src/lib/useReactDataTableState/useReactDataTableStateProps.ts @@ -12,6 +12,11 @@ export interface useReactDataTableStateProps */ initialColumnFilters: ColumnFilterState; + /** + * the initial after search filter + */ + initialAfterSearchFilter?: ColumnFilterState; + /** * the initial sorting */ diff --git a/yarn.lock b/yarn.lock index 15faefa..675d06a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2101,6 +2101,14 @@ eslint-plugin-unicorn "^61.0.2" typescript-eslint "^8.46.1" +"@neolution-ch/javascript-utils@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@neolution-ch/javascript-utils/-/javascript-utils-2.2.0.tgz#11260342ef66f6ca1b3933282683b687bfe5a20a" + integrity sha512-2QO1l94EjiSbQEVDAKtWlaKJgWFj8hXY43AOKvYpNYJ7y6BbOZAwhbjEFKwgSpfVizpuyqHSsTPSHSKG8WNYhQ== + dependencies: + date-fns "^2.30.0" + uuid "^9.0.1" + "@neolution-ch/react-pattern-ui@^5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@neolution-ch/react-pattern-ui/-/react-pattern-ui-5.3.0.tgz#c900bdfc6947cfbe108910425b4dcb4027cdf34f" @@ -6610,7 +6618,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -date-fns@^2.29.3: +date-fns@^2.29.3, date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -15893,6 +15901,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed"