ux(canvas): zoom controls float on the canvas' bottom-right corner
The canvas header runs out of room when the AI chat panel docks on the right — the board selector clipped and the zoom cluster was squeezed out entirely. The −/%/+ cluster now floats on the circuit canvas' bottom-right corner (always free), with stopPropagation so a click never starts a pan.
This commit is contained in:
parent
cd83813a9b
commit
f9a9657b5e
|
|
@ -264,6 +264,18 @@
|
||||||
height: 28px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Floating variant: pinned to the canvas' bottom-right corner (the header
|
||||||
|
got too tight once the AI chat panel docks on the right). Sits above the
|
||||||
|
canvas layers; dialogs/modals portal to <body> so they stay on top. */
|
||||||
|
.zoom-controls--floating {
|
||||||
|
position: absolute;
|
||||||
|
right: 14px;
|
||||||
|
bottom: 14px;
|
||||||
|
z-index: 60;
|
||||||
|
height: 32px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
.zoom-btn {
|
.zoom-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -2860,65 +2860,9 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="canvas-header-right">
|
<div className="canvas-header-right">
|
||||||
{/* Zoom controls */}
|
{/* Zoom moved to a floating cluster on the canvas'
|
||||||
<div className="zoom-controls">
|
bottom-right corner — the header gets too tight when the
|
||||||
<button
|
AI chat panel is docked. */}
|
||||||
className="zoom-btn"
|
|
||||||
onClick={() =>
|
|
||||||
handleWheel({
|
|
||||||
deltaY: 100,
|
|
||||||
clientX: 0,
|
|
||||||
clientY: 0,
|
|
||||||
preventDefault: () => {},
|
|
||||||
} as any)
|
|
||||||
}
|
|
||||||
title={t('editor.canvas.zoomOut')}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
>
|
|
||||||
<line x1="5" y1="12" x2="19" y2="12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="zoom-level"
|
|
||||||
onClick={handleResetView}
|
|
||||||
title={t('editor.canvas.resetView')}
|
|
||||||
>
|
|
||||||
{Math.round(zoom * 100)}%
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="zoom-btn"
|
|
||||||
onClick={() =>
|
|
||||||
handleWheel({
|
|
||||||
deltaY: -100,
|
|
||||||
clientX: 0,
|
|
||||||
clientY: 0,
|
|
||||||
preventDefault: () => {},
|
|
||||||
} as any)
|
|
||||||
}
|
|
||||||
title={t('editor.canvas.zoomIn')}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
>
|
|
||||||
<line x1="12" y1="5" x2="12" y2="19" />
|
|
||||||
<line x1="5" y1="12" x2="19" y2="12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Component count */}
|
{/* Component count */}
|
||||||
<span
|
<span
|
||||||
|
|
@ -3224,6 +3168,73 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
|
{/* Floating zoom controls — pinned to the canvas' bottom-right
|
||||||
|
corner (they lived in the canvas header, which gets too tight
|
||||||
|
when the AI chat panel is docked; the corner is always free).
|
||||||
|
stopPropagation so a click never starts a canvas pan/selection. */}
|
||||||
|
<div
|
||||||
|
className="zoom-controls zoom-controls--floating"
|
||||||
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="zoom-btn"
|
||||||
|
onClick={() =>
|
||||||
|
handleWheel({
|
||||||
|
deltaY: 100,
|
||||||
|
clientX: 0,
|
||||||
|
clientY: 0,
|
||||||
|
preventDefault: () => {},
|
||||||
|
} as any)
|
||||||
|
}
|
||||||
|
title={t('editor.canvas.zoomOut')}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="14"
|
||||||
|
height="14"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="zoom-level"
|
||||||
|
onClick={handleResetView}
|
||||||
|
title={t('editor.canvas.resetView')}
|
||||||
|
>
|
||||||
|
{Math.round(zoom * 100)}%
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="zoom-btn"
|
||||||
|
onClick={() =>
|
||||||
|
handleWheel({
|
||||||
|
deltaY: -100,
|
||||||
|
clientX: 0,
|
||||||
|
clientY: 0,
|
||||||
|
preventDefault: () => {},
|
||||||
|
} as any)
|
||||||
|
}
|
||||||
|
title={t('editor.canvas.zoomIn')}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="14"
|
||||||
|
height="14"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
>
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19" />
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue