@xyflow/system 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/esm/index.js +109 -93
  2. package/dist/esm/index.mjs +109 -93
  3. package/dist/esm/types/edges.d.ts +2 -2
  4. package/dist/esm/types/edges.d.ts.map +1 -1
  5. package/dist/esm/types/nodes.d.ts +12 -11
  6. package/dist/esm/types/nodes.d.ts.map +1 -1
  7. package/dist/esm/utils/edges/general.d.ts +2 -2
  8. package/dist/esm/utils/edges/general.d.ts.map +1 -1
  9. package/dist/esm/utils/edges/positions.d.ts.map +1 -1
  10. package/dist/esm/utils/general.d.ts +5 -0
  11. package/dist/esm/utils/general.d.ts.map +1 -1
  12. package/dist/esm/utils/graph.d.ts +6 -6
  13. package/dist/esm/utils/graph.d.ts.map +1 -1
  14. package/dist/esm/xydrag/XYDrag.d.ts +1 -2
  15. package/dist/esm/xydrag/XYDrag.d.ts.map +1 -1
  16. package/dist/esm/xyhandle/XYHandle.d.ts.map +1 -1
  17. package/dist/esm/xyhandle/utils.d.ts +0 -1
  18. package/dist/esm/xyhandle/utils.d.ts.map +1 -1
  19. package/dist/esm/xyresizer/XYResizer.d.ts +2 -1
  20. package/dist/esm/xyresizer/XYResizer.d.ts.map +1 -1
  21. package/dist/esm/xyresizer/utils.d.ts +5 -3
  22. package/dist/esm/xyresizer/utils.d.ts.map +1 -1
  23. package/dist/umd/index.js +1 -1
  24. package/dist/umd/types/edges.d.ts +2 -2
  25. package/dist/umd/types/edges.d.ts.map +1 -1
  26. package/dist/umd/types/nodes.d.ts +12 -11
  27. package/dist/umd/types/nodes.d.ts.map +1 -1
  28. package/dist/umd/utils/edges/general.d.ts +2 -2
  29. package/dist/umd/utils/edges/general.d.ts.map +1 -1
  30. package/dist/umd/utils/edges/positions.d.ts.map +1 -1
  31. package/dist/umd/utils/general.d.ts +5 -0
  32. package/dist/umd/utils/general.d.ts.map +1 -1
  33. package/dist/umd/utils/graph.d.ts +6 -6
  34. package/dist/umd/utils/graph.d.ts.map +1 -1
  35. package/dist/umd/xydrag/XYDrag.d.ts +1 -2
  36. package/dist/umd/xydrag/XYDrag.d.ts.map +1 -1
  37. package/dist/umd/xyhandle/XYHandle.d.ts.map +1 -1
  38. package/dist/umd/xyhandle/utils.d.ts +0 -1
  39. package/dist/umd/xyhandle/utils.d.ts.map +1 -1
  40. package/dist/umd/xyresizer/XYResizer.d.ts +2 -1
  41. package/dist/umd/xyresizer/XYResizer.d.ts.map +1 -1
  42. package/dist/umd/xyresizer/utils.d.ts +5 -3
  43. package/dist/umd/xyresizer/utils.d.ts.map +1 -1
  44. package/package.json +3 -3
package/dist/esm/index.js CHANGED
@@ -173,8 +173,9 @@ const getNodePositionWithOrigin = (node, nodeOrigin = [0, 0]) => {
173
173
  },
174
174
  };
175
175
  }
176
- const offsetX = (node.computed?.width ?? node.width ?? 0) * nodeOrigin[0];
177
- const offsetY = (node.computed?.height ?? node.height ?? 0) * nodeOrigin[1];
176
+ const { width, height } = getNodeDimensions(node);
177
+ const offsetX = width * nodeOrigin[0];
178
+ const offsetY = height * nodeOrigin[1];
178
179
  const position = {
179
180
  x: node.position.x - offsetX,
180
181
  y: node.position.y - offsetY,
@@ -206,8 +207,7 @@ const getNodesBounds = (nodes, params = { nodeOrigin: [0, 0], useRelativePositio
206
207
  const nodePos = getNodePositionWithOrigin(node, node.origin || params.nodeOrigin);
207
208
  return getBoundsOfBoxes(currBox, rectToBox({
208
209
  ...nodePos[params.useRelativePosition ? 'position' : 'positionAbsolute'],
209
- width: node.computed?.width ?? node.width ?? 0,
210
- height: node.computed?.height ?? node.height ?? 0,
210
+ ...getNodeDimensions(node),
211
211
  }));
212
212
  }, { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity });
213
213
  return boxToRect(box);
@@ -222,8 +222,8 @@ excludeNonSelectableNodes = false, nodeOrigin = [0, 0]) => {
222
222
  };
223
223
  const visibleNodes = nodes.reduce((res, node) => {
224
224
  const { computed, selectable = true, hidden = false } = node;
225
- const width = computed?.width ?? node.width ?? null;
226
- const height = computed?.height ?? node.height ?? null;
225
+ const width = computed?.width ?? node.width ?? node.initialWidth ?? null;
226
+ const height = computed?.height ?? node.height ?? node.initialHeight ?? null;
227
227
  if ((excludeNonSelectableNodes && !selectable) || hidden) {
228
228
  return res;
229
229
  }
@@ -517,6 +517,16 @@ const isMacOs = () => typeof navigator !== 'undefined' && navigator?.userAgent?.
517
517
  function isCoordinateExtent(extent) {
518
518
  return extent !== undefined && extent !== 'parent';
519
519
  }
520
+ function getNodeDimensions(node) {
521
+ return {
522
+ width: node.computed?.width ?? node.width ?? node.initialWidth ?? 0,
523
+ height: node.computed?.height ?? node.height ?? node.initialHeight ?? 0,
524
+ };
525
+ }
526
+ function nodeHasDimensions(node) {
527
+ return ((node.computed?.width ?? node.width ?? node.initialWidth) !== undefined &&
528
+ (node.computed?.height ?? node.height ?? node.initialHeight) !== undefined);
529
+ }
520
530
 
521
531
  function getPointerPosition(event, { snapGrid = [0, 0], snapToGrid = false, transform }) {
522
532
  const { x, y } = getEventPosition(event);
@@ -838,8 +848,8 @@ function getPoints({ source, sourcePosition = Position.Bottom, target, targetPos
838
848
  });
839
849
  // opposite handle positions, default case
840
850
  if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
841
- centerX = center.x || defaultCenterX;
842
- centerY = center.y || defaultCenterY;
851
+ centerX = center.x ?? defaultCenterX;
852
+ centerY = center.y ?? defaultCenterY;
843
853
  // --->
844
854
  // |
845
855
  // >---
@@ -983,7 +993,8 @@ function getSmoothStepPath({ sourceX, sourceY, sourcePosition = Position.Bottom,
983
993
  }
984
994
 
985
995
  function isNodeInitialized(node) {
986
- return !!(node?.[internalsSymbol]?.handleBounds || node?.handles?.length) && !!(node?.computed?.width || node?.width);
996
+ return (!!(node?.[internalsSymbol]?.handleBounds || node?.handles?.length) &&
997
+ !!(node?.computed?.width || node?.width || node?.initialWidth));
987
998
  }
988
999
  function getEdgePosition(params) {
989
1000
  const { sourceNode, targetNode } = params;
@@ -1026,8 +1037,8 @@ function toHandleBounds(handles) {
1026
1037
  const source = [];
1027
1038
  const target = [];
1028
1039
  for (const handle of handles) {
1029
- handle.width = handle.width || 1;
1030
- handle.height = handle.height || 1;
1040
+ handle.width = handle.width ?? 1;
1041
+ handle.height = handle.height ?? 1;
1031
1042
  if (handle.type === 'source') {
1032
1043
  source.push(handle);
1033
1044
  }
@@ -1043,8 +1054,7 @@ function toHandleBounds(handles) {
1043
1054
  function getHandlePosition(position, node, handle = null) {
1044
1055
  const x = (handle?.x ?? 0) + (node.computed?.positionAbsolute?.x ?? 0);
1045
1056
  const y = (handle?.y ?? 0) + (node.computed?.positionAbsolute?.y ?? 0);
1046
- const width = handle?.width || (node?.computed?.width ?? node?.width ?? 0);
1047
- const height = handle?.height || (node?.computed?.height ?? node?.height ?? 0);
1057
+ const { width, height } = handle ?? getNodeDimensions(node);
1048
1058
  switch (position) {
1049
1059
  case Position.Top:
1050
1060
  return [x + width / 2, y];
@@ -1359,19 +1369,19 @@ function getEventHandlerParams({ nodeId, dragItems, nodeLookup, }) {
1359
1369
  }
1360
1370
 
1361
1371
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1362
- function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragStop, }) {
1372
+ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragStop, }) {
1363
1373
  let lastPos = { x: null, y: null };
1364
1374
  let autoPanId = 0;
1365
1375
  let dragItems = [];
1366
1376
  let autoPanStarted = false;
1367
1377
  let mousePosition = { x: 0, y: 0 };
1368
- let dragEvent = null;
1369
1378
  let containerBounds = null;
1370
1379
  let dragStarted = false;
1371
- const d3Selection = select(domNode);
1380
+ let d3Selection = null;
1372
1381
  // public functions
1373
1382
  function update({ noDragClassName, handleSelector, domNode, isSelectable, nodeId }) {
1374
- function updateNodes({ x, y }) {
1383
+ d3Selection = select(domNode);
1384
+ function updateNodes({ x, y }, dragEvent) {
1375
1385
  const { nodeLookup, nodeExtent, snapGrid, snapToGrid, nodeOrigin, onNodeDrag, onSelectionDrag, onError, updateNodePositions, } = getStoreItems();
1376
1386
  lastPos = { x, y };
1377
1387
  let hasChange = false;
@@ -1417,15 +1427,18 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1417
1427
  return;
1418
1428
  }
1419
1429
  updateNodePositions(dragItems, true);
1420
- const onNodeOrSelectionDrag = nodeId ? onNodeDrag : wrapSelectionDragFunc(onSelectionDrag);
1421
- if (dragEvent && (onDrag || onNodeOrSelectionDrag)) {
1430
+ if (dragEvent && (onDrag || onNodeDrag || (!nodeId && onSelectionDrag))) {
1422
1431
  const [currentNode, currentNodes] = getEventHandlerParams({
1423
1432
  nodeId,
1424
1433
  dragItems,
1425
1434
  nodeLookup,
1426
1435
  });
1427
1436
  onDrag?.(dragEvent, dragItems, currentNode, currentNodes);
1428
- onNodeOrSelectionDrag?.(dragEvent, currentNode, currentNodes);
1437
+ onNodeDrag?.(dragEvent, currentNode, currentNodes);
1438
+ if (!nodeId) {
1439
+ const _onSelectionDrag = wrapSelectionDragFunc(onSelectionDrag);
1440
+ _onSelectionDrag(dragEvent, currentNode, currentNodes);
1441
+ }
1429
1442
  }
1430
1443
  }
1431
1444
  function autoPan() {
@@ -1438,7 +1451,7 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1438
1451
  lastPos.x = (lastPos.x ?? 0) - xMovement / transform[2];
1439
1452
  lastPos.y = (lastPos.y ?? 0) - yMovement / transform[2];
1440
1453
  if (panBy({ x: xMovement, y: yMovement })) {
1441
- updateNodes(lastPos);
1454
+ updateNodes(lastPos, null);
1442
1455
  }
1443
1456
  }
1444
1457
  autoPanId = requestAnimationFrame(autoPan);
@@ -1458,15 +1471,18 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1458
1471
  const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
1459
1472
  lastPos = pointerPos;
1460
1473
  dragItems = getDragItems(nodes, nodesDraggable, pointerPos, nodeId);
1461
- const onNodeOrSelectionDragStart = nodeId ? onNodeDragStart : wrapSelectionDragFunc(onSelectionDragStart);
1462
- if (dragItems.length > 0 && (onDragStart || onNodeOrSelectionDragStart)) {
1474
+ if (dragItems.length > 0 && (onDragStart || onNodeDragStart || (!nodeId && onSelectionDragStart))) {
1463
1475
  const [currentNode, currentNodes] = getEventHandlerParams({
1464
1476
  nodeId,
1465
1477
  dragItems,
1466
1478
  nodeLookup,
1467
1479
  });
1468
1480
  onDragStart?.(event.sourceEvent, dragItems, currentNode, currentNodes);
1469
- onNodeOrSelectionDragStart?.(event.sourceEvent, currentNode, currentNodes);
1481
+ onNodeDragStart?.(event.sourceEvent, currentNode, currentNodes);
1482
+ if (!nodeId) {
1483
+ const _onSelectionDragStart = wrapSelectionDragFunc(onSelectionDragStart);
1484
+ _onSelectionDragStart(event.sourceEvent, currentNode, currentNodes);
1485
+ }
1470
1486
  }
1471
1487
  }
1472
1488
  const d3DragInstance = drag()
@@ -1497,9 +1513,9 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1497
1513
  }
1498
1514
  // skip events without movement
1499
1515
  if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems && dragStarted) {
1500
- dragEvent = event.sourceEvent;
1516
+ // dragEvent = event.sourceEvent as MouseEvent;
1501
1517
  mousePosition = getEventPosition(event.sourceEvent, containerBounds);
1502
- updateNodes(pointerPos);
1518
+ updateNodes(pointerPos, event.sourceEvent);
1503
1519
  }
1504
1520
  })
1505
1521
  .on('end', (event) => {
@@ -1511,16 +1527,19 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1511
1527
  cancelAnimationFrame(autoPanId);
1512
1528
  if (dragItems.length > 0) {
1513
1529
  const { nodeLookup, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems();
1514
- const onNodeOrSelectionDragStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop);
1515
1530
  updateNodePositions(dragItems, false);
1516
- if (onDragStop || onNodeOrSelectionDragStop) {
1531
+ if (onDragStop || onNodeDragStop || (!nodeId && onSelectionDragStop)) {
1517
1532
  const [currentNode, currentNodes] = getEventHandlerParams({
1518
1533
  nodeId,
1519
1534
  dragItems,
1520
1535
  nodeLookup,
1521
1536
  });
1522
1537
  onDragStop?.(event.sourceEvent, dragItems, currentNode, currentNodes);
1523
- onNodeOrSelectionDragStop?.(event.sourceEvent, currentNode, currentNodes);
1538
+ onNodeDragStop?.(event.sourceEvent, currentNode, currentNodes);
1539
+ if (!nodeId) {
1540
+ const _onSelectionDragStop = wrapSelectionDragFunc(onSelectionDragStop);
1541
+ _onSelectionDragStop(event.sourceEvent, currentNode, currentNodes);
1542
+ }
1524
1543
  }
1525
1544
  }
1526
1545
  })
@@ -1534,7 +1553,7 @@ function XYDrag({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag,
1534
1553
  d3Selection.call(d3DragInstance);
1535
1554
  }
1536
1555
  function destroy() {
1537
- d3Selection.on('.drag', null);
1556
+ d3Selection?.on('.drag', null);
1538
1557
  }
1539
1558
  return {
1540
1559
  update,
@@ -1561,7 +1580,7 @@ function getHandles(node, handleBounds, type, currentHandle) {
1561
1580
  function getClosestHandle(pos, connectionRadius, handles) {
1562
1581
  let closestHandles = [];
1563
1582
  let minDistance = Infinity;
1564
- handles.forEach((handle) => {
1583
+ for (const handle of handles) {
1565
1584
  const distance = Math.sqrt(Math.pow(handle.x - pos.x, 2) + Math.pow(handle.y - pos.y, 2));
1566
1585
  if (distance <= connectionRadius) {
1567
1586
  if (distance < minDistance) {
@@ -1573,7 +1592,7 @@ function getClosestHandle(pos, connectionRadius, handles) {
1573
1592
  }
1574
1593
  minDistance = distance;
1575
1594
  }
1576
- });
1595
+ }
1577
1596
  if (!closestHandles.length) {
1578
1597
  return null;
1579
1598
  }
@@ -1609,9 +1628,6 @@ function getHandleType(edgeUpdaterType, handleDomNode) {
1609
1628
  }
1610
1629
  return null;
1611
1630
  }
1612
- function resetRecentHandle(handleDomNode, lib) {
1613
- handleDomNode?.classList.remove('valid', 'connecting', `${lib}-flow__handle-valid`, `${lib}-flow__handle-connecting`);
1614
- }
1615
1631
  function getConnectionStatus(isInsideConnectionRadius, isHandleValid) {
1616
1632
  let connectionStatus = null;
1617
1633
  if (isHandleValid) {
@@ -1637,7 +1653,6 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
1637
1653
  if (!containerBounds || !handleType) {
1638
1654
  return;
1639
1655
  }
1640
- let prevActiveHandle;
1641
1656
  let connectionPosition = getEventPosition(event, containerBounds);
1642
1657
  let autoPanStarted = false;
1643
1658
  let connection = null;
@@ -1705,16 +1720,6 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
1705
1720
  connectionStatus: getConnectionStatus(!!closestHandle, isValid),
1706
1721
  connectionEndHandle: result.endHandle,
1707
1722
  });
1708
- if (!closestHandle && !isValid && !handleDomNode) {
1709
- return resetRecentHandle(prevActiveHandle, lib);
1710
- }
1711
- if (connection?.source !== connection?.target && handleDomNode) {
1712
- resetRecentHandle(prevActiveHandle, lib);
1713
- prevActiveHandle = handleDomNode;
1714
- handleDomNode.classList.add('connecting', `${lib}-flow__handle-connecting`);
1715
- handleDomNode.classList.toggle('valid', isValid);
1716
- handleDomNode.classList.toggle(`${lib}-flow__handle-valid`, isValid);
1717
- }
1718
1723
  }
1719
1724
  function onPointerUp(event) {
1720
1725
  if ((closestHandle || handleDomNode) && connection && isValid) {
@@ -1726,7 +1731,6 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
1726
1731
  if (edgeUpdaterType) {
1727
1732
  onEdgeUpdateEnd?.(event);
1728
1733
  }
1729
- resetRecentHandle(prevActiveHandle, lib);
1730
1734
  cancelConnection();
1731
1735
  cancelAnimationFrame(autoPanId);
1732
1736
  autoPanStarted = false;
@@ -2252,7 +2256,7 @@ function xor(a, b) {
2252
2256
  }
2253
2257
  /**
2254
2258
  * Calculates new width & height and x & y of node after resize based on pointer position
2255
- * @description - Buckle up, this is a chunky one! If you want to determine the new dimensions of a node after a resize,
2259
+ * @description - Buckle up, this is a chunky one... If you want to determine the new dimensions of a node after a resize,
2256
2260
  * you have to account for all possible restrictions: min/max width/height of the node, the maximum extent the node is allowed
2257
2261
  * to move in (in this case: resize into) determined by the parent node, the minimal extent determined by child nodes
2258
2262
  * with expandParent or extent: 'parent' set and oh yeah, these things also have to work with keepAspectRatio!
@@ -2260,6 +2264,8 @@ function xor(a, b) {
2260
2264
  * strongest restriction. Because the resize affects x, y and width, height and width, height of a opposing side with keepAspectRatio,
2261
2265
  * the resize amount is always kept in distX & distY amount (the distance in mouse movement)
2262
2266
  * Instead of clamping each value, we first calculate the biggest 'clamp' (for the lack of a better name) and then apply it to all values.
2267
+ * To complicate things nodeOrigin has to be taken into account as well. This is done by offsetting the nodes as if their origin is [0, 0],
2268
+ * then calculating the restrictions as usual
2263
2269
  * @param startValues - starting values of resize
2264
2270
  * @param controlDirection - dimensions affected by the resize
2265
2271
  * @param pointerPosition - the current pointer position corrected for snapping
@@ -2267,7 +2273,7 @@ function xor(a, b) {
2267
2273
  * @param keepAspectRatio - prevent changes of asprect ratio
2268
2274
  * @returns x, y, width and height of the node after resize
2269
2275
  */
2270
- function getDimensionsAfterResize(startValues, controlDirection, pointerPosition, boundaries, keepAspectRatio, extent, childExtent) {
2276
+ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition, boundaries, keepAspectRatio, nodeOrigin, extent, childExtent) {
2271
2277
  let { affectsX, affectsY } = controlDirection;
2272
2278
  const { isHorizontal, isVertical } = controlDirection;
2273
2279
  const isDiagonal = isHorizontal && isVertical;
@@ -2278,6 +2284,8 @@ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition
2278
2284
  let distY = Math.floor(isVertical ? ySnapped - startValues.pointerY : 0);
2279
2285
  const newWidth = startWidth + (affectsX ? -distX : distX);
2280
2286
  const newHeight = startHeight + (affectsY ? -distY : distY);
2287
+ const originOffsetX = -nodeOrigin[0] * startWidth;
2288
+ const originOffsetY = -nodeOrigin[1] * startHeight;
2281
2289
  // Check if maxWidth, minWWidth, maxHeight, minHeight are restricting the resize
2282
2290
  let clampX = getSizeClamp(newWidth, minWidth, maxWidth);
2283
2291
  let clampY = getSizeClamp(newHeight, minHeight, maxHeight);
@@ -2286,16 +2294,16 @@ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition
2286
2294
  let xExtentClamp = 0;
2287
2295
  let yExtentClamp = 0;
2288
2296
  if (affectsX && distX < 0) {
2289
- xExtentClamp = getLowerExtentClamp(startX + distX, extent[0][0]);
2297
+ xExtentClamp = getLowerExtentClamp(startX + distX + originOffsetX, extent[0][0]);
2290
2298
  }
2291
2299
  else if (!affectsX && distX > 0) {
2292
- xExtentClamp = getUpperExtentClamp(startX + newWidth, extent[1][0]);
2300
+ xExtentClamp = getUpperExtentClamp(startX + newWidth + originOffsetX, extent[1][0]);
2293
2301
  }
2294
2302
  if (affectsY && distY < 0) {
2295
- yExtentClamp = getLowerExtentClamp(startY + distY, extent[0][1]);
2303
+ yExtentClamp = getLowerExtentClamp(startY + distY + originOffsetY, extent[0][1]);
2296
2304
  }
2297
2305
  else if (!affectsY && distY > 0) {
2298
- yExtentClamp = getUpperExtentClamp(startY + newHeight, extent[1][1]);
2306
+ yExtentClamp = getUpperExtentClamp(startY + newHeight + originOffsetY, extent[1][1]);
2299
2307
  }
2300
2308
  clampX = Math.max(clampX, xExtentClamp);
2301
2309
  clampY = Math.max(clampY, yExtentClamp);
@@ -2329,11 +2337,13 @@ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition
2329
2337
  if (extent) {
2330
2338
  let aspectExtentClamp = 0;
2331
2339
  if ((!affectsX && !affectsY) || (affectsX && !affectsY && isDiagonal)) {
2332
- aspectExtentClamp = getUpperExtentClamp(startY + newWidth / aspectRatio, extent[1][1]) * aspectRatio;
2340
+ aspectExtentClamp =
2341
+ getUpperExtentClamp(startY + originOffsetY + newWidth / aspectRatio, extent[1][1]) * aspectRatio;
2333
2342
  }
2334
2343
  else {
2335
2344
  aspectExtentClamp =
2336
- getLowerExtentClamp(startY + (affectsX ? distX : -distX) / aspectRatio, extent[0][1]) * aspectRatio;
2345
+ getLowerExtentClamp(startY + originOffsetY + (affectsX ? distX : -distX) / aspectRatio, extent[0][1]) *
2346
+ aspectRatio;
2337
2347
  }
2338
2348
  clampX = Math.max(clampX, aspectExtentClamp);
2339
2349
  }
@@ -2357,11 +2367,13 @@ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition
2357
2367
  if (extent) {
2358
2368
  let aspectExtentClamp = 0;
2359
2369
  if ((!affectsX && !affectsY) || (affectsY && !affectsX && isDiagonal)) {
2360
- aspectExtentClamp = getUpperExtentClamp(startX + newHeight * aspectRatio, extent[1][0]) / aspectRatio;
2370
+ aspectExtentClamp =
2371
+ getUpperExtentClamp(startX + newHeight * aspectRatio + originOffsetX, extent[1][0]) / aspectRatio;
2361
2372
  }
2362
2373
  else {
2363
2374
  aspectExtentClamp =
2364
- getLowerExtentClamp(startX + (affectsY ? distY : -distY) * aspectRatio, extent[0][0]) / aspectRatio;
2375
+ getLowerExtentClamp(startX + (affectsY ? distY : -distY) * aspectRatio + originOffsetX, extent[0][0]) /
2376
+ aspectRatio;
2365
2377
  }
2366
2378
  clampY = Math.max(clampY, aspectExtentClamp);
2367
2379
  }
@@ -2400,11 +2412,13 @@ function getDimensionsAfterResize(startValues, controlDirection, pointerPosition
2400
2412
  }
2401
2413
  }
2402
2414
  }
2415
+ const x = affectsX ? startX + distX : startX;
2416
+ const y = affectsY ? startY + distY : startY;
2403
2417
  return {
2404
2418
  width: startWidth + (affectsX ? -distX : distX),
2405
2419
  height: startHeight + (affectsY ? -distY : distY),
2406
- x: affectsX ? startX + distX : startX,
2407
- y: affectsY ? startY + distY : startY,
2420
+ x: nodeOrigin[0] * distX * (!affectsX ? 1 : -1) + x,
2421
+ y: nodeOrigin[1] * distY * (!affectsY ? 1 : -1) + y,
2408
2422
  };
2409
2423
  }
2410
2424
 
@@ -2431,13 +2445,16 @@ function nodeToParentExtent(node) {
2431
2445
  [node.computed.width, node.computed.height],
2432
2446
  ];
2433
2447
  }
2434
- function nodeToChildExtent(child, parent) {
2448
+ function nodeToChildExtent(child, parent, nodeOrigin) {
2449
+ const x = parent.position.x + child.position.x;
2450
+ const y = parent.position.y + child.position.y;
2451
+ const width = child.computed.width ?? 0;
2452
+ const height = child.computed.height ?? 0;
2453
+ const originOffsetX = nodeOrigin[0] * width;
2454
+ const originOffsetY = nodeOrigin[1] * height;
2435
2455
  return [
2436
- [parent.position.x + child.position.x, parent.position.y + child.position.y],
2437
- [
2438
- parent.position.x + child.position.x + child.computed.width,
2439
- parent.position.y + child.position.y + child.computed.height,
2440
- ],
2456
+ [x - originOffsetX, y - originOffsetY],
2457
+ [x + width - originOffsetX, y + height - originOffsetY],
2441
2458
  ];
2442
2459
  }
2443
2460
  function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
@@ -2453,7 +2470,7 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
2453
2470
  let childExtent = undefined;
2454
2471
  const dragHandler = drag()
2455
2472
  .on('start', (event) => {
2456
- const { nodeLookup, transform, snapGrid, snapToGrid } = getStoreItems();
2473
+ const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = getStoreItems();
2457
2474
  node = nodeLookup.get(nodeId);
2458
2475
  if (node) {
2459
2476
  const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
@@ -2488,7 +2505,7 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
2488
2505
  extent: child.extent,
2489
2506
  });
2490
2507
  if (child.extent === 'parent' || child.expandParent) {
2491
- const extent = nodeToChildExtent(child, node);
2508
+ const extent = nodeToChildExtent(child, node, child.origin ?? nodeOrigin);
2492
2509
  if (childExtent) {
2493
2510
  childExtent = [
2494
2511
  [Math.min(extent[0][0], childExtent[0][0]), Math.min(extent[0][1], childExtent[0][1])],
@@ -2505,35 +2522,34 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
2505
2522
  }
2506
2523
  })
2507
2524
  .on('drag', (event) => {
2508
- const { transform, snapGrid, snapToGrid } = getStoreItems();
2525
+ const { transform, snapGrid, snapToGrid, nodeOrigin: storeNodeOrigin } = getStoreItems();
2509
2526
  const pointerPosition = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
2510
2527
  const childChanges = [];
2511
2528
  if (node) {
2512
- const change = { ...initChange };
2513
2529
  const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues;
2514
- const { width, height, x, y } = getDimensionsAfterResize(startValues, controlDirection, pointerPosition, boundaries, keepAspectRatio, parentExtent, childExtent);
2530
+ const change = { ...initChange };
2531
+ const nodeOrigin = node.origin ?? storeNodeOrigin;
2532
+ const { width, height, x, y } = getDimensionsAfterResize(startValues, controlDirection, pointerPosition, boundaries, keepAspectRatio, nodeOrigin, parentExtent, childExtent);
2515
2533
  const isWidthChange = width !== prevWidth;
2516
2534
  const isHeightChange = height !== prevHeight;
2517
- if (controlDirection.affectsX || controlDirection.affectsY) {
2518
- const isXPosChange = x !== prevX && isWidthChange;
2519
- const isYPosChange = y !== prevY && isHeightChange;
2520
- if (isXPosChange || isYPosChange) {
2521
- change.isXPosChange = isXPosChange;
2522
- change.isYPosChange = isYPosChange;
2523
- change.x = isXPosChange ? x : prevX;
2524
- change.y = isYPosChange ? y : prevY;
2525
- prevValues.x = change.x;
2526
- prevValues.y = change.y;
2527
- // Fix expandParent when resizing from top/left
2528
- if (parentNode && node.expandParent) {
2529
- if (change.x < 0) {
2530
- prevValues.x = 0;
2531
- startValues.x = startValues.x - change.x;
2532
- }
2533
- if (change.y < 0) {
2534
- prevValues.y = 0;
2535
- startValues.y = startValues.y - change.y;
2536
- }
2535
+ const isXPosChange = x !== prevX && isWidthChange;
2536
+ const isYPosChange = y !== prevY && isHeightChange;
2537
+ if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1] == 1) {
2538
+ change.isXPosChange = isXPosChange;
2539
+ change.isYPosChange = isYPosChange;
2540
+ change.x = isXPosChange ? x : prevX;
2541
+ change.y = isYPosChange ? y : prevY;
2542
+ prevValues.x = change.x;
2543
+ prevValues.y = change.y;
2544
+ // Fix expandParent when resizing from top/left
2545
+ if (parentNode && node.expandParent) {
2546
+ if (change.x < 0) {
2547
+ prevValues.x = 0;
2548
+ startValues.x = startValues.x - change.x;
2549
+ }
2550
+ if (change.y < 0) {
2551
+ prevValues.y = 0;
2552
+ startValues.y = startValues.y - change.y;
2537
2553
  }
2538
2554
  }
2539
2555
  if (childNodes.length > 0) {
@@ -2541,8 +2557,8 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
2541
2557
  const yChange = y - prevY;
2542
2558
  for (const childNode of childNodes) {
2543
2559
  childNode.position = {
2544
- x: childNode.position.x - xChange,
2545
- y: childNode.position.y - yChange,
2560
+ x: childNode.position.x - xChange + nodeOrigin[0] * (width - prevWidth),
2561
+ y: childNode.position.y - yChange + nodeOrigin[1] * (height - prevHeight),
2546
2562
  };
2547
2563
  childChanges.push(childNode);
2548
2564
  }
@@ -2590,4 +2606,4 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange }) {
2590
2606
  };
2591
2607
  }
2592
2608
 
2593
- export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, adoptUserProvidedNodes, areConnectionMapsEqual, boxToRect, calcAutoPan, calculateNodePosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdges, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getMarkerId, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoers, getOverlappingArea, getPointerPosition, getPositionWithOrigin, getSmoothStepPath, getStraightPath, getViewportForBounds, handleConnectionChange, infiniteExtent, internalsSymbol, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isMouseEvent, isNodeBase, isNumeric, isRectObject, nodeToBox, nodeToRect, panBy, pointToRendererPoint, rectToBox, rendererPointToPoint, snapPosition, updateAbsolutePositions, updateConnectionLookup, updateEdge, updateNodeDimensions };
2609
+ export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, adoptUserProvidedNodes, areConnectionMapsEqual, boxToRect, calcAutoPan, calculateNodePosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdges, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoers, getOverlappingArea, getPointerPosition, getPositionWithOrigin, getSmoothStepPath, getStraightPath, getViewportForBounds, handleConnectionChange, infiniteExtent, internalsSymbol, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isMouseEvent, isNodeBase, isNumeric, isRectObject, nodeHasDimensions, nodeToBox, nodeToRect, panBy, pointToRendererPoint, rectToBox, rendererPointToPoint, snapPosition, updateAbsolutePositions, updateConnectionLookup, updateEdge, updateNodeDimensions };