@ -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 }), {}),
|
||||
});
|
||||
Reference in New Issue
Block a user