From 536b86dfdd4aea519153154c9ba89fdbbdb74efb Mon Sep 17 00:00:00 2001 From: Marco Schumacher Date: Mon, 29 Aug 2022 10:10:08 +0200 Subject: [PATCH] feat: reset table state button --- src/components/columnSelection.tsx | 11 +++++-- src/components/table.tsx | 20 +++++++++---- src/internalState/tableStateStorage.ts | 39 ++++++++++++++++++++----- src/internalState/useTableState.ts | 13 ++++++--- src/misc/queue.ts | 4 +++ src/theme/defaultTheme/defaultTexts.tsx | 1 + src/types.ts | 1 + 7 files changed, 70 insertions(+), 19 deletions(-) diff --git a/src/components/columnSelection.tsx b/src/components/columnSelection.tsx index a82df7e..e97542b 100644 --- a/src/components/columnSelection.tsx +++ b/src/components/columnSelection.tsx @@ -1,19 +1,22 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { useTheme } from '..'; import { useCssVariables } from '../theme/useCssVariables'; import { InternalColumn } from '../types'; import { FormControlLabel } from './formControlLabel'; -import { useTableContext } from './table'; +import { TableResetContext, useTableContext } from './table'; export function ColumnSelection(): JSX.Element { const IconButton = useTheme((t) => t.components.IconButton); const Popover = useTheme((t) => t.components.Popover); const Checkbox = useTheme((t) => t.components.Checkbox); + const Button = useTheme((t) => t.components.Button); const Settings = useTheme((t) => t.icons.Settings); const selectColumns = useTheme((t) => t.text.selectColumns); + const resetAll = useTheme((t) => t.text.resetAll); const cssVariables = useCssVariables(); const table = useTableContext(); + const reset = useContext(TableResetContext); const columns = table.useState('props.columns'); const hiddenColumns = table.useState('hiddenColumns'); @@ -54,6 +57,10 @@ export function ColumnSelection(): JSX.Element { label={column.header} > ))} + + diff --git a/src/components/table.tsx b/src/components/table.tsx index 9302a96..bf6d2a2 100644 --- a/src/components/table.tsx +++ b/src/components/table.tsx @@ -18,6 +18,7 @@ import { SelectComponent } from './selectComponent'; import { Virtualized } from './virtualized'; export const TableContext = createContext> | null>(null); +export const TableResetContext = createContext<() => void>(() => undefined); export const ColumnContext = createContext(null); export function useTableContext(): Store> { const value = useContext(TableContext); @@ -31,22 +32,29 @@ export function useColumnContext(): Id { } export function Table(props: TableProps): JSX.Element { - const table = useTableState(props); + const [table, resetState] = useTableState(props); + const [isHydrated, clearStorage] = useTableStateStorage(table); + + async function reset() { + await clearStorage(); + resetState(); + } useLayoutEffect(() => table.getState().props.debugRender?.('render table')); return ( - - - + + + + + ); } -function TableLoadingState() { +function TableLoadingState({ isHydrated }: { isHydrated: boolean }) { const loadingText = useTheme((t) => t.text.loading); - const isHydrated = useTableStateStorage(); const [showLoading, setShowLoading] = useState(false); useEffect(() => { diff --git a/src/internalState/tableStateStorage.ts b/src/internalState/tableStateStorage.ts index 0943a21..c3d3ef8 100644 --- a/src/internalState/tableStateStorage.ts +++ b/src/internalState/tableStateStorage.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; -import { useTableContext } from '..'; +import { Store } from 'schummar-state/react'; import { Queue } from '../misc/queue'; -import { SerializableValue } from '../types'; +import { InternalTableState, SerializableValue } from '../types'; const KEYS = ['sort', 'selection', 'expanded', 'hiddenColumns', 'filterValues', 'columnWidths', 'columnOrder'] as const; @@ -67,9 +67,9 @@ function parse(value: string) { }); } -export function useTableStateStorage() { - const table = useTableContext(); +export function useTableStateStorage(table: Store>) { const [isHydrated, setIsHydrated] = useState(false); + const [q] = useState(() => new Queue()); // On mount: load useEffect(() => { @@ -130,8 +130,6 @@ export function useTableStateStorage() { useEffect(() => { if (!isHydrated) return; - const q = new Queue(); - return table.subscribe( (state) => { if (!state.props.persist) return; @@ -173,5 +171,32 @@ export function useTableStateStorage() { ); }, [isHydrated]); - return isHydrated; + async function clear() { + await q.run(async () => { + const persist = table.getState().props.persist; + if (!persist) { + return; + } + + const { storage } = persist; + const keys = + 'keys' in storage + ? await storage.keys() + : await Promise.all( + Array(storage.length) + .fill(0) + .map((_x, i) => storage.key(i)), + ); + + for (const key of keys) { + if (key !== null) { + await storage.removeItem(key); + } + } + + q.clear(); + }); + } + + return [isHydrated, clear] as const; } diff --git a/src/internalState/useTableState.ts b/src/internalState/useTableState.ts index d54d9cf..ad53ad1 100644 --- a/src/internalState/useTableState.ts +++ b/src/internalState/useTableState.ts @@ -1,5 +1,5 @@ import { castDraft } from 'immer'; -import { useEffect, useMemo } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Store } from 'schummar-state/react'; import { InternalTableState, TableProps } from '../types'; import { calcItems } from './calcItems'; @@ -9,9 +9,14 @@ import { filterColumns } from './filterColumns'; import { normalizeExpanded } from './normalizeExpanded'; import { syncSelections } from './syncSelections'; -export function useTableState(_props: TableProps): Store> { +export function useTableState(_props: TableProps): [Store>, () => void] { + const [key, setKey] = useState({}); const props = calcProps(_props); + function reset() { + setKey({}); + } + const state = useMemo( () => new Store>({ @@ -35,7 +40,7 @@ export function useTableState(_props: TableProps): Store(_props: TableProps): Store { loading: ReactNode; clearFilters: ReactNode; deselectAll: ReactNode; + resetAll: ReactNode; }; /** Define styles. */ classes?: {