182 lines
5.2 KiB
TypeScript
182 lines
5.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import type { MobileWorkbenchSheetSnap } from "../components/mobile-workbench-sheet";
|
|
import { WORKBENCH_LAYOUT } from "../layout/workbench-layout";
|
|
|
|
const CONDITION_FEED_EXIT_MS = 170;
|
|
const CONDITION_FEED_EXPANDED_MIN_WIDTH = 1440;
|
|
const LARGE_SCREEN_QUERY = "(min-width: 1024px)";
|
|
|
|
type WorkbenchMobileSheet = "agent" | "condition" | "timeline" | null;
|
|
|
|
type UseWorkbenchResponsiveLayoutOptions = {
|
|
activeToolOpen: boolean;
|
|
agentPanelOpen: boolean;
|
|
devPanelOpen: boolean;
|
|
collapseAgentPanel: () => void;
|
|
expandAgentPanel: () => void;
|
|
onClearActiveTool: () => void;
|
|
};
|
|
|
|
export function useWorkbenchResponsiveLayout({
|
|
activeToolOpen,
|
|
agentPanelOpen,
|
|
devPanelOpen,
|
|
collapseAgentPanel,
|
|
expandAgentPanel,
|
|
onClearActiveTool
|
|
}: UseWorkbenchResponsiveLayoutOptions) {
|
|
const [isLargeScreen, setIsLargeScreen] = useState(false);
|
|
const [viewportWidth, setViewportWidth] = useState<number>(WORKBENCH_LAYOUT.desktopMinWidth);
|
|
const [agentPanelWidth, setAgentPanelWidth] = useState<number>(
|
|
WORKBENCH_LAYOUT.desktop.agentWidth
|
|
);
|
|
const [conditionFeedVisible, setConditionFeedVisible] = useState(false);
|
|
const [conditionFeedMounted, setConditionFeedMounted] = useState(false);
|
|
const [conditionFeedExpanded, setConditionFeedExpanded] = useState(false);
|
|
const [mobileSheet, setMobileSheet] = useState<WorkbenchMobileSheet>(null);
|
|
const [mobileSheetSnap, setMobileSheetSnap] = useState<MobileWorkbenchSheetSnap>("half");
|
|
const [agentCollapsedForCondition, setAgentCollapsedForCondition] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia(LARGE_SCREEN_QUERY);
|
|
const handleViewportChange = () => {
|
|
setIsLargeScreen(mediaQuery.matches);
|
|
setViewportWidth(window.innerWidth);
|
|
if (mediaQuery.matches) {
|
|
setMobileSheet(null);
|
|
}
|
|
};
|
|
|
|
handleViewportChange();
|
|
setConditionFeedVisible(false);
|
|
mediaQuery.addEventListener("change", handleViewportChange);
|
|
window.addEventListener("resize", handleViewportChange);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener("change", handleViewportChange);
|
|
window.removeEventListener("resize", handleViewportChange);
|
|
};
|
|
}, []);
|
|
|
|
const shouldShowConditionFeed = conditionFeedVisible && !activeToolOpen;
|
|
|
|
useEffect(() => {
|
|
if (shouldShowConditionFeed) {
|
|
setConditionFeedMounted(true);
|
|
return;
|
|
}
|
|
|
|
const timeoutId = window.setTimeout(() => {
|
|
setConditionFeedMounted(false);
|
|
}, CONDITION_FEED_EXIT_MS);
|
|
|
|
return () => window.clearTimeout(timeoutId);
|
|
}, [shouldShowConditionFeed]);
|
|
|
|
function closeMobileSheet() {
|
|
setMobileSheet(null);
|
|
}
|
|
|
|
function openAgentPanelForViewport() {
|
|
if (isLargeScreen) {
|
|
if (conditionFeedExpanded && viewportWidth < CONDITION_FEED_EXPANDED_MIN_WIDTH) {
|
|
setConditionFeedExpanded(false);
|
|
setAgentCollapsedForCondition(false);
|
|
}
|
|
expandAgentPanel();
|
|
return;
|
|
}
|
|
|
|
setMobileSheet("agent");
|
|
setMobileSheetSnap("half");
|
|
}
|
|
|
|
function handleConditionExpandedChange(nextExpanded: boolean) {
|
|
if (nextExpanded && isLargeScreen && viewportWidth < CONDITION_FEED_EXPANDED_MIN_WIDTH) {
|
|
setAgentCollapsedForCondition(agentPanelOpen);
|
|
collapseAgentPanel();
|
|
} else if (!nextExpanded && agentCollapsedForCondition) {
|
|
setAgentCollapsedForCondition(false);
|
|
expandAgentPanel();
|
|
}
|
|
setConditionFeedExpanded(nextExpanded);
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!isLargeScreen || shouldShowConditionFeed || !conditionFeedExpanded) {
|
|
return;
|
|
}
|
|
|
|
setConditionFeedExpanded(false);
|
|
if (agentCollapsedForCondition) {
|
|
setAgentCollapsedForCondition(false);
|
|
expandAgentPanel();
|
|
}
|
|
}, [
|
|
agentCollapsedForCondition,
|
|
conditionFeedExpanded,
|
|
expandAgentPanel,
|
|
isLargeScreen,
|
|
shouldShowConditionFeed
|
|
]);
|
|
|
|
function toggleConditionFeedForViewport() {
|
|
onClearActiveTool();
|
|
if (isLargeScreen) {
|
|
setConditionFeedVisible((current) => !current);
|
|
return;
|
|
}
|
|
|
|
if (mobileSheet === "condition") {
|
|
closeMobileSheet();
|
|
return;
|
|
}
|
|
|
|
setConditionFeedExpanded(false);
|
|
setMobileSheetSnap("half");
|
|
setMobileSheet("condition");
|
|
}
|
|
|
|
function openConditionFeedForViewport() {
|
|
setConditionFeedVisible(true);
|
|
onClearActiveTool();
|
|
if (!isLargeScreen) {
|
|
setMobileSheet("condition");
|
|
setMobileSheetSnap("half");
|
|
}
|
|
}
|
|
|
|
function openTimelineForViewport() {
|
|
if (isLargeScreen) return;
|
|
setMobileSheet("timeline");
|
|
setMobileSheetSnap("half");
|
|
}
|
|
|
|
const leftPanelOpen = isLargeScreen && agentPanelOpen;
|
|
const rightPanelOpen = isLargeScreen && (devPanelOpen || shouldShowConditionFeed);
|
|
const rightPanelExpanded =
|
|
isLargeScreen && shouldShowConditionFeed && conditionFeedExpanded && !devPanelOpen;
|
|
|
|
return {
|
|
agentPanelWidth,
|
|
closeMobileSheet,
|
|
conditionFeedExpanded,
|
|
conditionFeedMounted,
|
|
handleConditionExpandedChange,
|
|
isLargeScreen,
|
|
leftPanelOpen,
|
|
mobileSheet,
|
|
mobileSheetSnap,
|
|
openAgentPanelForViewport,
|
|
openConditionFeedForViewport,
|
|
openTimelineForViewport,
|
|
rightPanelExpanded,
|
|
rightPanelOpen,
|
|
setAgentPanelWidth,
|
|
setMobileSheetSnap,
|
|
shouldShowConditionFeed,
|
|
toggleConditionFeedForViewport,
|
|
viewportWidth
|
|
};
|
|
}
|