@nocobase/flow-engine 2.1.0-beta.11 → 2.1.0-beta.12

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 (36) hide show
  1. package/lib/components/FlowModelRenderer.d.ts +1 -1
  2. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
  3. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +48 -9
  4. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +19 -43
  5. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +332 -296
  6. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
  7. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +272 -0
  8. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
  9. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +247 -0
  10. package/lib/components/subModel/AddSubModelButton.js +11 -0
  11. package/lib/flowContext.js +27 -0
  12. package/lib/runjs-context/setup.js +1 -0
  13. package/lib/runjs-context/snippets/index.js +13 -2
  14. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
  15. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
  16. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
  17. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
  18. package/package.json +5 -4
  19. package/src/__tests__/flowContext.test.ts +65 -1
  20. package/src/__tests__/runjsContext.test.ts +3 -0
  21. package/src/__tests__/runjsContextRuntime.test.ts +2 -0
  22. package/src/__tests__/runjsSnippets.test.ts +21 -0
  23. package/src/components/FlowModelRenderer.tsx +3 -1
  24. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +17 -7
  25. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +63 -9
  26. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +457 -440
  27. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +95 -0
  28. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +547 -0
  29. package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +358 -0
  30. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +281 -0
  31. package/src/components/subModel/AddSubModelButton.tsx +15 -1
  32. package/src/flowContext.ts +30 -0
  33. package/src/runjs-context/setup.ts +1 -0
  34. package/src/runjs-context/snippets/index.ts +12 -1
  35. package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
  36. package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
@@ -41,6 +41,7 @@ __export(FlowsFloatContextMenu_exports, {
41
41
  });
42
42
  module.exports = __toCommonJS(FlowsFloatContextMenu_exports);
43
43
  var import_react = __toESM(require("react"));
44
+ var import_react_dom = require("react-dom");
44
45
  var import_antd = require("antd");
45
46
  var import_css = require("@emotion/css");
46
47
  var import_hooks = require("../../../../hooks");
@@ -48,238 +49,252 @@ var import_provider = require("../../../../provider");
48
49
  var import_utils = require("../../../../utils");
49
50
  var import__ = require("../../../..");
50
51
  var import_reactive = require("../../../../reactive");
51
- const detectButtonInDOM = /* @__PURE__ */ __name((container) => {
52
- if (!container) return false;
53
- const directChildren = container.children;
54
- for (let i = 0; i < directChildren.length; i++) {
55
- const child = directChildren[i];
56
- if (child.tagName === "BUTTON" || child.getAttribute("role") === "button" || child.classList.contains("ant-btn")) {
57
- return true;
58
- }
59
- }
60
- return false;
61
- }, "detectButtonInDOM");
62
- const renderToolbarItems = /* @__PURE__ */ __name((model, showDeleteButton, showCopyUidButton, flowEngine, settingsMenuLevel, extraToolbarItems) => {
63
- var _a, _b;
64
- const toolbarItems = ((_b = (_a = flowEngine == null ? void 0 : flowEngine.flowSettings) == null ? void 0 : _a.getToolbarItems) == null ? void 0 : _b.call(_a)) || [];
65
- const allToolbarItems = [...toolbarItems, ...extraToolbarItems || []];
66
- allToolbarItems.sort((a, b) => (a.sort || 0) - (b.sort || 0)).reverse();
67
- return allToolbarItems.filter((itemConfig) => {
68
- return itemConfig.visible ? itemConfig.visible(model) : true;
69
- }).map((itemConfig) => {
70
- const ItemComponent = itemConfig.component;
71
- if (itemConfig.key === "settings-menu") {
72
- return /* @__PURE__ */ import_react.default.createElement(
73
- ItemComponent,
74
- {
75
- key: itemConfig.key,
76
- model,
77
- id: model.uid,
78
- showDeleteButton,
79
- showCopyUidButton,
80
- menuLevels: settingsMenuLevel
81
- }
82
- );
83
- }
84
- return /* @__PURE__ */ import_react.default.createElement(ItemComponent, { key: itemConfig.key, model });
85
- });
86
- }, "renderToolbarItems");
87
- const TOOLBAR_ITEM_WIDTH = 19;
88
- const toolbarPositionToCSS = {
89
- inside: `
90
- top: 2px;
91
- `,
92
- above: `
93
- top: 0px;
94
- transform: translateY(-100%);
95
- padding-bottom: 0px;
96
- margin-bottom: -2px;
97
- `,
98
- below: `
99
- top: 0px;
100
- transform: translateY(100%);
101
- padding-top: 2px;
102
- margin-top: -2px;
103
- `
104
- };
105
- const floatContainerStyles = /* @__PURE__ */ __name(({ showBackground, showBorder, ctx, toolbarPosition = "inside", toolbarCount }) => import_css.css`
52
+ var import_useFloatToolbarPortal = require("./useFloatToolbarPortal");
53
+ var import_useFloatToolbarVisibility = require("./useFloatToolbarVisibility");
54
+ const TOOLBAR_Z_INDEX = 999;
55
+ const hostContainerStyles = import_css.css`
106
56
  position: relative;
107
57
 
108
- /* 当检测到button时使用inline-block */
109
58
  &.has-button-child {
110
59
  display: inline-block;
111
60
  }
61
+ `;
62
+ const toolbarPositionClassNames = {
63
+ inside: "nb-toolbar-position-inside",
64
+ above: "nb-toolbar-position-above",
65
+ below: "nb-toolbar-position-below"
66
+ };
67
+ const toolbarContainerStyles = /* @__PURE__ */ __name(({
68
+ showBackground,
69
+ showBorder,
70
+ ctx
71
+ }) => import_css.css`
72
+ z-index: ${TOOLBAR_Z_INDEX};
73
+ opacity: 0;
74
+ pointer-events: none;
75
+ overflow: visible;
76
+ transition: opacity 0.12s ease;
77
+ background: ${showBackground ? "var(--colorBgSettingsHover)" : "transparent"};
78
+ border: ${showBorder ? "2px solid var(--colorBorderSettingsHover)" : "none"};
79
+ border-radius: ${ctx.themeToken.borderRadiusLG}px;
112
80
 
113
- /* 正常的hover行为 - 添加延迟显示 */
114
- &:hover > .nb-toolbar-container {
81
+ &.nb-toolbar-visible {
115
82
  opacity: 1;
116
83
  transition-delay: 0.1s;
84
+ }
117
85
 
118
- .nb-toolbar-container-icons {
119
- display: block;
120
- }
86
+ &.nb-toolbar-portal {
87
+ top: 0;
88
+ left: 0;
121
89
  }
122
90
 
123
- /* 当有.hide-parent-menu类时隐藏菜单 */
124
- &.hide-parent-menu > .nb-toolbar-container {
125
- opacity: 0 !important;
91
+ &.nb-toolbar-portal-fixed {
92
+ position: fixed;
126
93
  }
127
94
 
128
- > .nb-toolbar-container {
95
+ &.nb-toolbar-portal-absolute {
129
96
  position: absolute;
130
- top: 0;
131
- bottom: 0;
132
- left: 0;
133
- right: 0;
134
- z-index: 999;
135
- opacity: 0;
136
- background: ${showBackground ? "var(--colorBgSettingsHover)" : ""};
137
- border: ${showBorder ? "2px solid var(--colorBorderSettingsHover)" : ""};
138
- border-radius: ${ctx.themeToken.borderRadiusLG}px;
97
+ }
98
+
99
+ &.nb-in-template {
100
+ background: var(--colorTemplateBgSettingsHover);
101
+ }
102
+
103
+ > .nb-toolbar-container-title {
139
104
  pointer-events: none;
140
- min-width: ${TOOLBAR_ITEM_WIDTH * toolbarCount}px;
105
+ position: absolute;
106
+ top: 2px;
107
+ left: 2px;
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 4px;
111
+ height: 16px;
112
+ padding: 0;
113
+ font-size: 12px;
114
+ line-height: 16px;
115
+ border-bottom-right-radius: 2px;
116
+ border-radius: 2px;
141
117
 
142
- &.nb-in-template {
143
- background: var(--colorTemplateBgSettingsHover);
118
+ .title-tag {
119
+ display: inline-flex;
120
+ padding: 0 3px;
121
+ border-radius: 2px;
122
+ background: var(--colorSettings);
123
+ color: #fff;
144
124
  }
125
+ }
145
126
 
146
- > .nb-toolbar-container-title {
147
- pointer-events: none;
148
- position: absolute;
149
- font-size: 12px;
150
- padding: 0;
151
- line-height: 16px;
152
- height: 16px;
153
- border-bottom-right-radius: 2px;
154
- border-radius: 2px;
155
- top: 2px;
156
- left: 2px;
157
- display: flex;
158
- align-items: center;
159
- gap: 4px;
127
+ > .nb-toolbar-container-icons {
128
+ display: none;
129
+ position: absolute;
130
+ right: 2px;
131
+ line-height: 16px;
132
+ pointer-events: all;
160
133
 
161
- .title-tag {
162
- padding: 0 3px;
163
- border-radius: 2px;
164
- background: var(--colorSettings);
165
- color: #fff;
166
- display: inline-flex;
167
- }
134
+ &.nb-toolbar-position-inside {
135
+ top: 2px;
168
136
  }
169
137
 
170
- > .nb-toolbar-container-icons {
171
- display: none; // 防止遮挡其它 icons
172
- position: absolute;
173
- right: 2px;
174
- ${toolbarPositionToCSS[toolbarPosition] || ""}
175
- line-height: 16px;
176
- pointer-events: all;
138
+ &.nb-toolbar-position-above {
139
+ top: 0;
140
+ transform: translateY(-100%);
141
+ padding-bottom: 0;
142
+ margin-bottom: -2px;
143
+ }
177
144
 
178
- .ant-space-item {
179
- background-color: var(--colorSettings);
180
- color: #fff;
181
- line-height: 16px;
182
- width: 16px;
183
- height: 16px;
184
- padding: 2px;
185
- display: flex;
186
- align-items: center;
187
- justify-content: center;
188
- }
145
+ &.nb-toolbar-position-below {
146
+ top: 0;
147
+ transform: translateY(100%);
148
+ padding-top: 2px;
149
+ margin-top: -2px;
189
150
  }
190
151
 
191
- /* 拖拽把手样式 - 参考 AirTable 样式 */
192
- > .resize-handle {
193
- position: absolute;
194
- pointer-events: all;
195
- background: var(--colorSettings);
196
- opacity: 0.6;
197
- border-radius: 4px;
152
+ .ant-space-item {
198
153
  display: flex;
199
154
  align-items: center;
200
155
  justify-content: center;
156
+ width: 16px;
157
+ height: 16px;
158
+ padding: 2px;
159
+ line-height: 16px;
160
+ background-color: var(--colorSettings);
161
+ color: #fff;
162
+ }
163
+ }
201
164
 
202
- &:hover {
203
- opacity: 0.9;
204
- background: var(--colorSettingsHover, var(--colorSettings));
205
- }
165
+ &.nb-toolbar-visible > .nb-toolbar-container-icons {
166
+ display: block;
167
+ }
206
168
 
207
- &::before {
208
- content: '';
209
- position: absolute;
210
- background: rgba(255, 255, 255, 0.9);
211
- border-radius: 50%;
212
- }
169
+ > .resize-handle {
170
+ position: absolute;
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ pointer-events: all;
175
+ opacity: 0.6;
176
+ border-radius: 4px;
177
+ background: var(--colorSettings);
213
178
 
214
- &::after {
215
- content: '';
216
- position: absolute;
217
- background: rgba(255, 255, 255, 0.9);
218
- border-radius: 50%;
219
- }
179
+ &:hover {
180
+ opacity: 0.9;
181
+ background: var(--colorSettingsHover, var(--colorSettings));
220
182
  }
221
183
 
222
- > .resize-handle-left {
223
- left: -4px;
224
- top: 50%;
225
- transform: translateY(-50%);
226
- width: 6px;
227
- height: 20px;
228
- cursor: ew-resize;
184
+ &::before {
185
+ content: '';
186
+ position: absolute;
187
+ border-radius: 50%;
188
+ background: rgba(255, 255, 255, 0.9);
189
+ }
229
190
 
230
- &::before {
231
- width: 2px;
232
- height: 2px;
233
- top: 6px;
234
- left: 50%;
235
- transform: translateX(-50%);
236
- box-shadow:
237
- 0 4px 0 rgba(255, 255, 255, 0.9),
238
- 0 8px 0 rgba(255, 255, 255, 0.9);
239
- }
191
+ &::after {
192
+ content: '';
193
+ position: absolute;
194
+ border-radius: 50%;
195
+ background: rgba(255, 255, 255, 0.9);
240
196
  }
197
+ }
241
198
 
242
- > .resize-handle-right {
243
- right: -4px;
244
- top: 50%;
245
- transform: translateY(-50%);
246
- width: 6px;
247
- height: 20px;
248
- cursor: ew-resize;
199
+ > .resize-handle-left {
200
+ top: 50%;
201
+ left: -4px;
202
+ width: 6px;
203
+ height: 20px;
204
+ cursor: ew-resize;
205
+ transform: translateY(-50%);
249
206
 
250
- &::before {
251
- width: 2px;
252
- height: 2px;
253
- top: 6px;
254
- left: 50%;
255
- transform: translateX(-50%);
256
- box-shadow:
257
- 0 4px 0 rgba(255, 255, 255, 0.9),
258
- 0 8px 0 rgba(255, 255, 255, 0.9);
259
- }
207
+ &::before {
208
+ top: 6px;
209
+ left: 50%;
210
+ width: 2px;
211
+ height: 2px;
212
+ transform: translateX(-50%);
213
+ box-shadow:
214
+ 0 4px 0 rgba(255, 255, 255, 0.9),
215
+ 0 8px 0 rgba(255, 255, 255, 0.9);
260
216
  }
217
+ }
218
+
219
+ > .resize-handle-right {
220
+ top: 50%;
221
+ right: -4px;
222
+ width: 6px;
223
+ height: 20px;
224
+ cursor: ew-resize;
225
+ transform: translateY(-50%);
261
226
 
262
- > .resize-handle-bottom {
263
- bottom: -4px;
227
+ &::before {
228
+ top: 6px;
264
229
  left: 50%;
230
+ width: 2px;
231
+ height: 2px;
265
232
  transform: translateX(-50%);
266
- width: 20px;
267
- height: 6px;
268
- cursor: ns-resize;
269
-
270
- &::before {
271
- width: 2px;
272
- height: 2px;
273
- left: 6px;
274
- top: 50%;
275
- transform: translateY(-50%);
276
- box-shadow:
277
- 4px 0 0 rgba(255, 255, 255, 0.9),
278
- 8px 0 0 rgba(255, 255, 255, 0.9);
279
- }
233
+ box-shadow:
234
+ 0 4px 0 rgba(255, 255, 255, 0.9),
235
+ 0 8px 0 rgba(255, 255, 255, 0.9);
280
236
  }
281
237
  }
282
- `, "floatContainerStyles");
238
+ `, "toolbarContainerStyles");
239
+ const detectButtonInDOM = /* @__PURE__ */ __name((container) => {
240
+ if (!container) return false;
241
+ const directChildren = container.children;
242
+ for (let i = 0; i < directChildren.length; i++) {
243
+ const child = directChildren[i];
244
+ if (child.tagName === "BUTTON" || child.getAttribute("role") === "button" || child.classList.contains("ant-btn")) {
245
+ return true;
246
+ }
247
+ }
248
+ return false;
249
+ }, "detectButtonInDOM");
250
+ const renderToolbarItems = /* @__PURE__ */ __name((model, showDeleteButton, showCopyUidButton, flowEngine, settingsMenuLevel, extraToolbarItems, onSettingsMenuOpenChange, getPopupContainer) => {
251
+ var _a, _b;
252
+ const toolbarItems = ((_b = (_a = flowEngine == null ? void 0 : flowEngine.flowSettings) == null ? void 0 : _a.getToolbarItems) == null ? void 0 : _b.call(_a)) || [];
253
+ const allToolbarItems = [...toolbarItems, ...extraToolbarItems || []];
254
+ allToolbarItems.sort((a, b) => (a.sort || 0) - (b.sort || 0)).reverse();
255
+ return allToolbarItems.filter((itemConfig) => {
256
+ return itemConfig.visible ? itemConfig.visible(model) : true;
257
+ }).map((itemConfig) => {
258
+ const ItemComponent = itemConfig.component;
259
+ if (itemConfig.key === "settings-menu") {
260
+ return /* @__PURE__ */ import_react.default.createElement(
261
+ ItemComponent,
262
+ {
263
+ key: itemConfig.key,
264
+ model,
265
+ id: model.uid,
266
+ showDeleteButton,
267
+ showCopyUidButton,
268
+ menuLevels: settingsMenuLevel,
269
+ onDropdownVisibleChange: onSettingsMenuOpenChange,
270
+ getPopupContainer
271
+ }
272
+ );
273
+ }
274
+ return /* @__PURE__ */ import_react.default.createElement(ItemComponent, { key: itemConfig.key, model });
275
+ });
276
+ }, "renderToolbarItems");
277
+ const buildToolbarContainerClassName = /* @__PURE__ */ __name(({
278
+ showBackground,
279
+ showBorder,
280
+ ctx,
281
+ portalRenderSnapshot,
282
+ isToolbarVisible,
283
+ className
284
+ }) => [
285
+ toolbarContainerStyles({ showBackground, showBorder, ctx }),
286
+ "nb-toolbar-portal",
287
+ (portalRenderSnapshot == null ? void 0 : portalRenderSnapshot.positioningMode) === "absolute" ? "nb-toolbar-portal-absolute" : "nb-toolbar-portal-fixed",
288
+ isToolbarVisible ? "nb-toolbar-visible" : "",
289
+ (className == null ? void 0 : className.includes("nb-in-template")) ? "nb-in-template" : ""
290
+ ].filter(Boolean).join(" "), "buildToolbarContainerClassName");
291
+ const buildToolbarContainerStyle = /* @__PURE__ */ __name((portalRect, toolbarStyle) => ({
292
+ top: `${portalRect.top}px`,
293
+ left: `${portalRect.left}px`,
294
+ width: `${portalRect.width}px`,
295
+ height: `${portalRect.height}px`,
296
+ ...(0, import_useFloatToolbarPortal.omitToolbarPortalInsetStyle)(toolbarStyle)
297
+ }), "buildToolbarContainerStyle");
283
298
  const isModelByIdProps = /* @__PURE__ */ __name((props) => {
284
299
  return "uid" in props && "modelClassName" in props && Boolean(props.uid) && Boolean(props.modelClassName);
285
300
  }, "isModelByIdProps");
@@ -290,9 +305,8 @@ const FlowsFloatContextMenu = (0, import_reactive.observer)((props) => {
290
305
  }
291
306
  if (isModelByIdProps(props)) {
292
307
  return /* @__PURE__ */ import_react.default.createElement(FlowsFloatContextMenuWithModelById, { ...props });
293
- } else {
294
- return /* @__PURE__ */ import_react.default.createElement(FlowsFloatContextMenuWithModel, { ...props });
295
308
  }
309
+ return /* @__PURE__ */ import_react.default.createElement(FlowsFloatContextMenuWithModel, { ...props });
296
310
  });
297
311
  const ResizeHandles = /* @__PURE__ */ __name((props) => {
298
312
  const isDraggingRef = (0, import_react.useRef)(false);
@@ -303,27 +317,13 @@ const ResizeHandles = /* @__PURE__ */ __name((props) => {
303
317
  (e) => {
304
318
  if (!isDraggingRef.current || !dragTypeRef.current) return;
305
319
  const deltaX = e.clientX - dragStartPosRef.current.x;
306
- const deltaY = e.clientY - dragStartPosRef.current.y;
307
- let resizeDistance = 0;
308
320
  switch (dragTypeRef.current) {
309
321
  case "left":
310
- resizeDistance = -deltaX;
311
- props.model.parent.emitter.emit("onResizeLeft", { resizeDistance, model: props.model });
322
+ props.model.parent.emitter.emit("onResizeLeft", { resizeDistance: -deltaX, model: props.model });
312
323
  break;
313
324
  case "right":
314
- resizeDistance = deltaX;
315
- props.model.parent.emitter.emit("onResizeRight", { resizeDistance, model: props.model });
316
- break;
317
- case "bottom":
318
- resizeDistance = deltaY;
319
- props.model.parent.emitter.emit("onResizeBottom", { resizeDistance, model: props.model });
320
- break;
321
- case "corner": {
322
- const widthDelta = deltaX;
323
- const heightDelta = deltaY;
324
- props.model.parent.emitter.emit("onResizeCorner", { widthDelta, heightDelta, model: props.model });
325
+ props.model.parent.emitter.emit("onResizeRight", { resizeDistance: deltaX, model: props.model });
325
326
  break;
326
- }
327
327
  }
328
328
  },
329
329
  [props.model]
@@ -336,7 +336,7 @@ const ResizeHandles = /* @__PURE__ */ __name((props) => {
336
336
  document.removeEventListener("mouseup", handleDragEnd);
337
337
  props.model.parent.emitter.emit("onResizeEnd");
338
338
  onDragEnd == null ? void 0 : onDragEnd();
339
- }, [handleDragMove, props.model, onDragEnd]);
339
+ }, [handleDragMove, onDragEnd, props.model]);
340
340
  const handleDragStart = (0, import_react.useCallback)(
341
341
  (e, type) => {
342
342
  e.preventDefault();
@@ -355,6 +355,8 @@ const ResizeHandles = /* @__PURE__ */ __name((props) => {
355
355
  {
356
356
  className: "resize-handle resize-handle-left",
357
357
  title: "\u62D6\u62FD\u8C03\u8282\u5BBD\u5EA6",
358
+ onMouseEnter: props.onMouseEnter,
359
+ onMouseLeave: props.onMouseLeave,
358
360
  onMouseDown: (e) => handleDragStart(e, "left")
359
361
  }
360
362
  ), /* @__PURE__ */ import_react.default.createElement(
@@ -362,6 +364,8 @@ const ResizeHandles = /* @__PURE__ */ __name((props) => {
362
364
  {
363
365
  className: "resize-handle resize-handle-right",
364
366
  title: "\u62D6\u62FD\u8C03\u8282\u5BBD\u5EA6",
367
+ onMouseEnter: props.onMouseEnter,
368
+ onMouseLeave: props.onMouseLeave,
365
369
  onMouseDown: (e) => handleDragStart(e, "right")
366
370
  }
367
371
  ));
@@ -384,28 +388,82 @@ const FlowsFloatContextMenuWithModel = (0, import_reactive.observer)(
384
388
  toolbarStyle,
385
389
  toolbarPosition = "inside"
386
390
  }) => {
387
- const [hideMenu, setHideMenu] = (0, import_react.useState)(false);
388
391
  const [hasButton, setHasButton] = (0, import_react.useState)(false);
389
392
  const containerRef = (0, import_react.useRef)(null);
390
- const flowEngine = (0, import_provider.useFlowEngine)();
391
- const [style, setStyle] = (0, import_react.useState)({});
392
393
  const toolbarContainerRef = (0, import_react.useRef)(null);
393
- const toolbarContainerStyle = (0, import_react.useMemo)(() => ({ ...toolbarStyle, ...style }), [style, toolbarStyle]);
394
+ const portalActionsRef = (0, import_react.useRef)({
395
+ updatePortalRect: /* @__PURE__ */ __name(() => {
396
+ }, "updatePortalRect"),
397
+ schedulePortalRectUpdate: /* @__PURE__ */ __name(() => {
398
+ }, "schedulePortalRectUpdate")
399
+ });
400
+ const modelUid = (model == null ? void 0 : model.uid) || "";
401
+ const flowEngine = (0, import_provider.useFlowEngine)();
402
+ const updatePortalRectProxy = (0, import_react.useCallback)(() => {
403
+ portalActionsRef.current.updatePortalRect();
404
+ }, []);
405
+ const schedulePortalRectUpdateProxy = (0, import_react.useCallback)(() => {
406
+ portalActionsRef.current.schedulePortalRectUpdate();
407
+ }, []);
408
+ const {
409
+ isToolbarVisible,
410
+ shouldRenderToolbar,
411
+ handleSettingsMenuOpenChange,
412
+ handleChildHover,
413
+ handleHostMouseEnter,
414
+ handleHostMouseLeave,
415
+ handleToolbarMouseEnter,
416
+ handleToolbarMouseLeave,
417
+ handleResizeDragStart,
418
+ handleResizeDragEnd
419
+ } = (0, import_useFloatToolbarVisibility.useFloatToolbarVisibility)({
420
+ modelUid,
421
+ containerRef,
422
+ toolbarContainerRef,
423
+ updatePortalRect: updatePortalRectProxy,
424
+ schedulePortalRectUpdate: schedulePortalRectUpdateProxy
425
+ });
426
+ const { portalRect, portalRenderSnapshot, getPopupContainer, updatePortalRect, schedulePortalRectUpdate } = (0, import_useFloatToolbarPortal.useFloatToolbarPortal)({
427
+ active: shouldRenderToolbar,
428
+ containerRef,
429
+ toolbarContainerRef,
430
+ toolbarStyle
431
+ });
432
+ portalActionsRef.current.updatePortalRect = updatePortalRect;
433
+ portalActionsRef.current.schedulePortalRectUpdate = schedulePortalRectUpdate;
434
+ const toolbarItems = (0, import_react.useMemo)(
435
+ () => model ? renderToolbarItems(
436
+ model,
437
+ showDeleteButton,
438
+ showCopyUidButton,
439
+ flowEngine,
440
+ settingsMenuLevel,
441
+ extraToolbarItems,
442
+ handleSettingsMenuOpenChange,
443
+ getPopupContainer
444
+ ) : [],
445
+ [
446
+ extraToolbarItems,
447
+ flowEngine,
448
+ getPopupContainer,
449
+ handleSettingsMenuOpenChange,
450
+ model,
451
+ settingsMenuLevel,
452
+ showCopyUidButton,
453
+ showDeleteButton
454
+ ]
455
+ );
394
456
  (0, import_react.useEffect)(() => {
395
- if (containerRef.current) {
396
- const hasButtonElement = detectButtonInDOM(containerRef.current);
397
- setHasButton(hasButtonElement);
457
+ const container = containerRef.current;
458
+ if (!container) {
459
+ return;
398
460
  }
399
- }, [children]);
400
- (0, import_react.useEffect)(() => {
401
- if (!containerRef.current) return;
402
- const observer2 = new MutationObserver(() => {
403
- if (containerRef.current) {
404
- const hasButtonElement = detectButtonInDOM(containerRef.current);
405
- setHasButton(hasButtonElement);
406
- }
407
- });
408
- observer2.observe(containerRef.current, {
461
+ const syncHasButton = /* @__PURE__ */ __name(() => {
462
+ setHasButton(detectButtonInDOM(container));
463
+ }, "syncHasButton");
464
+ syncHasButton();
465
+ const observer2 = new MutationObserver(syncHasButton);
466
+ observer2.observe(container, {
409
467
  childList: true,
410
468
  subtree: true,
411
469
  attributes: true,
@@ -415,15 +473,6 @@ const FlowsFloatContextMenuWithModel = (0, import_reactive.observer)(
415
473
  observer2.disconnect();
416
474
  };
417
475
  }, []);
418
- const handleChildHover = (0, import_react.useCallback)((e) => {
419
- const target = e.target;
420
- const childWithMenu = target.closest("[data-has-float-menu]");
421
- if (childWithMenu && childWithMenu !== containerRef.current) {
422
- setHideMenu(true);
423
- } else {
424
- setHideMenu(false);
425
- }
426
- }, []);
427
476
  if (!model) {
428
477
  const t = (0, import_utils.getT)(model || {});
429
478
  return /* @__PURE__ */ import_react.default.createElement(import_antd.Alert, { message: t("Invalid model provided"), type: "error" });
@@ -431,39 +480,61 @@ const FlowsFloatContextMenuWithModel = (0, import_reactive.observer)(
431
480
  if (!enabled || !children) {
432
481
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, children);
433
482
  }
434
- return /* @__PURE__ */ import_react.default.createElement(
483
+ const toolbarContainerClassName = buildToolbarContainerClassName({
484
+ showBackground,
485
+ showBorder,
486
+ ctx: model.context,
487
+ portalRenderSnapshot,
488
+ isToolbarVisible,
489
+ className
490
+ });
491
+ const toolbarContainerStyle = buildToolbarContainerStyle(portalRect, toolbarStyle);
492
+ const toolbarNode = shouldRenderToolbar ? /* @__PURE__ */ import_react.default.createElement(
435
493
  "div",
436
494
  {
437
- ref: containerRef,
438
- className: `${floatContainerStyles({
439
- showBackground,
440
- showBorder,
441
- ctx: model.context,
442
- toolbarPosition,
443
- toolbarCount: getToolbarCount(flowEngine, extraToolbarItems)
444
- })} ${hideMenu ? "hide-parent-menu" : ""} ${hasButton ? "has-button-child" : ""} ${className || ""}`,
445
- style: containerStyle,
446
- "data-has-float-menu": "true",
447
- onMouseMove: handleChildHover
495
+ ref: toolbarContainerRef,
496
+ className: `nb-toolbar-container ${toolbarContainerClassName}`,
497
+ style: toolbarContainerStyle,
498
+ "data-model-uid": model.uid
448
499
  },
449
- children,
450
- /* @__PURE__ */ import_react.default.createElement("div", { ref: toolbarContainerRef, className: "nb-toolbar-container", style: toolbarContainerStyle }, showTitle && (model.title || model.extraTitle) && /* @__PURE__ */ import_react.default.createElement("div", { className: "nb-toolbar-container-title" }, model.title && /* @__PURE__ */ import_react.default.createElement("span", { className: "title-tag" }, model.title), model.extraTitle && /* @__PURE__ */ import_react.default.createElement("span", { className: "title-tag" }, model.extraTitle)), /* @__PURE__ */ import_react.default.createElement(
500
+ showTitle && (model.title || model.extraTitle) && /* @__PURE__ */ import_react.default.createElement("div", { className: "nb-toolbar-container-title" }, model.title && /* @__PURE__ */ import_react.default.createElement("span", { className: "title-tag" }, model.title), model.extraTitle && /* @__PURE__ */ import_react.default.createElement("span", { className: "title-tag" }, model.extraTitle)),
501
+ /* @__PURE__ */ import_react.default.createElement(
451
502
  "div",
452
503
  {
453
- className: "nb-toolbar-container-icons",
504
+ className: `nb-toolbar-container-icons ${toolbarPositionClassNames[toolbarPosition]}`,
454
505
  onClick: (e) => e.stopPropagation(),
455
506
  onMouseDown: (e) => e.stopPropagation(),
456
- onMouseMove: (e) => e.stopPropagation()
507
+ onMouseMove: (e) => e.stopPropagation(),
508
+ onMouseEnter: handleToolbarMouseEnter,
509
+ onMouseLeave: handleToolbarMouseLeave
457
510
  },
458
- /* @__PURE__ */ import_react.default.createElement(import_antd.Space, { size: 3, align: "center" }, renderToolbarItems(
511
+ /* @__PURE__ */ import_react.default.createElement(import_antd.Space, { size: 3, align: "center" }, toolbarItems)
512
+ ),
513
+ showDragHandle && /* @__PURE__ */ import_react.default.createElement(
514
+ ResizeHandles,
515
+ {
459
516
  model,
460
- showDeleteButton,
461
- showCopyUidButton,
462
- flowEngine,
463
- settingsMenuLevel,
464
- extraToolbarItems
465
- ))
466
- ), showDragHandle && /* @__PURE__ */ import_react.default.createElement(ResizeHandles, { model, onDragStart: () => setStyle({ opacity: 1 }), onDragEnd: () => setStyle({}) }))
517
+ onMouseEnter: handleToolbarMouseEnter,
518
+ onMouseLeave: handleToolbarMouseLeave,
519
+ onDragStart: handleResizeDragStart,
520
+ onDragEnd: handleResizeDragEnd
521
+ }
522
+ )
523
+ ) : null;
524
+ return /* @__PURE__ */ import_react.default.createElement(
525
+ "div",
526
+ {
527
+ ref: containerRef,
528
+ className: `${hostContainerStyles} ${hasButton ? "has-button-child" : ""} ${className || ""}`,
529
+ style: containerStyle,
530
+ "data-has-float-menu": "true",
531
+ "data-float-menu-model-uid": model.uid,
532
+ onMouseMove: handleChildHover,
533
+ onMouseEnter: handleHostMouseEnter,
534
+ onMouseLeave: handleHostMouseLeave
535
+ },
536
+ children,
537
+ toolbarNode && ((portalRenderSnapshot == null ? void 0 : portalRenderSnapshot.mountElement) ? (0, import_react_dom.createPortal)(toolbarNode, portalRenderSnapshot.mountElement) : toolbarNode)
467
538
  );
468
539
  },
469
540
  {
@@ -471,53 +542,18 @@ const FlowsFloatContextMenuWithModel = (0, import_reactive.observer)(
471
542
  }
472
543
  );
473
544
  const FlowsFloatContextMenuWithModelById = (0, import_reactive.observer)(
474
- ({
475
- uid,
476
- modelClassName,
477
- children,
478
- enabled = true,
479
- showDeleteButton = true,
480
- showCopyUidButton = true,
481
- containerStyle,
482
- className,
483
- showTitle = false,
484
- settingsMenuLevel,
485
- extraToolbarItems,
486
- toolbarPosition
487
- }) => {
545
+ ({ uid, modelClassName, children, ...restProps }) => {
488
546
  const model = (0, import_hooks.useFlowModelById)(uid, modelClassName);
489
547
  const flowEngine = (0, import_provider.useFlowEngine)();
490
548
  if (!model) {
491
549
  return /* @__PURE__ */ import_react.default.createElement(import_antd.Alert, { message: flowEngine.translate("Model with ID {{uid}} not found", { uid }), type: "error" });
492
550
  }
493
- return /* @__PURE__ */ import_react.default.createElement(
494
- FlowsFloatContextMenuWithModel,
495
- {
496
- model,
497
- enabled,
498
- showDeleteButton,
499
- showCopyUidButton,
500
- containerStyle,
501
- className,
502
- showTitle,
503
- settingsMenuLevel,
504
- extraToolbarItems,
505
- toolbarPosition
506
- },
507
- children
508
- );
551
+ return /* @__PURE__ */ import_react.default.createElement(FlowsFloatContextMenuWithModel, { model, ...restProps }, children);
509
552
  },
510
553
  {
511
554
  displayName: "FlowsFloatContextMenuWithModelById"
512
555
  }
513
556
  );
514
- function getToolbarCount(flowEngine, extraToolbarItems) {
515
- var _a, _b;
516
- const toolbarItems = ((_b = (_a = flowEngine == null ? void 0 : flowEngine.flowSettings) == null ? void 0 : _a.getToolbarItems) == null ? void 0 : _b.call(_a)) || [];
517
- const allToolbarItems = [...toolbarItems, ...extraToolbarItems || []];
518
- return allToolbarItems.length;
519
- }
520
- __name(getToolbarCount, "getToolbarCount");
521
557
  // Annotate the CommonJS export names for ESM import in node:
522
558
  0 && (module.exports = {
523
559
  FlowsFloatContextMenu