Adds an opt-in project setting so open task-detail popups stay attached to the Board or List view where they were opened, instead of floating over every main-content view. - New project setting taskPopupsBoardListOnly (default: off) in settings-schema.ts and ProjectSettings type, with default preserved via settings-defaults tests. - usePoppedOutTasks now stores each popup's originating TaskView alongside its task snapshot (PoppedOutTaskEntry), keeping legacy tasks output for existing callers. - App.tsx adds isTaskPopupVisibleForView() gating helper and filters popped-out entries to the current view for rendering/keyboard-close handling, while hidden popups remain mounted in hook state (not cleared) so switching back to the originating view restores them with shared persisted geometry. - Settings -> Appearance gets a new "Keep task popups on their Board/List view" checkbox (AppearanceSection.tsx) with i18n strings and updated settings search text in SettingsModal. - Documentation updated in docs/dashboard-guide.md and docs/settings-reference.md to describe the render-only hide/restore behavior. - New/updated tests: App.taskPopupViewGating.test.tsx, usePoppedOutTasks.test.ts, AppearanceSection.test.tsx, settings-default-descriptions.test.tsx, settings-defaults.test.ts. Files changed: docs/dashboard-guide.md | 5 +- docs/settings-reference.md | 1 + .../core/src/__tests__/settings-defaults.test.ts | 13 +++ packages/core/src/settings-schema.ts | 5 + packages/core/src/types.ts | 7 ++ packages/dashboard/app/App.tsx | 49 +++++++-- .../app/__tests__/App.taskPopupViewGating.test.tsx | 113 +++++++++++++++++++++ .../dashboard/app/components/SettingsModal.tsx | 3 +- .../settings/sections/AppearanceSection.tsx | 8 ++ .../sections/__tests__/AppearanceSection.test.tsx | 21 ++++ .../settings-default-descriptions.test.tsx | 1 + .../app/hooks/__tests__/usePoppedOutTasks.test.ts | 14 +++ packages/dashboard/app/hooks/useAppSettings.ts | 4 + packages/dashboard/app/hooks/usePoppedOutTasks.ts | 27 +++-- packages/i18n/locales/en/app.json | 2 + 15 files changed, 255 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-7944 Fusion-Task-Lineage: 4b8ced0e-1853-429f-8482-163821a35ae6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
/*
|
|
FNXC:FloatingWindow 2026-06-24-00:00:
|
|
Popped-out task-detail windows — movable, resizable, non-blocking FloatingWindows. Each entry is a task snapshot; several can be open at once. Snapshots survive a tasks revalidation (rendering prefers the live row by id). Pop-out dedupes by task id. Extracted from AppInner.
|
|
*/
|
|
|
|
import { useCallback, useMemo, useState } from "react";
|
|
import type { Task, TaskDetail } from "@fusion/core";
|
|
import type { TaskView } from "./useViewState";
|
|
|
|
export interface PoppedOutTaskEntry {
|
|
task: Task | TaskDetail;
|
|
originTaskView?: TaskView;
|
|
}
|
|
|
|
export interface UsePoppedOutTasksResult {
|
|
entries: PoppedOutTaskEntry[];
|
|
tasks: Array<Task | TaskDetail>;
|
|
popOut: (task: Task | TaskDetail, originTaskView?: TaskView) => void;
|
|
close: (taskId: string) => void;
|
|
}
|
|
|
|
export function usePoppedOutTasks(): UsePoppedOutTasksResult {
|
|
const [entries, setEntries] = useState<PoppedOutTaskEntry[]>([]);
|
|
|
|
const popOut = useCallback((task: Task | TaskDetail, originTaskView?: TaskView) => {
|
|
setEntries((current) => (current.some((entry) => entry.task.id === task.id) ? current : [...current, { task, originTaskView }]));
|
|
}, []);
|
|
|
|
const close = useCallback((taskId: string) => {
|
|
setEntries((current) => current.filter((entry) => entry.task.id !== taskId));
|
|
}, []);
|
|
|
|
/*
|
|
FNXC:TaskPopupViewGating 2026-07-13-00:00:
|
|
Popups store the TaskView where they were opened so the opt-in view gate can attach each modal to its originating Board/List surface. The snapshot stays in hook state while hidden; callers that only need legacy task snapshots can keep reading `tasks`.
|
|
*/
|
|
const tasks = useMemo(() => entries.map((entry) => entry.task), [entries]);
|
|
|
|
return { entries, tasks, popOut, close };
|
|
}
|