editor: group resize handles for multi-select
This commit is contained in:
parent
cdcc9d049b
commit
510d478be3
@ -48,6 +48,25 @@ export function Canvas(props: CanvasProps) {
|
||||
|
||||
const bounds = useMemo(() => ({ w: props.screen.width, h: props.screen.height }), [props.screen.width, props.screen.height]);
|
||||
|
||||
const resizableSelected = useMemo(() => {
|
||||
return props.screen.nodes.filter(
|
||||
(n) => props.selectionIds.includes(n.id) && !n.locked && !n.hidden,
|
||||
);
|
||||
}, [props.screen.nodes, props.selectionIds]);
|
||||
|
||||
const groupResize = useMemo(() => {
|
||||
if (resizableSelected.length < 2) return null;
|
||||
const rects = resizableSelected.map((n) => n.rect);
|
||||
const x1 = Math.min(...rects.map((r) => r.x));
|
||||
const y1 = Math.min(...rects.map((r) => r.y));
|
||||
const x2 = Math.max(...rects.map((r) => r.x + r.w));
|
||||
const y2 = Math.max(...rects.map((r) => r.y + r.h));
|
||||
return {
|
||||
targetId: resizableSelected[0]!.id,
|
||||
rect: { x: x1, y: y1, w: x2 - x1, h: y2 - y1 },
|
||||
};
|
||||
}, [resizableSelected]);
|
||||
|
||||
const clientToCanvas = useCallback((clientX: number, clientY: number) => {
|
||||
const el = ref.current;
|
||||
if (!el) return null;
|
||||
@ -325,6 +344,7 @@ export function Canvas(props: CanvasProps) {
|
||||
key={node.id}
|
||||
node={node}
|
||||
selected={props.selectionIds.includes(node.id)}
|
||||
selectionCount={props.selectionIds.length}
|
||||
onPointerDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@ -368,6 +388,17 @@ export function Canvas(props: CanvasProps) {
|
||||
/>
|
||||
))}
|
||||
|
||||
{groupResize ? (
|
||||
<GroupSelectionResizeBox
|
||||
rect={groupResize.rect}
|
||||
onPointerDown={(e, handle) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
props.onBeginResize(e, groupResize.targetId, handle);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{box && (
|
||||
<div
|
||||
style={{
|
||||
@ -391,6 +422,7 @@ export function Canvas(props: CanvasProps) {
|
||||
function NodeView(props: {
|
||||
node: WidgetNode;
|
||||
selected: boolean;
|
||||
selectionCount: number;
|
||||
onPointerDown: (e: React.PointerEvent) => void;
|
||||
onContextMenu: (e: React.MouseEvent) => void;
|
||||
onResizePointerDown: (e: React.PointerEvent, handle: ResizeHandle) => void;
|
||||
@ -565,7 +597,35 @@ function NodeView(props: {
|
||||
}
|
||||
})()}
|
||||
|
||||
{props.selected && !node.locked && !node.hidden && <ResizeHandles onPointerDown={props.onResizePointerDown} />}
|
||||
{props.selected &&
|
||||
props.selectionCount === 1 &&
|
||||
!node.locked &&
|
||||
!node.hidden && <ResizeHandles onPointerDown={props.onResizePointerDown} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GroupSelectionResizeBox(props: {
|
||||
rect: { x: number; y: number; w: number; h: number };
|
||||
onPointerDown: (e: React.PointerEvent, handle: ResizeHandle) => void;
|
||||
}) {
|
||||
const r = props.rect;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: r.x,
|
||||
top: r.y,
|
||||
width: r.w,
|
||||
height: r.h,
|
||||
border: '1px solid rgba(24,144,255,0.9)',
|
||||
boxShadow: '0 0 0 2px rgba(24,144,255,0.18)',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ position: 'absolute', inset: 0, pointerEvents: 'auto' }}>
|
||||
<ResizeHandles onPointerDown={props.onPointerDown} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user