abdab2fb7e
## 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.
66 lines
1.3 KiB
TypeScript
66 lines
1.3 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
|
|
import {
|
|
openCommandConfirmationModal,
|
|
unmountFrontComponent,
|
|
useFrontComponentId,
|
|
type CommandConfirmationModalAccent,
|
|
} from '../front-component-api';
|
|
|
|
export type CommandModalProps = {
|
|
title: string;
|
|
subtitle: string;
|
|
execute: () => void | Promise<void>;
|
|
confirmButtonText?: string;
|
|
confirmButtonAccent?: CommandConfirmationModalAccent;
|
|
};
|
|
|
|
export const CommandModal = ({
|
|
title,
|
|
subtitle,
|
|
execute,
|
|
confirmButtonText,
|
|
confirmButtonAccent,
|
|
}: CommandModalProps) => {
|
|
const [hasExecuted, setHasExecuted] = useState(false);
|
|
|
|
const frontComponentId = useFrontComponentId();
|
|
|
|
useEffect(() => {
|
|
if (hasExecuted) {
|
|
return;
|
|
}
|
|
|
|
setHasExecuted(true);
|
|
|
|
const run = async () => {
|
|
const commandConfirmationModalResult = await openCommandConfirmationModal(
|
|
{
|
|
title,
|
|
subtitle,
|
|
confirmButtonText,
|
|
confirmButtonAccent,
|
|
},
|
|
);
|
|
|
|
if (commandConfirmationModalResult === 'confirm') {
|
|
await execute();
|
|
}
|
|
|
|
await unmountFrontComponent();
|
|
};
|
|
|
|
run();
|
|
}, [
|
|
title,
|
|
subtitle,
|
|
execute,
|
|
confirmButtonText,
|
|
confirmButtonAccent,
|
|
hasExecuted,
|
|
frontComponentId,
|
|
]);
|
|
|
|
return null;
|
|
};
|