feat(node-resizer): add flag to keep aspect ratio, cleanup example
This commit is contained in:
@@ -10,10 +10,21 @@ const controlStyle = {
|
|||||||
border: 'none',
|
border: 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ id, data }) => {
|
const CustomResizerNode: FC<NodeProps> = ({ data }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NodeResizeControl style={controlStyle}>
|
<NodeResizeControl
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
style={controlStyle}
|
||||||
|
>
|
||||||
<ResizeIcon />
|
<ResizeIcon />
|
||||||
</NodeResizeControl>
|
</NodeResizeControl>
|
||||||
|
|
||||||
@@ -24,4 +35,4 @@ const CustomNode: FC<NodeProps> = ({ id, data }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(CustomNode);
|
export default memo(CustomResizerNode);
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { memo, FC } from 'react';
|
|
||||||
import { Handle, Position, NodeProps } from 'reactflow';
|
|
||||||
|
|
||||||
import { NodeResizeControl } from '@reactflow/node-resizer';
|
|
||||||
import '@reactflow/node-resizer/dist/style.css';
|
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ id, data }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NodeResizeControl color="red" position={Position.Top} />
|
|
||||||
<NodeResizeControl color="red" position={Position.Bottom} />
|
|
||||||
<Handle type="target" position={Position.Left} />
|
|
||||||
<div style={{ padding: 10 }}>{data.label}</div>
|
|
||||||
<Handle type="source" position={Position.Right} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(CustomNode);
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { memo, FC } from 'react';
|
|
||||||
import { Handle, Position, NodeProps } from 'reactflow';
|
|
||||||
|
|
||||||
import { NodeResizeControl, OnResize, ShouldResize } from '@reactflow/node-resizer';
|
|
||||||
import '@reactflow/node-resizer/dist/style.css';
|
|
||||||
|
|
||||||
const shouldResize: ShouldResize = (event, params) => {
|
|
||||||
console.log('before resize', params);
|
|
||||||
|
|
||||||
if (params.width > 100) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResize: OnResize = (event, params) => {
|
|
||||||
console.log('resize', params.direction);
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ id, data }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NodeResizeControl color="red" position={Position.Left} />
|
|
||||||
<NodeResizeControl color="red" position={Position.Right} shouldResize={shouldResize} onResize={onResize} />
|
|
||||||
<Handle type="target" position={Position.Top} />
|
|
||||||
<div style={{ padding: 10 }}>{data.label}</div>
|
|
||||||
<Handle type="source" position={Position.Bottom} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(CustomNode);
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { memo, FC } from 'react';
|
||||||
|
import { Handle, Position, NodeProps } from 'reactflow';
|
||||||
|
import { NodeResizer } from '@reactflow/node-resizer';
|
||||||
|
|
||||||
|
import '@reactflow/node-resizer/dist/style.css';
|
||||||
|
|
||||||
|
const DefaultResizerNode: FC<NodeProps> = ({ data, selected }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NodeResizer
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
isVisible={data.isVisible ?? selected}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
/>
|
||||||
|
<Handle type="target" position={Position.Left} />
|
||||||
|
<div>{data.label}</div>
|
||||||
|
<Handle type="source" position={Position.Right} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DefaultResizerNode);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { memo, FC } from 'react';
|
||||||
|
import { Handle, Position, NodeProps } from 'reactflow';
|
||||||
|
import { NodeResizeControl } from '@reactflow/node-resizer';
|
||||||
|
|
||||||
|
import '@reactflow/node-resizer/dist/style.css';
|
||||||
|
|
||||||
|
const HorizontalResizerNode: FC<NodeProps> = ({ data }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NodeResizeControl
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
color="red"
|
||||||
|
position={Position.Left}
|
||||||
|
/>
|
||||||
|
<NodeResizeControl
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
color="red"
|
||||||
|
position={Position.Right}
|
||||||
|
/>
|
||||||
|
<Handle type="target" position={Position.Top} />
|
||||||
|
<div style={{ padding: 10 }}>{data.label}</div>
|
||||||
|
<Handle type="source" position={Position.Bottom} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(HorizontalResizerNode);
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { memo, FC } from 'react';
|
|
||||||
import { Handle, Position, NodeProps } from 'reactflow';
|
|
||||||
|
|
||||||
import { NodeResizer, ShouldResize, OnResize, OnResizeEnd, OnResizeStart } from '@reactflow/node-resizer';
|
|
||||||
import '@reactflow/node-resizer/dist/style.css';
|
|
||||||
|
|
||||||
const onResizeStart: OnResizeStart = (_, params) => {
|
|
||||||
console.log('resize start', params);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResize: OnResize = (_, params) => {
|
|
||||||
console.log('resize', params);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResizeEnd: OnResizeEnd = (_, params) => {
|
|
||||||
console.log('resize end', params);
|
|
||||||
};
|
|
||||||
|
|
||||||
const shouldResize: ShouldResize = (_, params) => {
|
|
||||||
console.log('should resize', params);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ data, selected }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NodeResizer
|
|
||||||
minWidth={100}
|
|
||||||
minHeight={100}
|
|
||||||
isVisible={selected}
|
|
||||||
shouldResize={shouldResize}
|
|
||||||
onResizeStart={onResizeStart}
|
|
||||||
onResize={onResize}
|
|
||||||
onResizeEnd={onResizeEnd}
|
|
||||||
/>
|
|
||||||
<Handle type="target" position={Position.Left} />
|
|
||||||
<div>{data.label}</div>
|
|
||||||
<Handle type="source" position={Position.Right} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(CustomNode);
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { memo, FC } from 'react';
|
||||||
|
import { Handle, Position, NodeProps } from 'reactflow';
|
||||||
|
|
||||||
|
import { NodeResizeControl } from '@reactflow/node-resizer';
|
||||||
|
import '@reactflow/node-resizer/dist/style.css';
|
||||||
|
|
||||||
|
const CustomNode: FC<NodeProps> = ({ id, data }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NodeResizeControl
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
color="red"
|
||||||
|
position={Position.Top}
|
||||||
|
/>
|
||||||
|
<NodeResizeControl
|
||||||
|
minWidth={data.minWidth ?? undefined}
|
||||||
|
maxWidth={data.maxWidth ?? undefined}
|
||||||
|
minHeight={data.minHeight ?? undefined}
|
||||||
|
maxHeight={data.maxHeight ?? undefined}
|
||||||
|
shouldResize={data.shouldResize ?? undefined}
|
||||||
|
onResizeStart={data.onResizeStart ?? undefined}
|
||||||
|
onResize={data.onResize ?? undefined}
|
||||||
|
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||||
|
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||||
|
color="red"
|
||||||
|
position={Position.Bottom}
|
||||||
|
/>
|
||||||
|
<Handle type="target" position={Position.Left} />
|
||||||
|
<div style={{ padding: 10 }}>{data.label}</div>
|
||||||
|
<Handle type="source" position={Position.Right} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(CustomNode);
|
||||||
@@ -1,73 +1,97 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import ReactFlow, { Controls, addEdge, Position, Connection, useNodesState, useEdgesState, Panel } from 'reactflow';
|
import ReactFlow, { Controls, addEdge, Connection, useNodesState, useEdgesState, Panel, Node, Edge } from 'reactflow';
|
||||||
|
|
||||||
import NodeResizerNode from './NodeResizerNode';
|
import DefaultResizer from './DefaultResizer';
|
||||||
import CustomResizer from './CustomResizer';
|
import CustomResizer from './CustomResizer';
|
||||||
import CustomResizer2 from './CustomResizer2';
|
import VerticalResizer from './VerticalResizer';
|
||||||
import CustomResizer3 from './CustomResizer3';
|
import HorizontalResizer from './HorizontalResizer';
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
resizer: NodeResizerNode,
|
defaultResizer: DefaultResizer,
|
||||||
customResizer: CustomResizer,
|
customResizer: CustomResizer,
|
||||||
customResizer2: CustomResizer2,
|
verticalResizer: VerticalResizer,
|
||||||
customResizer3: CustomResizer3,
|
horizontalResizer: HorizontalResizer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialEdges = [
|
const nodeStyle = {
|
||||||
{
|
border: '1px solid #222',
|
||||||
id: 'e1-2',
|
fontSize: 10,
|
||||||
source: '1',
|
backgroundColor: '#ddd',
|
||||||
target: '2',
|
};
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const initialNodes = [
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
|
const initialNodes: Node[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'defaultResizer',
|
||||||
data: { label: 'An input node' },
|
data: { label: 'default resizer' },
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
sourcePosition: Position.Right,
|
style: { ...nodeStyle },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '1a',
|
||||||
type: 'resizer',
|
type: 'defaultResizer',
|
||||||
data: { label: 'default resizer' },
|
data: {
|
||||||
|
label: 'default resizer with min and max dimensions',
|
||||||
|
minWidth: 100,
|
||||||
|
minHeight: 80,
|
||||||
|
maxWidth: 200,
|
||||||
|
maxHeight: 200,
|
||||||
|
},
|
||||||
|
position: { x: 0, y: 60 },
|
||||||
|
style: { ...nodeStyle, width: 100, height: 80 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1b',
|
||||||
|
type: 'defaultResizer',
|
||||||
|
data: { label: 'default resizer with initial size and aspect ratio', keepAspectRatio: true },
|
||||||
position: { x: 250, y: 0 },
|
position: { x: 250, y: 0 },
|
||||||
style: {
|
style: {
|
||||||
width: 200,
|
width: 174,
|
||||||
height: 150,
|
height: 123,
|
||||||
border: '1px solid #222',
|
...nodeStyle,
|
||||||
fontSize: 10,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '2',
|
||||||
type: 'customResizer',
|
type: 'customResizer',
|
||||||
data: { label: 'resize control with child component' },
|
data: { label: 'custom resize icon' },
|
||||||
|
position: { x: 0, y: 200 },
|
||||||
|
style: { width: 100, height: 60, ...nodeStyle },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
type: 'verticalResizer',
|
||||||
|
data: { label: 'vertical resizer' },
|
||||||
position: { x: 250, y: 200 },
|
position: { x: 250, y: 200 },
|
||||||
style: { border: '1px solid #222', fontSize: 10, width: 100 },
|
style: { ...nodeStyle },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3a',
|
||||||
|
type: 'verticalResizer',
|
||||||
|
data: {
|
||||||
|
label: 'vertical resizer with min/maxHeight and aspect ratio',
|
||||||
|
minHeight: 50,
|
||||||
|
maxHeight: 200,
|
||||||
|
keepAspectRatio: true,
|
||||||
|
},
|
||||||
|
position: { x: 400, y: 200 },
|
||||||
|
style: { ...nodeStyle, height: 50 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
type: 'customResizer2',
|
type: 'horizontalResizer',
|
||||||
data: { label: 'resize controls' },
|
data: { label: 'horizontal resizer with aspect ratio', keepAspectRatio: true },
|
||||||
position: { x: 100, y: 150 },
|
position: { x: 250, y: 300 },
|
||||||
style: { border: '1px solid #222', fontSize: 10 },
|
style: { ...nodeStyle },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '4a',
|
||||||
type: 'customResizer2',
|
type: 'horizontalResizer',
|
||||||
data: { label: 'min width and height' },
|
data: { label: 'horizontal resizer with maxWidth', maxWidth: 300 },
|
||||||
position: { x: 250, y: 250 },
|
position: { x: 250, y: 400 },
|
||||||
style: { border: '1px solid #222', fontSize: 10 },
|
style: { ...nodeStyle },
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
type: 'customResizer3',
|
|
||||||
data: { label: 'resize controls' },
|
|
||||||
position: { x: 400, y: 200 },
|
|
||||||
style: { border: '1px solid #222', fontSize: 10 },
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/E
|
|||||||
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
||||||
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
||||||
|
|
||||||
export { internalsSymbol, rectToBox, boxToRect, getBoundsOfRects } from './utils';
|
export { internalsSymbol, rectToBox, boxToRect, getBoundsOfRects, clamp } from './utils';
|
||||||
export {
|
export {
|
||||||
isNode,
|
isNode,
|
||||||
isEdge,
|
isEdge,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default function NodeResizer({
|
|||||||
minHeight = 10,
|
minHeight = 10,
|
||||||
maxWidth = Number.MAX_VALUE,
|
maxWidth = Number.MAX_VALUE,
|
||||||
maxHeight = Number.MAX_VALUE,
|
maxHeight = Number.MAX_VALUE,
|
||||||
|
keepAspectRatio = false,
|
||||||
shouldResize,
|
shouldResize,
|
||||||
onResizeStart,
|
onResizeStart,
|
||||||
onResize,
|
onResize,
|
||||||
@@ -41,6 +42,7 @@ export default function NodeResizer({
|
|||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
maxHeight={maxHeight}
|
maxHeight={maxHeight}
|
||||||
onResizeStart={onResizeStart}
|
onResizeStart={onResizeStart}
|
||||||
|
keepAspectRatio={keepAspectRatio}
|
||||||
shouldResize={shouldResize}
|
shouldResize={shouldResize}
|
||||||
onResize={onResize}
|
onResize={onResize}
|
||||||
onResizeEnd={onResizeEnd}
|
onResizeEnd={onResizeEnd}
|
||||||
@@ -59,6 +61,7 @@ export default function NodeResizer({
|
|||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
maxHeight={maxHeight}
|
maxHeight={maxHeight}
|
||||||
onResizeStart={onResizeStart}
|
onResizeStart={onResizeStart}
|
||||||
|
keepAspectRatio={keepAspectRatio}
|
||||||
shouldResize={shouldResize}
|
shouldResize={shouldResize}
|
||||||
onResize={onResize}
|
onResize={onResize}
|
||||||
onResizeEnd={onResizeEnd}
|
onResizeEnd={onResizeEnd}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
NodeDimensionChange,
|
NodeDimensionChange,
|
||||||
useNodeId,
|
useNodeId,
|
||||||
NodePositionChange,
|
NodePositionChange,
|
||||||
|
clamp,
|
||||||
} from '@reactflow/core';
|
} from '@reactflow/core';
|
||||||
|
|
||||||
import { ResizeDragEvent, ResizeControlProps, ResizeControlLineProps, ResizeControlVariant } from './types';
|
import { ResizeDragEvent, ResizeControlProps, ResizeControlLineProps, ResizeControlVariant } from './types';
|
||||||
@@ -20,6 +21,7 @@ const initStartValues = {
|
|||||||
...initPrevValues,
|
...initPrevValues,
|
||||||
pointerX: 0,
|
pointerX: 0,
|
||||||
pointerY: 0,
|
pointerY: 0,
|
||||||
|
aspectRatio: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ResizeControl({
|
function ResizeControl({
|
||||||
@@ -34,6 +36,7 @@ function ResizeControl({
|
|||||||
minHeight = 10,
|
minHeight = 10,
|
||||||
maxWidth = Number.MAX_VALUE,
|
maxWidth = Number.MAX_VALUE,
|
||||||
maxHeight = Number.MAX_VALUE,
|
maxHeight = Number.MAX_VALUE,
|
||||||
|
keepAspectRatio = false,
|
||||||
shouldResize,
|
shouldResize,
|
||||||
onResizeStart,
|
onResizeStart,
|
||||||
onResize,
|
onResize,
|
||||||
@@ -55,6 +58,12 @@ function ResizeControl({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selection = select(resizeControlRef.current);
|
const selection = select(resizeControlRef.current);
|
||||||
|
|
||||||
|
const enableX = controlPosition.includes('right') || controlPosition.includes('left');
|
||||||
|
const enableY = controlPosition.includes('bottom') || controlPosition.includes('top');
|
||||||
|
const invertX = controlPosition.includes('left');
|
||||||
|
const invertY = controlPosition.includes('top');
|
||||||
|
|
||||||
const dragHandler = drag<HTMLDivElement, unknown>()
|
const dragHandler = drag<HTMLDivElement, unknown>()
|
||||||
.on('start', (event: ResizeDragEvent) => {
|
.on('start', (event: ResizeDragEvent) => {
|
||||||
const node = store.getState().nodeInternals.get(id);
|
const node = store.getState().nodeInternals.get(id);
|
||||||
@@ -71,6 +80,7 @@ function ResizeControl({
|
|||||||
...prevValues.current,
|
...prevValues.current,
|
||||||
pointerX: xSnapped,
|
pointerX: xSnapped,
|
||||||
pointerY: ySnapped,
|
pointerY: ySnapped,
|
||||||
|
aspectRatio: prevValues.current.width / prevValues.current.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
onResizeStart?.(event, { ...prevValues.current });
|
onResizeStart?.(event, { ...prevValues.current });
|
||||||
@@ -79,10 +89,6 @@ function ResizeControl({
|
|||||||
const { nodeInternals, triggerNodeChanges } = store.getState();
|
const { nodeInternals, triggerNodeChanges } = store.getState();
|
||||||
const { xSnapped, ySnapped } = getPointerPosition(event);
|
const { xSnapped, ySnapped } = getPointerPosition(event);
|
||||||
const node = nodeInternals.get(id);
|
const node = nodeInternals.get(id);
|
||||||
const enableX = controlPosition.includes('right') || controlPosition.includes('left');
|
|
||||||
const enableY = controlPosition.includes('bottom') || controlPosition.includes('top');
|
|
||||||
const invertX = controlPosition.includes('left');
|
|
||||||
const invertY = controlPosition.includes('top');
|
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
const changes: NodeChange[] = [];
|
const changes: NodeChange[] = [];
|
||||||
@@ -93,13 +99,29 @@ function ResizeControl({
|
|||||||
height: startHeight,
|
height: startHeight,
|
||||||
x: startNodeX,
|
x: startNodeX,
|
||||||
y: startNodeY,
|
y: startNodeY,
|
||||||
|
aspectRatio,
|
||||||
} = startValues.current;
|
} = startValues.current;
|
||||||
|
|
||||||
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.current;
|
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.current;
|
||||||
const distX = Math.floor(enableX ? xSnapped - startX : 0);
|
const distX = Math.floor(enableX ? xSnapped - startX : 0);
|
||||||
const distY = Math.floor(enableY ? ySnapped - startY : 0);
|
const distY = Math.floor(enableY ? ySnapped - startY : 0);
|
||||||
const width = Math.min(Math.max(startWidth + (invertX ? -distX : distX), minWidth), maxWidth);
|
|
||||||
const height = Math.min(Math.max(startHeight + (invertY ? -distY : distY), minHeight), maxHeight);
|
let width = startWidth + (invertX ? -distX : distX);
|
||||||
|
let height = startHeight + (invertY ? -distY : distY);
|
||||||
|
|
||||||
|
width = clamp(width, minWidth, maxWidth);
|
||||||
|
height = clamp(height, minHeight, maxHeight);
|
||||||
|
|
||||||
|
if (keepAspectRatio) {
|
||||||
|
const nextAspectRatio = width / height;
|
||||||
|
const isDiagonal = enableX && enableY;
|
||||||
|
const isHorizontal = enableX && !enableY;
|
||||||
|
const isVertical = enableY && !enableX;
|
||||||
|
|
||||||
|
width = (nextAspectRatio <= aspectRatio && isDiagonal) || isVertical ? height * aspectRatio : width;
|
||||||
|
height = (nextAspectRatio > aspectRatio && isDiagonal) || isHorizontal ? width / aspectRatio : height;
|
||||||
|
}
|
||||||
|
|
||||||
const isWidthChange = width !== prevWidth;
|
const isWidthChange = width !== prevWidth;
|
||||||
const isHeightChange = height !== prevHeight;
|
const isHeightChange = height !== prevHeight;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export type NodeResizerProps = {
|
|||||||
minHeight?: number;
|
minHeight?: number;
|
||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
maxHeight?: number;
|
maxHeight?: number;
|
||||||
|
keepAspectRatio?: boolean;
|
||||||
shouldResize?: ShouldResize;
|
shouldResize?: ShouldResize;
|
||||||
onResizeStart?: OnResizeStart;
|
onResizeStart?: OnResizeStart;
|
||||||
onResize?: OnResize;
|
onResize?: OnResize;
|
||||||
@@ -48,7 +49,17 @@ export enum ResizeControlVariant {
|
|||||||
|
|
||||||
export type ResizeControlProps = Pick<
|
export type ResizeControlProps = Pick<
|
||||||
NodeResizerProps,
|
NodeResizerProps,
|
||||||
'nodeId' | 'color' | 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight' | 'shouldResize' | 'onResizeStart' | 'onResize' | 'onResizeEnd'
|
| 'nodeId'
|
||||||
|
| 'color'
|
||||||
|
| 'minWidth'
|
||||||
|
| 'minHeight'
|
||||||
|
| 'maxWidth'
|
||||||
|
| 'maxHeight'
|
||||||
|
| 'keepAspectRatio'
|
||||||
|
| 'shouldResize'
|
||||||
|
| 'onResizeStart'
|
||||||
|
| 'onResize'
|
||||||
|
| 'onResizeEnd'
|
||||||
> & {
|
> & {
|
||||||
position?: ControlPosition;
|
position?: ControlPosition;
|
||||||
variant?: ResizeControlVariant;
|
variant?: ResizeControlVariant;
|
||||||
|
|||||||
Reference in New Issue
Block a user