[Command menu items] Create engine commands (#18681)
## 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.
This commit is contained in:
+5
-4
@@ -1,5 +1,5 @@
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalResultBrowserEventName';
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_INSTANCE_ID } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalId';
|
||||
import { COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME } from '@/command-menu-item/confirmation-modal/constants/CommandMenuItemConfirmationModalResultBrowserEventName';
|
||||
import { commandMenuItemConfirmationModalConfigState } from '@/command-menu-item/confirmation-modal/states/commandMenuItemConfirmationModalState';
|
||||
import {
|
||||
type CommandMenuConfirmationModalResult,
|
||||
@@ -10,6 +10,7 @@ import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpe
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const CommandMenuConfirmationModalManager = () => {
|
||||
const commandMenuItemConfirmationModalConfig = useAtomStateValue(
|
||||
@@ -23,12 +24,12 @@ export const CommandMenuConfirmationModalManager = () => {
|
||||
commandMenuItemConfirmationModalConfigState,
|
||||
);
|
||||
|
||||
const callerId = commandMenuItemConfirmationModalConfig?.frontComponentId;
|
||||
const caller = commandMenuItemConfirmationModalConfig?.caller;
|
||||
|
||||
const emitConfirmationResult = (
|
||||
confirmationResult: CommandMenuConfirmationModalResult,
|
||||
) => {
|
||||
if (!callerId) {
|
||||
if (!isDefined(caller)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ export const CommandMenuConfirmationModalManager = () => {
|
||||
COMMAND_MENU_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME,
|
||||
{
|
||||
detail: {
|
||||
frontComponentId: callerId,
|
||||
caller,
|
||||
confirmationResult,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user