Unify complex dashboard modal presentation under persisted FloatingWindow geometry. - Migrate Create Room, Task Detail, Agent Detail, and GitHub Import modal presentations. - Preserve documented embedded and docked exceptions, dismissal behavior, and nested scrolling. - Add presentation-contract coverage and publish dashboard guidance and changesets. Files changed: ...n-8619-resize-persist-modals-floating-window.md | 7 ++ .changeset/fn-8621-create-room-floating-window.md | 7 ++ docs/dashboard-guide.md | 16 +++- docs/dashboard-modal-inventory.md | 12 +-- .../dashboard/app/components/AgentDetailView.css | 16 +--- .../dashboard/app/components/AgentDetailView.tsx | 102 ++++++++++++++++----- .../dashboard/app/components/CreateRoomModal.css | 19 +++- .../dashboard/app/components/CreateRoomModal.tsx | 57 ++++++++---- .../dashboard/app/components/FloatingWindow.css | 13 ++- .../dashboard/app/components/FloatingWindow.tsx | 15 +++ .../dashboard/app/components/GitHubImportModal.css | 11 +-- .../dashboard/app/components/GitHubImportModal.tsx | 40 ++++++-- .../dashboard/app/components/TaskDetailModal.css | 52 +---------- .../dashboard/app/components/TaskDetailModal.tsx | 58 ++++++------ .../__tests__/AgentDetailView.core.test.tsx | 2 +- .../AgentDetailView.mobile-scroll.test.tsx | 6 +- .../components/__tests__/CreateRoomModal.test.tsx | 62 +++++++++++-- .../components/__tests__/FloatingWindow.test.tsx | 1 + .../__tests__/GitHubImportModal.test.tsx | 8 +- ...etailModal.responsive-and-dependencies.test.tsx | 77 +++++++--------- .../__tests__/modal-presentation-contract.test.tsx | 74 +++++++++++++++ .../dashboard/app/hooks/useEmbeddedPresentation.ts | 2 +- .../dashboard/app/hooks/useModalResizePersist.ts | 5 + 23 files changed, 441 insertions(+), 221 deletions(-) Fusion-Task-Id: FN-8621 Fusion-Task-Lineage: 04b6f3fe-d527-4a21-a0cb-489eb20f5e91 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
48 lines
2.8 KiB
TypeScript
48 lines
2.8 KiB
TypeScript
/*
|
|
FNXC:EmbeddedPresentation 2026-06-22-12:00:
|
|
Seven modal components (ActivityLogModal, GitManagerModal, GitHubImportModal, ScheduledTasksModal, PlanningModeModal, SettingsModal, WorkflowNodeEditor) each independently grew the same "embedded vs modal" presentation switch for the right-dock / main-content-area redesign. Each derived `isEmbedded = presentation === "embedded"` locally and gated the same modal-only behaviors off it: mobile scroll lock, modal resize-persist, Escape-to-close, and overlay click-dismiss.
|
|
|
|
This hook collapses that copy-pasted pattern into one place. The returned booleans are the enabled-arg for the hooks/handlers the components already call (e.g. `useMobileScrollLock(open && scrollLockEnabled)`), so the gating stays a single boolean expression and the underlying hooks remain CALLED UNCONDITIONALLY (React hook rules) — only their enabled arg flips.
|
|
|
|
Embedded surfaces are persistent main-content destinations owned by the dock/router, so all four modal-only affordances are disabled when embedded; every flag is simply `!isEmbedded`. Modal presentation (the default) keeps every affordance on, byte-identical to the historical behavior.
|
|
*/
|
|
|
|
/** Presentation surface for a component that can render as a fixed dialog overlay or inline in the main content area. */
|
|
export type ModalPresentation = "modal" | "embedded";
|
|
|
|
/**
|
|
* Derived presentation flags shared by the embedded-capable modal components.
|
|
*
|
|
* - `isEmbedded` / `isModal` — the raw mode test.
|
|
* - `scrollLockEnabled` — gate for `useMobileScrollLock`; off when embedded (the host page owns scrolling).
|
|
* - `resizePersistEnabled` — gate for modal-only FloatingWindow geometry; off when embedded (the view fills its container).
|
|
* - `escapeEnabled` — gate for Escape-to-close handlers; off when embedded (the dock/router owns lifecycle).
|
|
* - `overlayDismissEnabled` — gate for backdrop click-to-dismiss; off when embedded (no overlay backdrop exists).
|
|
*/
|
|
export interface EmbeddedPresentation {
|
|
isEmbedded: boolean;
|
|
isModal: boolean;
|
|
scrollLockEnabled: boolean;
|
|
resizePersistEnabled: boolean;
|
|
escapeEnabled: boolean;
|
|
overlayDismissEnabled: boolean;
|
|
}
|
|
|
|
/**
|
|
* Resolve the shared embedded-presentation flags for a component.
|
|
*
|
|
* @param presentation - The component's `presentation` prop. Defaults to "modal" so callers that omit it keep full modal behavior.
|
|
*/
|
|
export function useEmbeddedPresentation(presentation: ModalPresentation = "modal"): EmbeddedPresentation {
|
|
const isEmbedded = presentation === "embedded";
|
|
// Every modal-only affordance is disabled in embedded mode; embedded surfaces are persistent and host-owned.
|
|
return {
|
|
isEmbedded,
|
|
isModal: !isEmbedded,
|
|
scrollLockEnabled: !isEmbedded,
|
|
resizePersistEnabled: !isEmbedded,
|
|
escapeEnabled: !isEmbedded,
|
|
overlayDismissEnabled: !isEmbedded,
|
|
};
|
|
}
|