@nocobase/flow-engine 2.2.0-beta.9 → 3.0.0-alpha.1
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 +55 -12
- 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 +41 -26
- 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/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/types.d.ts +3 -1
- package/lib/types.js +1 -0
- package/lib/utils/dirtyAwareApiClient.js +267 -13
- package/lib/utils/loadedPageCache.d.ts +1 -0
- package/lib/utils/loadedPageCache.js +6 -0
- package/package.json +4 -4
- package/src/__tests__/flowI18n.test.ts +11 -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 +66 -11
- 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 +44 -26
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +85 -2
- 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 +60 -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/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/types.ts +2 -0
- package/src/utils/__tests__/dirtyAwareApiClient.test.ts +321 -0
- package/src/utils/dirtyAwareApiClient.ts +325 -13
- package/src/utils/loadedPageCache.ts +7 -0
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,9 +242,9 @@ 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) => {
|
|
227
250
|
const lastOption = selectedOptions == null ? void 0 : selectedOptions[selectedOptions.length - 1];
|
|
@@ -289,12 +312,32 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
289
312
|
},
|
|
290
313
|
[cascaderOnDropdownVisibleChange, open]
|
|
291
314
|
);
|
|
315
|
+
const footerNode = (0, import_react.useMemo)(() => {
|
|
316
|
+
if (dropdownFooter !== void 0) {
|
|
317
|
+
return dropdownFooter;
|
|
318
|
+
}
|
|
319
|
+
if (onlyLeafSelectable) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
323
|
+
"div",
|
|
324
|
+
{
|
|
325
|
+
className: import_css.css`
|
|
326
|
+
padding: 6px 12px;
|
|
327
|
+
color: ${token.colorTextDescription};
|
|
328
|
+
border-top: 1px solid ${token.colorSplit};
|
|
329
|
+
font-size: ${token.fontSizeSM}px;
|
|
330
|
+
`
|
|
331
|
+
},
|
|
332
|
+
flowCtx.t("Double click to choose entire object")
|
|
333
|
+
);
|
|
334
|
+
}, [dropdownFooter, onlyLeafSelectable, token, flowCtx]);
|
|
292
335
|
const renderDropdown = (0, import_react.useCallback)(
|
|
293
336
|
(menu) => {
|
|
294
337
|
const cascaderMenuNode = cascaderDropdownRender ? cascaderDropdownRender(menu) : menu;
|
|
295
338
|
const cascaderMenu = import_react.default.isValidElement(cascaderMenuNode) ? cascaderMenuNode : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenuNode);
|
|
296
339
|
if (!isSearchEnabled || children === null) {
|
|
297
|
-
return cascaderMenu;
|
|
340
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenu, footerNode);
|
|
298
341
|
}
|
|
299
342
|
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
343
|
import_antd.Input,
|
|
@@ -306,9 +349,9 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
306
349
|
onChange: (e) => setSearchText(e.target.value),
|
|
307
350
|
onKeyDown: (e) => e.stopPropagation()
|
|
308
351
|
}
|
|
309
|
-
)), cascaderMenu);
|
|
352
|
+
)), cascaderMenu, footerNode);
|
|
310
353
|
},
|
|
311
|
-
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText]
|
|
354
|
+
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText, footerNode]
|
|
312
355
|
);
|
|
313
356
|
const inlinePlaceholder = typeof restCascaderProps.placeholder === "string" ? restCascaderProps.placeholder : flowCtx.t("Search");
|
|
314
357
|
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": {
|
|
@@ -347,19 +347,15 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
347
347
|
e.stopPropagation();
|
|
348
348
|
},
|
|
349
349
|
onChange: (e) => {
|
|
350
|
-
var _a;
|
|
351
350
|
e.stopPropagation();
|
|
352
351
|
const value = e.target.value;
|
|
353
352
|
if (shouldActivateSearchSubmenu) {
|
|
354
353
|
activateSearchSubmenu(searchKey);
|
|
355
354
|
}
|
|
356
|
-
if (
|
|
355
|
+
if (e.nativeEvent.isComposing || searchHandlers.isComposing(searchKey)) {
|
|
357
356
|
searchHandlers.updateInputValue(searchKey, value);
|
|
358
357
|
return;
|
|
359
358
|
}
|
|
360
|
-
if (!value && shouldActivateSearchSubmenu) {
|
|
361
|
-
deactivateSearchSubmenu(searchKey);
|
|
362
|
-
}
|
|
363
359
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
364
360
|
},
|
|
365
361
|
onCompositionStart: (e) => {
|
|
@@ -373,11 +369,7 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
373
369
|
e.stopPropagation();
|
|
374
370
|
const value = e.currentTarget.value;
|
|
375
371
|
if (shouldActivateSearchSubmenu) {
|
|
376
|
-
|
|
377
|
-
activateSearchSubmenu(searchKey);
|
|
378
|
-
} else {
|
|
379
|
-
deactivateSearchSubmenu(searchKey);
|
|
380
|
-
}
|
|
372
|
+
activateSearchSubmenu(searchKey);
|
|
381
373
|
}
|
|
382
374
|
searchHandlers.endComposition(searchKey, value);
|
|
383
375
|
},
|
|
@@ -385,10 +377,20 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
385
377
|
e.stopPropagation();
|
|
386
378
|
},
|
|
387
379
|
onKeyDown: (e) => {
|
|
380
|
+
if (e.key === "Escape" || e.key === "Tab") {
|
|
381
|
+
deactivateSearchSubmenu(searchKey);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (shouldActivateSearchSubmenu) {
|
|
385
|
+
activateSearchSubmenu(searchKey);
|
|
386
|
+
}
|
|
388
387
|
e.stopPropagation();
|
|
389
388
|
},
|
|
390
389
|
onMouseDown: (e) => {
|
|
391
390
|
e.stopPropagation();
|
|
391
|
+
if (shouldActivateSearchSubmenu) {
|
|
392
|
+
activateSearchSubmenu(searchKey);
|
|
393
|
+
}
|
|
392
394
|
},
|
|
393
395
|
size: "small",
|
|
394
396
|
style: {
|
|
@@ -409,7 +411,7 @@ const KEEP_OPEN_LABEL_STYLE = {
|
|
|
409
411
|
width: "100%"
|
|
410
412
|
};
|
|
411
413
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
412
|
-
const
|
|
414
|
+
const MENU_CLOSE_DELAY = 0.3;
|
|
413
415
|
const SUBMENU_MOTION_DISABLED = {
|
|
414
416
|
motionEnter: false,
|
|
415
417
|
motionLeave: false
|
|
@@ -417,13 +419,18 @@ const SUBMENU_MOTION_DISABLED = {
|
|
|
417
419
|
const dropdownPersistRegistry = /* @__PURE__ */ new Map();
|
|
418
420
|
const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
419
421
|
const engine = (0, import_provider.useFlowEngine)();
|
|
422
|
+
const { getPrefixCls } = import_react.default.useContext(import_antd.ConfigProvider.ConfigContext);
|
|
423
|
+
const triggerId = import_react.default.useId();
|
|
420
424
|
const [menuVisible, setMenuVisible] = (0, import_react.useState)(false);
|
|
421
425
|
const [openKeys, setOpenKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
422
|
-
const [activeSearchKey, setActiveSearchKey] = (0, import_react.useState)(null);
|
|
423
426
|
const [rootItems, setRootItems] = (0, import_react.useState)([]);
|
|
424
427
|
const [rootLoading, setRootLoading] = (0, import_react.useState)(false);
|
|
428
|
+
const activeSearchKeyRef = (0, import_react.useRef)(null);
|
|
425
429
|
const closeByOutsideClickRef = (0, import_react.useRef)(false);
|
|
426
430
|
const skipPreserveActiveSearchRef = (0, import_react.useRef)(false);
|
|
431
|
+
const triggerOpenClassName = `nb-lazy-dropdown-trigger-${triggerId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
432
|
+
const defaultOpenClassName = `${getPrefixCls("dropdown", props.prefixCls)}-open`;
|
|
433
|
+
const mergedOpenClassName = [props.openClassName ?? defaultOpenClassName, triggerOpenClassName].filter(Boolean).join(" ");
|
|
427
434
|
const dropdownMaxHeight = useNiceDropdownMaxHeight();
|
|
428
435
|
const t = engine.translate.bind(engine);
|
|
429
436
|
const { items: menuItems, keepDropdownOpen, persistKey, stateVersion, refreshKeys, ...dropdownMenuProps } = menu;
|
|
@@ -440,12 +447,12 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
440
447
|
useSubmenuStyles(menuVisible, dropdownMaxHeight);
|
|
441
448
|
const closeMenu = (0, import_react.useCallback)(() => {
|
|
442
449
|
setMenuVisible(false);
|
|
443
|
-
|
|
450
|
+
activeSearchKeyRef.current = null;
|
|
444
451
|
setOpenKeys(/* @__PURE__ */ new Set());
|
|
445
452
|
clearAllSearchValues();
|
|
446
453
|
}, [clearAllSearchValues]);
|
|
447
454
|
const activateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
448
|
-
|
|
455
|
+
activeSearchKeyRef.current = key;
|
|
449
456
|
setOpenKeys((prev) => {
|
|
450
457
|
if (prev.has(key)) return prev;
|
|
451
458
|
const next = new Set(prev);
|
|
@@ -454,34 +461,38 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
454
461
|
});
|
|
455
462
|
}, []);
|
|
456
463
|
const deactivateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
457
|
-
|
|
464
|
+
if (activeSearchKeyRef.current === key) {
|
|
465
|
+
activeSearchKeyRef.current = null;
|
|
466
|
+
}
|
|
458
467
|
}, []);
|
|
459
468
|
const closeActiveSearchForPath = (0, import_react.useCallback)(
|
|
460
469
|
(keyPath) => {
|
|
470
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
461
471
|
if (!activeSearchKey || keyPath === activeSearchKey || keyPath.startsWith(`${activeSearchKey}/`) || activeSearchKey.startsWith(`${keyPath}/`)) {
|
|
462
472
|
return;
|
|
463
473
|
}
|
|
464
474
|
skipPreserveActiveSearchRef.current = true;
|
|
465
475
|
clearSearchValue(activeSearchKey);
|
|
466
|
-
|
|
476
|
+
activeSearchKeyRef.current = null;
|
|
467
477
|
setOpenKeys((prev) => {
|
|
468
478
|
const next = new Set(prev);
|
|
469
479
|
next.delete(activeSearchKey);
|
|
470
480
|
return next;
|
|
471
481
|
});
|
|
472
482
|
},
|
|
473
|
-
[
|
|
483
|
+
[clearSearchValue]
|
|
474
484
|
);
|
|
475
485
|
const handleMenuOpenChange = (0, import_react.useCallback)(
|
|
476
486
|
(nextOpenKeys) => {
|
|
477
487
|
var _a, _b;
|
|
478
488
|
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
479
|
-
|
|
480
|
-
|
|
489
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
490
|
+
if (activeSearchKey && !normalized.includes(activeSearchKey)) {
|
|
491
|
+
if (skipPreserveActiveSearchRef.current) {
|
|
481
492
|
clearSearchValue(activeSearchKey);
|
|
482
|
-
|
|
493
|
+
activeSearchKeyRef.current = null;
|
|
483
494
|
} else {
|
|
484
|
-
normalized =
|
|
495
|
+
normalized = Array.from(openKeys);
|
|
485
496
|
}
|
|
486
497
|
}
|
|
487
498
|
if (!normalized.length && shouldPreventClose()) {
|
|
@@ -498,13 +509,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
498
509
|
(_b = dropdownMenuProps.onOpenChange) == null ? void 0 : _b.call(dropdownMenuProps, normalized);
|
|
499
510
|
skipPreserveActiveSearchRef.current = false;
|
|
500
511
|
},
|
|
501
|
-
[
|
|
512
|
+
[clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose]
|
|
502
513
|
);
|
|
503
514
|
(0, import_react.useEffect)(() => {
|
|
504
515
|
if (!menuVisible) return;
|
|
505
516
|
const markOutsideClick = /* @__PURE__ */ __name((event) => {
|
|
506
517
|
const target = event.target;
|
|
507
|
-
const
|
|
518
|
+
const isInsidePopup = target == null ? void 0 : target.closest(".ant-dropdown, .ant-dropdown-menu, .ant-dropdown-menu-submenu-popup");
|
|
519
|
+
const isInsideCurrentTrigger = target == null ? void 0 : target.closest(`.${triggerOpenClassName}`);
|
|
520
|
+
const isOutside = !isInsidePopup && !isInsideCurrentTrigger;
|
|
508
521
|
closeByOutsideClickRef.current = isOutside;
|
|
509
522
|
if (isOutside) {
|
|
510
523
|
closeMenu();
|
|
@@ -516,7 +529,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
516
529
|
document.removeEventListener("pointerdown", markOutsideClick, true);
|
|
517
530
|
document.removeEventListener("mousedown", markOutsideClick, true);
|
|
518
531
|
};
|
|
519
|
-
}, [closeMenu, menuVisible]);
|
|
532
|
+
}, [closeMenu, menuVisible, triggerOpenClassName]);
|
|
520
533
|
(0, import_react.useEffect)(() => {
|
|
521
534
|
if (!persistKey) return;
|
|
522
535
|
const until = dropdownPersistRegistry.get(persistKey) || 0;
|
|
@@ -734,13 +747,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
734
747
|
...props,
|
|
735
748
|
open: menuVisible,
|
|
736
749
|
destroyPopupOnHide: true,
|
|
750
|
+
mouseLeaveDelay: props.mouseLeaveDelay ?? MENU_CLOSE_DELAY,
|
|
751
|
+
openClassName: mergedOpenClassName,
|
|
737
752
|
overlayClassName,
|
|
738
753
|
placement: "bottomLeft",
|
|
739
754
|
menu: {
|
|
740
755
|
...dropdownMenuProps,
|
|
741
756
|
openKeys: Array.from(openKeys),
|
|
742
757
|
items,
|
|
743
|
-
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ??
|
|
758
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? MENU_CLOSE_DELAY,
|
|
744
759
|
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
745
760
|
onClick: /* @__PURE__ */ __name(() => {
|
|
746
761
|
}, "onClick"),
|
|
@@ -752,7 +767,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
752
767
|
}
|
|
753
768
|
},
|
|
754
769
|
onOpenChange: (visible, info) => {
|
|
755
|
-
if (!visible &&
|
|
770
|
+
if (!visible && activeSearchKeyRef.current && (info == null ? void 0 : info.source) === "trigger" && !closeByOutsideClickRef.current) {
|
|
756
771
|
return;
|
|
757
772
|
}
|
|
758
773
|
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>;
|