@nocobase/flow-engine 2.2.0-beta.8 → 2.2.0
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/lib/acl/Acl.d.ts +2 -1
- package/lib/acl/Acl.js +28 -0
- package/lib/components/FlowContextSelector.js +62 -13
- package/lib/components/FormItem.js +11 -7
- package/lib/components/MobilePopup.js +39 -10
- package/lib/components/MobilePopup.style.js +11 -1
- package/lib/components/subModel/LazyDropdown.js +62 -33
- package/lib/components/variables/VariableHybridInput.d.ts +9 -0
- package/lib/components/variables/VariableHybridInput.js +146 -17
- package/lib/components/variables/VariableInput.js +19 -7
- package/lib/components/variables/VariableTag.js +48 -36
- package/lib/components/variables/types.d.ts +21 -0
- package/lib/flowContext.d.ts +12 -1
- package/lib/flowContext.js +57 -12
- package/lib/flowEngine.js +6 -0
- package/lib/flowI18n.js +3 -3
- package/lib/locale/en-US.json +2 -0
- package/lib/locale/index.d.ts +4 -0
- package/lib/locale/zh-CN.json +2 -0
- package/lib/resources/flowResource.js +1 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.js +1 -0
- package/lib/utils/associationObjectVariable.d.ts +10 -0
- package/lib/utils/associationObjectVariable.js +10 -7
- package/lib/utils/dateVariable.d.ts +22 -0
- package/lib/utils/dateVariable.js +123 -16
- package/lib/utils/dirtyAwareApiClient.d.ts +1 -0
- package/lib/utils/dirtyAwareApiClient.js +280 -13
- package/lib/utils/index.d.ts +3 -3
- package/lib/utils/index.js +8 -0
- package/lib/utils/loadedPageCache.d.ts +1 -0
- package/lib/utils/loadedPageCache.js +6 -0
- package/lib/utils/params-resolvers.d.ts +3 -0
- package/lib/utils/params-resolvers.js +10 -0
- package/lib/utils/variablesParams.js +5 -0
- package/lib/views/createViewMeta.d.ts +1 -0
- package/lib/views/createViewMeta.js +53 -22
- package/package.json +4 -4
- package/src/__tests__/createViewMeta.popup.test.ts +84 -1
- package/src/__tests__/flowContext.test.ts +31 -0
- package/src/__tests__/flowI18n.test.ts +11 -0
- package/src/__tests__/objectVariable.test.ts +6 -1
- package/src/__tests__/runjsFormSubmit.test.ts +138 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +72 -6
- package/src/acl/Acl.tsx +36 -1
- package/src/acl/__tests__/Acl.test.tsx +70 -0
- package/src/components/FlowContextSelector.tsx +73 -12
- package/src/components/FormItem.tsx +12 -7
- package/src/components/MobilePopup.style.ts +12 -1
- package/src/components/MobilePopup.tsx +42 -10
- package/src/components/__tests__/FormItem.test.tsx +17 -2
- package/src/components/__tests__/MobilePopup.test.tsx +150 -0
- package/src/components/subModel/LazyDropdown.tsx +71 -38
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +85 -2
- package/src/components/subModel/__tests__/LazyDropdown.test.tsx +202 -0
- package/src/components/variables/VariableHybridInput.tsx +185 -14
- package/src/components/variables/VariableInput.tsx +32 -7
- package/src/components/variables/VariableTag.tsx +51 -37
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +95 -3
- package/src/components/variables/__tests__/VariableHybridInput.test.tsx +212 -0
- package/src/components/variables/__tests__/VariableInput.test.tsx +202 -6
- package/src/components/variables/__tests__/VariableTag.test.tsx +80 -0
- package/src/components/variables/types.ts +21 -0
- package/src/flowContext.ts +83 -9
- package/src/flowEngine.ts +6 -0
- package/src/flowI18n.ts +8 -3
- package/src/locale/__tests__/index.test.ts +21 -0
- package/src/locale/en-US.json +2 -0
- package/src/locale/zh-CN.json +2 -0
- package/src/resources/__tests__/flowResource.test.ts +3 -0
- package/src/resources/flowResource.ts +1 -0
- package/src/types.ts +2 -0
- package/src/utils/__tests__/dateVariable.test.ts +57 -4
- package/src/utils/__tests__/dirtyAwareApiClient.test.ts +321 -0
- package/src/utils/__tests__/variablesParams.test.ts +28 -1
- package/src/utils/associationObjectVariable.ts +9 -6
- package/src/utils/dateVariable.ts +145 -18
- package/src/utils/dirtyAwareApiClient.ts +348 -13
- package/src/utils/index.ts +17 -2
- package/src/utils/loadedPageCache.ts +7 -0
- package/src/utils/params-resolvers.ts +12 -0
- package/src/utils/variablesParams.ts +10 -0
- package/src/views/createViewMeta.ts +52 -18
package/lib/acl/Acl.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ interface CheckOptions {
|
|
|
5
5
|
actionName: string;
|
|
6
6
|
fields?: string[];
|
|
7
7
|
recordPkValue?: string | number;
|
|
8
|
-
allowedActions
|
|
8
|
+
allowedActions?: Record<string, Array<string | number>>;
|
|
9
9
|
}
|
|
10
10
|
export declare class ACL {
|
|
11
11
|
private flowEngine;
|
|
@@ -26,6 +26,7 @@ export declare class ACL {
|
|
|
26
26
|
verifyScope: (actionName: string, recordPkValue: any, allowedActions: any) => boolean;
|
|
27
27
|
parseAction(options: CheckOptions): any;
|
|
28
28
|
parseField(options: CheckOptions): boolean;
|
|
29
|
+
can(options: CheckOptions): boolean;
|
|
29
30
|
aclCheck(options: CheckOptions): Promise<boolean>;
|
|
30
31
|
}
|
|
31
32
|
export {};
|
package/lib/acl/Acl.js
CHANGED
|
@@ -154,6 +154,34 @@ const _ACL = class _ACL {
|
|
|
154
154
|
const allowed = whitelist.includes(fields[0]);
|
|
155
155
|
return allowed;
|
|
156
156
|
}
|
|
157
|
+
can(options) {
|
|
158
|
+
var _a;
|
|
159
|
+
const { allowAll } = this.data;
|
|
160
|
+
if (allowAll) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
const { actionName, allowedActions, recordPkValue } = options;
|
|
164
|
+
const hasRecordPkValue = recordPkValue !== void 0 && recordPkValue !== null;
|
|
165
|
+
const recordPermission = hasRecordPkValue && allowedActions ? this.verifyScope(actionName, recordPkValue, allowedActions) : null;
|
|
166
|
+
if (hasRecordPkValue && allowedActions && recordPermission !== true) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
const params = this.parseAction(options);
|
|
170
|
+
if (!params) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (!import_lodash.default.isEmpty(params.filter) && recordPermission !== true) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (!((_a = options.fields) == null ? void 0 : _a.length)) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const allowedFields = [].concat(params.whitelist || []).concat(params.fields || []).concat(params.appends || []);
|
|
180
|
+
if (!allowedFields.length) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return options.fields.every((field) => allowedFields.includes(field));
|
|
184
|
+
}
|
|
157
185
|
async aclCheck(options) {
|
|
158
186
|
const { allowAll } = this.data;
|
|
159
187
|
if (allowAll) {
|
|
@@ -57,6 +57,15 @@ const cascaderPopupAutoHeightClassName = import_css.css`
|
|
|
57
57
|
max-height: 50vh;
|
|
58
58
|
}
|
|
59
59
|
`;
|
|
60
|
+
function getMetaNodeTooltip(meta) {
|
|
61
|
+
if (!meta) {
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
const metaWithTooltip = meta;
|
|
65
|
+
const options = meta.options;
|
|
66
|
+
return metaWithTooltip.tooltip ?? (options == null ? void 0 : options.tooltip);
|
|
67
|
+
}
|
|
68
|
+
__name(getMetaNodeTooltip, "getMetaNodeTooltip");
|
|
60
69
|
const normalizePath = /* @__PURE__ */ __name((path) => {
|
|
61
70
|
if (!Array.isArray(path)) {
|
|
62
71
|
return void 0;
|
|
@@ -90,6 +99,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
90
99
|
value,
|
|
91
100
|
onChange,
|
|
92
101
|
children,
|
|
102
|
+
active,
|
|
93
103
|
metaTree,
|
|
94
104
|
showSearch = false,
|
|
95
105
|
parseValueToPath: customParseValueToPath = import_utils.parseValueToPath,
|
|
@@ -97,6 +107,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
97
107
|
open,
|
|
98
108
|
onlyLeafSelectable = false,
|
|
99
109
|
ignoreFieldNames,
|
|
110
|
+
dropdownFooter,
|
|
100
111
|
...cascaderProps
|
|
101
112
|
}) => {
|
|
102
113
|
const { token } = import_antd.theme.useToken();
|
|
@@ -111,15 +122,27 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
111
122
|
const disabled = meta ? !!(typeof meta.disabled === "function" ? meta.disabled() : meta.disabled) : false;
|
|
112
123
|
const disabledReason = meta ? typeof meta.disabledReason === "function" ? meta.disabledReason() : meta.disabledReason : void 0;
|
|
113
124
|
const baseLabel = typeof o.label === "string" ? flowCtx.t(o.label) : o.label;
|
|
114
|
-
const
|
|
125
|
+
const labelText = typeof baseLabel === "string" ? baseLabel : String(o.value);
|
|
126
|
+
const tooltip = getMetaNodeTooltip(meta);
|
|
127
|
+
const tooltipTitle = disabled ? disabledReason || tooltip || flowCtx.t("This variable is not available") : tooltip;
|
|
128
|
+
const label = tooltipTitle ? /* @__PURE__ */ import_react.default.createElement("span", null, baseLabel, /* @__PURE__ */ import_react.default.createElement(
|
|
115
129
|
import_antd.Tooltip,
|
|
116
130
|
{
|
|
117
|
-
title:
|
|
118
|
-
placement: "
|
|
119
|
-
|
|
131
|
+
title: typeof tooltipTitle === "string" ? flowCtx.t(tooltipTitle) : tooltipTitle,
|
|
132
|
+
placement: "top",
|
|
133
|
+
classNames: { root: "flow-variable-tip" },
|
|
120
134
|
destroyTooltipOnHide: true
|
|
121
135
|
},
|
|
122
|
-
/* @__PURE__ */ import_react.default.createElement(
|
|
136
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
137
|
+
import_icons.QuestionCircleOutlined,
|
|
138
|
+
{
|
|
139
|
+
"aria-label": `${labelText} tooltip`,
|
|
140
|
+
style: {
|
|
141
|
+
marginLeft: token.marginXXS,
|
|
142
|
+
color: disabled ? token.colorTextDisabled : token.colorTextDescription
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
)
|
|
123
146
|
)) : baseLabel;
|
|
124
147
|
return {
|
|
125
148
|
...o,
|
|
@@ -129,7 +152,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
129
152
|
};
|
|
130
153
|
});
|
|
131
154
|
},
|
|
132
|
-
[flowCtx]
|
|
155
|
+
[flowCtx, token.colorTextDescription, token.colorTextDisabled, token.marginXXS]
|
|
133
156
|
);
|
|
134
157
|
const [updateFlag, setUpdateFlag] = (0, import_react.useState)(0);
|
|
135
158
|
const [searchText, setSearchText] = (0, import_react.useState)("");
|
|
@@ -219,11 +242,12 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
219
242
|
(0, import_utils.preloadContextSelectorPath)(options, pathToPreload, triggerUpdate);
|
|
220
243
|
}, [options, pathToPreload, triggerUpdate]);
|
|
221
244
|
const defaultChildren = (0, import_react.useMemo)(() => {
|
|
222
|
-
const hasSelected = currentPath && currentPath.length > 0;
|
|
223
|
-
return /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { type: hasSelected ? "primary" : "default", style: defaultButtonStyle }, "x");
|
|
224
|
-
}, [currentPath]);
|
|
245
|
+
const hasSelected = active ?? Boolean(currentPath && currentPath.length > 0);
|
|
246
|
+
return /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { type: hasSelected ? "primary" : "default", style: defaultButtonStyle, disabled: cascaderProps.disabled }, "x");
|
|
247
|
+
}, [active, cascaderProps.disabled, currentPath]);
|
|
225
248
|
const handleChange = (0, import_react.useCallback)(
|
|
226
249
|
(selectedValues, selectedOptions) => {
|
|
250
|
+
var _a;
|
|
227
251
|
const lastOption = selectedOptions == null ? void 0 : selectedOptions[selectedOptions.length - 1];
|
|
228
252
|
if (!selectedValues || selectedValues.length === 0) {
|
|
229
253
|
onChange == null ? void 0 : onChange("", lastOption == null ? void 0 : lastOption.meta);
|
|
@@ -233,6 +257,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
233
257
|
const path = selectedValues.map(String);
|
|
234
258
|
const pathString = path.join(".");
|
|
235
259
|
const isLeaf = lastOption == null ? void 0 : lastOption.isLeaf;
|
|
260
|
+
const isSelectable = ((_a = lastOption == null ? void 0 : lastOption.meta) == null ? void 0 : _a.selectable) !== false;
|
|
236
261
|
const now = Date.now();
|
|
237
262
|
let formattedValue;
|
|
238
263
|
if (customFormatPathToValue) {
|
|
@@ -244,12 +269,16 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
244
269
|
formattedValue = (0, import_utils.formatPathToValue)(lastOption == null ? void 0 : lastOption.meta);
|
|
245
270
|
}
|
|
246
271
|
if (isLeaf) {
|
|
272
|
+
if (!isSelectable) {
|
|
273
|
+
setTempSelectedPath(path);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
247
276
|
onChange == null ? void 0 : onChange(formattedValue, lastOption == null ? void 0 : lastOption.meta);
|
|
248
277
|
setTempSelectedPath([]);
|
|
249
278
|
return;
|
|
250
279
|
}
|
|
251
280
|
const lastSelected = lastSelectedRef.current;
|
|
252
|
-
const isDoubleClick = !onlyLeafSelectable && (lastSelected == null ? void 0 : lastSelected.path) === pathString && now - lastSelected.time < 300;
|
|
281
|
+
const isDoubleClick = isSelectable && !onlyLeafSelectable && (lastSelected == null ? void 0 : lastSelected.path) === pathString && now - lastSelected.time < 300;
|
|
253
282
|
if (isDoubleClick) {
|
|
254
283
|
onChange == null ? void 0 : onChange(formattedValue, lastOption == null ? void 0 : lastOption.meta);
|
|
255
284
|
lastSelectedRef.current = null;
|
|
@@ -289,12 +318,32 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
289
318
|
},
|
|
290
319
|
[cascaderOnDropdownVisibleChange, open]
|
|
291
320
|
);
|
|
321
|
+
const footerNode = (0, import_react.useMemo)(() => {
|
|
322
|
+
if (dropdownFooter !== void 0) {
|
|
323
|
+
return dropdownFooter;
|
|
324
|
+
}
|
|
325
|
+
if (onlyLeafSelectable) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
329
|
+
"div",
|
|
330
|
+
{
|
|
331
|
+
className: import_css.css`
|
|
332
|
+
padding: 6px 12px;
|
|
333
|
+
color: ${token.colorTextDescription};
|
|
334
|
+
border-top: 1px solid ${token.colorSplit};
|
|
335
|
+
font-size: ${token.fontSizeSM}px;
|
|
336
|
+
`
|
|
337
|
+
},
|
|
338
|
+
flowCtx.t("Double click to choose entire object")
|
|
339
|
+
);
|
|
340
|
+
}, [dropdownFooter, onlyLeafSelectable, token, flowCtx]);
|
|
292
341
|
const renderDropdown = (0, import_react.useCallback)(
|
|
293
342
|
(menu) => {
|
|
294
343
|
const cascaderMenuNode = cascaderDropdownRender ? cascaderDropdownRender(menu) : menu;
|
|
295
344
|
const cascaderMenu = import_react.default.isValidElement(cascaderMenuNode) ? cascaderMenuNode : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenuNode);
|
|
296
345
|
if (!isSearchEnabled || children === null) {
|
|
297
|
-
return cascaderMenu;
|
|
346
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenu, footerNode);
|
|
298
347
|
}
|
|
299
348
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: cascaderSearchInputClassName }, /* @__PURE__ */ import_react.default.createElement(
|
|
300
349
|
import_antd.Input,
|
|
@@ -306,9 +355,9 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
306
355
|
onChange: (e) => setSearchText(e.target.value),
|
|
307
356
|
onKeyDown: (e) => e.stopPropagation()
|
|
308
357
|
}
|
|
309
|
-
)), cascaderMenu);
|
|
358
|
+
)), cascaderMenu, footerNode);
|
|
310
359
|
},
|
|
311
|
-
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText]
|
|
360
|
+
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText, footerNode]
|
|
312
361
|
);
|
|
313
362
|
const inlinePlaceholder = typeof restCascaderProps.placeholder === "string" ? restCascaderProps.placeholder : flowCtx.t("Search");
|
|
314
363
|
const hasSelectedPath = Array.isArray(effectivePath) && effectivePath.length > 0;
|
|
@@ -77,14 +77,18 @@ const formItemPropKeys = [
|
|
|
77
77
|
"required",
|
|
78
78
|
"showLabel"
|
|
79
79
|
];
|
|
80
|
+
const modelInternalPropKeys = ["globalSort"];
|
|
80
81
|
const FormItem = /* @__PURE__ */ __name(({
|
|
81
82
|
children,
|
|
82
83
|
showLabel = true,
|
|
83
84
|
labelWidth,
|
|
84
85
|
...rest
|
|
85
86
|
}) => {
|
|
87
|
+
const forwardedRest = Object.fromEntries(
|
|
88
|
+
Object.entries(rest).filter(([key]) => !modelInternalPropKeys.includes(key))
|
|
89
|
+
);
|
|
86
90
|
const childProps = Object.fromEntries(
|
|
87
|
-
Object.entries(
|
|
91
|
+
Object.entries(forwardedRest).filter(([key]) => !formItemPropKeys.includes(key))
|
|
88
92
|
);
|
|
89
93
|
const processedChildren = typeof children === "function" ? children : import_react.default.Children.map(children, (child) => {
|
|
90
94
|
if (import_react.default.isValidElement(child)) {
|
|
@@ -92,7 +96,7 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
92
96
|
}
|
|
93
97
|
return child;
|
|
94
98
|
});
|
|
95
|
-
const { label, labelWrap, colon = true, layout } =
|
|
99
|
+
const { label, labelWrap, colon = true, layout } = forwardedRest;
|
|
96
100
|
const effectiveLabelWrap = !layout || layout === "vertical" ? true : labelWrap;
|
|
97
101
|
const labelColStyle = layout === "vertical" ? { width: labelWidth, ...verticalFormItemLabelStyle } : { width: labelWidth };
|
|
98
102
|
const renderLabel = /* @__PURE__ */ __name(() => {
|
|
@@ -136,15 +140,15 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
136
140
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
137
141
|
import_antd.Form.Item,
|
|
138
142
|
{
|
|
139
|
-
...
|
|
140
|
-
style: { ...formItemStyle, ...
|
|
143
|
+
...forwardedRest,
|
|
144
|
+
style: { ...formItemStyle, ...forwardedRest.style },
|
|
141
145
|
labelCol: { style: labelColStyle },
|
|
142
146
|
layout,
|
|
143
147
|
label: renderLabel(),
|
|
144
148
|
colon: false,
|
|
145
|
-
extra:
|
|
146
|
-
tooltip:
|
|
147
|
-
title:
|
|
149
|
+
extra: forwardedRest.extra && /* @__PURE__ */ import_react.default.createElement("span", { style: { whiteSpace: "pre-wrap" } }, forwardedRest.extra),
|
|
150
|
+
tooltip: forwardedRest.tooltip && {
|
|
151
|
+
title: forwardedRest.tooltip,
|
|
148
152
|
overlayInnerStyle: { whiteSpace: "pre-line" }
|
|
149
153
|
}
|
|
150
154
|
},
|
|
@@ -47,15 +47,46 @@ var import_react_i18next = require("react-i18next");
|
|
|
47
47
|
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
|
+
const getMobilePopupMaxHeight = /* @__PURE__ */ __name(() => {
|
|
51
|
+
var _a;
|
|
52
|
+
if (typeof CSS !== "undefined" && ((_a = CSS.supports) == null ? void 0 : _a.call(CSS, "height", "100dvh"))) {
|
|
53
|
+
return "calc(100dvh - var(--nb-mobile-page-header-height, 46px))";
|
|
54
|
+
}
|
|
55
|
+
return "calc(100vh - var(--nb-mobile-page-header-height, 46px))";
|
|
56
|
+
}, "getMobilePopupMaxHeight");
|
|
50
57
|
const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
58
|
+
var _a;
|
|
51
59
|
const { title, visible, onClose: closePopup, children, minHeight, className, footer } = props;
|
|
52
60
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
53
61
|
const { componentCls, hashId } = (0, import_MobilePopup.useMobileActionDrawerStyle)();
|
|
54
|
-
const
|
|
62
|
+
const bodyStyles = (_a = props.styles) == null ? void 0 : _a.body;
|
|
63
|
+
const defaultMaxHeight = getMobilePopupMaxHeight();
|
|
64
|
+
const popupStyle = (0, import_react.useMemo)(() => {
|
|
65
|
+
return {
|
|
66
|
+
minHeight: (bodyStyles == null ? void 0 : bodyStyles.minHeight) ?? minHeight,
|
|
67
|
+
height: bodyStyles == null ? void 0 : bodyStyles.height,
|
|
68
|
+
maxHeight: (bodyStyles == null ? void 0 : bodyStyles.maxHeight) ?? defaultMaxHeight
|
|
69
|
+
};
|
|
70
|
+
}, [bodyStyles == null ? void 0 : bodyStyles.height, bodyStyles == null ? void 0 : bodyStyles.maxHeight, bodyStyles == null ? void 0 : bodyStyles.minHeight, defaultMaxHeight, minHeight]);
|
|
71
|
+
const bodyStyle = (0, import_react.useMemo)(() => {
|
|
55
72
|
return {
|
|
56
|
-
|
|
73
|
+
padding: 0,
|
|
74
|
+
maxHeight: defaultMaxHeight,
|
|
75
|
+
overflowY: "auto",
|
|
76
|
+
overflowX: "hidden",
|
|
77
|
+
...bodyStyles
|
|
57
78
|
};
|
|
58
|
-
}, [
|
|
79
|
+
}, [bodyStyles, defaultMaxHeight]);
|
|
80
|
+
const handleCloseKeyDown = (0, import_react.useCallback)(
|
|
81
|
+
(event) => {
|
|
82
|
+
if (event.key !== "Enter" && event.key !== " ") {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
closePopup();
|
|
87
|
+
},
|
|
88
|
+
[closePopup]
|
|
89
|
+
);
|
|
59
90
|
const theme = (0, import_react.useMemo)(() => {
|
|
60
91
|
return {
|
|
61
92
|
token: {
|
|
@@ -75,21 +106,19 @@ const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
|
75
106
|
onClose: closePopup,
|
|
76
107
|
onMaskClick: closePopup,
|
|
77
108
|
bodyClassName: "nb-mobile-action-drawer-body",
|
|
78
|
-
bodyStyle
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
maskStyle: style,
|
|
82
|
-
style,
|
|
109
|
+
bodyStyle,
|
|
110
|
+
style: popupStyle,
|
|
83
111
|
destroyOnClose: true
|
|
84
112
|
},
|
|
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",
|
|
113
|
+
/* @__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
114
|
"span",
|
|
87
115
|
{
|
|
88
116
|
className: "nb-mobile-action-drawer-close-icon",
|
|
89
117
|
onClick: closePopup,
|
|
90
118
|
role: "button",
|
|
91
119
|
tabIndex: 0,
|
|
92
|
-
"aria-label": t("Close")
|
|
120
|
+
"aria-label": t("Close"),
|
|
121
|
+
onKeyDown: handleCloseKeyDown
|
|
93
122
|
},
|
|
94
123
|
/* @__PURE__ */ import_react.default.createElement(CloseOutline, null)
|
|
95
124
|
)),
|
|
@@ -127,7 +127,7 @@ const useMobileActionDrawerStyle = genStyleHook("nb-mobile-action-drawer", (toke
|
|
|
127
127
|
borderBottom: `1px solid ${token.colorSplit}`,
|
|
128
128
|
position: "sticky",
|
|
129
129
|
top: 0,
|
|
130
|
-
backgroundColor:
|
|
130
|
+
backgroundColor: token.colorBgContainer,
|
|
131
131
|
zIndex: 1e3,
|
|
132
132
|
// to match the button named 'Add block'
|
|
133
133
|
"& + .nb-grid-container > .nb-grid > .nb-grid-warp > .ant-btn": {
|
|
@@ -137,11 +137,21 @@ const useMobileActionDrawerStyle = genStyleHook("nb-mobile-action-drawer", (toke
|
|
|
137
137
|
".nb-mobile-action-drawer-placeholder": {
|
|
138
138
|
display: "inline-block",
|
|
139
139
|
padding: 12,
|
|
140
|
+
flex: "0 0 auto",
|
|
140
141
|
visibility: "hidden"
|
|
141
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
|
+
},
|
|
142
151
|
".nb-mobile-action-drawer-close-icon": {
|
|
143
152
|
display: "inline-block",
|
|
144
153
|
padding: 12,
|
|
154
|
+
flex: "0 0 auto",
|
|
145
155
|
cursor: "pointer"
|
|
146
156
|
},
|
|
147
157
|
".nb-mobile-action-drawer-body": {
|
|
@@ -44,12 +44,6 @@ var import_css = require("@emotion/css");
|
|
|
44
44
|
var import_antd = require("antd");
|
|
45
45
|
var import_react = __toESM(require("react"));
|
|
46
46
|
var import_provider = require("../../provider");
|
|
47
|
-
const useNiceDropdownMaxHeight = /* @__PURE__ */ __name(() => {
|
|
48
|
-
return (0, import_react.useMemo)(() => {
|
|
49
|
-
const maxHeight = Math.min(window.innerHeight * 0.6, 400);
|
|
50
|
-
return maxHeight;
|
|
51
|
-
}, []);
|
|
52
|
-
}, "useNiceDropdownMaxHeight");
|
|
53
47
|
const useAsyncMenuItems = /* @__PURE__ */ __name((menuVisible, rootItems, resetKey, openKeySet, refreshKeys) => {
|
|
54
48
|
const [loadedChildren, setLoadedChildren] = (0, import_react.useState)({});
|
|
55
49
|
const [loadingKeys, setLoadingKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
@@ -347,19 +341,15 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
347
341
|
e.stopPropagation();
|
|
348
342
|
},
|
|
349
343
|
onChange: (e) => {
|
|
350
|
-
var _a;
|
|
351
344
|
e.stopPropagation();
|
|
352
345
|
const value = e.target.value;
|
|
353
346
|
if (shouldActivateSearchSubmenu) {
|
|
354
347
|
activateSearchSubmenu(searchKey);
|
|
355
348
|
}
|
|
356
|
-
if (
|
|
349
|
+
if (e.nativeEvent.isComposing || searchHandlers.isComposing(searchKey)) {
|
|
357
350
|
searchHandlers.updateInputValue(searchKey, value);
|
|
358
351
|
return;
|
|
359
352
|
}
|
|
360
|
-
if (!value && shouldActivateSearchSubmenu) {
|
|
361
|
-
deactivateSearchSubmenu(searchKey);
|
|
362
|
-
}
|
|
363
353
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
364
354
|
},
|
|
365
355
|
onCompositionStart: (e) => {
|
|
@@ -373,11 +363,7 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
373
363
|
e.stopPropagation();
|
|
374
364
|
const value = e.currentTarget.value;
|
|
375
365
|
if (shouldActivateSearchSubmenu) {
|
|
376
|
-
|
|
377
|
-
activateSearchSubmenu(searchKey);
|
|
378
|
-
} else {
|
|
379
|
-
deactivateSearchSubmenu(searchKey);
|
|
380
|
-
}
|
|
366
|
+
activateSearchSubmenu(searchKey);
|
|
381
367
|
}
|
|
382
368
|
searchHandlers.endComposition(searchKey, value);
|
|
383
369
|
},
|
|
@@ -385,10 +371,20 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
385
371
|
e.stopPropagation();
|
|
386
372
|
},
|
|
387
373
|
onKeyDown: (e) => {
|
|
374
|
+
if (e.key === "Escape" || e.key === "Tab") {
|
|
375
|
+
deactivateSearchSubmenu(searchKey);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (shouldActivateSearchSubmenu) {
|
|
379
|
+
activateSearchSubmenu(searchKey);
|
|
380
|
+
}
|
|
388
381
|
e.stopPropagation();
|
|
389
382
|
},
|
|
390
383
|
onMouseDown: (e) => {
|
|
391
384
|
e.stopPropagation();
|
|
385
|
+
if (shouldActivateSearchSubmenu) {
|
|
386
|
+
activateSearchSubmenu(searchKey);
|
|
387
|
+
}
|
|
392
388
|
},
|
|
393
389
|
size: "small",
|
|
394
390
|
style: {
|
|
@@ -409,7 +405,8 @@ const KEEP_OPEN_LABEL_STYLE = {
|
|
|
409
405
|
width: "100%"
|
|
410
406
|
};
|
|
411
407
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
412
|
-
const
|
|
408
|
+
const DEFAULT_DROPDOWN_MAX_HEIGHT = 400;
|
|
409
|
+
const MENU_CLOSE_DELAY = 0.3;
|
|
413
410
|
const SUBMENU_MOTION_DISABLED = {
|
|
414
411
|
motionEnter: false,
|
|
415
412
|
motionLeave: false
|
|
@@ -417,14 +414,21 @@ const SUBMENU_MOTION_DISABLED = {
|
|
|
417
414
|
const dropdownPersistRegistry = /* @__PURE__ */ new Map();
|
|
418
415
|
const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
419
416
|
const engine = (0, import_provider.useFlowEngine)();
|
|
417
|
+
const { getPrefixCls } = import_react.default.useContext(import_antd.ConfigProvider.ConfigContext);
|
|
418
|
+
const { token } = import_antd.theme.useToken();
|
|
419
|
+
const triggerId = import_react.default.useId();
|
|
420
|
+
const showArrow = Boolean(props.arrow);
|
|
420
421
|
const [menuVisible, setMenuVisible] = (0, import_react.useState)(false);
|
|
422
|
+
const [dropdownMaxHeight, setDropdownMaxHeight] = (0, import_react.useState)(DEFAULT_DROPDOWN_MAX_HEIGHT);
|
|
421
423
|
const [openKeys, setOpenKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
422
|
-
const [activeSearchKey, setActiveSearchKey] = (0, import_react.useState)(null);
|
|
423
424
|
const [rootItems, setRootItems] = (0, import_react.useState)([]);
|
|
424
425
|
const [rootLoading, setRootLoading] = (0, import_react.useState)(false);
|
|
426
|
+
const activeSearchKeyRef = (0, import_react.useRef)(null);
|
|
425
427
|
const closeByOutsideClickRef = (0, import_react.useRef)(false);
|
|
426
428
|
const skipPreserveActiveSearchRef = (0, import_react.useRef)(false);
|
|
427
|
-
const
|
|
429
|
+
const triggerOpenClassName = `nb-lazy-dropdown-trigger-${triggerId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
430
|
+
const defaultOpenClassName = `${getPrefixCls("dropdown", props.prefixCls)}-open`;
|
|
431
|
+
const mergedOpenClassName = [props.openClassName ?? defaultOpenClassName, triggerOpenClassName].filter(Boolean).join(" ");
|
|
428
432
|
const t = engine.translate.bind(engine);
|
|
429
433
|
const { items: menuItems, keepDropdownOpen, persistKey, stateVersion, refreshKeys, ...dropdownMenuProps } = menu;
|
|
430
434
|
const { loadedChildren, loadingKeys, handleLoadChildren } = useAsyncMenuItems(
|
|
@@ -438,14 +442,31 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
438
442
|
const { searchValues, inputValues, clearSearchValue, clearAllSearchValues } = searchHandlers;
|
|
439
443
|
const { requestKeepOpen, shouldPreventClose } = useKeepDropdownOpen();
|
|
440
444
|
useSubmenuStyles(menuVisible, dropdownMaxHeight);
|
|
445
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
446
|
+
if (!menuVisible) return;
|
|
447
|
+
const updateDropdownMaxHeight = /* @__PURE__ */ __name(() => {
|
|
448
|
+
const trigger = document.querySelector(`.${triggerOpenClassName}`);
|
|
449
|
+
if (!trigger) return;
|
|
450
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
451
|
+
const placementOffset = token.marginXXS + (showArrow ? token.sizePopupArrow / 2 : 0);
|
|
452
|
+
const reservedSpace = placementOffset + token.marginXXS;
|
|
453
|
+
const availableAbove = triggerRect.top - reservedSpace;
|
|
454
|
+
const availableBelow = window.innerHeight - triggerRect.bottom - reservedSpace;
|
|
455
|
+
const nextMaxHeight = Math.min(DEFAULT_DROPDOWN_MAX_HEIGHT, Math.max(0, availableAbove, availableBelow));
|
|
456
|
+
setDropdownMaxHeight(nextMaxHeight);
|
|
457
|
+
}, "updateDropdownMaxHeight");
|
|
458
|
+
updateDropdownMaxHeight();
|
|
459
|
+
window.addEventListener("resize", updateDropdownMaxHeight);
|
|
460
|
+
return () => window.removeEventListener("resize", updateDropdownMaxHeight);
|
|
461
|
+
}, [menuVisible, showArrow, token.marginXXS, token.sizePopupArrow, triggerOpenClassName]);
|
|
441
462
|
const closeMenu = (0, import_react.useCallback)(() => {
|
|
442
463
|
setMenuVisible(false);
|
|
443
|
-
|
|
464
|
+
activeSearchKeyRef.current = null;
|
|
444
465
|
setOpenKeys(/* @__PURE__ */ new Set());
|
|
445
466
|
clearAllSearchValues();
|
|
446
467
|
}, [clearAllSearchValues]);
|
|
447
468
|
const activateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
448
|
-
|
|
469
|
+
activeSearchKeyRef.current = key;
|
|
449
470
|
setOpenKeys((prev) => {
|
|
450
471
|
if (prev.has(key)) return prev;
|
|
451
472
|
const next = new Set(prev);
|
|
@@ -454,34 +475,38 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
454
475
|
});
|
|
455
476
|
}, []);
|
|
456
477
|
const deactivateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
457
|
-
|
|
478
|
+
if (activeSearchKeyRef.current === key) {
|
|
479
|
+
activeSearchKeyRef.current = null;
|
|
480
|
+
}
|
|
458
481
|
}, []);
|
|
459
482
|
const closeActiveSearchForPath = (0, import_react.useCallback)(
|
|
460
483
|
(keyPath) => {
|
|
484
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
461
485
|
if (!activeSearchKey || keyPath === activeSearchKey || keyPath.startsWith(`${activeSearchKey}/`) || activeSearchKey.startsWith(`${keyPath}/`)) {
|
|
462
486
|
return;
|
|
463
487
|
}
|
|
464
488
|
skipPreserveActiveSearchRef.current = true;
|
|
465
489
|
clearSearchValue(activeSearchKey);
|
|
466
|
-
|
|
490
|
+
activeSearchKeyRef.current = null;
|
|
467
491
|
setOpenKeys((prev) => {
|
|
468
492
|
const next = new Set(prev);
|
|
469
493
|
next.delete(activeSearchKey);
|
|
470
494
|
return next;
|
|
471
495
|
});
|
|
472
496
|
},
|
|
473
|
-
[
|
|
497
|
+
[clearSearchValue]
|
|
474
498
|
);
|
|
475
499
|
const handleMenuOpenChange = (0, import_react.useCallback)(
|
|
476
500
|
(nextOpenKeys) => {
|
|
477
501
|
var _a, _b;
|
|
478
502
|
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
479
|
-
|
|
480
|
-
|
|
503
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
504
|
+
if (activeSearchKey && !normalized.includes(activeSearchKey)) {
|
|
505
|
+
if (skipPreserveActiveSearchRef.current) {
|
|
481
506
|
clearSearchValue(activeSearchKey);
|
|
482
|
-
|
|
507
|
+
activeSearchKeyRef.current = null;
|
|
483
508
|
} else {
|
|
484
|
-
normalized =
|
|
509
|
+
normalized = Array.from(openKeys);
|
|
485
510
|
}
|
|
486
511
|
}
|
|
487
512
|
if (!normalized.length && shouldPreventClose()) {
|
|
@@ -498,13 +523,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
498
523
|
(_b = dropdownMenuProps.onOpenChange) == null ? void 0 : _b.call(dropdownMenuProps, normalized);
|
|
499
524
|
skipPreserveActiveSearchRef.current = false;
|
|
500
525
|
},
|
|
501
|
-
[
|
|
526
|
+
[clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose]
|
|
502
527
|
);
|
|
503
528
|
(0, import_react.useEffect)(() => {
|
|
504
529
|
if (!menuVisible) return;
|
|
505
530
|
const markOutsideClick = /* @__PURE__ */ __name((event) => {
|
|
506
531
|
const target = event.target;
|
|
507
|
-
const
|
|
532
|
+
const isInsidePopup = target == null ? void 0 : target.closest(".ant-dropdown, .ant-dropdown-menu, .ant-dropdown-menu-submenu-popup");
|
|
533
|
+
const isInsideCurrentTrigger = target == null ? void 0 : target.closest(`.${triggerOpenClassName}`);
|
|
534
|
+
const isOutside = !isInsidePopup && !isInsideCurrentTrigger;
|
|
508
535
|
closeByOutsideClickRef.current = isOutside;
|
|
509
536
|
if (isOutside) {
|
|
510
537
|
closeMenu();
|
|
@@ -516,7 +543,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
516
543
|
document.removeEventListener("pointerdown", markOutsideClick, true);
|
|
517
544
|
document.removeEventListener("mousedown", markOutsideClick, true);
|
|
518
545
|
};
|
|
519
|
-
}, [closeMenu, menuVisible]);
|
|
546
|
+
}, [closeMenu, menuVisible, triggerOpenClassName]);
|
|
520
547
|
(0, import_react.useEffect)(() => {
|
|
521
548
|
if (!persistKey) return;
|
|
522
549
|
const until = dropdownPersistRegistry.get(persistKey) || 0;
|
|
@@ -734,13 +761,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
734
761
|
...props,
|
|
735
762
|
open: menuVisible,
|
|
736
763
|
destroyPopupOnHide: true,
|
|
764
|
+
mouseLeaveDelay: props.mouseLeaveDelay ?? MENU_CLOSE_DELAY,
|
|
765
|
+
openClassName: mergedOpenClassName,
|
|
737
766
|
overlayClassName,
|
|
738
767
|
placement: "bottomLeft",
|
|
739
768
|
menu: {
|
|
740
769
|
...dropdownMenuProps,
|
|
741
770
|
openKeys: Array.from(openKeys),
|
|
742
771
|
items,
|
|
743
|
-
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ??
|
|
772
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? MENU_CLOSE_DELAY,
|
|
744
773
|
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
745
774
|
onClick: /* @__PURE__ */ __name(() => {
|
|
746
775
|
}, "onClick"),
|
|
@@ -752,7 +781,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
752
781
|
}
|
|
753
782
|
},
|
|
754
783
|
onOpenChange: (visible, info) => {
|
|
755
|
-
if (!visible &&
|
|
784
|
+
if (!visible && activeSearchKeyRef.current && (info == null ? void 0 : info.source) === "trigger" && !closeByOutsideClickRef.current) {
|
|
756
785
|
return;
|
|
757
786
|
}
|
|
758
787
|
if (!visible && shouldPreventClose()) {
|
|
@@ -17,11 +17,20 @@ export interface VariableHybridInputProps {
|
|
|
17
17
|
value?: string;
|
|
18
18
|
onChange?: (value: string) => void;
|
|
19
19
|
disabled?: boolean;
|
|
20
|
+
readOnly?: boolean;
|
|
20
21
|
placeholder?: string;
|
|
21
22
|
addonBefore?: React.ReactNode;
|
|
22
23
|
metaTree?: MetaTreeNode[] | (() => MetaTreeNode[] | Promise<MetaTreeNode[]>);
|
|
23
24
|
converters?: VariableHybridInputConverters;
|
|
24
25
|
style?: React.CSSProperties;
|
|
25
26
|
className?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Validation status — turns the input border red (`error`) or amber
|
|
29
|
+
* (`warning`). Usually omitted: when rendered inside an antd `Form.Item`, the
|
|
30
|
+
* status is read automatically from `FormItemInputContext`, so dropping this
|
|
31
|
+
* into a `Form.Item` with failing rules colours the border with no extra
|
|
32
|
+
* wiring. An explicit prop wins over the inherited form status.
|
|
33
|
+
*/
|
|
34
|
+
status?: 'error' | 'warning';
|
|
26
35
|
}
|
|
27
36
|
export declare const VariableHybridInput: React.NamedExoticComponent<VariableHybridInputProps>;
|