2363 refactor the dialog component to use the new scope architecture (#2415)

* create Scope

* refactor dialog manager

* finished refactoring

* modify closeDialog to use a recoilcallback

* modify according to comments + add effet component

* fix spreadsheet import stories
This commit is contained in:
bosiraphael
2023-11-10 12:36:25 +01:00
committed by GitHub
parent 6a700ad1a5
commit e0289ba9f2
22 changed files with 249 additions and 152 deletions

View File

@ -0,0 +1,24 @@
import { useDialogManagerScopedStates } from '../hooks/internal/useDialogManagerScopedStates';
import { useDialogManager } from '../hooks/useDialogManager';
import { Dialog } from './Dialog';
import { DialogManagerEffect } from './DialogManagerEffect';
export const DialogManager = ({ children }: React.PropsWithChildren) => {
const { dialogInternal } = useDialogManagerScopedStates();
const { closeDialog } = useDialogManager();
return (
<>
<DialogManagerEffect />
{children}
{dialogInternal.queue.map(({ buttons, children, id, message, title }) => (
<Dialog
key={id}
{...{ title, message, buttons, id, children }}
onClose={() => closeDialog(id)}
/>
))}
</>
);
};

View File

@ -0,0 +1,22 @@
import { useEffect } from 'react';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useDialogManagerScopedStates } from '../hooks/internal/useDialogManagerScopedStates';
import { DialogHotkeyScope } from '../types/DialogHotkeyScope';
export const DialogManagerEffect = () => {
const { dialogInternal } = useDialogManagerScopedStates();
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
useEffect(() => {
if (dialogInternal.queue.length === 0) {
return;
}
setHotkeyScopeAndMemorizePreviousScope(DialogHotkeyScope.Dialog);
}, [dialogInternal.queue, setHotkeyScopeAndMemorizePreviousScope]);
return <></>;
};

View File

@ -0,0 +1,25 @@
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { DialogManagerScopeInternalContext } from '../../scopes/scope-internal-context/DialogManagerScopeInternalContext';
import { dialogInternalScopedState } from '../../states/dialogInternalScopedState';
type useDialogManagerScopedStatesProps = {
dialogManagerScopeId?: string;
};
export const useDialogManagerScopedStates = (
props?: useDialogManagerScopedStatesProps,
) => {
const scopeId = useAvailableScopeIdOrThrow(
DialogManagerScopeInternalContext,
props?.dialogManagerScopeId,
);
const [dialogInternal, setDialogInternal] = useRecoilScopedStateV2(
dialogInternalScopedState,
scopeId,
);
return { dialogInternal, setDialogInternal };
};

View File

@ -0,0 +1,62 @@
import { useRecoilCallback } from 'recoil';
import { v4 } from 'uuid';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { DialogManagerScopeInternalContext } from '../scopes/scope-internal-context/DialogManagerScopeInternalContext';
import { dialogInternalScopedState } from '../states/dialogInternalScopedState';
import { DialogOptions } from '../types/DialogOptions';
type useDialogManagerProps = {
dialogManagerScopeId?: string;
};
export const useDialogManager = (props?: useDialogManagerProps) => {
const scopeId = useAvailableScopeIdOrThrow(
DialogManagerScopeInternalContext,
props?.dialogManagerScopeId,
);
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
const closeDialog = useRecoilCallback(
({ set }) =>
(id: string) => {
set(dialogInternalScopedState({ scopeId: scopeId }), (prevState) => ({
...prevState,
queue: prevState.queue.filter((snackBar) => snackBar.id !== id),
}));
goBackToPreviousHotkeyScope();
},
[goBackToPreviousHotkeyScope, scopeId],
);
const setDialogQueue = useRecoilCallback(
({ set }) =>
(newValue) =>
set(dialogInternalScopedState({ scopeId: scopeId }), (prev) => {
if (prev.queue.length >= prev.maxQueue) {
return {
...prev,
queue: [...prev.queue.slice(1), newValue] as DialogOptions[],
};
}
return {
...prev,
queue: [...prev.queue, newValue] as DialogOptions[],
};
}),
[scopeId],
);
const enqueueDialog = (options?: Omit<DialogOptions, 'id'>) => {
setDialogQueue({
id: v4(),
...options,
});
};
return { closeDialog, enqueueDialog };
};

View File

@ -0,0 +1,23 @@
import { ReactNode } from 'react';
import { DialogManagerScopeInternalContext } from './scope-internal-context/DialogManagerScopeInternalContext';
type DialogManagerScopeProps = {
children: ReactNode;
dialogManagerScopeId: string;
};
export const DialogManagerScope = ({
children,
dialogManagerScopeId,
}: DialogManagerScopeProps) => {
return (
<DialogManagerScopeInternalContext.Provider
value={{
scopeId: dialogManagerScopeId,
}}
>
{children}
</DialogManagerScopeInternalContext.Provider>
);
};

View File

@ -0,0 +1,7 @@
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
type DialogManagerScopeInternalContextProps = ScopedStateKey;
export const DialogManagerScopeInternalContext =
createScopeInternalContext<DialogManagerScopeInternalContextProps>();

View File

@ -0,0 +1,16 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
import { DialogOptions } from '../types/DialogOptions';
type DialogState = {
maxQueue: number;
queue: DialogOptions[];
};
export const dialogInternalScopedState = createScopedState<DialogState>({
key: 'dialog/internal-state',
defaultValue: {
maxQueue: 2,
queue: [],
},
});

View File

@ -0,0 +1,5 @@
import { DialogProps } from '../components/Dialog';
export type DialogOptions = DialogProps & {
id: string;
};

View File

@ -1,49 +0,0 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { dialogInternalState } from '../states/dialogState';
import { DialogHotkeyScope } from '../types/DialogHotkeyScope';
import { Dialog } from './Dialog';
export const DialogProvider = ({ children }: React.PropsWithChildren) => {
const [dialogInternal, setDialogInternal] =
useRecoilState(dialogInternalState);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
// Handle dialog close event
const handleDialogClose = (id: string) => {
setDialogInternal((prevState) => ({
...prevState,
queue: prevState.queue.filter((snackBar) => snackBar.id !== id),
}));
goBackToPreviousHotkeyScope();
};
useEffect(() => {
if (dialogInternal.queue.length === 0) {
return;
}
setHotkeyScopeAndMemorizePreviousScope(DialogHotkeyScope.Dialog);
}, [dialogInternal.queue, setHotkeyScopeAndMemorizePreviousScope]);
return (
<>
{children}
{dialogInternal.queue.map(({ buttons, children, id, message, title }) => (
<Dialog
key={id}
{...{ title, message, buttons, id, children }}
onClose={() => handleDialogClose(id)}
/>
))}
</>
);
};

View File

@ -1,17 +0,0 @@
import { useSetRecoilState } from 'recoil';
import { v4 as uuidv4 } from 'uuid';
import { DialogOptions, dialogSetQueueState } from '../states/dialogState';
export const useDialog = () => {
const setDialogQueue = useSetRecoilState(dialogSetQueueState);
const enqueueDialog = (options?: Omit<DialogOptions, 'id'>) => {
setDialogQueue({
id: uuidv4(),
...options,
});
};
return { enqueueDialog };
};

View File

@ -1,39 +0,0 @@
import { atom, selector } from 'recoil';
import { DialogProps } from '../components/Dialog';
export type DialogOptions = DialogProps & {
id: string;
};
export type DialogState = {
maxQueue: number;
queue: DialogOptions[];
};
export const dialogInternalState = atom<DialogState>({
key: 'dialog/internal-state',
default: {
maxQueue: 2,
queue: [],
},
});
export const dialogSetQueueState = selector<DialogOptions | null>({
key: 'dialog/queue-state',
get: ({ get: _get }) => null, // We don't care about getting the value
set: ({ set }, newValue) =>
set(dialogInternalState, (prev) => {
if (prev.queue.length >= prev.maxQueue) {
return {
...prev,
queue: [...prev.queue.slice(1), newValue] as DialogOptions[],
};
}
return {
...prev,
queue: [...prev.queue, newValue] as DialogOptions[],
};
}),
});