Promote nested pane to full row/column by dragging gutter past sibling

This commit is contained in:
megaproxy 2026-05-25 20:24:47 +01:00
parent dbd6c163c3
commit 150e5f09cb
5 changed files with 445 additions and 25 deletions

View file

@ -18,6 +18,7 @@ import {
type Orientation,
type LeafNode,
type LeafShellSpec,
type Box,
newLeaf,
splitLeaf,
closeLeaf,
@ -35,6 +36,7 @@ import {
updateSplitRatio,
swapLeaves,
findNeighborInDirection,
promoteFromGutter,
MIN_PANE_PX,
type Direction,
serialize,
@ -584,6 +586,18 @@ export default function App() {
setTree((t) => updateSplitRatio(t, splitId, ratio));
}, []);
// ---- promote-out gesture state -----------------------------------------
// armedPromotionBox is non-null while the user is mid-drag and past the
// 75% threshold on a sibling pane. We render a translucent preview at
// that position so the user knows what releasing will do.
const [armedPromotionBox, setArmedPromotionBox] = useState<Box | null>(null);
const onGutterArmedChange = useCallback((box: Box | null) => {
setArmedPromotionBox(box);
}, []);
const onGutterPromote = useCallback((splitId: NodeId) => {
setTree((t) => promoteFromGutter(t, splitId) ?? t);
}, []);
// ---- global broadcast state (derived from tree) -------------------------
const broadcastStats = useMemo(() => {
let on = 0;
@ -728,8 +742,25 @@ export default function App() {
info={g}
containerRef={paneWrapRef}
onRatioChange={onGutterRatio}
onArmedChange={onGutterArmedChange}
onPromote={onGutterPromote}
/>
))}
{armedPromotionBox && (
<div
className="promote-preview"
style={{
position: "absolute",
top: `${armedPromotionBox.top * 100}%`,
left: `${armedPromotionBox.left * 100}%`,
width: `${armedPromotionBox.width * 100}%`,
height: `${armedPromotionBox.height * 100}%`,
pointerEvents: "none",
zIndex: 20,
}}
aria-hidden="true"
/>
)}
</OrchestrationProvider>
)}
</div>