feat: create view from selected filters and sorts + switch to newly created view on view creation (#1301)

* feat: create view from selected filters and sorts

Closes #1292

* refactor: use selector to obtain table filters where query option

* refactor: activate exhaustive deps eslint rule for useRecoilCallback

* feat: switch to newly created view on view creation

Closes #1297

* refactor: code review

- use `useCallback` instead of `useRecoilCallback`
- rename `useTableViews` to `useViews`
- move filter-n-sort selectors to /states/selector subfolder
This commit is contained in:
Thaïs
2023-08-25 12:43:21 +02:00
committed by GitHub
parent de569f4c06
commit c3d6451dd0
23 changed files with 233 additions and 162 deletions

View File

@ -136,7 +136,7 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
setInitialPointerPositionX(null);
setResizedFieldId(null);
},
[resizedFieldId, columnsById, setResizedFieldId],
[resizedFieldId, columnsById, columns, onColumnsChange, setColumns],
);
useTrackPointer({

View File

@ -1,6 +1,5 @@
import { useRecoilCallback } from 'recoil';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
@ -13,8 +12,6 @@ export function useLeaveTableFocus() {
const disableSoftFocus = useDisableSoftFocus();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
const setHotkeyScope = useSetHotkeyScope();
return useRecoilCallback(
({ snapshot }) =>
() => {
@ -37,6 +34,6 @@ export function useLeaveTableFocus() {
closeCurrentCellInEditMode();
disableSoftFocus();
},
[setHotkeyScope, closeCurrentCellInEditMode, disableSoftFocus],
[closeCurrentCellInEditMode, disableSoftFocus],
);
}

View File

@ -50,6 +50,6 @@ export function useSetEntityTableData() {
set(isFetchingEntityTableDataState, false);
},
[],
[resetTableRowSelection, tableContextScopeId],
);
}

View File

@ -1,6 +1,6 @@
import { type FormEvent, useCallback, useRef, useState } from 'react';
import { useTheme } from '@emotion/react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { v4 } from 'uuid';
@ -16,6 +16,10 @@ import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
import { savedFiltersScopedState } from '@/ui/filter-n-sort/states/savedFiltersScopedState';
import { savedSortsScopedState } from '@/ui/filter-n-sort/states/savedSortsScopedState';
import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState';
import {
IconChevronLeft,
IconFileImport,
@ -29,11 +33,12 @@ import {
visibleTableColumnsState,
} from '@/ui/table/states/tableColumnsState';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import {
currentTableViewIdState,
type TableView,
tableViewEditModeState,
tableViewsByIdState,
@ -60,6 +65,8 @@ export function TableOptionsDropdownContent({
}: TableOptionsDropdownButtonProps) {
const theme = useTheme();
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
const { closeDropdownButton } = useDropdownButton({ key: 'options' });
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
@ -72,10 +79,6 @@ export function TableOptionsDropdownContent({
const [viewEditMode, setViewEditMode] = useRecoilState(
tableViewEditModeState,
);
const [views, setViews] = useRecoilScopedState(
tableViewsState,
TableRecoilScopeContext,
);
const visibleColumns = useRecoilValue(visibleTableColumnsState);
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
const viewsById = useRecoilScopedValue(
@ -124,31 +127,56 @@ export function TableOptionsDropdownContent({
}
}, [setViewEditMode]);
const handleViewNameSubmit = useCallback(
(event?: FormEvent) => {
event?.preventDefault();
const handleViewNameSubmit = useRecoilCallback(
({ set, snapshot }) =>
async (event?: FormEvent) => {
event?.preventDefault();
if (viewEditMode.mode && viewEditInputRef.current?.value) {
const name = viewEditInputRef.current.value;
const nextViews =
viewEditMode.mode === 'create'
? [...views, { id: v4(), name }]
: views.map((view) =>
view.id === viewEditMode.viewId ? { ...view, name } : view,
);
const name = viewEditInputRef.current?.value;
(onViewsChange ?? setViews)(nextViews);
}
if (!viewEditMode.mode || !name) {
return resetViewEditMode();
}
resetViewEditMode();
},
const views = await snapshot.getPromise(tableViewsState(tableScopeId));
if (viewEditMode.mode === 'create') {
const viewToCreate = { id: v4(), name };
const nextViews = [...views, viewToCreate];
const selectedFilters = await snapshot.getPromise(
filtersScopedState(tableScopeId),
);
set(savedFiltersScopedState(viewToCreate.id), selectedFilters);
const selectedSorts = await snapshot.getPromise(
sortsScopedState(tableScopeId),
);
set(savedSortsScopedState(viewToCreate.id), selectedSorts);
set(tableViewsState(tableScopeId), nextViews);
await Promise.resolve(onViewsChange?.(nextViews));
set(currentTableViewIdState(tableScopeId), viewToCreate.id);
}
if (viewEditMode.mode === 'edit') {
const nextViews = views.map((view) =>
view.id === viewEditMode.viewId ? { ...view, name } : view,
);
set(tableViewsState(tableScopeId), nextViews);
await Promise.resolve(onViewsChange?.(nextViews));
}
return resetViewEditMode();
},
[
onViewsChange,
resetViewEditMode,
setViews,
tableScopeId,
viewEditMode.mode,
viewEditMode.viewId,
views,
],
);

View File

@ -97,7 +97,7 @@ export const TableUpdateViewButtonGroup = ({
);
set(savedSortsScopedState(currentViewId), selectedSorts);
},
[currentViewId, onViewSubmit],
[currentViewId, onViewSubmit, tableScopeId],
);
useScopedHotkeys(

View File

@ -73,6 +73,10 @@ export const TableViewsDropdownButton = ({
key: 'options',
});
const [, setCurrentViewId] = useRecoilScopedState(
currentTableViewIdState,
TableRecoilScopeContext,
);
const currentView = useRecoilScopedValue(
currentTableViewState,
TableRecoilScopeContext,
@ -81,10 +85,6 @@ export const TableViewsDropdownButton = ({
tableViewsState,
TableRecoilScopeContext,
);
const [, setCurrentViewId] = useRecoilScopedState(
currentTableViewIdState,
TableRecoilScopeContext,
);
const setViewEditMode = useSetRecoilState(tableViewEditModeState);
const {
@ -104,10 +104,10 @@ export const TableViewsDropdownButton = ({
set(filtersScopedState(tableScopeId), savedFilters);
set(sortsScopedState(tableScopeId), savedSorts);
setCurrentViewId(viewId);
set(currentTableViewIdState(tableScopeId), viewId);
setIsUnfolded(false);
},
[setCurrentViewId],
[tableScopeId],
);
const handleAddViewButtonClick = useCallback(() => {
@ -126,12 +126,15 @@ export const TableViewsDropdownButton = ({
);
const handleDeleteViewButtonClick = useCallback(
(event: MouseEvent<HTMLButtonElement>, viewId: string) => {
async (event: MouseEvent<HTMLButtonElement>, viewId: string) => {
event.stopPropagation();
if (currentView?.id === viewId) setCurrentViewId(undefined);
(onViewsChange ?? setViews)(views.filter((view) => view.id !== viewId));
const nextViews = views.filter((view) => view.id !== viewId);
setViews(nextViews);
await Promise.resolve(onViewsChange?.(nextViews));
setIsUnfolded(false);
},
[currentView?.id, onViewsChange, setCurrentViewId, setViews, views],