## Description - Introduces a new engine command execution model that replaces the previous approach of mapping `EngineComponentKey` to React components. Instead, engine commands are now mounted headlessly via `HeadlessEngineCommandMountRoot`, with their execution context populated synchronously before mounting. - Creates new headless command components - Moves error handling from the SDK layer to the host app by wrapping all mounted commands with a new `CommandMenuItemErrorBoundary` The new flow works as follows: - When a command menu item with an `engineComponentKey` is clicked, `useCommandMenuItemFrontComponentCommands` calls `useMountEngineCommand`, which synchronously reads the current context store (object metadata, selected records, filters, view ID, etc.) and writes a `MountedEngineCommandContext` into `mountedEngineCommandsState`. - The command is then mounted into `mountedEngineCommandsState`, which triggers `HeadlessEngineCommandMountRoot` to render the corresponding headless component from `ENGINE_COMPONENT_KEY_HEADLESS_COMPONENT_MAP`, wrapped in `CommandMenuItemErrorBoundary`, `ContextStoreComponentInstanceContext.Provider`, and `EngineCommandComponentInstanceContext.Provider`. - Each command component reads its execution context and delegates to one of the 4 execution patterns: `HeadlessEngineCommandWrapperEffect` (simple actions), `HeadlessConfirmationModalEngineCommandEffect` (destructive actions needing confirmation), `HeadlessNavigateEngineCommand` (GO_TO_* commands), or `HeadlessOpenSidePanelPageEngineCommand` (SEARCH_RECORDS, ASK_AI, VIEW_PREVIOUS_AI_CHATS). - After execution, the command self-unmounts via `useUnmountEngineCommand`, which removes the entry from `mountedEngineCommandsState` and stops rendering the component.
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import { useStore } from 'jotai';
|
|
import { useCallback } from 'react';
|
|
|
|
import { COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalId';
|
|
import {
|
|
type CommandMenuItemConfirmationModalConfig,
|
|
commandMenuItemConfirmationModalConfigState,
|
|
} from '@/command-menu-item/confirmation-modal/states/commandMenuItemConfirmationModalState';
|
|
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
|
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
|
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
|
|
|
export const useCommandMenuConfirmationModal = () => {
|
|
const store = useStore();
|
|
const setCommandMenuItemConfirmationModalConfig = useSetAtomState(
|
|
commandMenuItemConfirmationModalConfigState,
|
|
);
|
|
const { openModal } = useModal();
|
|
|
|
const openConfirmationModal = useCallback(
|
|
(config: CommandMenuItemConfirmationModalConfig) => {
|
|
const existingCommandMenuItemConfirmationModalConfig = store.get(
|
|
commandMenuItemConfirmationModalConfigState.atom,
|
|
);
|
|
const isCommandMenuItemConfirmationModalOpened = store.get(
|
|
isModalOpenedComponentState.atomFamily({
|
|
instanceId: COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID,
|
|
}),
|
|
);
|
|
|
|
if (
|
|
existingCommandMenuItemConfirmationModalConfig !== null ||
|
|
isCommandMenuItemConfirmationModalOpened
|
|
) {
|
|
throw new Error(
|
|
'Command menu item confirmation modal is already active for another caller',
|
|
);
|
|
}
|
|
|
|
setCommandMenuItemConfirmationModalConfig(config);
|
|
|
|
openModal(COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID);
|
|
},
|
|
[store, setCommandMenuItemConfirmationModalConfig, openModal],
|
|
);
|
|
|
|
return { openConfirmationModal };
|
|
};
|