feat(web): MS23-P2-008 panel grid responsive layout
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
This commit is contained in:
@@ -1,27 +1,107 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { OrchestratorPanel } from "@/components/mission-control/OrchestratorPanel";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export interface PanelConfig {
|
||||
sessionId?: string;
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
interface MissionControlPanelProps {
|
||||
panels: readonly string[];
|
||||
panelSessionIds?: readonly (string | undefined)[];
|
||||
panels: PanelConfig[];
|
||||
onAddPanel: () => void;
|
||||
onRemovePanel: (index: number) => void;
|
||||
onExpandPanel: (index: number) => void;
|
||||
}
|
||||
|
||||
export const MIN_PANEL_COUNT = 1;
|
||||
export const MAX_PANEL_COUNT = 6;
|
||||
|
||||
export function MissionControlPanel({
|
||||
panels,
|
||||
panelSessionIds,
|
||||
onAddPanel,
|
||||
onRemovePanel,
|
||||
onExpandPanel,
|
||||
}: MissionControlPanelProps): React.JSX.Element {
|
||||
const expandedPanelIndex = panels.findIndex((panel) => panel.expanded);
|
||||
const expandedPanel = expandedPanelIndex >= 0 ? panels[expandedPanelIndex] : undefined;
|
||||
const canAddPanel = panels.length < MAX_PANEL_COUNT;
|
||||
const canRemovePanel = panels.length > MIN_PANEL_COUNT;
|
||||
|
||||
useEffect(() => {
|
||||
if (expandedPanelIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent): void => {
|
||||
if (event.key === "Escape") {
|
||||
onExpandPanel(expandedPanelIndex);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
|
||||
return (): void => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [expandedPanelIndex, onExpandPanel]);
|
||||
|
||||
return (
|
||||
<div className="grid h-full min-h-0 auto-rows-fr grid-cols-1 gap-4 overflow-y-auto pr-1 md:grid-cols-2">
|
||||
{panels.map((panelId, index) => {
|
||||
const sessionId = panelSessionIds?.[index];
|
||||
|
||||
if (sessionId === undefined) {
|
||||
return <OrchestratorPanel key={panelId} />;
|
||||
}
|
||||
|
||||
return <OrchestratorPanel key={panelId} sessionId={sessionId} />;
|
||||
})}
|
||||
<div className="flex h-full min-h-0 flex-col gap-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-sm font-medium text-muted-foreground">Panels</h2>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={onAddPanel}
|
||||
disabled={!canAddPanel}
|
||||
aria-label="Add panel"
|
||||
title={canAddPanel ? "Add panel" : "Maximum of 6 panels"}
|
||||
>
|
||||
<span aria-hidden="true" className="text-lg leading-none">
|
||||
+
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="min-h-0 flex-1">
|
||||
{expandedPanelIndex >= 0 && expandedPanel ? (
|
||||
<div className="h-full min-h-0">
|
||||
<OrchestratorPanel
|
||||
{...(expandedPanel.sessionId !== undefined
|
||||
? { sessionId: expandedPanel.sessionId }
|
||||
: {})}
|
||||
onClose={() => {
|
||||
onRemovePanel(expandedPanelIndex);
|
||||
}}
|
||||
closeDisabled={!canRemovePanel}
|
||||
onExpand={() => {
|
||||
onExpandPanel(expandedPanelIndex);
|
||||
}}
|
||||
expanded
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid h-full min-h-0 auto-rows-fr grid-cols-1 gap-4 overflow-y-auto pr-1 md:grid-cols-2 xl:grid-cols-3">
|
||||
{panels.map((panel, index) => (
|
||||
<OrchestratorPanel
|
||||
key={`panel-${String(index)}`}
|
||||
{...(panel.sessionId !== undefined ? { sessionId: panel.sessionId } : {})}
|
||||
onClose={() => {
|
||||
onRemovePanel(index);
|
||||
}}
|
||||
closeDisabled={!canRemovePanel}
|
||||
onExpand={() => {
|
||||
onExpandPanel(index);
|
||||
}}
|
||||
expanded={panel.expanded ?? false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user