|
| 1 | +import { ISortBy } from "@patternfly/react-table"; |
| 2 | +import { useState, useEffect, useMemo } from "react"; |
| 3 | + |
| 4 | +export enum DataViewSortParams { |
| 5 | + SORT_BY = 'sortBy', |
| 6 | + DIRECTION = 'direction' |
| 7 | +}; |
| 8 | + |
| 9 | +const validateDirection = (direction: string | null | undefined, defaultDirection: ISortBy['direction']): ISortBy['direction'] => ( |
| 10 | + direction === 'asc' || direction === 'desc' ? direction : defaultDirection |
| 11 | +); |
| 12 | + |
| 13 | +export interface DataViewSortConfig { |
| 14 | + /** Attribute to sort the entries by */ |
| 15 | + sortBy: string | undefined; |
| 16 | + /** Sort direction */ |
| 17 | + direction: ISortBy['direction']; |
| 18 | +}; |
| 19 | + |
| 20 | +export interface UseDataViewSortProps { |
| 21 | + /** Initial sort config */ |
| 22 | + initialSort?: DataViewSortConfig; |
| 23 | + /** Current search parameters as a string */ |
| 24 | + searchParams?: URLSearchParams; |
| 25 | + /** Function to set search parameters */ |
| 26 | + setSearchParams?: (params: URLSearchParams) => void; |
| 27 | + /** Default direction */ |
| 28 | + defaultDirection?: ISortBy['direction']; |
| 29 | + /** Sort by URL param name */ |
| 30 | + sortByParam?: string; |
| 31 | + /** Direction URL param name */ |
| 32 | + directionParam?: string; |
| 33 | +}; |
| 34 | + |
| 35 | +export const useDataViewSort = (props?: UseDataViewSortProps) => { |
| 36 | + const { |
| 37 | + initialSort, |
| 38 | + searchParams, |
| 39 | + setSearchParams, |
| 40 | + defaultDirection = 'asc', |
| 41 | + sortByParam = DataViewSortParams.SORT_BY, |
| 42 | + directionParam = DataViewSortParams.DIRECTION |
| 43 | + } = props ?? {}; |
| 44 | + |
| 45 | + const isUrlSyncEnabled = useMemo(() => searchParams && !!setSearchParams, [ searchParams, setSearchParams ]); |
| 46 | + |
| 47 | + const [ state, setState ] = useState<DataViewSortConfig>({ |
| 48 | + sortBy: searchParams?.get(sortByParam) ?? initialSort?.sortBy, |
| 49 | + direction: validateDirection(searchParams?.get(directionParam) as ISortBy['direction'], initialSort?.direction), |
| 50 | + }); |
| 51 | + |
| 52 | + const updateSearchParams = (sortBy: string, direction: ISortBy['direction']) => { |
| 53 | + if (isUrlSyncEnabled && sortBy) { |
| 54 | + const params = new URLSearchParams(searchParams); |
| 55 | + params.set(sortByParam, `${sortBy}`); |
| 56 | + params.set(directionParam, `${direction}`); |
| 57 | + setSearchParams?.(params); |
| 58 | + } |
| 59 | + }; |
| 60 | + |
| 61 | + useEffect(() => { |
| 62 | + state.sortBy && state.direction && updateSearchParams(state.sortBy, state.direction); |
| 63 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 64 | + }, []); |
| 65 | + |
| 66 | + useEffect(() => { |
| 67 | + const currentSortBy = searchParams?.get(sortByParam) || state.sortBy; |
| 68 | + const currentDirection = searchParams?.get(directionParam) as ISortBy['direction'] || state.direction; |
| 69 | + const validDirection = validateDirection(currentDirection, defaultDirection); |
| 70 | + currentSortBy !== state.sortBy || validDirection !== state.direction && setState({ sortBy: currentSortBy, direction: validDirection }); |
| 71 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 72 | + }, [ searchParams?.toString() ]); |
| 73 | + |
| 74 | + const onSort = ( |
| 75 | + _event: React.MouseEvent | React.KeyboardEvent | MouseEvent | undefined, |
| 76 | + newSortBy: string, |
| 77 | + newSortDirection: ISortBy['direction'] |
| 78 | + ) => { |
| 79 | + setState({ sortBy: newSortBy, direction: newSortDirection }); |
| 80 | + updateSearchParams(newSortBy, newSortDirection); |
| 81 | + }; |
| 82 | + |
| 83 | + return { |
| 84 | + ...state, |
| 85 | + onSort |
| 86 | + }; |
| 87 | +}; |
0 commit comments