@flowgram.ai/free-container-plugin 0.1.0-alpha.10

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.
@@ -0,0 +1,840 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/node-into-container/constant.ts
13
+ var NodeIntoContainerType = /* @__PURE__ */ ((NodeIntoContainerType2) => {
14
+ NodeIntoContainerType2["In"] = "in";
15
+ NodeIntoContainerType2["Out"] = "out";
16
+ return NodeIntoContainerType2;
17
+ })(NodeIntoContainerType || {});
18
+
19
+ // src/node-into-container/service.ts
20
+ import { throttle } from "lodash";
21
+ import { inject, injectable } from "inversify";
22
+ import {
23
+ Rectangle,
24
+ DisposableCollection,
25
+ Emitter
26
+ } from "@flowgram.ai/utils";
27
+ import {
28
+ WorkflowDocument,
29
+ WorkflowDragService,
30
+ WorkflowLinesManager,
31
+ WorkflowOperationBaseService,
32
+ WorkflowSelectService
33
+ } from "@flowgram.ai/free-layout-core";
34
+ import { HistoryService } from "@flowgram.ai/free-history-plugin";
35
+ import { FlowNodeRenderData, FlowNodeBaseType } from "@flowgram.ai/document";
36
+ import { PlaygroundConfigEntity, TransformData } from "@flowgram.ai/core";
37
+ var NodeIntoContainerService = class {
38
+ constructor() {
39
+ this.emitter = new Emitter();
40
+ this.toDispose = new DisposableCollection();
41
+ this.on = this.emitter.event;
42
+ }
43
+ init() {
44
+ this.initState();
45
+ this.toDispose.push(this.emitter);
46
+ }
47
+ ready() {
48
+ this.toDispose.push(this.listenDragToContainer());
49
+ }
50
+ dispose() {
51
+ this.initState();
52
+ this.toDispose.dispose();
53
+ }
54
+ /** 将节点移出容器 */
55
+ async moveOutContainer(params) {
56
+ const { node } = params;
57
+ const parentNode = node.parent;
58
+ const containerNode = parentNode?.parent;
59
+ const nodeJSON = this.document.toNodeJSON(node);
60
+ if (!parentNode || !containerNode || !this.isContainer(parentNode) || !nodeJSON.meta?.position) {
61
+ return;
62
+ }
63
+ const parentTransform = parentNode.getData(TransformData);
64
+ this.operationService.moveNode(node, {
65
+ parent: containerNode
66
+ });
67
+ await this.nextFrame();
68
+ parentTransform.fireChange();
69
+ this.operationService.updateNodePosition(node, {
70
+ x: parentTransform.position.x + nodeJSON.meta.position.x,
71
+ y: parentTransform.position.y + nodeJSON.meta.position.y
72
+ });
73
+ this.emitter.fire({
74
+ type: "out" /* Out */,
75
+ node,
76
+ sourceContainer: parentNode,
77
+ targetContainer: containerNode
78
+ });
79
+ }
80
+ /** 能否将节点移出容器 */
81
+ canMoveOutContainer(node) {
82
+ const parentNode = node.parent;
83
+ const containerNode = parentNode?.parent;
84
+ if (!parentNode || !containerNode || !this.isContainer(parentNode)) {
85
+ return false;
86
+ }
87
+ const canDrop = this.dragService.canDropToNode({
88
+ dragNodeType: node.flowNodeType,
89
+ dragNode: node,
90
+ dropNodeType: containerNode?.flowNodeType,
91
+ dropNode: containerNode
92
+ });
93
+ if (!canDrop.allowDrop) {
94
+ return false;
95
+ }
96
+ return true;
97
+ }
98
+ /** 移除节点所有非法连线 */
99
+ async clearInvalidLines(params) {
100
+ const { dragNode, sourceParent } = params;
101
+ if (!dragNode) {
102
+ return;
103
+ }
104
+ if (dragNode.parent === sourceParent) {
105
+ return;
106
+ }
107
+ if (dragNode.parent?.flowNodeType === FlowNodeBaseType.GROUP || sourceParent?.flowNodeType === FlowNodeBaseType.GROUP) {
108
+ return;
109
+ }
110
+ await this.removeNodeLines(dragNode);
111
+ }
112
+ /** 移除节点连线 */
113
+ async removeNodeLines(node) {
114
+ const lines = this.linesManager.getAllLines();
115
+ lines.forEach((line) => {
116
+ if (line.from.id !== node.id && line.to?.id !== node.id) {
117
+ return;
118
+ }
119
+ line.dispose();
120
+ });
121
+ await this.nextFrame();
122
+ }
123
+ /** 初始化状态 */
124
+ initState() {
125
+ this.state = {
126
+ isDraggingNode: false,
127
+ isSkipEvent: false,
128
+ transforms: void 0,
129
+ dragNode: void 0,
130
+ dropNode: void 0,
131
+ sourceParent: void 0
132
+ };
133
+ }
134
+ /** 监听节点拖拽 */
135
+ listenDragToContainer() {
136
+ const draggingNode = (e) => this.draggingNode(e);
137
+ const throttledDraggingNode = throttle(draggingNode, 200);
138
+ return this.dragService.onNodesDrag(async (event) => {
139
+ if (this.selectService.selectedNodes.length !== 1) {
140
+ return;
141
+ }
142
+ if (event.type === "onDragStart") {
143
+ if (this.state.isSkipEvent) {
144
+ this.state.isSkipEvent = false;
145
+ return;
146
+ }
147
+ this.historyService.startTransaction();
148
+ this.state.isDraggingNode = true;
149
+ this.state.transforms = this.getContainerTransforms();
150
+ this.state.dragNode = this.selectService.selectedNodes[0];
151
+ this.state.dropNode = void 0;
152
+ this.state.sourceParent = this.state.dragNode?.parent;
153
+ await this.dragOutContainer(event);
154
+ }
155
+ if (event.type === "onDragging") {
156
+ throttledDraggingNode(event);
157
+ }
158
+ if (event.type === "onDragEnd") {
159
+ if (this.state.isSkipEvent) {
160
+ return;
161
+ }
162
+ throttledDraggingNode.cancel();
163
+ draggingNode(event);
164
+ await this.dropNodeToContainer();
165
+ await this.clearInvalidLines({
166
+ dragNode: this.state.dragNode,
167
+ sourceParent: this.state.sourceParent
168
+ });
169
+ this.setDropNode(void 0);
170
+ this.initState();
171
+ this.historyService.endTransaction();
172
+ }
173
+ });
174
+ }
175
+ /** 监听节点拖拽出容器 */
176
+ async dragOutContainer(event) {
177
+ const { dragNode } = this.state;
178
+ const activated = event.triggerEvent.metaKey || event.triggerEvent.ctrlKey;
179
+ if (!activated || // 必须按住指定按键
180
+ !dragNode || // 必须有一个节点
181
+ !this.canMoveOutContainer(dragNode)) {
182
+ return;
183
+ }
184
+ this.moveOutContainer({ node: dragNode });
185
+ this.state.isSkipEvent = true;
186
+ event.dragger.stop(event.dragEvent.clientX, event.dragEvent.clientY);
187
+ await this.nextFrame();
188
+ this.dragService.startDragSelectedNodes(event.triggerEvent);
189
+ }
190
+ /** 获取重叠位置 */
191
+ getCollisionTransform(params) {
192
+ const { targetRect, targetPoint, transforms, withPadding = false } = params;
193
+ const collisionTransform = transforms.find((transform) => {
194
+ const { bounds, entity } = transform;
195
+ const padding = withPadding ? this.document.layout.getPadding(entity) : { left: 0, right: 0 };
196
+ const transformRect = new Rectangle(
197
+ bounds.x + padding.left + padding.right,
198
+ bounds.y,
199
+ bounds.width,
200
+ bounds.height
201
+ );
202
+ if (targetRect) {
203
+ return this.isRectIntersects(targetRect, transformRect);
204
+ }
205
+ if (targetPoint) {
206
+ return this.isPointInRect(targetPoint, transformRect);
207
+ }
208
+ return false;
209
+ });
210
+ return collisionTransform;
211
+ }
212
+ /** 设置放置节点高亮 */
213
+ setDropNode(dropNode) {
214
+ if (this.state.dropNode === dropNode) {
215
+ return;
216
+ }
217
+ if (this.state.dropNode) {
218
+ const renderData2 = this.state.dropNode.getData(FlowNodeRenderData);
219
+ const renderDom2 = renderData2.node?.children?.[0];
220
+ if (renderDom2) {
221
+ renderDom2.classList.remove("selected");
222
+ }
223
+ }
224
+ this.state.dropNode = dropNode;
225
+ if (!dropNode) {
226
+ return;
227
+ }
228
+ const renderData = dropNode.getData(FlowNodeRenderData);
229
+ const renderDom = renderData.node?.children?.[0];
230
+ if (renderDom) {
231
+ renderDom.classList.add("selected");
232
+ }
233
+ }
234
+ /** 获取容器节点transforms */
235
+ getContainerTransforms() {
236
+ return this.document.getAllNodes().filter((node) => {
237
+ if (node.originParent) {
238
+ return node.getNodeMeta().selectable && node.originParent.getNodeMeta().selectable;
239
+ }
240
+ return node.getNodeMeta().selectable;
241
+ }).filter((node) => this.isContainer(node)).sort((a, b) => {
242
+ const aIndex = a.renderData.stackIndex;
243
+ const bIndex = b.renderData.stackIndex;
244
+ return bIndex - aIndex;
245
+ }).map((node) => node.transform);
246
+ }
247
+ /** 放置节点到容器 */
248
+ async dropNodeToContainer() {
249
+ const { dropNode, dragNode, isDraggingNode } = this.state;
250
+ if (!isDraggingNode || !dragNode || !dropNode) {
251
+ return;
252
+ }
253
+ return await this.moveIntoContainer({
254
+ node: dragNode,
255
+ containerNode: dropNode
256
+ });
257
+ }
258
+ /** 拖拽节点 */
259
+ draggingNode(nodeDragEvent) {
260
+ const { dragNode, isDraggingNode, transforms = [] } = this.state;
261
+ if (!isDraggingNode || !dragNode || !transforms?.length) {
262
+ return this.setDropNode(void 0);
263
+ }
264
+ const mousePos = this.playgroundConfig.getPosFromMouseEvent(nodeDragEvent.dragEvent);
265
+ const availableTransforms = transforms.filter(
266
+ (transform) => transform.entity.id !== dragNode.id
267
+ );
268
+ const collisionTransform = this.getCollisionTransform({
269
+ targetPoint: mousePos,
270
+ transforms: availableTransforms
271
+ });
272
+ const dropNode = collisionTransform?.entity;
273
+ const canDrop = this.canDropToContainer({
274
+ dragNode,
275
+ dropNode
276
+ });
277
+ if (!canDrop) {
278
+ return this.setDropNode(void 0);
279
+ }
280
+ return this.setDropNode(dropNode);
281
+ }
282
+ /** 判断能否将节点拖入容器 */
283
+ canDropToContainer(params) {
284
+ const { dragNode, dropNode } = params;
285
+ const isDropContainer = dropNode?.getNodeMeta().isContainer;
286
+ if (!dropNode || !isDropContainer || this.isParent(dragNode, dropNode)) {
287
+ return false;
288
+ }
289
+ if (dragNode.flowNodeType === FlowNodeBaseType.GROUP && dropNode.flowNodeType !== FlowNodeBaseType.GROUP) {
290
+ return false;
291
+ }
292
+ const canDrop = this.dragService.canDropToNode({
293
+ dragNodeType: dragNode.flowNodeType,
294
+ dropNodeType: dropNode?.flowNodeType,
295
+ dragNode,
296
+ dropNode
297
+ });
298
+ if (!canDrop.allowDrop) {
299
+ return false;
300
+ }
301
+ return true;
302
+ }
303
+ /** 判断一个节点是否为另一个节点的父节点(向上查找直到根节点) */
304
+ isParent(node, parent) {
305
+ let current = node.parent;
306
+ while (current) {
307
+ if (current.id === parent.id) {
308
+ return true;
309
+ }
310
+ current = current.parent;
311
+ }
312
+ return false;
313
+ }
314
+ /** 将节点移入容器 */
315
+ async moveIntoContainer(params) {
316
+ const { node, containerNode } = params;
317
+ const parentNode = node.parent;
318
+ this.operationService.moveNode(node, {
319
+ parent: containerNode
320
+ });
321
+ this.operationService.updateNodePosition(node, this.adjustSubNodePosition(node, containerNode));
322
+ await this.nextFrame();
323
+ this.emitter.fire({
324
+ type: "in" /* In */,
325
+ node,
326
+ sourceContainer: parentNode,
327
+ targetContainer: containerNode
328
+ });
329
+ }
330
+ /**
331
+ * 如果存在容器节点,且传入鼠标坐标,需要用容器的坐标减去传入的鼠标坐标
332
+ */
333
+ adjustSubNodePosition(targetNode, containerNode) {
334
+ if (containerNode.flowNodeType === FlowNodeBaseType.ROOT) {
335
+ return targetNode.transform.position;
336
+ }
337
+ const nodeWorldTransform = targetNode.transform.transform.worldTransform;
338
+ const containerWorldTransform = containerNode.transform.transform.worldTransform;
339
+ const nodePosition = {
340
+ x: nodeWorldTransform.tx,
341
+ y: nodeWorldTransform.ty
342
+ };
343
+ const isParentEmpty = !containerNode.children || containerNode.children.length === 0;
344
+ const containerPadding = this.document.layout.getPadding(containerNode);
345
+ if (isParentEmpty) {
346
+ return {
347
+ x: 0,
348
+ y: containerPadding.top
349
+ };
350
+ } else {
351
+ return {
352
+ x: nodePosition.x - containerWorldTransform.tx,
353
+ y: nodePosition.y - containerWorldTransform.ty
354
+ };
355
+ }
356
+ }
357
+ isContainer(node) {
358
+ return node?.getNodeMeta().isContainer ?? false;
359
+ }
360
+ /** 判断点是否在矩形内 */
361
+ isPointInRect(point, rect) {
362
+ return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
363
+ }
364
+ /** 判断两个矩形是否相交 */
365
+ isRectIntersects(rectA, rectB) {
366
+ const hasHorizontalOverlap = rectA.right > rectB.left && rectA.left < rectB.right;
367
+ const hasVerticalOverlap = rectA.bottom > rectB.top && rectA.top < rectB.bottom;
368
+ return hasHorizontalOverlap && hasVerticalOverlap;
369
+ }
370
+ async nextFrame() {
371
+ await new Promise((resolve) => requestAnimationFrame(resolve));
372
+ }
373
+ };
374
+ __decorateClass([
375
+ inject(WorkflowDragService)
376
+ ], NodeIntoContainerService.prototype, "dragService", 2);
377
+ __decorateClass([
378
+ inject(WorkflowDocument)
379
+ ], NodeIntoContainerService.prototype, "document", 2);
380
+ __decorateClass([
381
+ inject(PlaygroundConfigEntity)
382
+ ], NodeIntoContainerService.prototype, "playgroundConfig", 2);
383
+ __decorateClass([
384
+ inject(WorkflowOperationBaseService)
385
+ ], NodeIntoContainerService.prototype, "operationService", 2);
386
+ __decorateClass([
387
+ inject(WorkflowLinesManager)
388
+ ], NodeIntoContainerService.prototype, "linesManager", 2);
389
+ __decorateClass([
390
+ inject(HistoryService)
391
+ ], NodeIntoContainerService.prototype, "historyService", 2);
392
+ __decorateClass([
393
+ inject(WorkflowSelectService)
394
+ ], NodeIntoContainerService.prototype, "selectService", 2);
395
+ NodeIntoContainerService = __decorateClass([
396
+ injectable()
397
+ ], NodeIntoContainerService);
398
+
399
+ // src/node-into-container/plugin.tsx
400
+ import { definePluginCreator } from "@flowgram.ai/core";
401
+ var createContainerNodePlugin = definePluginCreator({
402
+ onBind: ({ bind }) => {
403
+ bind(NodeIntoContainerService).toSelf().inSingletonScope();
404
+ },
405
+ onInit(ctx, options) {
406
+ ctx.get(NodeIntoContainerService).init();
407
+ },
408
+ onReady(ctx, options) {
409
+ if (options.disableNodeIntoContainer !== true) {
410
+ ctx.get(NodeIntoContainerService).ready();
411
+ }
412
+ },
413
+ onDispose(ctx) {
414
+ ctx.get(NodeIntoContainerService).dispose();
415
+ }
416
+ });
417
+
418
+ // src/sub-canvas/hooks/use-node-size.ts
419
+ import { useState, useEffect } from "react";
420
+ import {
421
+ useCurrentEntity,
422
+ WorkflowNodePortsData
423
+ } from "@flowgram.ai/free-layout-core";
424
+ import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
425
+ var useNodeSize = () => {
426
+ const node = useCurrentEntity();
427
+ const nodeMeta = node.getNodeMeta();
428
+ const { size = { width: 300, height: 200 }, isContainer } = nodeMeta;
429
+ const transform = node.getData(FlowNodeTransformData2);
430
+ const [width, setWidth] = useState(size.width);
431
+ const [height, setHeight] = useState(size.height);
432
+ const updatePorts = () => {
433
+ const portsData = node.getData(WorkflowNodePortsData);
434
+ portsData.updateDynamicPorts();
435
+ };
436
+ const updateSize = () => {
437
+ if (node.blocks.length === 0) {
438
+ setWidth(size.width);
439
+ setHeight(size.height);
440
+ return;
441
+ }
442
+ setWidth(transform.bounds.width);
443
+ setHeight(transform.bounds.height);
444
+ };
445
+ useEffect(() => {
446
+ const dispose = transform.onDataChange(() => {
447
+ updateSize();
448
+ updatePorts();
449
+ });
450
+ return () => dispose.dispose();
451
+ }, [transform, width, height]);
452
+ useEffect(() => {
453
+ updateSize();
454
+ }, []);
455
+ if (!isContainer) {
456
+ return;
457
+ }
458
+ return {
459
+ width,
460
+ height
461
+ };
462
+ };
463
+
464
+ // src/sub-canvas/hooks/use-sync-node-render-size.ts
465
+ import { useLayoutEffect } from "react";
466
+ import { useCurrentEntity as useCurrentEntity2 } from "@flowgram.ai/free-layout-core";
467
+ var useSyncNodeRenderSize = (nodeSize) => {
468
+ const node = useCurrentEntity2();
469
+ useLayoutEffect(() => {
470
+ if (!nodeSize) {
471
+ return;
472
+ }
473
+ node.renderData.node.style.width = nodeSize.width + "px";
474
+ node.renderData.node.style.height = nodeSize.height + "px";
475
+ }, [nodeSize?.width, nodeSize?.height]);
476
+ };
477
+
478
+ // src/sub-canvas/components/background/index.tsx
479
+ import React from "react";
480
+ import { useCurrentEntity as useCurrentEntity3 } from "@flowgram.ai/free-layout-core";
481
+ import { useService } from "@flowgram.ai/core";
482
+ import { BackgroundConfig } from "@flowgram.ai/background-plugin";
483
+
484
+ // src/sub-canvas/components/background/style.ts
485
+ import styled from "styled-components";
486
+ var SubCanvasBackgroundStyle = styled.div`
487
+ width: 100%;
488
+ height: 100%;
489
+ inset: 56px 18px 18px;
490
+ /* 背景色现在通过 style 属性动态设置 */
491
+ `;
492
+
493
+ // src/sub-canvas/components/background/index.tsx
494
+ var SubCanvasBackground = () => {
495
+ const node = useCurrentEntity3();
496
+ let backgroundConfig = {};
497
+ try {
498
+ backgroundConfig = useService(BackgroundConfig);
499
+ } catch (error) {
500
+ }
501
+ const gridSize = backgroundConfig.gridSize ?? 20;
502
+ const dotSize = backgroundConfig.dotSize ?? 1;
503
+ const dotColor = backgroundConfig.dotColor ?? "#eceeef";
504
+ const dotOpacity = backgroundConfig.dotOpacity ?? 0.5;
505
+ const backgroundColor = backgroundConfig.backgroundColor ?? "#f2f3f5";
506
+ const dotFillColor = backgroundConfig.dotFillColor === dotColor ? "" : backgroundConfig.dotFillColor;
507
+ const patternId = `sub-canvas-dot-pattern-${node.id}`;
508
+ return /* @__PURE__ */ React.createElement(
509
+ SubCanvasBackgroundStyle,
510
+ {
511
+ className: "sub-canvas-background",
512
+ "data-flow-editor-selectable": "true",
513
+ style: { backgroundColor }
514
+ },
515
+ /* @__PURE__ */ React.createElement("svg", { width: "100%", height: "100%" }, /* @__PURE__ */ React.createElement("pattern", { id: patternId, width: gridSize, height: gridSize, patternUnits: "userSpaceOnUse" }, /* @__PURE__ */ React.createElement(
516
+ "circle",
517
+ {
518
+ cx: dotSize,
519
+ cy: dotSize,
520
+ r: dotSize,
521
+ stroke: dotColor,
522
+ fill: dotFillColor,
523
+ fillOpacity: dotOpacity
524
+ }
525
+ )), /* @__PURE__ */ React.createElement(
526
+ "rect",
527
+ {
528
+ width: "100%",
529
+ height: "100%",
530
+ fill: `url(#${patternId})`,
531
+ "data-node-panel-container": node.id
532
+ }
533
+ ))
534
+ );
535
+ };
536
+
537
+ // src/sub-canvas/components/border/index.tsx
538
+ import React2 from "react";
539
+
540
+ // src/sub-canvas/components/border/style.ts
541
+ import styled2 from "styled-components";
542
+ var SubCanvasBorderStyle = styled2.div`
543
+ pointer-events: none;
544
+
545
+ position: relative;
546
+
547
+ display: flex;
548
+ align-items: center;
549
+
550
+ width: 100%;
551
+ height: 100%;
552
+
553
+ background-color: transparent;
554
+ border: 1px solid var(--coz-stroke-plus, rgba(6, 7, 9, 15%));
555
+ border-color: var(--coz-bg-plus, rgb(249, 249, 249));
556
+ border-style: solid;
557
+ border-width: 8px;
558
+ border-radius: 8px;
559
+
560
+ &::before {
561
+ content: '';
562
+
563
+ position: absolute;
564
+ z-index: 0;
565
+ inset: -4px;
566
+
567
+ background-color: transparent;
568
+ border-color: var(--coz-bg-plus, rgb(249, 249, 249));
569
+ border-style: solid;
570
+ border-width: 4px;
571
+ border-radius: 8px;
572
+ }
573
+ `;
574
+
575
+ // src/sub-canvas/components/border/index.tsx
576
+ var SubCanvasBorder = ({ style, children }) => /* @__PURE__ */ React2.createElement(
577
+ SubCanvasBorderStyle,
578
+ {
579
+ className: "sub-canvas-border",
580
+ style: {
581
+ ...style
582
+ }
583
+ },
584
+ children
585
+ );
586
+
587
+ // src/sub-canvas/components/render/index.tsx
588
+ import React5 from "react";
589
+
590
+ // src/sub-canvas/components/render/style.ts
591
+ import styled3 from "styled-components";
592
+ var SubCanvasRenderStyle = styled3.div`
593
+ width: 100%;
594
+ height: 100%;
595
+ `;
596
+
597
+ // src/sub-canvas/components/tips/index.tsx
598
+ import React4 from "react";
599
+ import { I18n } from "@flowgram.ai/i18n";
600
+
601
+ // src/sub-canvas/components/tips/use-control.ts
602
+ import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
603
+ import { useCurrentEntity as useCurrentEntity4 } from "@flowgram.ai/free-layout-core";
604
+ import { useService as useService2 } from "@flowgram.ai/core";
605
+
606
+ // src/sub-canvas/components/tips/global-store.ts
607
+ var STORAGE_KEY = "workflow-move-into-sub-canvas-tip-visible";
608
+ var STORAGE_VALUE = "false";
609
+ var TipsGlobalStore = class _TipsGlobalStore {
610
+ constructor() {
611
+ this.closed = false;
612
+ }
613
+ static get instance() {
614
+ if (!this._instance) {
615
+ this._instance = new _TipsGlobalStore();
616
+ }
617
+ return this._instance;
618
+ }
619
+ isClosed() {
620
+ return this.isCloseForever() || this.closed;
621
+ }
622
+ close() {
623
+ this.closed = true;
624
+ }
625
+ isCloseForever() {
626
+ return localStorage.getItem(STORAGE_KEY) === STORAGE_VALUE;
627
+ }
628
+ closeForever() {
629
+ localStorage.setItem(STORAGE_KEY, STORAGE_VALUE);
630
+ }
631
+ };
632
+
633
+ // src/sub-canvas/components/tips/use-control.ts
634
+ var useControlTips = () => {
635
+ const node = useCurrentEntity4();
636
+ const [visible, setVisible] = useState2(false);
637
+ const globalStore = TipsGlobalStore.instance;
638
+ const nodeIntoContainerService = useService2(NodeIntoContainerService);
639
+ const show = useCallback(() => {
640
+ if (globalStore.isClosed()) {
641
+ return;
642
+ }
643
+ setVisible(true);
644
+ }, [globalStore]);
645
+ const close = useCallback(() => {
646
+ globalStore.close();
647
+ setVisible(false);
648
+ }, [globalStore]);
649
+ const closeForever = useCallback(() => {
650
+ globalStore.closeForever();
651
+ close();
652
+ }, [close, globalStore]);
653
+ useEffect2(() => {
654
+ const inDisposer = nodeIntoContainerService.on((e) => {
655
+ if (e.type !== "in" /* In */) {
656
+ return;
657
+ }
658
+ if (e.targetContainer === node) {
659
+ show();
660
+ }
661
+ });
662
+ const outDisposer = nodeIntoContainerService.on((e) => {
663
+ if (e.type !== "out" /* Out */) {
664
+ return;
665
+ }
666
+ if (e.sourceContainer === node && !node.blocks.length) {
667
+ setVisible(false);
668
+ }
669
+ });
670
+ return () => {
671
+ inDisposer.dispose();
672
+ outDisposer.dispose();
673
+ };
674
+ }, [nodeIntoContainerService, node, show, close, visible]);
675
+ return {
676
+ visible,
677
+ close,
678
+ closeForever
679
+ };
680
+ };
681
+
682
+ // src/sub-canvas/components/tips/style.ts
683
+ import styled4 from "styled-components";
684
+ var SubCanvasTipsStyle = styled4.div`
685
+ pointer-events: auto;
686
+ position: absolute;
687
+ top: 0;
688
+
689
+ width: 100%;
690
+ height: 28px;
691
+
692
+ .container {
693
+ height: 100%;
694
+ background-color: #e4e6f5;
695
+ border-radius: 4px 4px 0 0;
696
+
697
+ .content {
698
+ overflow: hidden;
699
+ display: inline-flex;
700
+ align-items: center;
701
+ justify-content: center;
702
+
703
+ width: 100%;
704
+ height: 100%;
705
+
706
+ .text {
707
+ font-size: 14px;
708
+ font-weight: 400;
709
+ font-style: normal;
710
+ line-height: 20px;
711
+ color: rgba(15, 21, 40, 82%);
712
+ text-overflow: ellipsis;
713
+ }
714
+
715
+ .custom-content {
716
+ display: flex;
717
+ align-items: center;
718
+ justify-content: center;
719
+ width: 100%;
720
+ height: 100%;
721
+
722
+ /* 为自定义内容提供默认样式,但允许覆盖 */
723
+ font-size: 14px;
724
+ font-weight: 400;
725
+ line-height: 20px;
726
+ color: rgba(15, 21, 40, 82%);
727
+
728
+ /* 确保自定义内容不会超出容器 */
729
+ overflow: hidden;
730
+ }
731
+
732
+ .space {
733
+ width: 128px;
734
+ }
735
+ }
736
+
737
+ .actions {
738
+ position: absolute;
739
+ top: 0;
740
+ right: 0;
741
+
742
+ display: flex;
743
+ gap: 8px;
744
+ align-items: center;
745
+
746
+ height: 28px;
747
+ padding: 0 16px;
748
+
749
+ .close-forever {
750
+ cursor: pointer;
751
+
752
+ padding: 0 3px;
753
+
754
+ font-size: 12px;
755
+ font-weight: 400;
756
+ font-style: normal;
757
+ line-height: 12px;
758
+ color: rgba(32, 41, 69, 62%);
759
+ }
760
+
761
+ .close {
762
+ display: flex;
763
+ cursor: pointer;
764
+ height: 100%;
765
+ align-items: center;
766
+ }
767
+ }
768
+ }
769
+ `;
770
+
771
+ // src/sub-canvas/components/tips/is-mac-os.ts
772
+ var isMacOS = /(Macintosh|MacIntel|MacPPC|Mac68K|iPad)/.test(navigator.userAgent);
773
+
774
+ // src/sub-canvas/components/tips/icon-close.tsx
775
+ import React3 from "react";
776
+ var IconClose = () => /* @__PURE__ */ React3.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "none", viewBox: "0 0 16 16" }, /* @__PURE__ */ React3.createElement(
777
+ "path",
778
+ {
779
+ fill: "#060709",
780
+ fillOpacity: "0.5",
781
+ d: "M12.13 12.128a.5.5 0 0 0 .001-.706L8.71 8l3.422-3.423a.5.5 0 0 0-.001-.705.5.5 0 0 0-.706-.002L8.002 7.293 4.579 3.87a.5.5 0 0 0-.705.002.5.5 0 0 0-.002.705L7.295 8l-3.423 3.422a.5.5 0 0 0 .002.706c.195.195.51.197.705.001l3.423-3.422 3.422 3.422c.196.196.51.194.706-.001"
782
+ }
783
+ ));
784
+
785
+ // src/sub-canvas/components/tips/index.tsx
786
+ var SubCanvasTips = ({ tipText, neverRemindText }) => {
787
+ const { visible, close, closeForever } = useControlTips();
788
+ const displayContent = tipText || I18n.t("Hold {{key}} to drag node out", { key: isMacOS ? "Cmd \u2318" : "Ctrl" });
789
+ if (!visible) {
790
+ return null;
791
+ }
792
+ return /* @__PURE__ */ React4.createElement(SubCanvasTipsStyle, { className: "sub-canvas-tips" }, /* @__PURE__ */ React4.createElement("div", { className: "container" }, /* @__PURE__ */ React4.createElement("div", { className: "content" }, typeof displayContent === "string" ? /* @__PURE__ */ React4.createElement("p", { className: "text" }, displayContent) : /* @__PURE__ */ React4.createElement("div", { className: "custom-content" }, displayContent), /* @__PURE__ */ React4.createElement(
793
+ "div",
794
+ {
795
+ className: "space",
796
+ style: {
797
+ width: 0
798
+ }
799
+ }
800
+ )), /* @__PURE__ */ React4.createElement("div", { className: "actions" }, /* @__PURE__ */ React4.createElement("p", { className: "close-forever", onClick: closeForever }, neverRemindText || I18n.t("Never Remind")), /* @__PURE__ */ React4.createElement("div", { className: "close", onClick: close }, /* @__PURE__ */ React4.createElement(IconClose, null)))));
801
+ };
802
+
803
+ // src/sub-canvas/components/render/index.tsx
804
+ var SubCanvasRender = ({
805
+ className,
806
+ style,
807
+ offsetY = 0,
808
+ tipText
809
+ }) => {
810
+ const nodeSize = useNodeSize();
811
+ const nodeHeight = nodeSize?.height ?? 0;
812
+ useSyncNodeRenderSize(nodeSize);
813
+ return /* @__PURE__ */ React5.createElement(
814
+ SubCanvasRenderStyle,
815
+ {
816
+ className: `sub-canvas-render ${className ?? ""}`,
817
+ style: {
818
+ height: nodeHeight + offsetY,
819
+ ...style
820
+ },
821
+ "data-flow-editor-selectable": "true",
822
+ onDragStart: (e) => {
823
+ e.stopPropagation();
824
+ }
825
+ },
826
+ /* @__PURE__ */ React5.createElement(SubCanvasBorder, null, /* @__PURE__ */ React5.createElement(SubCanvasBackground, null), /* @__PURE__ */ React5.createElement(SubCanvasTips, { tipText }))
827
+ );
828
+ };
829
+ export {
830
+ NodeIntoContainerService,
831
+ NodeIntoContainerType,
832
+ SubCanvasBackground,
833
+ SubCanvasBorder,
834
+ SubCanvasRender,
835
+ SubCanvasTips,
836
+ createContainerNodePlugin,
837
+ useNodeSize,
838
+ useSyncNodeRenderSize
839
+ };
840
+ //# sourceMappingURL=index.js.map