feat(web/mazenet): glide transitions for service fleet + inspector panels
This commit is contained in:
@@ -27,12 +27,14 @@ interface Props {
|
||||
onAddDecky?: (netId: string) => void;
|
||||
setSelection?: (sel: Selection) => void;
|
||||
pendingChanges?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Inspector: React.FC<Props> = ({
|
||||
selection, nets, nodes, edges, topologyStatus, onClose,
|
||||
onDeleteNet, onDeleteNode, onDeleteEdge, onRemoveService, onAddDecky, setSelection,
|
||||
pendingChanges = 0,
|
||||
className = '',
|
||||
}) => {
|
||||
const net = selection?.type === 'net' ? nets.find((n) => n.id === selection.id) : undefined;
|
||||
const node = selection?.type === 'node' ? nodes.find((n) => n.id === selection.id) : undefined;
|
||||
@@ -58,7 +60,7 @@ const Inspector: React.FC<Props> = ({
|
||||
const isObserved = node?.kind === 'observed';
|
||||
|
||||
return (
|
||||
<aside className="maze-inspector">
|
||||
<aside className={`maze-inspector ${className}`}>
|
||||
<div className="maze-inspector-title">
|
||||
<Crosshair size={12} className="violet-accent" />
|
||||
<span>INSPECTOR</span>
|
||||
|
||||
@@ -63,7 +63,21 @@ body.maze-fullscreen .maze-shell {
|
||||
min-height: 0;
|
||||
margin: 0 -32px -32px;
|
||||
border-top: 1px solid var(--border);
|
||||
transition: grid-template-columns 260ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.maze-palette,
|
||||
.maze-inspector {
|
||||
transition: opacity 200ms ease, transform 260ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-width: 0;
|
||||
}
|
||||
.maze-palette.collapsed,
|
||||
.maze-inspector.collapsed {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.maze-palette.collapsed { transform: translateX(-8px); }
|
||||
.maze-inspector.collapsed { transform: translateX(8px); }
|
||||
|
||||
/* ── Palette ────────────────────────────────── */
|
||||
.maze-palette {
|
||||
|
||||
@@ -626,12 +626,15 @@ const MazeNET: React.FC = () => {
|
||||
<div
|
||||
className="maze-shell"
|
||||
style={{
|
||||
gridTemplateColumns: `${paletteOpen ? '240px ' : ''}1fr${inspectorOpen ? ' 320px' : ''}`,
|
||||
gridTemplateColumns: `${paletteOpen ? '240px' : '0px'} 1fr ${inspectorOpen ? '320px' : '0px'}`,
|
||||
}}
|
||||
>
|
||||
{paletteOpen && (
|
||||
<Palette services={services} archetypes={archetypes} startPaletteDrag={interaction.startPaletteDrag} />
|
||||
)}
|
||||
<Palette
|
||||
services={services}
|
||||
archetypes={archetypes}
|
||||
startPaletteDrag={interaction.startPaletteDrag}
|
||||
className={paletteOpen ? '' : 'collapsed'}
|
||||
/>
|
||||
<Canvas
|
||||
ref={canvasRef}
|
||||
nets={nets}
|
||||
@@ -673,32 +676,31 @@ const MazeNET: React.FC = () => {
|
||||
{interaction.paletteDrag.label}
|
||||
</div>
|
||||
)}
|
||||
{inspectorOpen && (
|
||||
<Inspector
|
||||
selection={selection}
|
||||
setSelection={setSelection}
|
||||
nets={nets}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
topologyStatus={topoStatus}
|
||||
onClose={() => setInspectorOpen(false)}
|
||||
onDeleteNet={removeNet}
|
||||
onDeleteNode={removeNode}
|
||||
onDeleteEdge={removeEdge}
|
||||
onRemoveService={removeServiceFromNode}
|
||||
onAddDecky={(netId) => {
|
||||
const net = nets.find((n) => n.id === netId);
|
||||
if (!net) return;
|
||||
onPaletteDrop(
|
||||
{ kind: 'archetype', slug: archetypes[0]?.slug ?? 'deaddeck',
|
||||
services: archetypes[0]?.services.slice(0, 2) ?? [],
|
||||
label: archetypes[0]?.name ?? 'DECKY',
|
||||
clientX: 0, clientY: 0 },
|
||||
{ x: net.x + 40, y: net.y + 60 }, netId, null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Inspector
|
||||
selection={selection}
|
||||
setSelection={setSelection}
|
||||
nets={nets}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
topologyStatus={topoStatus}
|
||||
onClose={() => setInspectorOpen(false)}
|
||||
onDeleteNet={removeNet}
|
||||
onDeleteNode={removeNode}
|
||||
onDeleteEdge={removeEdge}
|
||||
onRemoveService={removeServiceFromNode}
|
||||
onAddDecky={(netId) => {
|
||||
const net = nets.find((n) => n.id === netId);
|
||||
if (!net) return;
|
||||
onPaletteDrop(
|
||||
{ kind: 'archetype', slug: archetypes[0]?.slug ?? 'deaddeck',
|
||||
services: archetypes[0]?.services.slice(0, 2) ?? [],
|
||||
label: archetypes[0]?.name ?? 'DECKY',
|
||||
clientX: 0, clientY: 0 },
|
||||
{ x: net.x + 40, y: net.y + 60 }, netId, null,
|
||||
);
|
||||
}}
|
||||
className={inspectorOpen ? '' : 'collapsed'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -22,9 +22,10 @@ interface Props {
|
||||
services: ServiceDef[];
|
||||
archetypes: Archetype[];
|
||||
startPaletteDrag: (d: Omit<PaletteDrag, 'clientX' | 'clientY'>, e: React.MouseEvent) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Palette: React.FC<Props> = ({ services, archetypes, startPaletteDrag }) => {
|
||||
const Palette: React.FC<Props> = ({ services, archetypes, startPaletteDrag, className = '' }) => {
|
||||
const start = (d: Omit<PaletteDrag, 'clientX' | 'clientY'>) =>
|
||||
(e: React.MouseEvent) => {
|
||||
if (e.button !== 0) return;
|
||||
@@ -33,7 +34,7 @@ const Palette: React.FC<Props> = ({ services, archetypes, startPaletteDrag }) =>
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="maze-palette">
|
||||
<div className={`maze-palette ${className}`}>
|
||||
<div className="palette-group">
|
||||
<label>① NETWORKS</label>
|
||||
<div className="palette-item" onMouseDown={start({ kind: 'network-subnet', slug: 'subnet', label: 'SUBNET' })}>
|
||||
|
||||
Reference in New Issue
Block a user