@flowgram.ai/free-container-plugin 0.2.27 → 0.2.29

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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as _flowgram_ai_utils from '@flowgram.ai/utils';
2
+ import * as _flowgram_ai_free_layout_core from '@flowgram.ai/free-layout-core';
2
3
  import { WorkflowNodeEntity } from '@flowgram.ai/free-layout-core';
4
+ import * as _flowgram_ai_document from '@flowgram.ai/document';
3
5
  import { FlowNodeTransformData } from '@flowgram.ai/document';
4
6
  import * as _flowgram_ai_core from '@flowgram.ai/core';
5
7
  import React, { FC, CSSProperties, ReactNode } from 'react';
@@ -70,12 +72,8 @@ declare class NodeIntoContainerService {
70
72
  private listenDragToContainer;
71
73
  /** 监听节点拖拽出容器 */
72
74
  private dragOutContainer;
73
- /** 获取重叠位置 */
74
- private getCollisionTransform;
75
75
  /** 设置放置节点高亮 */
76
76
  private setDropNode;
77
- /** 获取容器节点transforms */
78
- private getContainerTransforms;
79
77
  /** 放置节点到容器 */
80
78
  private dropNodeToContainer;
81
79
  /** 拖拽节点 */
@@ -89,16 +87,6 @@ declare class NodeIntoContainerService {
89
87
  private isParent;
90
88
  /** 将节点移入容器 */
91
89
  private moveIntoContainer;
92
- /**
93
- * 如果存在容器节点,且传入鼠标坐标,需要用容器的坐标减去传入的鼠标坐标
94
- */
95
- private adjustSubNodePosition;
96
- private isContainer;
97
- /** 判断点是否在矩形内 */
98
- private isPointInRect;
99
- /** 判断两个矩形是否相交 */
100
- private isRectIntersects;
101
- private nextFrame;
102
90
  }
103
91
 
104
92
  declare const createContainerNodePlugin: _flowgram_ai_core.PluginCreator<WorkflowContainerPluginOptions>;
@@ -162,4 +150,26 @@ interface SubCanvasTipsProps {
162
150
  }
163
151
  declare const SubCanvasTips: ({ tipText, neverRemindText }: SubCanvasTipsProps) => React.JSX.Element | null;
164
152
 
165
- export { type NodeIntoContainerEvent, NodeIntoContainerService, type NodeIntoContainerState, NodeIntoContainerType, type NodeSize, SubCanvasBackground, SubCanvasBorder, SubCanvasRender, SubCanvasTips, type WorkflowContainerPluginOptions, createContainerNodePlugin, useNodeSize, useSyncNodeRenderSize };
153
+ /**
154
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
155
+ * SPDX-License-Identifier: MIT
156
+ */
157
+ declare const ContainerUtils: {
158
+ nextFrame: () => Promise<void>;
159
+ isContainer: (node?: _flowgram_ai_free_layout_core.WorkflowNodeEntity) => boolean;
160
+ adjustSubNodePosition: (params: {
161
+ targetNode: _flowgram_ai_free_layout_core.WorkflowNodeEntity;
162
+ containerNode: _flowgram_ai_free_layout_core.WorkflowNodeEntity;
163
+ containerPadding: _flowgram_ai_utils.PaddingSchema;
164
+ }) => _flowgram_ai_utils.IPoint;
165
+ getContainerTransforms: (allNodes: _flowgram_ai_free_layout_core.WorkflowNodeEntity[]) => _flowgram_ai_document.FlowNodeTransformData[];
166
+ getCollisionTransform: (params: {
167
+ transforms: _flowgram_ai_document.FlowNodeTransformData[];
168
+ targetRect?: _flowgram_ai_utils.Rectangle;
169
+ targetPoint?: _flowgram_ai_utils.PositionSchema;
170
+ withPadding?: boolean;
171
+ document: _flowgram_ai_free_layout_core.WorkflowDocument;
172
+ }) => _flowgram_ai_document.FlowNodeTransformData | undefined;
173
+ };
174
+
175
+ export { ContainerUtils, type NodeIntoContainerEvent, NodeIntoContainerService, type NodeIntoContainerState, NodeIntoContainerType, type NodeSize, SubCanvasBackground, SubCanvasBorder, SubCanvasRender, SubCanvasTips, type WorkflowContainerPluginOptions, createContainerNodePlugin, useNodeSize, useSyncNodeRenderSize };
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ var __decorateClass = (decorators, target, key, kind) => {
38
38
  // src/index.ts
39
39
  var src_exports = {};
40
40
  __export(src_exports, {
41
+ ContainerUtils: () => ContainerUtils,
41
42
  NodeIntoContainerService: () => NodeIntoContainerService,
42
43
  NodeIntoContainerType: () => NodeIntoContainerType,
43
44
  SubCanvasBackground: () => SubCanvasBackground,
@@ -60,15 +61,109 @@ var NodeIntoContainerType = /* @__PURE__ */ ((NodeIntoContainerType2) => {
60
61
  // src/node-into-container/service.ts
61
62
  var import_lodash = require("lodash");
62
63
  var import_inversify = require("inversify");
63
- var import_utils = require("@flowgram.ai/utils");
64
+ var import_utils2 = require("@flowgram.ai/utils");
64
65
  var import_free_layout_core = require("@flowgram.ai/free-layout-core");
65
66
  var import_free_history_plugin = require("@flowgram.ai/free-history-plugin");
66
- var import_document = require("@flowgram.ai/document");
67
+ var import_document2 = require("@flowgram.ai/document");
67
68
  var import_core = require("@flowgram.ai/core");
69
+
70
+ // src/utils/next-frame.ts
71
+ var nextFrame = async () => {
72
+ await new Promise((resolve) => requestAnimationFrame(resolve));
73
+ };
74
+
75
+ // src/utils/is-container.ts
76
+ var isContainer = (node) => node?.getNodeMeta().isContainer ?? false;
77
+
78
+ // src/utils/get-container-transforms.ts
79
+ var getContainerTransforms = (allNodes) => allNodes.filter((node) => {
80
+ if (node.originParent) {
81
+ return node.getNodeMeta().selectable && node.originParent.getNodeMeta().selectable;
82
+ }
83
+ return node.getNodeMeta().selectable;
84
+ }).filter((node) => isContainer(node)).sort((a, b) => {
85
+ const aIndex = a.renderData.stackIndex;
86
+ const bIndex = b.renderData.stackIndex;
87
+ return bIndex - aIndex;
88
+ }).map((node) => node.transform);
89
+
90
+ // src/utils/get-collision-transform.ts
91
+ var import_utils = require("@flowgram.ai/utils");
92
+
93
+ // src/utils/is-rect-intersects.ts
94
+ var isRectIntersects = (rectA, rectB) => {
95
+ const hasHorizontalOverlap = rectA.right > rectB.left && rectA.left < rectB.right;
96
+ const hasVerticalOverlap = rectA.bottom > rectB.top && rectA.top < rectB.bottom;
97
+ return hasHorizontalOverlap && hasVerticalOverlap;
98
+ };
99
+
100
+ // src/utils/is-point-in-rect.ts
101
+ var isPointInRect = (point, rect) => point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
102
+
103
+ // src/utils/get-collision-transform.ts
104
+ var getCollisionTransform = (params) => {
105
+ const { targetRect, targetPoint, transforms, withPadding = false, document } = params;
106
+ const collisionTransform = transforms.find((transform) => {
107
+ const { bounds, entity } = transform;
108
+ const padding = withPadding ? document.layout.getPadding(entity) : { left: 0, right: 0 };
109
+ const transformRect = new import_utils.Rectangle(
110
+ bounds.x + padding.left + padding.right,
111
+ bounds.y,
112
+ bounds.width,
113
+ bounds.height
114
+ );
115
+ if (targetRect) {
116
+ return isRectIntersects(targetRect, transformRect);
117
+ }
118
+ if (targetPoint) {
119
+ return isPointInRect(targetPoint, transformRect);
120
+ }
121
+ return false;
122
+ });
123
+ return collisionTransform;
124
+ };
125
+
126
+ // src/utils/adjust-sub-node-position.ts
127
+ var import_document = require("@flowgram.ai/document");
128
+ var adjustSubNodePosition = (params) => {
129
+ const { targetNode, containerNode, containerPadding } = params;
130
+ if (containerNode.flowNodeType === import_document.FlowNodeBaseType.ROOT) {
131
+ return targetNode.transform.position;
132
+ }
133
+ const nodeWorldTransform = targetNode.transform.transform.worldTransform;
134
+ const containerWorldTransform = containerNode.transform.transform.worldTransform;
135
+ const nodePosition = {
136
+ x: nodeWorldTransform.tx,
137
+ y: nodeWorldTransform.ty
138
+ };
139
+ const isParentEmpty = !containerNode.children || containerNode.children.length === 0;
140
+ if (isParentEmpty) {
141
+ return {
142
+ x: 0,
143
+ y: containerPadding.top
144
+ };
145
+ } else {
146
+ return {
147
+ x: nodePosition.x - containerWorldTransform.tx,
148
+ y: nodePosition.y - containerWorldTransform.ty
149
+ };
150
+ }
151
+ };
152
+
153
+ // src/utils/index.ts
154
+ var ContainerUtils = {
155
+ nextFrame,
156
+ isContainer,
157
+ adjustSubNodePosition,
158
+ getContainerTransforms,
159
+ getCollisionTransform
160
+ };
161
+
162
+ // src/node-into-container/service.ts
68
163
  var NodeIntoContainerService = class {
69
164
  constructor() {
70
- this.emitter = new import_utils.Emitter();
71
- this.toDispose = new import_utils.DisposableCollection();
165
+ this.emitter = new import_utils2.Emitter();
166
+ this.toDispose = new import_utils2.DisposableCollection();
72
167
  this.on = this.emitter.event;
73
168
  }
74
169
  init() {
@@ -88,14 +183,14 @@ var NodeIntoContainerService = class {
88
183
  const parentNode = node.parent;
89
184
  const containerNode = parentNode?.parent;
90
185
  const nodeJSON = this.document.toNodeJSON(node);
91
- if (!parentNode || !containerNode || !this.isContainer(parentNode) || !nodeJSON.meta?.position) {
186
+ if (!parentNode || !containerNode || !ContainerUtils.isContainer(parentNode) || !nodeJSON.meta?.position) {
92
187
  return;
93
188
  }
94
189
  const parentTransform = parentNode.getData(import_core.TransformData);
95
190
  this.operationService.moveNode(node, {
96
191
  parent: containerNode
97
192
  });
98
- await this.nextFrame();
193
+ await ContainerUtils.nextFrame();
99
194
  parentTransform.fireChange();
100
195
  this.operationService.updateNodePosition(node, {
101
196
  x: parentTransform.position.x + nodeJSON.meta.position.x,
@@ -112,7 +207,7 @@ var NodeIntoContainerService = class {
112
207
  canMoveOutContainer(node) {
113
208
  const parentNode = node.parent;
114
209
  const containerNode = parentNode?.parent;
115
- if (!parentNode || !containerNode || !this.isContainer(parentNode)) {
210
+ if (!parentNode || !containerNode || !ContainerUtils.isContainer(parentNode)) {
116
211
  return false;
117
212
  }
118
213
  const canDrop = this.dragService.canDropToNode({
@@ -135,7 +230,7 @@ var NodeIntoContainerService = class {
135
230
  if (dragNode.parent === sourceParent) {
136
231
  return;
137
232
  }
138
- if (dragNode.parent?.flowNodeType === import_document.FlowNodeBaseType.GROUP || sourceParent?.flowNodeType === import_document.FlowNodeBaseType.GROUP) {
233
+ if (dragNode.parent?.flowNodeType === import_document2.FlowNodeBaseType.GROUP || sourceParent?.flowNodeType === import_document2.FlowNodeBaseType.GROUP) {
139
234
  return;
140
235
  }
141
236
  await this.removeNodeLines(dragNode);
@@ -149,7 +244,7 @@ var NodeIntoContainerService = class {
149
244
  }
150
245
  line.dispose();
151
246
  });
152
- await this.nextFrame();
247
+ await ContainerUtils.nextFrame();
153
248
  }
154
249
  /** 初始化状态 */
155
250
  initState() {
@@ -177,7 +272,7 @@ var NodeIntoContainerService = class {
177
272
  }
178
273
  this.historyService.startTransaction();
179
274
  this.state.isDraggingNode = true;
180
- this.state.transforms = this.getContainerTransforms();
275
+ this.state.transforms = ContainerUtils.getContainerTransforms(this.document.getAllNodes());
181
276
  this.state.dragNode = this.selectService.selectedNodes[0];
182
277
  this.state.dropNode = void 0;
183
278
  this.state.sourceParent = this.state.dragNode?.parent;
@@ -215,38 +310,16 @@ var NodeIntoContainerService = class {
215
310
  this.moveOutContainer({ node: dragNode });
216
311
  this.state.isSkipEvent = true;
217
312
  event.dragger.stop(event.dragEvent.clientX, event.dragEvent.clientY);
218
- await this.nextFrame();
313
+ await ContainerUtils.nextFrame();
219
314
  this.dragService.startDragSelectedNodes(event.triggerEvent);
220
315
  }
221
- /** 获取重叠位置 */
222
- getCollisionTransform(params) {
223
- const { targetRect, targetPoint, transforms, withPadding = false } = params;
224
- const collisionTransform = transforms.find((transform) => {
225
- const { bounds, entity } = transform;
226
- const padding = withPadding ? this.document.layout.getPadding(entity) : { left: 0, right: 0 };
227
- const transformRect = new import_utils.Rectangle(
228
- bounds.x + padding.left + padding.right,
229
- bounds.y,
230
- bounds.width,
231
- bounds.height
232
- );
233
- if (targetRect) {
234
- return this.isRectIntersects(targetRect, transformRect);
235
- }
236
- if (targetPoint) {
237
- return this.isPointInRect(targetPoint, transformRect);
238
- }
239
- return false;
240
- });
241
- return collisionTransform;
242
- }
243
316
  /** 设置放置节点高亮 */
244
317
  setDropNode(dropNode) {
245
318
  if (this.state.dropNode === dropNode) {
246
319
  return;
247
320
  }
248
321
  if (this.state.dropNode) {
249
- const renderData2 = this.state.dropNode.getData(import_document.FlowNodeRenderData);
322
+ const renderData2 = this.state.dropNode.getData(import_document2.FlowNodeRenderData);
250
323
  const renderDom2 = renderData2.node?.children?.[0];
251
324
  if (renderDom2) {
252
325
  renderDom2.classList.remove("selected");
@@ -256,25 +329,12 @@ var NodeIntoContainerService = class {
256
329
  if (!dropNode) {
257
330
  return;
258
331
  }
259
- const renderData = dropNode.getData(import_document.FlowNodeRenderData);
332
+ const renderData = dropNode.getData(import_document2.FlowNodeRenderData);
260
333
  const renderDom = renderData.node?.children?.[0];
261
334
  if (renderDom) {
262
335
  renderDom.classList.add("selected");
263
336
  }
264
337
  }
265
- /** 获取容器节点transforms */
266
- getContainerTransforms() {
267
- return this.document.getAllNodes().filter((node) => {
268
- if (node.originParent) {
269
- return node.getNodeMeta().selectable && node.originParent.getNodeMeta().selectable;
270
- }
271
- return node.getNodeMeta().selectable;
272
- }).filter((node) => this.isContainer(node)).sort((a, b) => {
273
- const aIndex = a.renderData.stackIndex;
274
- const bIndex = b.renderData.stackIndex;
275
- return bIndex - aIndex;
276
- }).map((node) => node.transform);
277
- }
278
338
  /** 放置节点到容器 */
279
339
  async dropNodeToContainer() {
280
340
  const { dropNode, dragNode, isDraggingNode } = this.state;
@@ -296,9 +356,10 @@ var NodeIntoContainerService = class {
296
356
  const availableTransforms = transforms.filter(
297
357
  (transform) => transform.entity.id !== dragNode.id
298
358
  );
299
- const collisionTransform = this.getCollisionTransform({
359
+ const collisionTransform = ContainerUtils.getCollisionTransform({
300
360
  targetPoint: mousePos,
301
- transforms: availableTransforms
361
+ transforms: availableTransforms,
362
+ document: this.document
302
363
  });
303
364
  const dropNode = collisionTransform?.entity;
304
365
  const canDrop = this.canDropToContainer({
@@ -317,7 +378,7 @@ var NodeIntoContainerService = class {
317
378
  if (!dropNode || !isDropContainer || this.isParent(dragNode, dropNode)) {
318
379
  return false;
319
380
  }
320
- if (dragNode.flowNodeType === import_document.FlowNodeBaseType.GROUP && dropNode.flowNodeType !== import_document.FlowNodeBaseType.GROUP) {
381
+ if (dragNode.flowNodeType === import_document2.FlowNodeBaseType.GROUP && dropNode.flowNodeType !== import_document2.FlowNodeBaseType.GROUP) {
321
382
  return false;
322
383
  }
323
384
  const canDrop = this.dragService.canDropToNode({
@@ -349,8 +410,14 @@ var NodeIntoContainerService = class {
349
410
  this.operationService.moveNode(node, {
350
411
  parent: containerNode
351
412
  });
352
- this.operationService.updateNodePosition(node, this.adjustSubNodePosition(node, containerNode));
353
- await this.nextFrame();
413
+ const containerPadding = this.document.layout.getPadding(containerNode);
414
+ const position = ContainerUtils.adjustSubNodePosition({
415
+ targetNode: node,
416
+ containerNode,
417
+ containerPadding
418
+ });
419
+ this.operationService.updateNodePosition(node, position);
420
+ await ContainerUtils.nextFrame();
354
421
  this.emitter.fire({
355
422
  type: "in" /* In */,
356
423
  node,
@@ -358,49 +425,6 @@ var NodeIntoContainerService = class {
358
425
  targetContainer: containerNode
359
426
  });
360
427
  }
361
- /**
362
- * 如果存在容器节点,且传入鼠标坐标,需要用容器的坐标减去传入的鼠标坐标
363
- */
364
- adjustSubNodePosition(targetNode, containerNode) {
365
- if (containerNode.flowNodeType === import_document.FlowNodeBaseType.ROOT) {
366
- return targetNode.transform.position;
367
- }
368
- const nodeWorldTransform = targetNode.transform.transform.worldTransform;
369
- const containerWorldTransform = containerNode.transform.transform.worldTransform;
370
- const nodePosition = {
371
- x: nodeWorldTransform.tx,
372
- y: nodeWorldTransform.ty
373
- };
374
- const isParentEmpty = !containerNode.children || containerNode.children.length === 0;
375
- const containerPadding = this.document.layout.getPadding(containerNode);
376
- if (isParentEmpty) {
377
- return {
378
- x: 0,
379
- y: containerPadding.top
380
- };
381
- } else {
382
- return {
383
- x: nodePosition.x - containerWorldTransform.tx,
384
- y: nodePosition.y - containerWorldTransform.ty
385
- };
386
- }
387
- }
388
- isContainer(node) {
389
- return node?.getNodeMeta().isContainer ?? false;
390
- }
391
- /** 判断点是否在矩形内 */
392
- isPointInRect(point, rect) {
393
- return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
394
- }
395
- /** 判断两个矩形是否相交 */
396
- isRectIntersects(rectA, rectB) {
397
- const hasHorizontalOverlap = rectA.right > rectB.left && rectA.left < rectB.right;
398
- const hasVerticalOverlap = rectA.bottom > rectB.top && rectA.top < rectB.bottom;
399
- return hasHorizontalOverlap && hasVerticalOverlap;
400
- }
401
- async nextFrame() {
402
- await new Promise((resolve) => requestAnimationFrame(resolve));
403
- }
404
428
  };
405
429
  __decorateClass([
406
430
  (0, import_inversify.inject)(import_free_layout_core.WorkflowDragService)
@@ -449,12 +473,12 @@ var createContainerNodePlugin = (0, import_core2.definePluginCreator)({
449
473
  // src/sub-canvas/hooks/use-node-size.ts
450
474
  var import_react = require("react");
451
475
  var import_free_layout_core2 = require("@flowgram.ai/free-layout-core");
452
- var import_document2 = require("@flowgram.ai/document");
476
+ var import_document3 = require("@flowgram.ai/document");
453
477
  var useNodeSize = () => {
454
478
  const node = (0, import_free_layout_core2.useCurrentEntity)();
455
479
  const nodeMeta = node.getNodeMeta();
456
- const { size = { width: 300, height: 200 }, isContainer } = nodeMeta;
457
- const transform = node.getData(import_document2.FlowNodeTransformData);
480
+ const { size = { width: 300, height: 200 }, isContainer: isContainer2 } = nodeMeta;
481
+ const transform = node.getData(import_document3.FlowNodeTransformData);
458
482
  const [width, setWidth] = (0, import_react.useState)(size.width);
459
483
  const [height, setHeight] = (0, import_react.useState)(size.height);
460
484
  const updatePorts = () => {
@@ -480,7 +504,7 @@ var useNodeSize = () => {
480
504
  (0, import_react.useEffect)(() => {
481
505
  updateSize();
482
506
  }, []);
483
- if (!isContainer) {
507
+ if (!isContainer2) {
484
508
  return;
485
509
  }
486
510
  return {
@@ -856,6 +880,7 @@ var SubCanvasRender = ({
856
880
  };
857
881
  // Annotate the CommonJS export names for ESM import in node:
858
882
  0 && (module.exports = {
883
+ ContainerUtils,
859
884
  NodeIntoContainerService,
860
885
  NodeIntoContainerType,
861
886
  SubCanvasBackground,