feat: create view from current table columns + persist view fields on… (#1308)
feat: create view from current table columns + persist view fields on Update View button click Closes #1302, Closes #1307
This commit is contained in:
@ -1,10 +1,6 @@
|
||||
import { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { SortType } from '@/ui/filter-n-sort/types/interface';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
@ -92,7 +88,6 @@ type OwnProps<SortField> = {
|
||||
viewName: string;
|
||||
viewIcon?: React.ReactNode;
|
||||
availableSorts?: Array<SortType<SortField>>;
|
||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||
onViewsChange?: (views: TableView[]) => void;
|
||||
onViewSubmit?: () => void;
|
||||
onImport?: () => void;
|
||||
@ -102,7 +97,6 @@ type OwnProps<SortField> = {
|
||||
export function EntityTable<SortField>({
|
||||
viewName,
|
||||
availableSorts,
|
||||
onColumnsChange,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
onImport,
|
||||
@ -131,7 +125,6 @@ export function EntityTable<SortField>({
|
||||
<TableHeader
|
||||
viewName={viewName}
|
||||
availableSorts={availableSorts}
|
||||
onColumnsChange={onColumnsChange}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
onImport={onImport}
|
||||
@ -139,7 +132,7 @@ export function EntityTable<SortField>({
|
||||
<ScrollWrapper>
|
||||
<div>
|
||||
<StyledTable>
|
||||
<EntityTableHeader onColumnsChange={onColumnsChange} />
|
||||
<EntityTableHeader />
|
||||
<EntityTableBody />
|
||||
</StyledTable>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { cloneElement, ComponentProps, useRef } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { IconButton } from '@/ui/button/components/IconButton';
|
||||
import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem';
|
||||
@ -9,8 +8,10 @@ import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'
|
||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||
import { IconPlus } from '@/ui/icon';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { hiddenTableColumnsState } from '../states/tableColumnsState';
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
|
||||
const StyledColumnMenu = styled(StyledDropdownMenu)`
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
@ -29,7 +30,10 @@ export const EntityTableColumnMenu = ({
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const theme = useTheme();
|
||||
|
||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
||||
const hiddenColumns = useRecoilScopedValue(
|
||||
hiddenTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [ref],
|
||||
|
||||
@ -1,23 +1,20 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import { IconButton } from '@/ui/button/components/IconButton';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { IconPlus } from '@/ui/icon';
|
||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { resizeFieldOffsetState } from '../states/resizeFieldOffsetState';
|
||||
import {
|
||||
hiddenTableColumnsState,
|
||||
tableColumnsByIdState,
|
||||
tableColumnsState,
|
||||
visibleTableColumnsState,
|
||||
} from '../states/tableColumnsState';
|
||||
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { tableColumnsByIdScopedSelector } from '../states/selectors/tableColumnsByIdScopedSelector';
|
||||
import { visibleTableColumnsScopedSelector } from '../states/selectors/visibleTableColumnsScopedSelector';
|
||||
import { tableColumnsScopedState } from '../states/tableColumnsScopedState';
|
||||
|
||||
import { ColumnHead } from './ColumnHead';
|
||||
import { EntityTableColumnMenu } from './EntityTableColumnMenu';
|
||||
@ -78,18 +75,26 @@ const StyledEntityTableColumnMenu = styled(EntityTableColumnMenu)`
|
||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
export type EntityTableHeaderProps = {
|
||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||
};
|
||||
|
||||
export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
||||
export function EntityTableHeader() {
|
||||
const theme = useTheme();
|
||||
|
||||
const [columns, setColumns] = useRecoilState(tableColumnsState);
|
||||
const [offset, setOffset] = useRecoilState(resizeFieldOffsetState);
|
||||
const columnsById = useRecoilValue(tableColumnsByIdState);
|
||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
||||
const visibleColumns = useRecoilValue(visibleTableColumnsState);
|
||||
const [columns, setColumns] = useRecoilScopedState(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const columnsById = useRecoilScopedValue(
|
||||
tableColumnsByIdScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const hiddenColumns = useRecoilScopedValue(
|
||||
hiddenTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const visibleColumns = useRecoilScopedValue(
|
||||
visibleTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
||||
number | null
|
||||
@ -129,14 +134,14 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
||||
: column,
|
||||
);
|
||||
|
||||
(onColumnsChange ?? setColumns)(nextColumns);
|
||||
setColumns(nextColumns);
|
||||
}
|
||||
|
||||
set(resizeFieldOffsetState, 0);
|
||||
setInitialPointerPositionX(null);
|
||||
setResizedFieldId(null);
|
||||
},
|
||||
[resizedFieldId, columnsById, columns, onColumnsChange, setColumns],
|
||||
[resizedFieldId, columnsById, columns, setColumns],
|
||||
);
|
||||
|
||||
useTrackPointer({
|
||||
@ -158,9 +163,9 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
||||
column.id === columnId ? { ...column, isVisible: true } : column,
|
||||
);
|
||||
|
||||
(onColumnsChange ?? setColumns)(nextColumns);
|
||||
setColumns(nextColumns);
|
||||
},
|
||||
[columns, onColumnsChange, setColumns],
|
||||
[columns, setColumns],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { ViewFieldContext } from '../contexts/ViewFieldContext';
|
||||
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
||||
import { visibleTableColumnsState } from '../states/tableColumnsState';
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { visibleTableColumnsScopedSelector } from '../states/selectors/visibleTableColumnsScopedSelector';
|
||||
|
||||
import { CheckboxCell } from './CheckboxCell';
|
||||
import { EntityTableCell } from './EntityTableCell';
|
||||
@ -14,7 +16,10 @@ const StyledRow = styled.tr<{ selected: boolean }>`
|
||||
`;
|
||||
|
||||
export function EntityTableRow({ rowId }: { rowId: string }) {
|
||||
const columns = useRecoilValue(visibleTableColumnsState);
|
||||
const columns = useRecoilScopedValue(
|
||||
visibleTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const { currentRowSelected } = useCurrentRowSelected();
|
||||
|
||||
return (
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
|
||||
import { numberOfTableRowsState } from '../states/numberOfTableRowsState';
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { numberOfTableColumnsScopedSelector } from '../states/selectors/numberOfTableColumnsScopedSelector';
|
||||
import { softFocusPositionState } from '../states/softFocusPositionState';
|
||||
import { numberOfTableColumnsState } from '../states/tableColumnsState';
|
||||
|
||||
import { useSetSoftFocusPosition } from './useSetSoftFocusPosition';
|
||||
|
||||
// TODO: stories
|
||||
export function useMoveSoftFocus() {
|
||||
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
|
||||
const setSoftFocusPosition = useSetSoftFocusPosition();
|
||||
|
||||
const moveUp = useRecoilCallback(
|
||||
@ -64,7 +68,7 @@ export function useMoveSoftFocus() {
|
||||
.valueOrThrow();
|
||||
|
||||
const numberOfTableColumns = snapshot
|
||||
.getLoadable(numberOfTableColumnsState)
|
||||
.getLoadable(numberOfTableColumnsScopedSelector(tableScopeId))
|
||||
.valueOrThrow();
|
||||
|
||||
const numberOfTableRows = snapshot
|
||||
@ -101,7 +105,7 @@ export function useMoveSoftFocus() {
|
||||
});
|
||||
}
|
||||
},
|
||||
[setSoftFocusPosition],
|
||||
[setSoftFocusPosition, tableScopeId],
|
||||
);
|
||||
|
||||
const moveLeft = useRecoilCallback(
|
||||
@ -112,7 +116,7 @@ export function useMoveSoftFocus() {
|
||||
.valueOrThrow();
|
||||
|
||||
const numberOfTableColumns = snapshot
|
||||
.getLoadable(numberOfTableColumnsState)
|
||||
.getLoadable(numberOfTableColumnsScopedSelector(tableScopeId))
|
||||
.valueOrThrow();
|
||||
|
||||
const currentColumnNumber = softFocusPosition.column;
|
||||
@ -142,7 +146,7 @@ export function useMoveSoftFocus() {
|
||||
});
|
||||
}
|
||||
},
|
||||
[setSoftFocusPosition],
|
||||
[setSoftFocusPosition, tableScopeId],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { type TableView } from '../../states/tableViewsState';
|
||||
@ -11,14 +7,12 @@ import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
|
||||
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
|
||||
|
||||
type TableOptionsDropdownProps = {
|
||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||
onViewsChange?: (views: TableView[]) => void;
|
||||
onImport?: () => void;
|
||||
customHotkeyScope: HotkeyScope;
|
||||
};
|
||||
|
||||
export function TableOptionsDropdown({
|
||||
onColumnsChange,
|
||||
onViewsChange,
|
||||
onImport,
|
||||
customHotkeyScope,
|
||||
@ -30,7 +24,6 @@ export function TableOptionsDropdown({
|
||||
dropdownKey="options"
|
||||
dropdownComponents={
|
||||
<TableOptionsDropdownContent
|
||||
onColumnsChange={onColumnsChange}
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { type FormEvent, useCallback, useRef, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -27,16 +27,16 @@ import {
|
||||
IconPlus,
|
||||
IconTag,
|
||||
} from '@/ui/icon';
|
||||
import {
|
||||
hiddenTableColumnsState,
|
||||
tableColumnsState,
|
||||
visibleTableColumnsState,
|
||||
} from '@/ui/table/states/tableColumnsState';
|
||||
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||
import {
|
||||
currentTableViewIdState,
|
||||
type TableView,
|
||||
@ -49,7 +49,6 @@ import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
import { TableOptionsDropdownSection } from './TableOptionsDropdownSection';
|
||||
|
||||
type TableOptionsDropdownButtonProps = {
|
||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||
onViewsChange?: (views: TableView[]) => void;
|
||||
onImport?: () => void;
|
||||
};
|
||||
@ -59,7 +58,6 @@ enum Option {
|
||||
}
|
||||
|
||||
export function TableOptionsDropdownContent({
|
||||
onColumnsChange,
|
||||
onViewsChange,
|
||||
onImport,
|
||||
}: TableOptionsDropdownButtonProps) {
|
||||
@ -75,28 +73,37 @@ export function TableOptionsDropdownContent({
|
||||
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [columns, setColumns] = useRecoilState(tableColumnsState);
|
||||
const [viewEditMode, setViewEditMode] = useRecoilState(
|
||||
tableViewEditModeState,
|
||||
);
|
||||
const visibleColumns = useRecoilValue(visibleTableColumnsState);
|
||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
||||
const [columns, setColumns] = useRecoilScopedState(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const visibleColumns = useRecoilScopedValue(
|
||||
visibleTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const hiddenColumns = useRecoilScopedValue(
|
||||
hiddenTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const viewsById = useRecoilScopedValue(
|
||||
tableViewsByIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const handleColumnVisibilityChange = useCallback(
|
||||
(columnId: string, nextIsVisible: boolean) => {
|
||||
async (columnId: string, nextIsVisible: boolean) => {
|
||||
const nextColumns = columns.map((column) =>
|
||||
column.id === columnId
|
||||
? { ...column, isVisible: nextIsVisible }
|
||||
: column,
|
||||
);
|
||||
|
||||
(onColumnsChange ?? setColumns)(nextColumns);
|
||||
setColumns(nextColumns);
|
||||
},
|
||||
[columns, onColumnsChange, setColumns],
|
||||
[columns, setColumns],
|
||||
);
|
||||
|
||||
const renderFieldActions = useCallback(
|
||||
@ -144,6 +151,14 @@ export function TableOptionsDropdownContent({
|
||||
const viewToCreate = { id: v4(), name };
|
||||
const nextViews = [...views, viewToCreate];
|
||||
|
||||
const currentColumns = await snapshot.getPromise(
|
||||
tableColumnsScopedState(tableScopeId),
|
||||
);
|
||||
set(
|
||||
savedTableColumnsScopedState(viewToCreate.id),
|
||||
currentColumns.map((column) => ({ ...column, id: v4() })),
|
||||
);
|
||||
|
||||
const selectedFilters = await snapshot.getPromise(
|
||||
filtersScopedState(tableScopeId),
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { Button, ButtonSize } from '@/ui/button/components/Button';
|
||||
@ -22,6 +22,9 @@ import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextS
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||
import { canPersistTableColumnsScopedSelector } from '../../states/selectors/canPersistTableColumnsScopedSelector';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import {
|
||||
currentTableViewIdState,
|
||||
tableViewEditModeState,
|
||||
@ -56,12 +59,38 @@ export const TableUpdateViewButtonGroup = ({
|
||||
currentTableViewIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const currentColumns = useRecoilScopedValue(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const setSavedColumns = useSetRecoilState(
|
||||
savedTableColumnsScopedState(currentViewId),
|
||||
);
|
||||
const canPersistColumns = useRecoilValue(
|
||||
canPersistTableColumnsScopedSelector([tableScopeId, currentViewId]),
|
||||
);
|
||||
|
||||
const selectedFilters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const setSavedFilters = useSetRecoilState(
|
||||
savedFiltersScopedState(currentViewId),
|
||||
);
|
||||
const canPersistFilters = useRecoilValue(
|
||||
canPersistFiltersScopedSelector([tableScopeId, currentViewId]),
|
||||
);
|
||||
|
||||
const selectedSorts = useRecoilScopedValue(
|
||||
sortsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const setSavedSorts = useSetRecoilState(savedSortsScopedState(currentViewId));
|
||||
const canPersistSorts = useRecoilValue(
|
||||
canPersistSortsScopedSelector([tableScopeId, currentViewId]),
|
||||
);
|
||||
|
||||
const setViewEditMode = useSetRecoilState(tableViewEditModeState);
|
||||
|
||||
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
|
||||
@ -82,23 +111,21 @@ export const TableUpdateViewButtonGroup = ({
|
||||
setIsDropdownOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleViewSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
await Promise.resolve(onViewSubmit?.());
|
||||
const handleViewSubmit = useCallback(async () => {
|
||||
await Promise.resolve(onViewSubmit?.());
|
||||
|
||||
const selectedFilters = await snapshot.getPromise(
|
||||
filtersScopedState(tableScopeId),
|
||||
);
|
||||
set(savedFiltersScopedState(currentViewId), selectedFilters);
|
||||
|
||||
const selectedSorts = await snapshot.getPromise(
|
||||
sortsScopedState(tableScopeId),
|
||||
);
|
||||
set(savedSortsScopedState(currentViewId), selectedSorts);
|
||||
},
|
||||
[currentViewId, onViewSubmit, tableScopeId],
|
||||
);
|
||||
setSavedColumns(currentColumns);
|
||||
setSavedFilters(selectedFilters);
|
||||
setSavedSorts(selectedSorts);
|
||||
}, [
|
||||
currentColumns,
|
||||
onViewSubmit,
|
||||
selectedFilters,
|
||||
selectedSorts,
|
||||
setSavedColumns,
|
||||
setSavedFilters,
|
||||
setSavedSorts,
|
||||
]);
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Enter, Key.Escape],
|
||||
@ -112,7 +139,10 @@ export const TableUpdateViewButtonGroup = ({
|
||||
<ButtonGroup size={ButtonSize.Small}>
|
||||
<Button
|
||||
title="Update view"
|
||||
disabled={!currentViewId || (!canPersistFilters && !canPersistSorts)}
|
||||
disabled={
|
||||
!currentViewId ||
|
||||
(!canPersistColumns && !canPersistFilters && !canPersistSorts)
|
||||
}
|
||||
onClick={handleViewSubmit}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@ -33,6 +33,8 @@ import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoi
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import { TableViewsHotkeyScope } from '../../types/TableViewsHotkeyScope';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(
|
||||
@ -95,6 +97,9 @@ export const TableViewsDropdownButton = ({
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId?: string) => {
|
||||
const savedColumns = await snapshot.getPromise(
|
||||
savedTableColumnsScopedState(viewId),
|
||||
);
|
||||
const savedFilters = await snapshot.getPromise(
|
||||
savedFiltersScopedState(viewId),
|
||||
);
|
||||
@ -102,6 +107,7 @@ export const TableViewsDropdownButton = ({
|
||||
savedSortsScopedState(viewId),
|
||||
);
|
||||
|
||||
set(tableColumnsScopedState(tableScopeId), savedColumns);
|
||||
set(filtersScopedState(tableScopeId), savedFilters);
|
||||
set(sortsScopedState(tableScopeId), savedSorts);
|
||||
set(currentTableViewIdState(tableScopeId), viewId);
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
export const savedTableColumnsScopedState = atomFamily<
|
||||
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
string | undefined
|
||||
>({
|
||||
key: 'savedTableColumnsScopedState',
|
||||
default: [],
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { savedTableColumnsScopedState } from '../savedTableColumnsScopedState';
|
||||
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||
|
||||
export const canPersistTableColumnsScopedSelector = selectorFamily({
|
||||
key: 'canPersistTableColumnsScopedSelector',
|
||||
get:
|
||||
([scopeId, viewId]: [string, string | undefined]) =>
|
||||
({ get }) =>
|
||||
!isDeeplyEqual(
|
||||
get(savedTableColumnsScopedState(viewId)),
|
||||
get(tableColumnsScopedState(scopeId)),
|
||||
),
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||
|
||||
export const hiddenTableColumnsScopedSelector = selectorFamily({
|
||||
key: 'hiddenTableColumnsScopedSelector',
|
||||
get:
|
||||
(scopeId: string) =>
|
||||
({ get }) =>
|
||||
get(tableColumnsScopedState(scopeId)).filter(
|
||||
(column) => !column.isVisible,
|
||||
),
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||
|
||||
export const numberOfTableColumnsScopedSelector = selectorFamily({
|
||||
key: 'numberOfTableColumnsScopedSelector',
|
||||
get:
|
||||
(scopeId: string) =>
|
||||
({ get }) =>
|
||||
get(tableColumnsScopedState(scopeId)).length,
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { savedTableColumnsScopedState } from '../savedTableColumnsScopedState';
|
||||
|
||||
export const savedTableColumnsByIdScopedSelector = selectorFamily({
|
||||
key: 'savedTableColumnsByIdScopedSelector',
|
||||
get:
|
||||
(viewId: string | undefined) =>
|
||||
({ get }) =>
|
||||
get(savedTableColumnsScopedState(viewId)).reduce<
|
||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||
|
||||
export const tableColumnsByIdScopedSelector = selectorFamily({
|
||||
key: 'tableColumnsByIdScopedSelector',
|
||||
get:
|
||||
(scopeId: string) =>
|
||||
({ get }) =>
|
||||
get(tableColumnsScopedState(scopeId)).reduce<
|
||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||
|
||||
export const visibleTableColumnsScopedSelector = selectorFamily({
|
||||
key: 'visibleTableColumnsScopedSelector',
|
||||
get:
|
||||
(scopeId: string) =>
|
||||
({ get }) =>
|
||||
get(tableColumnsScopedState(scopeId)).filter(
|
||||
(column) => column.isVisible,
|
||||
),
|
||||
});
|
||||
14
front/src/modules/ui/table/states/tableColumnsScopedState.ts
Normal file
14
front/src/modules/ui/table/states/tableColumnsScopedState.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
export const tableColumnsScopedState = atomFamily<
|
||||
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
string
|
||||
>({
|
||||
key: 'tableColumnsScopedState',
|
||||
default: [],
|
||||
});
|
||||
@ -1,37 +0,0 @@
|
||||
import { atom, selector } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
export const tableColumnsState = atom<ViewFieldDefinition<ViewFieldMetadata>[]>(
|
||||
{
|
||||
key: 'tableColumnsState',
|
||||
default: [],
|
||||
},
|
||||
);
|
||||
|
||||
export const tableColumnsByIdState = selector({
|
||||
key: 'tableColumnsByIdState',
|
||||
get: ({ get }) =>
|
||||
get(tableColumnsState).reduce<
|
||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
||||
});
|
||||
|
||||
export const numberOfTableColumnsState = selector<number>({
|
||||
key: 'numberOfTableColumnsState',
|
||||
get: ({ get }) => get(tableColumnsState).length,
|
||||
});
|
||||
|
||||
export const visibleTableColumnsState = selector({
|
||||
key: 'visibleTableColumnsState',
|
||||
get: ({ get }) => get(tableColumnsState).filter((column) => column.isVisible),
|
||||
});
|
||||
|
||||
export const hiddenTableColumnsState = selector({
|
||||
key: 'hiddenTableColumnsState',
|
||||
get: ({ get }) =>
|
||||
get(tableColumnsState).filter((column) => !column.isVisible),
|
||||
});
|
||||
@ -1,10 +1,6 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { FilterDropdownButton } from '@/ui/filter-n-sort/components/FilterDropdownButton';
|
||||
import SortAndFilterBar from '@/ui/filter-n-sort/components/SortAndFilterBar';
|
||||
import { SortDropdownButton } from '@/ui/filter-n-sort/components/SortDropdownButton';
|
||||
@ -26,7 +22,6 @@ import { TableViewsHotkeyScope } from '../../types/TableViewsHotkeyScope';
|
||||
type OwnProps<SortField> = {
|
||||
viewName: string;
|
||||
availableSorts?: Array<SortType<SortField>>;
|
||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||
onViewsChange?: (views: TableView[]) => void;
|
||||
onViewSubmit?: () => void;
|
||||
onImport?: () => void;
|
||||
@ -35,7 +30,6 @@ type OwnProps<SortField> = {
|
||||
export function TableHeader<SortField>({
|
||||
viewName,
|
||||
availableSorts,
|
||||
onColumnsChange,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
onImport,
|
||||
@ -89,7 +83,6 @@ export function TableHeader<SortField>({
|
||||
/>
|
||||
<TableOptionsDropdown
|
||||
onImport={onImport}
|
||||
onColumnsChange={onColumnsChange}
|
||||
onViewsChange={onViewsChange}
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user