@nocobase/flow-engine 2.2.0-beta.1 → 2.2.0-beta.11

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 (85) hide show
  1. package/lib/JSRunner.d.ts +1 -0
  2. package/lib/JSRunner.js +110 -20
  3. package/lib/components/FieldModelRenderer.js +44 -31
  4. package/lib/components/MobilePopup.js +28 -10
  5. package/lib/components/MobilePopup.style.js +27 -6
  6. package/lib/components/dnd/index.js +9 -2
  7. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +86 -32
  8. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +20 -0
  9. package/lib/flowContext.d.ts +1 -1
  10. package/lib/flowContext.js +97 -42
  11. package/lib/flowEngine.js +6 -0
  12. package/lib/flowSettings.d.ts +5 -1
  13. package/lib/flowSettings.js +70 -0
  14. package/lib/locale/en-US.json +1 -0
  15. package/lib/locale/index.d.ts +2 -0
  16. package/lib/locale/zh-CN.json +1 -0
  17. package/lib/resources/apiResource.js +2 -1
  18. package/lib/resources/baseRecordResource.js +6 -17
  19. package/lib/resources/multiRecordResource.js +13 -3
  20. package/lib/resources/singleRecordResource.js +7 -2
  21. package/lib/runjs-context/helpers.js +12 -5
  22. package/lib/types.d.ts +12 -0
  23. package/lib/utils/dataSourceDirty.d.ts +20 -0
  24. package/lib/utils/dataSourceDirty.js +139 -0
  25. package/lib/utils/dirtyAwareApiClient.d.ts +11 -0
  26. package/lib/utils/dirtyAwareApiClient.js +632 -0
  27. package/lib/utils/index.d.ts +1 -1
  28. package/lib/utils/index.js +11 -11
  29. package/lib/utils/loadedPageCache.d.ts +1 -0
  30. package/lib/utils/loadedPageCache.js +6 -0
  31. package/lib/utils/openViewRouteState.d.ts +28 -0
  32. package/lib/utils/openViewRouteState.js +125 -0
  33. package/lib/utils/parsePathnameToViewParams.d.ts +3 -0
  34. package/lib/utils/parsePathnameToViewParams.js +18 -1
  35. package/lib/utils/resolveRunJSObjectValues.js +3 -2
  36. package/lib/utils/runjsModuleLoader.js +0 -30
  37. package/lib/views/ViewNavigation.js +5 -0
  38. package/package.json +4 -4
  39. package/src/JSRunner.ts +112 -25
  40. package/src/__tests__/JSRunner.test.ts +4 -5
  41. package/src/__tests__/flowContext.test.ts +154 -0
  42. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +51 -0
  43. package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
  44. package/src/__tests__/flowSettings.test.ts +72 -0
  45. package/src/__tests__/runjsRuntimeFeatures.test.ts +15 -2
  46. package/src/__tests__/viewScopedFlowEngine.test.ts +72 -6
  47. package/src/components/FieldModelRenderer.tsx +50 -36
  48. package/src/components/MobilePopup.style.ts +34 -7
  49. package/src/components/MobilePopup.tsx +30 -10
  50. package/src/components/__tests__/FieldModelRenderer.test.tsx +165 -0
  51. package/src/components/__tests__/MobilePopup.style.test.tsx +103 -0
  52. package/src/components/__tests__/MobilePopup.test.tsx +109 -0
  53. package/src/components/dnd/index.tsx +11 -2
  54. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +105 -35
  55. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +381 -12
  56. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +28 -0
  57. package/src/flowContext.ts +104 -36
  58. package/src/flowEngine.ts +6 -0
  59. package/src/flowSettings.ts +85 -1
  60. package/src/locale/en-US.json +1 -0
  61. package/src/locale/zh-CN.json +1 -0
  62. package/src/resources/apiResource.ts +2 -1
  63. package/src/resources/baseRecordResource.ts +6 -23
  64. package/src/resources/multiRecordResource.ts +13 -3
  65. package/src/resources/singleRecordResource.ts +6 -1
  66. package/src/runjs-context/helpers.ts +12 -6
  67. package/src/types.ts +14 -0
  68. package/src/utils/__tests__/dirtyAwareApiClient.test.ts +713 -0
  69. package/src/utils/__tests__/openViewRouteState.test.ts +40 -0
  70. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +36 -0
  71. package/src/utils/dataSourceDirty.ts +126 -0
  72. package/src/utils/dirtyAwareApiClient.ts +742 -0
  73. package/src/utils/index.ts +10 -9
  74. package/src/utils/loadedPageCache.ts +7 -0
  75. package/src/utils/openViewRouteState.ts +107 -0
  76. package/src/utils/parsePathnameToViewParams.ts +23 -1
  77. package/src/utils/resolveRunJSObjectValues.ts +5 -2
  78. package/src/utils/runjsModuleLoader.ts +0 -32
  79. package/src/views/ViewNavigation.ts +6 -1
  80. package/src/views/__tests__/ViewNavigation.test.ts +15 -0
  81. package/lib/utils/safeGlobals.d.ts +0 -28
  82. package/lib/utils/safeGlobals.js +0 -367
  83. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +0 -38
  84. package/src/utils/__tests__/safeGlobals.test.ts +0 -106
  85. package/src/utils/safeGlobals.ts +0 -406
package/lib/JSRunner.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface JSRunnerOptions {
27
27
  * 3. Fallback keeps v1-compatible behavior (enabled).
28
28
  */
29
29
  export declare function shouldPreprocessRunJSTemplates(options?: Pick<JSRunnerOptions, 'preprocessTemplates' | 'version'>): boolean;
30
+ export declare const RUNJS_ALLOWED_BARE_GLOBAL_NAMES: readonly ["ctx", "console", "window", "document", "navigator", "setTimeout", "clearTimeout", "setInterval", "clearInterval", "Array", "ArrayBuffer", "BigInt", "BigInt64Array", "BigUint64Array", "Boolean", "DataView", "Date", "Error", "EvalError", "FinalizationRegistry", "Float32Array", "Float64Array", "Int8Array", "Int16Array", "Int32Array", "Map", "Math", "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "TypeError", "URIError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "WeakMap", "WeakRef", "WeakSet", "JSON", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "isFinite", "isNaN", "parseFloat", "parseInt", "undefined", "NaN", "Infinity", "fetch", "localStorage", "sessionStorage", "XMLHttpRequest", "WebSocket", "Worker", "SharedWorker", "ServiceWorker", "BroadcastChannel", "EventSource", "indexedDB", "caches", "Function", "eval", "globalThis", "Intl", "Blob", "URL", "location"];
30
31
  export declare class JSRunner {
31
32
  private globals;
32
33
  private timeoutMs;
package/lib/JSRunner.js CHANGED
@@ -28,6 +28,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
28
28
  var JSRunner_exports = {};
29
29
  __export(JSRunner_exports, {
30
30
  JSRunner: () => JSRunner,
31
+ RUNJS_ALLOWED_BARE_GLOBAL_NAMES: () => RUNJS_ALLOWED_BARE_GLOBAL_NAMES,
31
32
  shouldPreprocessRunJSTemplates: () => shouldPreprocessRunJSTemplates
32
33
  });
33
34
  module.exports = __toCommonJS(JSRunner_exports);
@@ -40,6 +41,113 @@ function shouldPreprocessRunJSTemplates(options) {
40
41
  return (options == null ? void 0 : options.version) !== "v2";
41
42
  }
42
43
  __name(shouldPreprocessRunJSTemplates, "shouldPreprocessRunJSTemplates");
44
+ const RUNJS_BROWSER_GLOBAL_NAMES = [
45
+ "fetch",
46
+ "localStorage",
47
+ "sessionStorage",
48
+ "XMLHttpRequest",
49
+ "WebSocket",
50
+ "Worker",
51
+ "SharedWorker",
52
+ "ServiceWorker",
53
+ "BroadcastChannel",
54
+ "EventSource",
55
+ "indexedDB",
56
+ "caches",
57
+ "Function",
58
+ "eval",
59
+ "globalThis",
60
+ "Intl",
61
+ "Blob",
62
+ "URL",
63
+ "location"
64
+ ];
65
+ const RUNJS_ALLOWED_BARE_GLOBAL_NAMES = [
66
+ "ctx",
67
+ "console",
68
+ "window",
69
+ "document",
70
+ "navigator",
71
+ "setTimeout",
72
+ "clearTimeout",
73
+ "setInterval",
74
+ "clearInterval",
75
+ "Array",
76
+ "ArrayBuffer",
77
+ "BigInt",
78
+ "BigInt64Array",
79
+ "BigUint64Array",
80
+ "Boolean",
81
+ "DataView",
82
+ "Date",
83
+ "Error",
84
+ "EvalError",
85
+ "FinalizationRegistry",
86
+ "Float32Array",
87
+ "Float64Array",
88
+ "Int8Array",
89
+ "Int16Array",
90
+ "Int32Array",
91
+ "Map",
92
+ "Math",
93
+ "Number",
94
+ "Object",
95
+ "Promise",
96
+ "Proxy",
97
+ "RangeError",
98
+ "ReferenceError",
99
+ "Reflect",
100
+ "RegExp",
101
+ "Set",
102
+ "String",
103
+ "Symbol",
104
+ "SyntaxError",
105
+ "TypeError",
106
+ "URIError",
107
+ "Uint8Array",
108
+ "Uint8ClampedArray",
109
+ "Uint16Array",
110
+ "Uint32Array",
111
+ "WeakMap",
112
+ "WeakRef",
113
+ "WeakSet",
114
+ "JSON",
115
+ "decodeURI",
116
+ "decodeURIComponent",
117
+ "encodeURI",
118
+ "encodeURIComponent",
119
+ "isFinite",
120
+ "isNaN",
121
+ "parseFloat",
122
+ "parseInt",
123
+ "undefined",
124
+ "NaN",
125
+ "Infinity",
126
+ ...RUNJS_BROWSER_GLOBAL_NAMES
127
+ ];
128
+ function collectRunJSBrowserGlobals(providedGlobals = {}) {
129
+ const windowGlobal = providedGlobals.window;
130
+ if (!windowGlobal || typeof windowGlobal !== "object") {
131
+ return {};
132
+ }
133
+ const windowRecord = windowGlobal;
134
+ const globals = {};
135
+ RUNJS_BROWSER_GLOBAL_NAMES.forEach((name) => {
136
+ if (Object.prototype.hasOwnProperty.call(providedGlobals, name)) {
137
+ return;
138
+ }
139
+ try {
140
+ const value = windowRecord[name];
141
+ if (typeof value === "undefined") {
142
+ return;
143
+ }
144
+ globals[name] = name === "fetch" && typeof value === "function" ? value.bind(windowGlobal) : value;
145
+ } catch {
146
+ }
147
+ });
148
+ return globals;
149
+ }
150
+ __name(collectRunJSBrowserGlobals, "collectRunJSBrowserGlobals");
43
151
  const BARE_CTX_TEMPLATE_RE = /(^|[=(:,[\s)])(\{\{\s*(ctx(?:\.|\[|\?\.)[^}]*)\s*\}\})/m;
44
152
  function extractDeprecatedCtxTemplateUsage(code) {
45
153
  const src = String(code || "");
@@ -78,7 +186,6 @@ const _JSRunner = class _JSRunner {
78
186
  globals;
79
187
  timeoutMs;
80
188
  constructor(options = {}) {
81
- var _a, _b;
82
189
  const bindWindowFn = /* @__PURE__ */ __name((key) => {
83
190
  if (typeof window !== "undefined" && typeof window[key] === "function") {
84
191
  return window[key].bind(window);
@@ -87,25 +194,7 @@ const _JSRunner = class _JSRunner {
87
194
  return typeof fn === "function" ? fn.bind(globalThis) : fn;
88
195
  }, "bindWindowFn");
89
196
  const providedGlobals = options.globals || {};
90
- const liftedGlobals = {};
91
- if (!Object.prototype.hasOwnProperty.call(providedGlobals, "Blob")) {
92
- try {
93
- const blobCtor = (_a = providedGlobals.window) == null ? void 0 : _a.Blob;
94
- if (typeof blobCtor !== "undefined") {
95
- liftedGlobals.Blob = blobCtor;
96
- }
97
- } catch {
98
- }
99
- }
100
- if (!Object.prototype.hasOwnProperty.call(providedGlobals, "URL")) {
101
- try {
102
- const urlCtor = (_b = providedGlobals.window) == null ? void 0 : _b.URL;
103
- if (typeof urlCtor !== "undefined") {
104
- liftedGlobals.URL = urlCtor;
105
- }
106
- } catch {
107
- }
108
- }
197
+ const liftedGlobals = collectRunJSBrowserGlobals(providedGlobals);
109
198
  this.globals = {
110
199
  console,
111
200
  setTimeout: bindWindowFn("setTimeout"),
@@ -169,5 +258,6 @@ let JSRunner = _JSRunner;
169
258
  // Annotate the CommonJS export names for ESM import in node:
170
259
  0 && (module.exports = {
171
260
  JSRunner,
261
+ RUNJS_ALLOWED_BARE_GLOBAL_NAMES,
172
262
  shouldPreprocessRunJSTemplates
173
263
  });
@@ -58,41 +58,54 @@ const flowModelRendererPropKeys = [
58
58
  "extraToolbarItems"
59
59
  ];
60
60
  function FieldModelRenderer(props) {
61
- const { model, ...rest } = props;
61
+ const { model, onChange, ...rest } = props;
62
62
  const composingRef = (0, import_react.useRef)(false);
63
- const handleChange = /* @__PURE__ */ __name((e) => {
64
- var _a;
65
- let val;
66
- if (e && e.target && typeof e.target.value !== "undefined") {
67
- val = e.target.value;
68
- } else if (typeof e === "string" || typeof e === "number" || typeof e === "boolean" || typeof e === "object" && !(e instanceof Event)) {
69
- val = e;
70
- } else {
71
- val = null;
72
- }
73
- model.setProps({ value: val });
74
- if (!composingRef.current) {
75
- (_a = props.onChange) == null ? void 0 : _a.call(props, val);
76
- }
77
- }, "handleChange");
78
- const handleCompositionStart = /* @__PURE__ */ __name(() => {
63
+ const handleChange = (0, import_react.useCallback)(
64
+ (e) => {
65
+ var _a;
66
+ let val;
67
+ if (e && e.target && typeof e.target.value !== "undefined") {
68
+ val = e.target.value;
69
+ } else if (typeof e === "string" || typeof e === "number" || typeof e === "boolean" || typeof e === "object" && !(e instanceof Event)) {
70
+ val = e;
71
+ } else {
72
+ val = null;
73
+ }
74
+ const isComposing = composingRef.current || ((_a = e == null ? void 0 : e.nativeEvent) == null ? void 0 : _a.isComposing);
75
+ if (isComposing) {
76
+ return;
77
+ }
78
+ model.setProps({ value: val });
79
+ if (!composingRef.current) {
80
+ onChange == null ? void 0 : onChange(val);
81
+ }
82
+ },
83
+ [model, onChange]
84
+ );
85
+ const handleCompositionStart = (0, import_react.useCallback)(() => {
79
86
  composingRef.current = true;
80
- }, "handleCompositionStart");
81
- const handleCompositionEnd = /* @__PURE__ */ __name((e, flag = true) => {
82
- composingRef.current = false;
83
- if (flag) {
84
- props.onChange(e);
85
- }
86
- }, "handleCompositionEnd");
87
- const modelProps = {
88
- onCompositionStart: handleCompositionStart,
89
- onCompositionEnd: handleCompositionEnd,
90
- ...import_lodash.default.omit(rest, flowModelRendererPropKeys),
91
- onChange: handleChange
92
- };
87
+ }, []);
88
+ const handleCompositionEnd = (0, import_react.useCallback)(
89
+ (e, flag = true) => {
90
+ composingRef.current = false;
91
+ if (flag) {
92
+ handleChange(e);
93
+ }
94
+ },
95
+ [handleChange]
96
+ );
97
+ const modelProps = (0, import_react.useMemo)(
98
+ () => ({
99
+ onCompositionStart: handleCompositionStart,
100
+ onCompositionEnd: handleCompositionEnd,
101
+ ...import_lodash.default.omit(rest, flowModelRendererPropKeys),
102
+ onChange: handleChange
103
+ }),
104
+ [handleChange, handleCompositionEnd, handleCompositionStart, rest]
105
+ );
93
106
  (0, import_react.useEffect)(() => {
94
107
  model && model.setProps(modelProps);
95
- }, [modelProps]);
108
+ }, [model, modelProps]);
96
109
  return /* @__PURE__ */ import_react.default.createElement(import_FlowModelRenderer.FlowModelRenderer, { model, ...rest });
97
110
  }
98
111
  __name(FieldModelRenderer, "FieldModelRenderer");
@@ -48,14 +48,34 @@ var import_lazy_helper = require("../lazy-helper");
48
48
  const { Popup } = (0, import_lazy_helper.lazy)(() => import("antd-mobile"), "Popup");
49
49
  const { CloseOutline } = (0, import_lazy_helper.lazy)(() => import("antd-mobile-icons"), "CloseOutline");
50
50
  const MobilePopup = /* @__PURE__ */ __name((props) => {
51
+ var _a;
51
52
  const { title, visible, onClose: closePopup, children, minHeight, className, footer } = props;
52
53
  const { t } = (0, import_react_i18next.useTranslation)();
53
54
  const { componentCls, hashId } = (0, import_MobilePopup.useMobileActionDrawerStyle)();
54
- const style = (0, import_react.useMemo)(() => {
55
+ const bodyStyles = (_a = props.styles) == null ? void 0 : _a.body;
56
+ const popupStyle = (0, import_react.useMemo)(() => {
55
57
  return {
56
- minHeight
58
+ minHeight: (bodyStyles == null ? void 0 : bodyStyles.minHeight) ?? minHeight,
59
+ height: bodyStyles == null ? void 0 : bodyStyles.height,
60
+ maxHeight: bodyStyles == null ? void 0 : bodyStyles.maxHeight
57
61
  };
58
- }, [minHeight]);
62
+ }, [bodyStyles == null ? void 0 : bodyStyles.height, bodyStyles == null ? void 0 : bodyStyles.maxHeight, bodyStyles == null ? void 0 : bodyStyles.minHeight, minHeight]);
63
+ const bodyStyle = (0, import_react.useMemo)(() => {
64
+ return {
65
+ padding: 0,
66
+ ...bodyStyles
67
+ };
68
+ }, [bodyStyles]);
69
+ const handleCloseKeyDown = (0, import_react.useCallback)(
70
+ (event) => {
71
+ if (event.key !== "Enter" && event.key !== " ") {
72
+ return;
73
+ }
74
+ event.preventDefault();
75
+ closePopup();
76
+ },
77
+ [closePopup]
78
+ );
59
79
  const theme = (0, import_react.useMemo)(() => {
60
80
  return {
61
81
  token: {
@@ -75,21 +95,19 @@ const MobilePopup = /* @__PURE__ */ __name((props) => {
75
95
  onClose: closePopup,
76
96
  onMaskClick: closePopup,
77
97
  bodyClassName: "nb-mobile-action-drawer-body",
78
- bodyStyle: {
79
- padding: 0
80
- },
81
- maskStyle: style,
82
- style,
98
+ bodyStyle,
99
+ style: popupStyle,
83
100
  destroyOnClose: true
84
101
  },
85
- /* @__PURE__ */ import_react.default.createElement("div", { className: "nb-mobile-action-drawer-header" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "nb-mobile-action-drawer-placeholder" }, /* @__PURE__ */ import_react.default.createElement(CloseOutline, null)), /* @__PURE__ */ import_react.default.createElement("span", null, title), /* @__PURE__ */ import_react.default.createElement(
102
+ /* @__PURE__ */ import_react.default.createElement("div", { className: "nb-mobile-action-drawer-header" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "nb-mobile-action-drawer-placeholder" }, /* @__PURE__ */ import_react.default.createElement(CloseOutline, null)), /* @__PURE__ */ import_react.default.createElement("span", { className: "nb-mobile-action-drawer-title" }, title), /* @__PURE__ */ import_react.default.createElement(
86
103
  "span",
87
104
  {
88
105
  className: "nb-mobile-action-drawer-close-icon",
89
106
  onClick: closePopup,
90
107
  role: "button",
91
108
  tabIndex: 0,
92
- "aria-label": t("Close")
109
+ "aria-label": t("Close"),
110
+ onKeyDown: handleCloseKeyDown
93
111
  },
94
112
  /* @__PURE__ */ import_react.default.createElement(CloseOutline, null)
95
113
  )),
@@ -96,9 +96,17 @@ const genStyleHook = /* @__PURE__ */ __name((component, styleFn) => {
96
96
  return [genCommonStyle(token, prefixCls), styleInterpolation];
97
97
  }
98
98
  );
99
- const memoizedWrapSSR = (0, import_react.useMemo)(() => {
100
- return wrapSSR;
101
- }, [theme2, token, hashId, prefixCls, iconPrefixCls, rootPrefixCls, props]);
99
+ const wrapSSRDeps = [theme2, token, hashId, prefixCls, iconPrefixCls, rootPrefixCls, props];
100
+ const wrapSSRCacheRef = (0, import_react.useRef)();
101
+ const currentCache = wrapSSRCacheRef.current;
102
+ let memoizedWrapSSR = (currentCache == null ? void 0 : currentCache.wrapSSR) || wrapSSR;
103
+ if (!currentCache || wrapSSRDeps.some((dep, index) => dep !== currentCache.deps[index])) {
104
+ wrapSSRCacheRef.current = {
105
+ deps: wrapSSRDeps,
106
+ wrapSSR
107
+ };
108
+ memoizedWrapSSR = wrapSSR;
109
+ }
102
110
  return {
103
111
  wrapSSR: memoizedWrapSSR,
104
112
  hashId,
@@ -112,14 +120,14 @@ const useMobileActionDrawerStyle = genStyleHook("nb-mobile-action-drawer", (toke
112
120
  return {
113
121
  [componentCls]: {
114
122
  ".nb-mobile-action-drawer-header": {
115
- height: "var(--nb-mobile-page-header-height)",
123
+ height: "var(--nb-mobile-page-header-height, 46px)",
116
124
  display: "flex",
117
125
  alignItems: "center",
118
126
  justifyContent: "space-between",
119
127
  borderBottom: `1px solid ${token.colorSplit}`,
120
128
  position: "sticky",
121
129
  top: 0,
122
- backgroundColor: "white",
130
+ backgroundColor: token.colorBgContainer,
123
131
  zIndex: 1e3,
124
132
  // to match the button named 'Add block'
125
133
  "& + .nb-grid-container > .nb-grid > .nb-grid-warp > .ant-btn": {
@@ -129,17 +137,30 @@ const useMobileActionDrawerStyle = genStyleHook("nb-mobile-action-drawer", (toke
129
137
  ".nb-mobile-action-drawer-placeholder": {
130
138
  display: "inline-block",
131
139
  padding: 12,
140
+ flex: "0 0 auto",
132
141
  visibility: "hidden"
133
142
  },
143
+ ".nb-mobile-action-drawer-title": {
144
+ flex: "1 1 auto",
145
+ minWidth: 0,
146
+ overflow: "hidden",
147
+ textAlign: "center",
148
+ textOverflow: "ellipsis",
149
+ whiteSpace: "nowrap"
150
+ },
134
151
  ".nb-mobile-action-drawer-close-icon": {
135
152
  display: "inline-block",
136
153
  padding: 12,
154
+ flex: "0 0 auto",
137
155
  cursor: "pointer"
138
156
  },
139
157
  ".nb-mobile-action-drawer-body": {
140
158
  borderTopLeftRadius: 8,
141
159
  borderTopRightRadius: 8,
142
- maxHeight: "calc(100% - var(--nb-mobile-page-header-height))",
160
+ maxHeight: "calc(100vh - var(--nb-mobile-page-header-height, 46px))",
161
+ "@supports (height: 100dvh)": {
162
+ maxHeight: "calc(100dvh - var(--nb-mobile-page-header-height, 46px))"
163
+ },
143
164
  overflowY: "auto",
144
165
  overflowX: "hidden",
145
166
  backgroundColor: token.colorBgLayout,
@@ -57,6 +57,10 @@ const EMPTY_COLUMN_UID = "EMPTY_COLUMN";
57
57
  const TOOLBAR_DRAG_ACTIVITY_EVENT = "nb-toolbar-drag-activity";
58
58
  const TOOLBAR_DRAG_ANCHOR_EVENT = "nb-toolbar-drag-anchor";
59
59
  const MENU_SUBMENU_POPUP_SELECTOR = ".ant-menu-submenu-popup";
60
+ const getToolbarModelUidFromNode = /* @__PURE__ */ __name((node) => {
61
+ var _a;
62
+ return ((_a = node == null ? void 0 : node.closest(".nb-toolbar-container[data-model-uid]")) == null ? void 0 : _a.getAttribute("data-model-uid")) || null;
63
+ }, "getToolbarModelUidFromNode");
60
64
  const resolveOverlayAnchorTransform = /* @__PURE__ */ __name(({
61
65
  activeId,
62
66
  active,
@@ -76,7 +80,7 @@ const resolveOverlayAnchorTransform = /* @__PURE__ */ __name(({
76
80
  const resolveDraggableHostNode = /* @__PURE__ */ __name((activatorNode) => {
77
81
  const ownerDocument = activatorNode == null ? void 0 : activatorNode.ownerDocument;
78
82
  const floatToolbarContainer = activatorNode == null ? void 0 : activatorNode.closest(".nb-toolbar-container[data-model-uid]");
79
- const toolbarModelUid = floatToolbarContainer == null ? void 0 : floatToolbarContainer.getAttribute("data-model-uid");
83
+ const toolbarModelUid = getToolbarModelUidFromNode(activatorNode);
80
84
  if (!ownerDocument || !toolbarModelUid) {
81
85
  return activatorNode;
82
86
  }
@@ -99,6 +103,7 @@ const DragHandler = /* @__PURE__ */ __name(({
99
103
  const dragHandlerRef = (0, import_react.useRef)(null);
100
104
  const draggableNodeRef = (0, import_react.useRef)(null);
101
105
  const pointerPressCleanupRef = (0, import_react.useRef)(null);
106
+ const toolbarDragModelUidRef = (0, import_react.useRef)(null);
102
107
  const isDraggingRef = (0, import_react.useRef)(isDragging);
103
108
  const isPointerPressActiveRef = (0, import_react.useRef)(false);
104
109
  const isToolbarDragActiveRef = (0, import_react.useRef)(false);
@@ -128,9 +133,11 @@ const DragHandler = /* @__PURE__ */ __name(({
128
133
  if (!ownerDocument) {
129
134
  return;
130
135
  }
136
+ const toolbarModelUid = getToolbarModelUidFromNode(dragHandlerRef.current) || toolbarDragModelUidRef.current || model.uid;
137
+ toolbarDragModelUidRef.current = active ? toolbarModelUid : null;
131
138
  ownerDocument.dispatchEvent(
132
139
  new CustomEvent(TOOLBAR_DRAG_ACTIVITY_EVENT, {
133
- detail: { active, modelUid: model.uid }
140
+ detail: { active, modelUid: toolbarModelUid }
134
141
  })
135
142
  );
136
143
  },
@@ -44,6 +44,7 @@ var import_react = __toESM(require("react"));
44
44
  var import_react_dom = require("react-dom");
45
45
  var import_antd = require("antd");
46
46
  var import_css = require("@emotion/css");
47
+ var import_ahooks = require("ahooks");
47
48
  var import_hooks = require("../../../../hooks");
48
49
  var import_provider = require("../../../../provider");
49
50
  var import_utils = require("../../../../utils");
@@ -312,6 +313,38 @@ const buildToolbarContainerStyle = /* @__PURE__ */ __name((portalRect, toolbarSt
312
313
  const isModelByIdProps = /* @__PURE__ */ __name((props) => {
313
314
  return "uid" in props && "modelClassName" in props && Boolean(props.uid) && Boolean(props.modelClassName);
314
315
  }, "isModelByIdProps");
316
+ const stopResizeInteractionEvent = /* @__PURE__ */ __name((event) => {
317
+ var _a;
318
+ if (!event) {
319
+ return;
320
+ }
321
+ event.preventDefault();
322
+ event.stopPropagation();
323
+ const nativeEvent = "nativeEvent" in event ? event.nativeEvent : event;
324
+ (_a = nativeEvent.stopImmediatePropagation) == null ? void 0 : _a.call(nativeEvent);
325
+ }, "stopResizeInteractionEvent");
326
+ const pendingResizeClickSuppressions = /* @__PURE__ */ new WeakMap();
327
+ const clearPendingResizeClickSuppression = /* @__PURE__ */ __name((ownerDocument) => {
328
+ const pending = pendingResizeClickSuppressions.get(ownerDocument);
329
+ if (!pending) {
330
+ return;
331
+ }
332
+ ownerDocument.removeEventListener("click", pending.listener, true);
333
+ (ownerDocument.defaultView || window).clearTimeout(pending.timer);
334
+ pendingResizeClickSuppressions.delete(ownerDocument);
335
+ }, "clearPendingResizeClickSuppression");
336
+ const suppressNextResizeClick = /* @__PURE__ */ __name((ownerDocument) => {
337
+ clearPendingResizeClickSuppression(ownerDocument);
338
+ const listener = /* @__PURE__ */ __name((event) => {
339
+ stopResizeInteractionEvent(event);
340
+ clearPendingResizeClickSuppression(ownerDocument);
341
+ }, "listener");
342
+ const timer = (ownerDocument.defaultView || window).setTimeout(() => {
343
+ clearPendingResizeClickSuppression(ownerDocument);
344
+ }, 0);
345
+ pendingResizeClickSuppressions.set(ownerDocument, { listener, timer });
346
+ ownerDocument.addEventListener("click", listener, true);
347
+ }, "suppressNextResizeClick");
315
348
  const FlowsFloatContextMenu = (0, import_reactive.observer)((props) => {
316
349
  const ctx = (0, import__.useFlowContext)();
317
350
  if (!ctx.flowSettingsEnabled) {
@@ -326,44 +359,64 @@ const ResizeHandles = /* @__PURE__ */ __name((props) => {
326
359
  const isDraggingRef = (0, import_react.useRef)(false);
327
360
  const dragTypeRef = (0, import_react.useRef)(null);
328
361
  const dragStartPosRef = (0, import_react.useRef)({ x: 0, y: 0 });
362
+ const dragOwnerDocumentRef = (0, import_react.useRef)(null);
329
363
  const { onDragStart, onDragEnd } = props;
330
- const handleDragMove = (0, import_react.useCallback)(
331
- (e) => {
332
- if (!isDraggingRef.current || !dragTypeRef.current) return;
333
- const deltaX = e.clientX - dragStartPosRef.current.x;
334
- switch (dragTypeRef.current) {
335
- case "left":
336
- props.model.parent.emitter.emit("onResizeLeft", { resizeDistance: -deltaX, model: props.model });
337
- break;
338
- case "right":
339
- props.model.parent.emitter.emit("onResizeRight", { resizeDistance: deltaX, model: props.model });
340
- break;
341
- }
342
- },
343
- [props.model]
344
- );
345
- const handleDragEnd = (0, import_react.useCallback)(() => {
364
+ const handleDragMove = (0, import_ahooks.useMemoizedFn)((e) => {
365
+ if (!isDraggingRef.current || !dragTypeRef.current) return;
366
+ stopResizeInteractionEvent(e);
367
+ const deltaX = e.clientX - dragStartPosRef.current.x;
368
+ switch (dragTypeRef.current) {
369
+ case "left":
370
+ props.model.parent.emitter.emit("onResizeLeft", { resizeDistance: -deltaX, model: props.model });
371
+ break;
372
+ case "right":
373
+ props.model.parent.emitter.emit("onResizeRight", { resizeDistance: deltaX, model: props.model });
374
+ break;
375
+ }
376
+ });
377
+ const handleDragEnd = (0, import_ahooks.useMemoizedFn)((e) => {
378
+ if (!isDraggingRef.current) {
379
+ return;
380
+ }
381
+ stopResizeInteractionEvent(e);
382
+ const ownerDocument = dragOwnerDocumentRef.current || document;
383
+ ownerDocument.removeEventListener("mousemove", handleDragMove, true);
384
+ ownerDocument.removeEventListener("mouseup", handleDragEnd, true);
385
+ suppressNextResizeClick(ownerDocument);
346
386
  isDraggingRef.current = false;
347
387
  dragTypeRef.current = null;
348
388
  dragStartPosRef.current = { x: 0, y: 0 };
349
- document.removeEventListener("mousemove", handleDragMove);
350
- document.removeEventListener("mouseup", handleDragEnd);
389
+ dragOwnerDocumentRef.current = null;
351
390
  props.model.parent.emitter.emit("onResizeEnd");
352
391
  onDragEnd == null ? void 0 : onDragEnd();
353
- }, [handleDragMove, onDragEnd, props.model]);
354
- const handleDragStart = (0, import_react.useCallback)(
355
- (e, type) => {
356
- e.preventDefault();
357
- e.stopPropagation();
358
- isDraggingRef.current = true;
359
- dragTypeRef.current = type;
360
- dragStartPosRef.current = { x: e.clientX, y: e.clientY };
361
- document.addEventListener("mousemove", handleDragMove);
362
- document.addEventListener("mouseup", handleDragEnd);
363
- onDragStart == null ? void 0 : onDragStart();
364
- },
365
- [handleDragMove, handleDragEnd, onDragStart]
366
- );
392
+ });
393
+ (0, import_react.useEffect)(() => {
394
+ return () => {
395
+ const dragOwnerDocument = dragOwnerDocumentRef.current;
396
+ dragOwnerDocument == null ? void 0 : dragOwnerDocument.removeEventListener("mousemove", handleDragMove, true);
397
+ dragOwnerDocument == null ? void 0 : dragOwnerDocument.removeEventListener("mouseup", handleDragEnd, true);
398
+ if (isDraggingRef.current) {
399
+ suppressNextResizeClick(dragOwnerDocument || document);
400
+ props.model.parent.emitter.emit("onResizeEnd");
401
+ onDragEnd == null ? void 0 : onDragEnd();
402
+ }
403
+ isDraggingRef.current = false;
404
+ dragTypeRef.current = null;
405
+ dragStartPosRef.current = { x: 0, y: 0 };
406
+ dragOwnerDocumentRef.current = null;
407
+ };
408
+ }, [handleDragMove, handleDragEnd, onDragEnd, props.model]);
409
+ const handleDragStart = (0, import_ahooks.useMemoizedFn)((e, type) => {
410
+ stopResizeInteractionEvent(e);
411
+ const ownerDocument = e.currentTarget.ownerDocument;
412
+ isDraggingRef.current = true;
413
+ dragTypeRef.current = type;
414
+ dragStartPosRef.current = { x: e.clientX, y: e.clientY };
415
+ dragOwnerDocumentRef.current = ownerDocument;
416
+ ownerDocument.addEventListener("mousemove", handleDragMove, true);
417
+ ownerDocument.addEventListener("mouseup", handleDragEnd, true);
418
+ onDragStart == null ? void 0 : onDragStart();
419
+ });
367
420
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(
368
421
  "div",
369
422
  {
@@ -466,6 +519,7 @@ const FlowsFloatContextMenuWithModel = (0, import_reactive.observer)(
466
519
  getPopupContainer,
467
520
  handleSettingsMenuOpenChange,
468
521
  model,
522
+ modelUid,
469
523
  settingsMenuLevel,
470
524
  showCopyUidButton,
471
525
  showDeleteButton,
@@ -44,6 +44,17 @@ const getToolbarModelUidFromTarget = /* @__PURE__ */ __name((target) => {
44
44
  }
45
45
  return ((_a = target.closest(".nb-toolbar-container[data-model-uid]")) == null ? void 0 : _a.getAttribute("data-model-uid")) || null;
46
46
  }, "getToolbarModelUidFromTarget");
47
+ const escapeCssAttributeValue = /* @__PURE__ */ __name((value) => {
48
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\a ").replace(/\r/g, "\\d ").replace(/\f/g, "\\c ");
49
+ }, "escapeCssAttributeValue");
50
+ const isToolbarModelUidRendered = /* @__PURE__ */ __name((ownerDocument, modelUid) => {
51
+ if (!ownerDocument) {
52
+ return false;
53
+ }
54
+ return !!ownerDocument.querySelector(
55
+ `.nb-toolbar-container[data-model-uid="${escapeCssAttributeValue(modelUid)}"]`
56
+ );
57
+ }, "isToolbarModelUidRendered");
47
58
  const isNodeWithinDescendantFloatToolbar = /* @__PURE__ */ __name((target, container, currentModelUid) => {
48
59
  const targetModelUid = getToolbarModelUidFromTarget(target);
49
60
  if (!container || !targetModelUid || targetModelUid === currentModelUid) {
@@ -222,6 +233,15 @@ const useFloatToolbarVisibility = /* @__PURE__ */ __name(({
222
233
  const isCurrentHostTarget = !childWithMenu || childWithMenu === containerRef.current;
223
234
  if (isCurrentHostTarget) {
224
235
  clearHideToolbarTimer();
236
+ setActiveChildToolbarIds((prevIds) => {
237
+ var _a;
238
+ if (!prevIds.length) {
239
+ return prevIds;
240
+ }
241
+ const ownerDocument = ((_a = containerRef.current) == null ? void 0 : _a.ownerDocument) ?? null;
242
+ const nextIds = prevIds.filter((id) => isToolbarModelUidRendered(ownerDocument, id));
243
+ return nextIds.length === prevIds.length ? prevIds : nextIds;
244
+ });
225
245
  setHostHovered(true);
226
246
  }
227
247
  setHideMenu(!!childWithMenu && childWithMenu !== containerRef.current);
@@ -7,7 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { ISchema } from '@formily/json-schema';
10
- import { APIClient, RequestOptions } from '@nocobase/sdk';
10
+ import type { APIClient, RequestOptions } from '@nocobase/sdk';
11
11
  import type { Router } from '@remix-run/router';
12
12
  import { MessageInstance } from 'antd/es/message/interface';
13
13
  import type { HookAPI } from 'antd/es/modal/useModal';