@nocobase/flow-engine 2.2.0-alpha.7 → 2.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/FlowContextSelector.js +12 -55
- package/lib/components/FormItem.js +7 -11
- package/lib/components/variables/VariableHybridInput.d.ts +0 -9
- package/lib/components/variables/VariableHybridInput.js +17 -146
- package/lib/components/variables/VariableInput.js +1 -3
- package/lib/components/variables/types.d.ts +0 -18
- package/lib/flowI18n.js +3 -3
- package/package.json +4 -4
- package/src/__tests__/flowI18n.test.ts +0 -11
- package/src/components/FlowContextSelector.tsx +11 -66
- package/src/components/FormItem.tsx +7 -12
- package/src/components/__tests__/FormItem.test.tsx +2 -17
- package/src/components/variables/VariableHybridInput.tsx +14 -185
- package/src/components/variables/VariableInput.tsx +1 -3
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +3 -60
- package/src/components/variables/__tests__/VariableInput.test.tsx +5 -63
- package/src/components/variables/types.ts +0 -18
- package/src/flowI18n.ts +3 -8
- package/src/components/variables/__tests__/VariableHybridInput.test.tsx +0 -212
|
@@ -57,15 +57,6 @@ 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");
|
|
69
60
|
const normalizePath = /* @__PURE__ */ __name((path) => {
|
|
70
61
|
if (!Array.isArray(path)) {
|
|
71
62
|
return void 0;
|
|
@@ -99,7 +90,6 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
99
90
|
value,
|
|
100
91
|
onChange,
|
|
101
92
|
children,
|
|
102
|
-
active,
|
|
103
93
|
metaTree,
|
|
104
94
|
showSearch = false,
|
|
105
95
|
parseValueToPath: customParseValueToPath = import_utils.parseValueToPath,
|
|
@@ -107,7 +97,6 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
107
97
|
open,
|
|
108
98
|
onlyLeafSelectable = false,
|
|
109
99
|
ignoreFieldNames,
|
|
110
|
-
dropdownFooter,
|
|
111
100
|
...cascaderProps
|
|
112
101
|
}) => {
|
|
113
102
|
const { token } = import_antd.theme.useToken();
|
|
@@ -122,27 +111,15 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
122
111
|
const disabled = meta ? !!(typeof meta.disabled === "function" ? meta.disabled() : meta.disabled) : false;
|
|
123
112
|
const disabledReason = meta ? typeof meta.disabledReason === "function" ? meta.disabledReason() : meta.disabledReason : void 0;
|
|
124
113
|
const baseLabel = typeof o.label === "string" ? flowCtx.t(o.label) : o.label;
|
|
125
|
-
const
|
|
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(
|
|
114
|
+
const label = disabled ? /* @__PURE__ */ import_react.default.createElement("span", null, baseLabel, /* @__PURE__ */ import_react.default.createElement(
|
|
129
115
|
import_antd.Tooltip,
|
|
130
116
|
{
|
|
131
|
-
title:
|
|
132
|
-
placement: "
|
|
133
|
-
|
|
117
|
+
title: disabledReason || flowCtx.t("This variable is not available"),
|
|
118
|
+
placement: "right",
|
|
119
|
+
overlayClassName: "flow-variable-disabled-tip",
|
|
134
120
|
destroyTooltipOnHide: true
|
|
135
121
|
},
|
|
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
|
-
)
|
|
122
|
+
/* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { marginLeft: 6, color: "rgba(0,0,0,0.35)" } })
|
|
146
123
|
)) : baseLabel;
|
|
147
124
|
return {
|
|
148
125
|
...o,
|
|
@@ -152,7 +129,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
152
129
|
};
|
|
153
130
|
});
|
|
154
131
|
},
|
|
155
|
-
[flowCtx
|
|
132
|
+
[flowCtx]
|
|
156
133
|
);
|
|
157
134
|
const [updateFlag, setUpdateFlag] = (0, import_react.useState)(0);
|
|
158
135
|
const [searchText, setSearchText] = (0, import_react.useState)("");
|
|
@@ -242,9 +219,9 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
242
219
|
(0, import_utils.preloadContextSelectorPath)(options, pathToPreload, triggerUpdate);
|
|
243
220
|
}, [options, pathToPreload, triggerUpdate]);
|
|
244
221
|
const defaultChildren = (0, import_react.useMemo)(() => {
|
|
245
|
-
const hasSelected =
|
|
246
|
-
return /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { type: hasSelected ? "primary" : "default", style: defaultButtonStyle
|
|
247
|
-
}, [
|
|
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]);
|
|
248
225
|
const handleChange = (0, import_react.useCallback)(
|
|
249
226
|
(selectedValues, selectedOptions) => {
|
|
250
227
|
const lastOption = selectedOptions == null ? void 0 : selectedOptions[selectedOptions.length - 1];
|
|
@@ -312,32 +289,12 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
312
289
|
},
|
|
313
290
|
[cascaderOnDropdownVisibleChange, open]
|
|
314
291
|
);
|
|
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]);
|
|
335
292
|
const renderDropdown = (0, import_react.useCallback)(
|
|
336
293
|
(menu) => {
|
|
337
294
|
const cascaderMenuNode = cascaderDropdownRender ? cascaderDropdownRender(menu) : menu;
|
|
338
295
|
const cascaderMenu = import_react.default.isValidElement(cascaderMenuNode) ? cascaderMenuNode : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenuNode);
|
|
339
296
|
if (!isSearchEnabled || children === null) {
|
|
340
|
-
return
|
|
297
|
+
return cascaderMenu;
|
|
341
298
|
}
|
|
342
299
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: cascaderSearchInputClassName }, /* @__PURE__ */ import_react.default.createElement(
|
|
343
300
|
import_antd.Input,
|
|
@@ -349,9 +306,9 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
349
306
|
onChange: (e) => setSearchText(e.target.value),
|
|
350
307
|
onKeyDown: (e) => e.stopPropagation()
|
|
351
308
|
}
|
|
352
|
-
)), cascaderMenu
|
|
309
|
+
)), cascaderMenu);
|
|
353
310
|
},
|
|
354
|
-
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText
|
|
311
|
+
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText]
|
|
355
312
|
);
|
|
356
313
|
const inlinePlaceholder = typeof restCascaderProps.placeholder === "string" ? restCascaderProps.placeholder : flowCtx.t("Search");
|
|
357
314
|
const hasSelectedPath = Array.isArray(effectivePath) && effectivePath.length > 0;
|
|
@@ -77,18 +77,14 @@ const formItemPropKeys = [
|
|
|
77
77
|
"required",
|
|
78
78
|
"showLabel"
|
|
79
79
|
];
|
|
80
|
-
const modelInternalPropKeys = ["globalSort"];
|
|
81
80
|
const FormItem = /* @__PURE__ */ __name(({
|
|
82
81
|
children,
|
|
83
82
|
showLabel = true,
|
|
84
83
|
labelWidth,
|
|
85
84
|
...rest
|
|
86
85
|
}) => {
|
|
87
|
-
const forwardedRest = Object.fromEntries(
|
|
88
|
-
Object.entries(rest).filter(([key]) => !modelInternalPropKeys.includes(key))
|
|
89
|
-
);
|
|
90
86
|
const childProps = Object.fromEntries(
|
|
91
|
-
Object.entries(
|
|
87
|
+
Object.entries(rest).filter(([key]) => !formItemPropKeys.includes(key))
|
|
92
88
|
);
|
|
93
89
|
const processedChildren = typeof children === "function" ? children : import_react.default.Children.map(children, (child) => {
|
|
94
90
|
if (import_react.default.isValidElement(child)) {
|
|
@@ -96,7 +92,7 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
96
92
|
}
|
|
97
93
|
return child;
|
|
98
94
|
});
|
|
99
|
-
const { label, labelWrap, colon = true, layout } =
|
|
95
|
+
const { label, labelWrap, colon = true, layout } = rest;
|
|
100
96
|
const effectiveLabelWrap = !layout || layout === "vertical" ? true : labelWrap;
|
|
101
97
|
const labelColStyle = layout === "vertical" ? { width: labelWidth, ...verticalFormItemLabelStyle } : { width: labelWidth };
|
|
102
98
|
const renderLabel = /* @__PURE__ */ __name(() => {
|
|
@@ -140,15 +136,15 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
140
136
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
141
137
|
import_antd.Form.Item,
|
|
142
138
|
{
|
|
143
|
-
...
|
|
144
|
-
style: { ...formItemStyle, ...
|
|
139
|
+
...rest,
|
|
140
|
+
style: { ...formItemStyle, ...rest.style },
|
|
145
141
|
labelCol: { style: labelColStyle },
|
|
146
142
|
layout,
|
|
147
143
|
label: renderLabel(),
|
|
148
144
|
colon: false,
|
|
149
|
-
extra:
|
|
150
|
-
tooltip:
|
|
151
|
-
title:
|
|
145
|
+
extra: rest.extra && /* @__PURE__ */ import_react.default.createElement("span", { style: { whiteSpace: "pre-wrap" } }, rest.extra),
|
|
146
|
+
tooltip: rest.tooltip && {
|
|
147
|
+
title: rest.tooltip,
|
|
152
148
|
overlayInnerStyle: { whiteSpace: "pre-line" }
|
|
153
149
|
}
|
|
154
150
|
},
|
|
@@ -17,20 +17,11 @@ export interface VariableHybridInputProps {
|
|
|
17
17
|
value?: string;
|
|
18
18
|
onChange?: (value: string) => void;
|
|
19
19
|
disabled?: boolean;
|
|
20
|
-
readOnly?: boolean;
|
|
21
20
|
placeholder?: string;
|
|
22
21
|
addonBefore?: React.ReactNode;
|
|
23
22
|
metaTree?: MetaTreeNode[] | (() => MetaTreeNode[] | Promise<MetaTreeNode[]>);
|
|
24
23
|
converters?: VariableHybridInputConverters;
|
|
25
24
|
style?: React.CSSProperties;
|
|
26
25
|
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';
|
|
35
26
|
}
|
|
36
27
|
export declare const VariableHybridInput: React.NamedExoticComponent<VariableHybridInputProps>;
|
|
@@ -42,7 +42,6 @@ __export(VariableHybridInput_exports, {
|
|
|
42
42
|
module.exports = __toCommonJS(VariableHybridInput_exports);
|
|
43
43
|
var import_css = require("@emotion/css");
|
|
44
44
|
var import_antd = require("antd");
|
|
45
|
-
var import_context = require("antd/es/form/context");
|
|
46
45
|
var import_react = __toESM(require("react"));
|
|
47
46
|
var import_FlowContextProvider = require("../../FlowContextProvider");
|
|
48
47
|
var import_FlowContextSelector = require("../FlowContextSelector");
|
|
@@ -84,28 +83,14 @@ function normalizeVariableKey(value) {
|
|
|
84
83
|
return value.replace(/^\{\{\s*/, "").replace(/\s*\}\}$/, "").trim();
|
|
85
84
|
}
|
|
86
85
|
__name(normalizeVariableKey, "normalizeVariableKey");
|
|
87
|
-
function renderHTML(value,
|
|
86
|
+
function renderHTML(value, labelMap, regExp) {
|
|
88
87
|
const re = new RegExp(regExp.source, regExp.flags.includes("g") ? regExp.flags : `${regExp.flags}g`);
|
|
89
88
|
return escapeHtml(value || "").replace(re, (matched) => {
|
|
90
|
-
const label =
|
|
89
|
+
const label = labelMap.get(normalizeVariableKey(matched)) || matched;
|
|
91
90
|
return createTagHTML(matched, label);
|
|
92
91
|
});
|
|
93
92
|
}
|
|
94
93
|
__name(renderHTML, "renderHTML");
|
|
95
|
-
function resolveTitlesByPath(roots, path, ctxT) {
|
|
96
|
-
if (!roots || !path || !path.length) return void 0;
|
|
97
|
-
const titles = [];
|
|
98
|
-
let nodes = roots;
|
|
99
|
-
for (const segment of path) {
|
|
100
|
-
if (!nodes) return void 0;
|
|
101
|
-
const matched = nodes.find((node) => node.name === segment);
|
|
102
|
-
if (!matched) return void 0;
|
|
103
|
-
titles.push(reactNodeToPlainText(matched.title || matched.name));
|
|
104
|
-
nodes = Array.isArray(matched.children) ? matched.children : void 0;
|
|
105
|
-
}
|
|
106
|
-
return titles.map(ctxT).join("/");
|
|
107
|
-
}
|
|
108
|
-
__name(resolveTitlesByPath, "resolveTitlesByPath");
|
|
109
94
|
function buildLabelMap(nodes, ctxT, converters) {
|
|
110
95
|
const map = /* @__PURE__ */ new Map();
|
|
111
96
|
function walk(items = [], parentTitles = []) {
|
|
@@ -127,34 +112,6 @@ function buildLabelMap(nodes, ctxT, converters) {
|
|
|
127
112
|
return map;
|
|
128
113
|
}
|
|
129
114
|
__name(buildLabelMap, "buildLabelMap");
|
|
130
|
-
function collectReferencePaths(value, regExp, parseValueToPath) {
|
|
131
|
-
const re = new RegExp(regExp.source, regExp.flags.includes("g") ? regExp.flags : `${regExp.flags}g`);
|
|
132
|
-
const paths = [];
|
|
133
|
-
for (const matched of value.match(re) ?? []) {
|
|
134
|
-
const path = parseValueToPath(matched);
|
|
135
|
-
if (path && path.length) {
|
|
136
|
-
paths.push(path);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return paths;
|
|
140
|
-
}
|
|
141
|
-
__name(collectReferencePaths, "collectReferencePaths");
|
|
142
|
-
async function preloadReferencePath(path, roots) {
|
|
143
|
-
let nodes = roots;
|
|
144
|
-
let didLoad = false;
|
|
145
|
-
for (const segment of path) {
|
|
146
|
-
if (!nodes) break;
|
|
147
|
-
const matched = nodes.find((node) => node.name === segment);
|
|
148
|
-
if (!matched) break;
|
|
149
|
-
if (typeof matched.children === "function") {
|
|
150
|
-
matched.children = await (0, import_utils.loadMetaTreeChildren)(matched);
|
|
151
|
-
didLoad = true;
|
|
152
|
-
}
|
|
153
|
-
nodes = Array.isArray(matched.children) ? matched.children : void 0;
|
|
154
|
-
}
|
|
155
|
-
return didLoad;
|
|
156
|
-
}
|
|
157
|
-
__name(preloadReferencePath, "preloadReferencePath");
|
|
158
115
|
function pasteHTML(container, html, indexes) {
|
|
159
116
|
var _a;
|
|
160
117
|
const selection = (_a = window.getSelection) == null ? void 0 : _a.call(window);
|
|
@@ -256,64 +213,21 @@ function getCurrentRange(element) {
|
|
|
256
213
|
}
|
|
257
214
|
__name(getCurrentRange, "getCurrentRange");
|
|
258
215
|
const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
259
|
-
|
|
260
|
-
const { addonBefore, className, converters, disabled, metaTree, onChange, placeholder, readOnly, style } = props;
|
|
216
|
+
const { addonBefore, className, converters, disabled, metaTree, onChange, placeholder, style } = props;
|
|
261
217
|
const { token } = import_antd.theme.useToken();
|
|
262
218
|
const ctx = (0, import_FlowContextProvider.useFlowContext)();
|
|
263
219
|
const { resolvedMetaTree } = (0, import_useResolvedMetaTree.useResolvedMetaTree)(metaTree);
|
|
264
|
-
const formItemStatus = (_a = (0, import_react.useContext)(import_context.FormItemInputContext)) == null ? void 0 : _a.status;
|
|
265
|
-
const effectiveStatus = props.status ?? formItemStatus;
|
|
266
220
|
const inputRef = (0, import_react.useRef)(null);
|
|
267
221
|
const [isComposing, setIsComposing] = (0, import_react.useState)(false);
|
|
268
222
|
const [changed, setChanged] = (0, import_react.useState)(false);
|
|
269
223
|
const [range, setRange] = (0, import_react.useState)([-1, 0, -1, 0]);
|
|
270
224
|
const value = typeof props.value === "string" ? props.value : props.value == null ? "" : String(props.value);
|
|
271
225
|
const variableRegExp = (converters == null ? void 0 : converters.variableRegExp) ?? DEFAULT_VARIABLE_REGEXP;
|
|
272
|
-
const parseValueToPath = (converters == null ? void 0 : converters.parseValueToPath) ?? import_utils.parseValueToPath;
|
|
273
|
-
const [loadedFlag, setLoadedFlag] = (0, import_react.useState)(0);
|
|
274
|
-
(0, import_react.useEffect)(() => {
|
|
275
|
-
if (!value || !Array.isArray(resolvedMetaTree) || !resolvedMetaTree.length) {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const paths = collectReferencePaths(value, variableRegExp, parseValueToPath);
|
|
279
|
-
if (!paths.length) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
let cancelled = false;
|
|
283
|
-
const run = /* @__PURE__ */ __name(async () => {
|
|
284
|
-
let didLoad = false;
|
|
285
|
-
for (const path of paths) {
|
|
286
|
-
const loaded = await preloadReferencePath(path, resolvedMetaTree);
|
|
287
|
-
if (cancelled) return;
|
|
288
|
-
didLoad = didLoad || loaded;
|
|
289
|
-
}
|
|
290
|
-
if (didLoad && !cancelled) {
|
|
291
|
-
setLoadedFlag((prev) => prev + 1);
|
|
292
|
-
}
|
|
293
|
-
}, "run");
|
|
294
|
-
run();
|
|
295
|
-
return () => {
|
|
296
|
-
cancelled = true;
|
|
297
|
-
};
|
|
298
|
-
}, [value, resolvedMetaTree, variableRegExp, parseValueToPath]);
|
|
299
226
|
const labelMap = (0, import_react.useMemo)(
|
|
300
227
|
() => buildLabelMap(resolvedMetaTree, ctx.t, converters),
|
|
301
|
-
|
|
302
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
303
|
-
[resolvedMetaTree, ctx, converters, loadedFlag]
|
|
228
|
+
[resolvedMetaTree, ctx, converters]
|
|
304
229
|
);
|
|
305
|
-
const
|
|
306
|
-
(matched) => {
|
|
307
|
-
const mapped = labelMap.get(normalizeVariableKey(matched));
|
|
308
|
-
if (mapped) return mapped;
|
|
309
|
-
const path = parseValueToPath(matched);
|
|
310
|
-
return resolveTitlesByPath(resolvedMetaTree, path, ctx.t);
|
|
311
|
-
},
|
|
312
|
-
// `loadedFlag` is read so a resolved lazy level re-creates this callback and re-renders the tags. `ctx` carries the translation fn.
|
|
313
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
314
|
-
[labelMap, parseValueToPath, resolvedMetaTree, ctx, loadedFlag]
|
|
315
|
-
);
|
|
316
|
-
const [html, setHtml] = (0, import_react.useState)(() => renderHTML(value, variableRegExp, resolveLabel));
|
|
230
|
+
const [html, setHtml] = (0, import_react.useState)(() => renderHTML(value, labelMap, variableRegExp));
|
|
317
231
|
const emitChange = (0, import_react.useCallback)(
|
|
318
232
|
(target) => {
|
|
319
233
|
onChange == null ? void 0 : onChange(getDomValue(target).trim());
|
|
@@ -321,20 +235,20 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
321
235
|
[onChange]
|
|
322
236
|
);
|
|
323
237
|
(0, import_react.useEffect)(() => {
|
|
324
|
-
setHtml(renderHTML(value,
|
|
238
|
+
setHtml(renderHTML(value, labelMap, variableRegExp));
|
|
325
239
|
if (!changed) {
|
|
326
240
|
setRange([-1, 0, -1, 0]);
|
|
327
241
|
}
|
|
328
|
-
}, [value,
|
|
242
|
+
}, [value, labelMap]);
|
|
329
243
|
(0, import_react.useEffect)(() => {
|
|
330
|
-
var
|
|
244
|
+
var _a;
|
|
331
245
|
const element = inputRef.current;
|
|
332
246
|
if (!element) return;
|
|
333
247
|
if (document.activeElement !== element) return;
|
|
334
248
|
const nextRange = new Range();
|
|
335
249
|
if (changed) {
|
|
336
250
|
if (range.join() === "-1,0,-1,0") return;
|
|
337
|
-
const selection = (
|
|
251
|
+
const selection = (_a = window.getSelection) == null ? void 0 : _a.call(window);
|
|
338
252
|
if (!selection) return;
|
|
339
253
|
try {
|
|
340
254
|
const children = Array.from(element.childNodes);
|
|
@@ -390,13 +304,12 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
390
304
|
);
|
|
391
305
|
const handleInput = (0, import_react.useCallback)(
|
|
392
306
|
({ currentTarget }) => {
|
|
393
|
-
if (readOnly) return;
|
|
394
307
|
if (isComposing) return;
|
|
395
308
|
setChanged(true);
|
|
396
309
|
setRange(getCurrentRange(currentTarget));
|
|
397
310
|
emitChange(currentTarget);
|
|
398
311
|
},
|
|
399
|
-
[emitChange, isComposing
|
|
312
|
+
[emitChange, isComposing]
|
|
400
313
|
);
|
|
401
314
|
const handleBlur = (0, import_react.useCallback)(({ currentTarget }) => {
|
|
402
315
|
setRange(getCurrentRange(currentTarget));
|
|
@@ -409,7 +322,6 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
409
322
|
const handlePaste = (0, import_react.useCallback)(
|
|
410
323
|
(event) => {
|
|
411
324
|
event.preventDefault();
|
|
412
|
-
if (readOnly) return;
|
|
413
325
|
const text = event.clipboardData.getData("text/plain").replace(/\n/g, " ");
|
|
414
326
|
if (!text) return;
|
|
415
327
|
setChanged(true);
|
|
@@ -417,18 +329,17 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
417
329
|
setRange(getCurrentRange(event.currentTarget));
|
|
418
330
|
emitChange(event.currentTarget);
|
|
419
331
|
},
|
|
420
|
-
[emitChange
|
|
332
|
+
[emitChange]
|
|
421
333
|
);
|
|
422
334
|
const handleCompositionStart = (0, import_react.useCallback)(() => setIsComposing(true), []);
|
|
423
335
|
const handleCompositionEnd = (0, import_react.useCallback)(
|
|
424
336
|
({ currentTarget }) => {
|
|
425
337
|
setIsComposing(false);
|
|
426
|
-
if (readOnly) return;
|
|
427
338
|
setChanged(true);
|
|
428
339
|
setRange(getCurrentRange(currentTarget));
|
|
429
340
|
emitChange(currentTarget);
|
|
430
341
|
},
|
|
431
|
-
[emitChange
|
|
342
|
+
[emitChange]
|
|
432
343
|
);
|
|
433
344
|
const wrapperClassName = (0, import_react.useMemo)(
|
|
434
345
|
() => import_css.css`
|
|
@@ -546,42 +457,6 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
546
457
|
border-color: ${token.colorBorder};
|
|
547
458
|
}
|
|
548
459
|
}
|
|
549
|
-
|
|
550
|
-
&.is-readonly {
|
|
551
|
-
cursor: default;
|
|
552
|
-
|
|
553
|
-
&:hover {
|
|
554
|
-
border-color: ${token.colorBorder};
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
&.is-error {
|
|
559
|
-
border-color: ${token.colorError};
|
|
560
|
-
|
|
561
|
-
&:hover {
|
|
562
|
-
border-color: ${token.colorErrorBorderHover};
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
&:focus,
|
|
566
|
-
&:focus-visible {
|
|
567
|
-
border-color: ${token.colorError};
|
|
568
|
-
box-shadow: 0 0 0 ${token.controlOutlineWidth}px ${token.colorErrorOutline};
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
&.is-warning {
|
|
573
|
-
border-color: ${token.colorWarning};
|
|
574
|
-
|
|
575
|
-
&:hover {
|
|
576
|
-
border-color: ${token.colorWarningBorderHover};
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
&:focus,
|
|
580
|
-
&:focus-visible {
|
|
581
|
-
border-color: ${token.colorWarning};
|
|
582
|
-
box-shadow: 0 0 0 ${token.controlOutlineWidth}px ${token.colorWarningOutline};
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
460
|
`;
|
|
586
461
|
}, [token, addonBefore]);
|
|
587
462
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_antd.Space.Compact, { className: (0, import_css.cx)("nb-variable-hybrid-input", wrapperClassName, className), style }, addonBefore != null && /* @__PURE__ */ import_react.default.createElement("span", { className: addonClassName }, addonBefore), /* @__PURE__ */ import_react.default.createElement(
|
|
@@ -591,13 +466,9 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
591
466
|
role: "textbox",
|
|
592
467
|
"aria-label": "textbox",
|
|
593
468
|
className: (0, import_css.cx)(editorClassName, {
|
|
594
|
-
"is-disabled": disabled
|
|
595
|
-
"is-readonly": readOnly,
|
|
596
|
-
"is-error": effectiveStatus === "error",
|
|
597
|
-
"is-warning": effectiveStatus === "warning"
|
|
469
|
+
"is-disabled": disabled
|
|
598
470
|
}),
|
|
599
|
-
contentEditable: !disabled
|
|
600
|
-
"aria-readonly": readOnly,
|
|
471
|
+
contentEditable: !disabled,
|
|
601
472
|
"data-placeholder": placeholder,
|
|
602
473
|
onInput: handleInput,
|
|
603
474
|
onBlur: handleBlur,
|
|
@@ -612,10 +483,10 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
612
483
|
{
|
|
613
484
|
metaTree,
|
|
614
485
|
disabled,
|
|
615
|
-
parseValueToPath,
|
|
486
|
+
parseValueToPath: (converters == null ? void 0 : converters.parseValueToPath) ?? import_utils.parseValueToPath,
|
|
616
487
|
formatPathToValue: (item) => {
|
|
617
|
-
var
|
|
618
|
-
return ((
|
|
488
|
+
var _a;
|
|
489
|
+
return ((_a = converters == null ? void 0 : converters.formatPathToValue) == null ? void 0 : _a.call(converters, item)) || (0, import_utils.formatPathToValue)(item);
|
|
619
490
|
},
|
|
620
491
|
onChange: handleSelectorChange
|
|
621
492
|
}
|
|
@@ -188,7 +188,7 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
|
|
|
188
188
|
}
|
|
189
189
|
}, "restoreFromValue");
|
|
190
190
|
restoreFromValue();
|
|
191
|
-
}, [resolvedMetaTree, innerValue, resolvePathFromValue, currentMetaTreeNode
|
|
191
|
+
}, [resolvedMetaTree, innerValue, resolvePathFromValue, currentMetaTreeNode]);
|
|
192
192
|
const ValueComponent = (0, import_react.useMemo)(() => {
|
|
193
193
|
const Component = renderInputComponent == null ? void 0 : renderInputComponent(resolvedMetaTreeNode);
|
|
194
194
|
const CustomComponent = resolvedMetaTreeNode == null ? void 0 : resolvedMetaTreeNode.render;
|
|
@@ -325,8 +325,6 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
|
|
|
325
325
|
{
|
|
326
326
|
metaTree: resolvedMetaTree,
|
|
327
327
|
value: innerValue,
|
|
328
|
-
active: (0, import_utils.isVariableValue)(innerValue),
|
|
329
|
-
disabled,
|
|
330
328
|
onChange: handleVariableSelect,
|
|
331
329
|
parseValueToPath: resolvePathFromValue,
|
|
332
330
|
formatPathToValue: resolveValueFromPath,
|
|
@@ -13,30 +13,12 @@ export interface FlowContextSelectorProps extends Omit<CascaderProps<ContextSele
|
|
|
13
13
|
value?: string;
|
|
14
14
|
onChange?: (value: string, metaTreeNode?: MetaTreeNode) => void;
|
|
15
15
|
children?: CascaderProps<ContextSelectorItem>['children'];
|
|
16
|
-
/**
|
|
17
|
-
* Controls whether the default `x` trigger button is rendered as active
|
|
18
|
-
* (`type="primary"`). When omitted, the selector falls back to its parsed
|
|
19
|
-
* `value` path (`true` iff a valid variable path is currently selected).
|
|
20
|
-
*
|
|
21
|
-
* Use this when callers intentionally feed synthetic paths such as
|
|
22
|
-
* `['constant']` / `['null']` into the cascader to keep menu state aligned,
|
|
23
|
-
* but only want real variable references to show the blue active button.
|
|
24
|
-
*/
|
|
25
|
-
active?: boolean;
|
|
26
16
|
metaTree?: MetaTreeNode[] | (() => MetaTreeNode[] | Promise<MetaTreeNode[]>);
|
|
27
17
|
parseValueToPath?: (value: string) => string[] | undefined;
|
|
28
18
|
formatPathToValue?: (item: MetaTreeNode) => string;
|
|
29
19
|
open?: boolean;
|
|
30
20
|
onlyLeafSelectable?: boolean;
|
|
31
21
|
ignoreFieldNames?: string[];
|
|
32
|
-
/**
|
|
33
|
-
* Footer rendered at the bottom of the dropdown. Defaults to a muted
|
|
34
|
-
* "Double click to choose entire object" hint when non-leaf selection is
|
|
35
|
-
* allowed (`onlyLeafSelectable` is false) — since double-clicking a non-leaf
|
|
36
|
-
* node selects the whole object. Pass an explicit node to override, or `null`
|
|
37
|
-
* to hide it.
|
|
38
|
-
*/
|
|
39
|
-
dropdownFooter?: React.ReactNode;
|
|
40
22
|
}
|
|
41
23
|
export interface ContextSelectorItem {
|
|
42
24
|
label: React.ReactNode;
|
package/lib/flowI18n.js
CHANGED
|
@@ -81,7 +81,7 @@ const _FlowI18n = class _FlowI18n {
|
|
|
81
81
|
* @private
|
|
82
82
|
*/
|
|
83
83
|
isTemplate(str) {
|
|
84
|
-
return /\{\{\s*t\s*\(\s*
|
|
84
|
+
return /\{\{\s*t\s*\(\s*["'`].*?["'`]\s*(?:,\s*.*?)?\s*\)\s*\}\}/g.test(str);
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* 编译模板字符串
|
|
@@ -89,8 +89,8 @@ const _FlowI18n = class _FlowI18n {
|
|
|
89
89
|
*/
|
|
90
90
|
compileTemplate(template) {
|
|
91
91
|
return template.replace(
|
|
92
|
-
/\{\{\s*t\s*\(\s*
|
|
93
|
-
(match,
|
|
92
|
+
/\{\{\s*t\s*\(\s*["'`](.*?)["'`]\s*(?:,\s*((?:[^{}]|\{[^}]*\})*?))?\s*\)\s*\}\}/g,
|
|
93
|
+
(match, key, optionsStr) => {
|
|
94
94
|
try {
|
|
95
95
|
let templateOptions = {};
|
|
96
96
|
if (optionsStr) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "2.2.0-
|
|
3
|
+
"version": "2.2.0-beta.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@formily/antd-v5": "1.x",
|
|
10
10
|
"@formily/reactive": "2.x",
|
|
11
|
-
"@nocobase/sdk": "2.2.0-
|
|
12
|
-
"@nocobase/shared": "2.2.0-
|
|
11
|
+
"@nocobase/sdk": "2.2.0-beta.10",
|
|
12
|
+
"@nocobase/shared": "2.2.0-beta.10",
|
|
13
13
|
"ahooks": "^3.7.2",
|
|
14
14
|
"axios": "^1.7.0",
|
|
15
15
|
"dayjs": "^1.11.9",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
],
|
|
38
38
|
"author": "NocoBase Team",
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "d572beec24de46df948f28db72b25303a63bf62d"
|
|
41
41
|
}
|
|
@@ -17,17 +17,6 @@ describe('FlowI18n', () => {
|
|
|
17
17
|
expect(i18n.translate("{{ t('Hello') }}")).toBe('你好');
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it('keeps embedded quotes of a different type inside the key', () => {
|
|
21
|
-
// A single-quoted key whose text contains double quotes (and vice versa) must not be truncated at the first inner
|
|
22
|
-
// quote.
|
|
23
|
-
const key = 'Unlike "Post-action event", it listens for data changes.';
|
|
24
|
-
const table: Record<string, string> = { [key]: '与“操作后事件”不同,它监听数据变动。' };
|
|
25
|
-
const i18n = new FlowI18n({ i18n: { t: (k: string) => table[k] ?? k } });
|
|
26
|
-
|
|
27
|
-
expect(i18n.translate(`{{t('${key}', { ns: "workflow" })}}`)).toBe(table[key]);
|
|
28
|
-
expect(i18n.translate(`{{t("It's here", { ns: "workflow" })}}`)).toBe("It's here");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
20
|
it('template compile ignores malformed options', () => {
|
|
32
21
|
const spy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
33
22
|
const i18n = new FlowI18n({ i18n: { t: (k: string) => k } });
|