@ -1,46 +1,101 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
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 { ViewBar, type ViewBarProps } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector';
|
||||
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
|
||||
|
||||
import { BoardOptionsDropdown } from './BoardOptionsDropdown';
|
||||
|
||||
export type BoardHeaderProps = ComponentProps<'div'> & {
|
||||
export type BoardHeaderProps = {
|
||||
className?: string;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
} & Pick<
|
||||
ViewBarProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSubmit' | 'scopeContext'
|
||||
>;
|
||||
} & Pick<ViewBarProps, 'scopeContext'>;
|
||||
|
||||
export function BoardHeader({
|
||||
className,
|
||||
onStageAdd,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
defaultViewName,
|
||||
}: BoardHeaderProps) {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const canPersistBoardCardFields = useRecoilValue(
|
||||
canPersistBoardCardFieldsScopedFamilySelector([
|
||||
tableScopeId,
|
||||
currentViewId,
|
||||
]),
|
||||
);
|
||||
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [savedBoardCardFields, setSavedBoardCardFields] = useRecoilState(
|
||||
savedBoardCardFieldsFamilyState(currentViewId),
|
||||
);
|
||||
|
||||
const handleViewBarReset = () => setBoardCardFields(savedBoardCardFields);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const savedBoardCardFields = await snapshot.getPromise(
|
||||
savedBoardCardFieldsFamilyState(viewId),
|
||||
);
|
||||
set(boardCardFieldsScopedState(tableScopeId), savedBoardCardFields);
|
||||
},
|
||||
[tableScopeId],
|
||||
);
|
||||
|
||||
const handleCurrentViewSubmit = async () => {
|
||||
if (canPersistBoardCardFields) {
|
||||
setSavedBoardCardFields(boardCardFields);
|
||||
}
|
||||
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||
<ViewBar
|
||||
defaultViewName={defaultViewName}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
onViewsChange={onViewsChange}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistBoardCardFields,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
className={className}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,20 +10,22 @@ import {
|
||||
|
||||
type BoardOptionsDropdownProps = Pick<
|
||||
BoardOptionsDropdownContentProps,
|
||||
'customHotkeyScope' | 'onStageAdd' | 'onViewsChange' | 'scopeContext'
|
||||
'customHotkeyScope' | 'onStageAdd' | 'scopeContext'
|
||||
>;
|
||||
|
||||
export function BoardOptionsDropdown({
|
||||
customHotkeyScope,
|
||||
...props
|
||||
onStageAdd,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownProps) {
|
||||
return (
|
||||
<DropdownButton
|
||||
buttonComponents={<BoardOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<BoardOptionsDropdownContent
|
||||
{...props}
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { type Context, useRef, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -23,15 +23,17 @@ import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
@ -40,7 +42,6 @@ import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
@ -60,10 +61,10 @@ type ColumnForCreate = {
|
||||
export function BoardOptionsDropdownContent({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownContentProps) {
|
||||
const theme = useTheme();
|
||||
const scopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
@ -106,15 +107,24 @@ export function BoardOptionsDropdownContent({
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const { upsertView } = useUpsertView({
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
});
|
||||
const { upsertView } = useUpsertView({ scopeContext });
|
||||
|
||||
const handleViewNameSubmit = async () => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
await upsertView(name);
|
||||
};
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const boardCardFields = await snapshot.getPromise(
|
||||
boardCardFieldsScopedState(scopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
if (view && isCreateMode) {
|
||||
set(savedBoardCardFieldsFamilyState(view.id), boardCardFields);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
|
||||
|
||||
@ -6,10 +6,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import {
|
||||
BoardHeader,
|
||||
BoardHeaderProps,
|
||||
} from '@/ui/board/components/BoardHeader';
|
||||
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
||||
import { StyledBoard } from '@/ui/board/components/StyledBoard';
|
||||
import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
@ -39,10 +36,7 @@ export type EntityBoardProps = {
|
||||
onColumnDelete?: (boardColumnId: string) => void;
|
||||
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
|
||||
scopeContext: Context<string | null>;
|
||||
} & Pick<
|
||||
BoardHeaderProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSubmit'
|
||||
>;
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -57,12 +51,9 @@ const StyledBoardHeader = styled(BoardHeader)`
|
||||
|
||||
export function EntityBoard({
|
||||
boardOptions,
|
||||
defaultViewName,
|
||||
onColumnAdd,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
}: EntityBoardProps) {
|
||||
const [boardColumns] = useRecoilState(boardColumnsState);
|
||||
@ -139,13 +130,7 @@ export function EntityBoard({
|
||||
|
||||
return (boardColumns?.length ?? 0) > 0 ? (
|
||||
<StyledWrapper>
|
||||
<StyledBoardHeader
|
||||
defaultViewName={defaultViewName}
|
||||
onStageAdd={onColumnAdd}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
<StyledBoardHeader onStageAdd={onColumnAdd} scopeContext={scopeContext} />
|
||||
<ScrollWrapper>
|
||||
<StyledBoard ref={boardRef}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
export const savedBoardCardFieldsFamilyState = atomFamily<
|
||||
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
string | undefined
|
||||
>({
|
||||
key: 'savedBoardCardFieldsFamilyState',
|
||||
default: [],
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
||||
|
||||
export const canPersistBoardCardFieldsScopedFamilySelector = selectorFamily({
|
||||
key: 'canPersistBoardCardFieldsScopedFamilySelector',
|
||||
get:
|
||||
([scopeId, viewId]: [string, string | undefined]) =>
|
||||
({ get }) =>
|
||||
!isDeeplyEqual(
|
||||
get(savedBoardCardFieldsFamilyState(viewId)),
|
||||
get(boardCardFieldsScopedState(scopeId)),
|
||||
),
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
||||
|
||||
export const savedBoardCardFieldsByKeyFamilySelector = selectorFamily({
|
||||
key: 'savedBoardCardFieldsByKeyFamilySelector',
|
||||
get:
|
||||
(viewId: string | undefined) =>
|
||||
({ get }) =>
|
||||
get(savedBoardCardFieldsFamilyState(viewId)).reduce<
|
||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||
>((result, field) => ({ ...result, [field.key]: field }), {}),
|
||||
});
|
||||
@ -87,18 +87,9 @@ const StyledTableContainer = styled.div`
|
||||
|
||||
type OwnProps = {
|
||||
updateEntityMutation: any;
|
||||
} & Pick<
|
||||
TableHeaderProps,
|
||||
'defaultViewName' | 'onImport' | 'onViewsChange' | 'onViewSubmit'
|
||||
>;
|
||||
} & Pick<TableHeaderProps, 'onImport'>;
|
||||
|
||||
export function EntityTable({
|
||||
defaultViewName,
|
||||
onImport,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
updateEntityMutation,
|
||||
}: OwnProps) {
|
||||
export function EntityTable({ onImport, updateEntityMutation }: OwnProps) {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const setRowSelectedState = useSetRowSelectedState();
|
||||
@ -135,12 +126,7 @@ export function EntityTable({
|
||||
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer ref={tableBodyRef}>
|
||||
<TableHeader
|
||||
defaultViewName={defaultViewName}
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
/>
|
||||
<TableHeader onImport={onImport} />
|
||||
<ScrollWrapper>
|
||||
<div>
|
||||
<StyledTable className="entity-table-cell">
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
|
||||
@ -8,13 +7,11 @@ import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
|
||||
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
|
||||
|
||||
type TableOptionsDropdownProps = {
|
||||
onViewsChange?: (views: View[]) => void;
|
||||
onImport?: () => void;
|
||||
customHotkeyScope: HotkeyScope;
|
||||
};
|
||||
|
||||
export function TableOptionsDropdown({
|
||||
onViewsChange,
|
||||
onImport,
|
||||
customHotkeyScope,
|
||||
}: TableOptionsDropdownProps) {
|
||||
@ -23,12 +20,7 @@ export function TableOptionsDropdown({
|
||||
buttonComponents={<TableOptionsDropdownButton />}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownId={TableOptionsDropdownId}
|
||||
dropdownComponents={
|
||||
<TableOptionsDropdownContent
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={<TableOptionsDropdownContent onImport={onImport} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader';
|
||||
@ -11,31 +11,33 @@ import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/icon';
|
||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
|
||||
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
type TableOptionsDropdownButtonProps = {
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
onImport?: () => void;
|
||||
};
|
||||
|
||||
type TableOptionsMenu = 'fields';
|
||||
|
||||
export function TableOptionsDropdownContent({
|
||||
onViewsChange,
|
||||
onImport,
|
||||
}: TableOptionsDropdownButtonProps) {
|
||||
const scopeId = useContextScopeId(TableRecoilScopeContext);
|
||||
|
||||
const { closeDropdownButton } = useDropdownButton({
|
||||
dropdownId: TableOptionsDropdownId,
|
||||
});
|
||||
@ -60,17 +62,28 @@ export function TableOptionsDropdownContent({
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const { handleColumnVisibilityChange } = useTableColumns();
|
||||
|
||||
const { upsertView } = useUpsertView({
|
||||
onViewsChange,
|
||||
scopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
|
||||
const { handleColumnVisibilityChange } = useTableColumns();
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const tableColumns = await snapshot.getPromise(
|
||||
tableColumnsScopedState(scopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
const handleViewNameSubmit = async () => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
await upsertView(name);
|
||||
};
|
||||
if (view && isCreateMode) {
|
||||
set(savedTableColumnsFamilyState(view.id), tableColumns);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const handleSelectMenu = (option: TableOptionsMenu) => {
|
||||
handleViewNameSubmit();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
@ -5,7 +6,8 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'
|
||||
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 { ViewBar, ViewBarProps } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBar } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
@ -18,14 +20,11 @@ import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
export type TableHeaderProps = {
|
||||
onImport?: () => void;
|
||||
} & Pick<ViewBarProps, 'defaultViewName' | 'onViewsChange' | 'onViewSubmit'>;
|
||||
};
|
||||
|
||||
export function TableHeader({
|
||||
onImport,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
...props
|
||||
}: TableHeaderProps) {
|
||||
export function TableHeader({ onImport }: TableHeaderProps) {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
@ -43,9 +42,7 @@ export function TableHeader({
|
||||
savedTableColumnsFamilyState(currentViewId),
|
||||
);
|
||||
|
||||
function handleViewBarReset() {
|
||||
setTableColumns(savedTableColumns);
|
||||
}
|
||||
const handleViewBarReset = () => setTableColumns(savedTableColumns);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
@ -58,32 +55,36 @@ export function TableHeader({
|
||||
[tableScopeId],
|
||||
);
|
||||
|
||||
async function handleViewSubmit() {
|
||||
async function handleCurrentViewSubmit() {
|
||||
if (canPersistTableColumns) {
|
||||
setSavedTableColumns(tableColumns);
|
||||
}
|
||||
|
||||
await onViewSubmit?.();
|
||||
await onCurrentViewSubmit?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||
<ViewBar
|
||||
{...props}
|
||||
canPersistViewFields={canPersistTableColumns}
|
||||
onReset={handleViewBarReset}
|
||||
onViewSelect={handleViewSelect}
|
||||
onViewSubmit={handleViewSubmit}
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={TableOptionsDropdownId}
|
||||
scopeContext={TableRecoilScopeContext}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistTableColumns,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
onImport={onImport}
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={TableOptionsDropdownId}
|
||||
scopeContext={TableRecoilScopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type OwnProps = ComponentProps<'div'> & {
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
leftComponent?: ReactNode;
|
||||
rightComponent?: ReactNode;
|
||||
bottomComponent?: ReactNode;
|
||||
@ -40,14 +41,14 @@ const StyledRightSection = styled.div`
|
||||
`;
|
||||
|
||||
export function TopBar({
|
||||
className,
|
||||
leftComponent,
|
||||
rightComponent,
|
||||
bottomComponent,
|
||||
displayBottomBorder = true,
|
||||
...props
|
||||
}: OwnProps) {
|
||||
return (
|
||||
<StyledContainer {...props}>
|
||||
<StyledContainer className={className}>
|
||||
<StyledTopBar displayBottomBorder={displayBottomBorder}>
|
||||
<StyledLeftSection>{leftComponent}</StyledLeftSection>
|
||||
<StyledRightSection>{rightComponent}</StyledRightSection>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Context, useCallback, useState } from 'react';
|
||||
import { type Context, useCallback, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
@ -21,6 +21,8 @@ import { canPersistSortsScopedFamilySelector } from '@/ui/view-bar/states/select
|
||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: inline-flex;
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
@ -28,22 +30,20 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export type UpdateViewButtonGroupProps = {
|
||||
canPersistViewFields?: boolean;
|
||||
hotkeyScope: string;
|
||||
onViewEditModeChange?: () => void;
|
||||
onViewSubmit?: () => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
export const UpdateViewButtonGroup = ({
|
||||
canPersistViewFields,
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
}: UpdateViewButtonGroupProps) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
||||
const { canPersistViewFields, onCurrentViewSubmit } =
|
||||
useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
@ -89,7 +89,7 @@ export const UpdateViewButtonGroup = ({
|
||||
if (canPersistFilters) setSavedFilters(filters);
|
||||
if (canPersistSorts) setSavedSorts(sorts);
|
||||
|
||||
await onViewSubmit?.();
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ComponentProps, Context, ReactNode } from 'react';
|
||||
import type { Context, ReactNode } from 'react';
|
||||
|
||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { TopBar } from '@/ui/top-bar/TopBar';
|
||||
@ -8,38 +8,22 @@ import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||
|
||||
import { FilterDropdownButton } from './FilterDropdownButton';
|
||||
import { SortDropdownButton } from './SortDropdownButton';
|
||||
import {
|
||||
UpdateViewButtonGroup,
|
||||
type UpdateViewButtonGroupProps,
|
||||
} from './UpdateViewButtonGroup';
|
||||
import ViewBarDetails, { type ViewBarDetailsProps } from './ViewBarDetails';
|
||||
import {
|
||||
ViewsDropdownButton,
|
||||
type ViewsDropdownButtonProps,
|
||||
} from './ViewsDropdownButton';
|
||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||
import ViewBarDetails from './ViewBarDetails';
|
||||
import { ViewsDropdownButton } from './ViewsDropdownButton';
|
||||
|
||||
export type ViewBarProps = ComponentProps<'div'> & {
|
||||
export type ViewBarProps = {
|
||||
className?: string;
|
||||
optionsDropdownButton: ReactNode;
|
||||
optionsDropdownKey: string;
|
||||
scopeContext: Context<string | null>;
|
||||
} & Pick<
|
||||
ViewsDropdownButtonProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSelect'
|
||||
> &
|
||||
Pick<ViewBarDetailsProps, 'canPersistViewFields' | 'onReset'> &
|
||||
Pick<UpdateViewButtonGroupProps, 'onViewSubmit'>;
|
||||
};
|
||||
|
||||
export const ViewBar = ({
|
||||
canPersistViewFields,
|
||||
defaultViewName,
|
||||
onReset,
|
||||
onViewsChange,
|
||||
onViewSelect,
|
||||
onViewSubmit,
|
||||
className,
|
||||
optionsDropdownButton,
|
||||
optionsDropdownKey,
|
||||
scopeContext,
|
||||
...props
|
||||
}: ViewBarProps) => {
|
||||
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
|
||||
dropdownId: optionsDropdownKey,
|
||||
@ -47,13 +31,10 @@ export const ViewBar = ({
|
||||
|
||||
return (
|
||||
<TopBar
|
||||
{...props}
|
||||
className={className}
|
||||
leftComponent={
|
||||
<ViewsDropdownButton
|
||||
defaultViewName={defaultViewName}
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSelect={onViewSelect}
|
||||
hotkeyScope={ViewsHotkeyScope.ListDropdown}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
@ -75,15 +56,11 @@ export const ViewBar = ({
|
||||
}
|
||||
bottomComponent={
|
||||
<ViewBarDetails
|
||||
canPersistViewFields={canPersistViewFields}
|
||||
context={scopeContext}
|
||||
hasFilterButton
|
||||
onReset={onReset}
|
||||
rightComponent={
|
||||
<UpdateViewButtonGroup
|
||||
canPersistViewFields={canPersistViewFields}
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
onViewSubmit={onViewSubmit}
|
||||
hotkeyScope={ViewsHotkeyScope.CreateDropdown}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Context, ReactNode } from 'react';
|
||||
import { type Context, type ReactNode, useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@ -7,6 +7,7 @@ import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextS
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveFilter } from '../hooks/useRemoveFilter';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
@ -23,10 +24,8 @@ import { AddFilterFromDropdownButton } from './AddFilterFromDetailsButton';
|
||||
import SortOrFilterChip from './SortOrFilterChip';
|
||||
|
||||
export type ViewBarDetailsProps = {
|
||||
canPersistViewFields?: boolean;
|
||||
context: Context<string | null>;
|
||||
hasFilterButton?: boolean;
|
||||
onReset?: () => void;
|
||||
rightComponent?: ReactNode;
|
||||
};
|
||||
|
||||
@ -99,12 +98,11 @@ const StyledAddFilterContainer = styled.div`
|
||||
`;
|
||||
|
||||
function ViewBarDetails({
|
||||
canPersistViewFields,
|
||||
context,
|
||||
hasFilterButton = false,
|
||||
onReset,
|
||||
rightComponent,
|
||||
}: ViewBarDetailsProps) {
|
||||
const { canPersistViewFields, onViewBarReset } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(context);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(currentViewIdScopedState, context);
|
||||
@ -155,7 +153,7 @@ function ViewBarDetails({
|
||||
|
||||
const removeFilter = useRemoveFilter(context);
|
||||
function handleCancelClick() {
|
||||
onReset?.();
|
||||
onViewBarReset?.();
|
||||
setFilters(savedFilters);
|
||||
setSorts(savedSorts);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
type Context,
|
||||
type MouseEvent,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
@ -22,7 +23,6 @@ import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
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 DropdownButton from '@/ui/view-bar/components/DropdownButton';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
@ -33,10 +33,12 @@ import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/curren
|
||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveView } from '../hooks/useRemoveView';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(
|
||||
StyledDropdownMenuItemsContainer,
|
||||
)`
|
||||
@ -71,39 +73,28 @@ const StyledViewName = styled.span`
|
||||
`;
|
||||
|
||||
export type ViewsDropdownButtonProps = {
|
||||
defaultViewName: string;
|
||||
hotkeyScope: ViewsHotkeyScope;
|
||||
onViewEditModeChange?: () => void;
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
onViewSelect?: (viewId: string) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
export const ViewsDropdownButton = ({
|
||||
defaultViewName,
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
onViewsChange,
|
||||
onViewSelect,
|
||||
scopeContext,
|
||||
}: ViewsDropdownButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
const { defaultViewName, onViewSelect } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const [, setCurrentViewId] = useRecoilScopedState(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
scopeContext,
|
||||
);
|
||||
const [views, setViews] = useRecoilScopedState(
|
||||
viewsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const views = useRecoilScopedValue(viewsScopedState, scopeContext);
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
const {
|
||||
@ -146,20 +137,17 @@ export const ViewsDropdownButton = ({
|
||||
[setViewEditMode, onViewEditModeChange],
|
||||
);
|
||||
|
||||
const handleDeleteViewButtonClick = useCallback(
|
||||
async (event: MouseEvent<HTMLButtonElement>, viewId: string) => {
|
||||
event.stopPropagation();
|
||||
const { removeView } = useRemoveView({ scopeContext });
|
||||
|
||||
if (currentView?.id === viewId) setCurrentViewId(undefined);
|
||||
const handleDeleteViewButtonClick = async (
|
||||
event: MouseEvent<HTMLButtonElement>,
|
||||
viewId: string,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const nextViews = views.filter((view) => view.id !== viewId);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
setIsUnfolded(false);
|
||||
},
|
||||
[currentView?.id, onViewsChange, setCurrentViewId, setViews, views],
|
||||
);
|
||||
await removeView(viewId);
|
||||
setIsUnfolded(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
isUnfolded
|
||||
|
||||
14
front/src/modules/ui/view-bar/contexts/ViewBarContext.ts
Normal file
14
front/src/modules/ui/view-bar/contexts/ViewBarContext.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { View } from '../types/View';
|
||||
|
||||
export const ViewBarContext = createContext<{
|
||||
canPersistViewFields?: boolean;
|
||||
defaultViewName?: string;
|
||||
onCurrentViewSubmit?: () => void | Promise<void>;
|
||||
onViewBarReset?: () => void;
|
||||
onViewCreate?: (view: View) => void | Promise<void>;
|
||||
onViewEdit?: (view: View) => void | Promise<void>;
|
||||
onViewRemove?: (viewId: string) => void | Promise<void>;
|
||||
onViewSelect?: (viewId: string) => void | Promise<void>;
|
||||
}>({});
|
||||
37
front/src/modules/ui/view-bar/hooks/useRemoveView.ts
Normal file
37
front/src/modules/ui/view-bar/hooks/useRemoveView.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { type Context, useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
|
||||
export const useRemoveView = ({
|
||||
scopeContext,
|
||||
}: {
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const { onViewRemove } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const removeView = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentViewId = await snapshot.getPromise(
|
||||
currentViewIdScopedState(recoilScopeId),
|
||||
);
|
||||
|
||||
if (currentViewId === viewId)
|
||||
set(currentViewIdScopedState(recoilScopeId), undefined);
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.filter((view) => view.id !== viewId),
|
||||
);
|
||||
await onViewRemove?.(viewId);
|
||||
},
|
||||
[onViewRemove, recoilScopeId],
|
||||
);
|
||||
|
||||
return { removeView };
|
||||
};
|
||||
@ -1,37 +1,30 @@
|
||||
import { Context, useCallback } from 'react';
|
||||
import { type Context, useCallback, useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
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 { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '../states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '../states/savedSortsFamilyState';
|
||||
import { viewsByIdScopedSelector } from '../states/selectors/viewsByIdScopedSelector';
|
||||
import { sortsScopedState } from '../states/sortsScopedState';
|
||||
import { viewEditModeState } from '../states/viewEditModeState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
import type { View } from '../types/View';
|
||||
|
||||
export const useUpsertView = ({
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
}: {
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const { onViewCreate, onViewEdit } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const filters = useRecoilScopedValue(filtersScopedState, scopeContext);
|
||||
const sorts = useRecoilScopedValue(sortsScopedState, scopeContext);
|
||||
|
||||
const [, setCurrentViewId] = useRecoilScopedState(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [views, setViews] = useRecoilScopedState(
|
||||
viewsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [viewEditMode, setViewEditMode] = useRecoilState(viewEditModeState);
|
||||
|
||||
const resetViewEditMode = useCallback(
|
||||
@ -40,44 +33,63 @@ export const useUpsertView = ({
|
||||
);
|
||||
|
||||
const upsertView = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({ set, snapshot }) =>
|
||||
async (name?: string) => {
|
||||
if (!viewEditMode.mode || !name) return resetViewEditMode();
|
||||
if (!viewEditMode.mode || !name) {
|
||||
resetViewEditMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewEditMode.mode === 'create') {
|
||||
const viewToCreate = { id: v4(), name };
|
||||
const nextViews = [...views, viewToCreate];
|
||||
const createdView = { id: v4(), name };
|
||||
|
||||
set(savedFiltersFamilyState(viewToCreate.id), filters);
|
||||
set(savedSortsFamilyState(viewToCreate.id), sorts);
|
||||
set(savedFiltersFamilyState(createdView.id), filters);
|
||||
set(savedSortsFamilyState(createdView.id), sorts);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) => [
|
||||
...previousViews,
|
||||
createdView,
|
||||
]);
|
||||
|
||||
setCurrentViewId(viewToCreate.id);
|
||||
await onViewCreate?.(createdView);
|
||||
|
||||
resetViewEditMode();
|
||||
|
||||
set(currentViewIdScopedState(recoilScopeId), createdView.id);
|
||||
|
||||
return createdView;
|
||||
}
|
||||
|
||||
if (viewEditMode.mode === 'edit') {
|
||||
const nextViews = views.map((view) =>
|
||||
view.id === viewEditMode.viewId ? { ...view, name } : view,
|
||||
if (viewEditMode.mode === 'edit' && viewEditMode.viewId) {
|
||||
const viewsById = await snapshot.getPromise(
|
||||
viewsByIdScopedSelector(recoilScopeId),
|
||||
);
|
||||
const editedView = { ...viewsById[viewEditMode.viewId], name };
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.map((previousView) =>
|
||||
previousView.id === viewEditMode.viewId
|
||||
? editedView
|
||||
: previousView,
|
||||
),
|
||||
);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
}
|
||||
await onViewEdit?.(editedView);
|
||||
|
||||
return resetViewEditMode();
|
||||
resetViewEditMode();
|
||||
|
||||
return editedView;
|
||||
}
|
||||
},
|
||||
[
|
||||
filters,
|
||||
onViewsChange,
|
||||
onViewCreate,
|
||||
onViewEdit,
|
||||
recoilScopeId,
|
||||
resetViewEditMode,
|
||||
setCurrentViewId,
|
||||
setViews,
|
||||
sorts,
|
||||
viewEditMode.mode,
|
||||
viewEditMode.viewId,
|
||||
views,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user