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:
@ -136,7 +136,7 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
||||
setInitialPointerPositionX(null);
|
||||
setResizedFieldId(null);
|
||||
},
|
||||
[resizedFieldId, columnsById, setResizedFieldId],
|
||||
[resizedFieldId, columnsById, columns, onColumnsChange, setColumns],
|
||||
);
|
||||
|
||||
useTrackPointer({
|
||||
|
||||
@ -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],
|
||||
);
|
||||
}
|
||||
|
||||
@ -50,6 +50,6 @@ export function useSetEntityTableData() {
|
||||
|
||||
set(isFetchingEntityTableDataState, false);
|
||||
},
|
||||
[],
|
||||
[resetTableRowSelection, tableContextScopeId],
|
||||
);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ export const TableUpdateViewButtonGroup = ({
|
||||
);
|
||||
set(savedSortsScopedState(currentViewId), selectedSorts);
|
||||
},
|
||||
[currentViewId, onViewSubmit],
|
||||
[currentViewId, onViewSubmit, tableScopeId],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
|
||||
@ -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],
|
||||
|
||||
Reference in New Issue
Block a user