@nocobase/flow-engine 2.1.11 → 2.2.0-alpha.2
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/JSRunner.d.ts +1 -0
- package/lib/JSRunner.js +110 -20
- package/lib/components/FlowContextSelector.js +24 -3
- package/lib/components/variables/VariableHybridInput.d.ts +8 -0
- package/lib/components/variables/VariableHybridInput.js +128 -12
- package/lib/components/variables/types.d.ts +8 -0
- package/lib/flowContext.d.ts +1 -1
- package/lib/flowContext.js +33 -7
- package/lib/flowI18n.js +3 -3
- package/lib/locale/en-US.json +1 -0
- package/lib/locale/index.d.ts +2 -0
- package/lib/locale/zh-CN.json +1 -0
- package/lib/resources/apiResource.js +2 -1
- package/lib/resources/baseRecordResource.js +6 -17
- package/lib/resources/multiRecordResource.js +13 -3
- package/lib/resources/singleRecordResource.js +7 -2
- package/lib/runjs-context/helpers.js +12 -5
- package/lib/utils/dataSourceDirty.d.ts +20 -0
- package/lib/utils/dataSourceDirty.js +139 -0
- package/lib/utils/dirtyAwareApiClient.d.ts +11 -0
- package/lib/utils/dirtyAwareApiClient.js +378 -0
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +11 -11
- package/lib/utils/openViewRouteState.d.ts +28 -0
- package/lib/utils/openViewRouteState.js +125 -0
- package/lib/utils/parsePathnameToViewParams.d.ts +3 -0
- package/lib/utils/parsePathnameToViewParams.js +18 -1
- package/lib/utils/resolveRunJSObjectValues.js +3 -2
- package/lib/utils/runjsModuleLoader.js +0 -30
- package/lib/views/ViewNavigation.js +5 -0
- package/package.json +4 -4
- package/src/JSRunner.ts +112 -25
- package/src/__tests__/JSRunner.test.ts +4 -5
- package/src/__tests__/flowContext.test.ts +88 -0
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +51 -0
- package/src/__tests__/flowI18n.test.ts +11 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +15 -2
- package/src/components/FlowContextSelector.tsx +33 -2
- package/src/components/variables/VariableHybridInput.tsx +166 -9
- package/src/components/variables/__tests__/VariableHybridInput.test.tsx +178 -0
- package/src/components/variables/types.ts +8 -0
- package/src/flowContext.ts +43 -8
- package/src/flowI18n.ts +8 -3
- package/src/locale/en-US.json +1 -0
- package/src/locale/zh-CN.json +1 -0
- package/src/resources/apiResource.ts +2 -1
- package/src/resources/baseRecordResource.ts +6 -23
- package/src/resources/multiRecordResource.ts +13 -3
- package/src/resources/singleRecordResource.ts +6 -1
- package/src/runjs-context/helpers.ts +12 -6
- package/src/utils/__tests__/dirtyAwareApiClient.test.ts +392 -0
- package/src/utils/__tests__/openViewRouteState.test.ts +40 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +36 -0
- package/src/utils/dataSourceDirty.ts +126 -0
- package/src/utils/dirtyAwareApiClient.ts +430 -0
- package/src/utils/index.ts +10 -9
- package/src/utils/openViewRouteState.ts +107 -0
- package/src/utils/parsePathnameToViewParams.ts +23 -1
- package/src/utils/resolveRunJSObjectValues.ts +5 -2
- package/src/utils/runjsModuleLoader.ts +0 -32
- package/src/views/ViewNavigation.ts +6 -1
- package/src/views/__tests__/ViewNavigation.test.ts +15 -0
- package/lib/utils/safeGlobals.d.ts +0 -28
- package/lib/utils/safeGlobals.js +0 -367
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +0 -38
- package/src/utils/__tests__/safeGlobals.test.ts +0 -106
- package/src/utils/safeGlobals.ts +0 -406
package/lib/JSRunner.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export interface JSRunnerOptions {
|
|
|
27
27
|
* 3. Fallback keeps v1-compatible behavior (enabled).
|
|
28
28
|
*/
|
|
29
29
|
export declare function shouldPreprocessRunJSTemplates(options?: Pick<JSRunnerOptions, 'preprocessTemplates' | 'version'>): boolean;
|
|
30
|
+
export declare const RUNJS_ALLOWED_BARE_GLOBAL_NAMES: readonly ["ctx", "console", "window", "document", "navigator", "setTimeout", "clearTimeout", "setInterval", "clearInterval", "Array", "ArrayBuffer", "BigInt", "BigInt64Array", "BigUint64Array", "Boolean", "DataView", "Date", "Error", "EvalError", "FinalizationRegistry", "Float32Array", "Float64Array", "Int8Array", "Int16Array", "Int32Array", "Map", "Math", "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "TypeError", "URIError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "WeakMap", "WeakRef", "WeakSet", "JSON", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "isFinite", "isNaN", "parseFloat", "parseInt", "undefined", "NaN", "Infinity", "fetch", "localStorage", "sessionStorage", "XMLHttpRequest", "WebSocket", "Worker", "SharedWorker", "ServiceWorker", "BroadcastChannel", "EventSource", "indexedDB", "caches", "Function", "eval", "globalThis", "Intl", "Blob", "URL", "location"];
|
|
30
31
|
export declare class JSRunner {
|
|
31
32
|
private globals;
|
|
32
33
|
private timeoutMs;
|
package/lib/JSRunner.js
CHANGED
|
@@ -28,6 +28,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
28
28
|
var JSRunner_exports = {};
|
|
29
29
|
__export(JSRunner_exports, {
|
|
30
30
|
JSRunner: () => JSRunner,
|
|
31
|
+
RUNJS_ALLOWED_BARE_GLOBAL_NAMES: () => RUNJS_ALLOWED_BARE_GLOBAL_NAMES,
|
|
31
32
|
shouldPreprocessRunJSTemplates: () => shouldPreprocessRunJSTemplates
|
|
32
33
|
});
|
|
33
34
|
module.exports = __toCommonJS(JSRunner_exports);
|
|
@@ -40,6 +41,113 @@ function shouldPreprocessRunJSTemplates(options) {
|
|
|
40
41
|
return (options == null ? void 0 : options.version) !== "v2";
|
|
41
42
|
}
|
|
42
43
|
__name(shouldPreprocessRunJSTemplates, "shouldPreprocessRunJSTemplates");
|
|
44
|
+
const RUNJS_BROWSER_GLOBAL_NAMES = [
|
|
45
|
+
"fetch",
|
|
46
|
+
"localStorage",
|
|
47
|
+
"sessionStorage",
|
|
48
|
+
"XMLHttpRequest",
|
|
49
|
+
"WebSocket",
|
|
50
|
+
"Worker",
|
|
51
|
+
"SharedWorker",
|
|
52
|
+
"ServiceWorker",
|
|
53
|
+
"BroadcastChannel",
|
|
54
|
+
"EventSource",
|
|
55
|
+
"indexedDB",
|
|
56
|
+
"caches",
|
|
57
|
+
"Function",
|
|
58
|
+
"eval",
|
|
59
|
+
"globalThis",
|
|
60
|
+
"Intl",
|
|
61
|
+
"Blob",
|
|
62
|
+
"URL",
|
|
63
|
+
"location"
|
|
64
|
+
];
|
|
65
|
+
const RUNJS_ALLOWED_BARE_GLOBAL_NAMES = [
|
|
66
|
+
"ctx",
|
|
67
|
+
"console",
|
|
68
|
+
"window",
|
|
69
|
+
"document",
|
|
70
|
+
"navigator",
|
|
71
|
+
"setTimeout",
|
|
72
|
+
"clearTimeout",
|
|
73
|
+
"setInterval",
|
|
74
|
+
"clearInterval",
|
|
75
|
+
"Array",
|
|
76
|
+
"ArrayBuffer",
|
|
77
|
+
"BigInt",
|
|
78
|
+
"BigInt64Array",
|
|
79
|
+
"BigUint64Array",
|
|
80
|
+
"Boolean",
|
|
81
|
+
"DataView",
|
|
82
|
+
"Date",
|
|
83
|
+
"Error",
|
|
84
|
+
"EvalError",
|
|
85
|
+
"FinalizationRegistry",
|
|
86
|
+
"Float32Array",
|
|
87
|
+
"Float64Array",
|
|
88
|
+
"Int8Array",
|
|
89
|
+
"Int16Array",
|
|
90
|
+
"Int32Array",
|
|
91
|
+
"Map",
|
|
92
|
+
"Math",
|
|
93
|
+
"Number",
|
|
94
|
+
"Object",
|
|
95
|
+
"Promise",
|
|
96
|
+
"Proxy",
|
|
97
|
+
"RangeError",
|
|
98
|
+
"ReferenceError",
|
|
99
|
+
"Reflect",
|
|
100
|
+
"RegExp",
|
|
101
|
+
"Set",
|
|
102
|
+
"String",
|
|
103
|
+
"Symbol",
|
|
104
|
+
"SyntaxError",
|
|
105
|
+
"TypeError",
|
|
106
|
+
"URIError",
|
|
107
|
+
"Uint8Array",
|
|
108
|
+
"Uint8ClampedArray",
|
|
109
|
+
"Uint16Array",
|
|
110
|
+
"Uint32Array",
|
|
111
|
+
"WeakMap",
|
|
112
|
+
"WeakRef",
|
|
113
|
+
"WeakSet",
|
|
114
|
+
"JSON",
|
|
115
|
+
"decodeURI",
|
|
116
|
+
"decodeURIComponent",
|
|
117
|
+
"encodeURI",
|
|
118
|
+
"encodeURIComponent",
|
|
119
|
+
"isFinite",
|
|
120
|
+
"isNaN",
|
|
121
|
+
"parseFloat",
|
|
122
|
+
"parseInt",
|
|
123
|
+
"undefined",
|
|
124
|
+
"NaN",
|
|
125
|
+
"Infinity",
|
|
126
|
+
...RUNJS_BROWSER_GLOBAL_NAMES
|
|
127
|
+
];
|
|
128
|
+
function collectRunJSBrowserGlobals(providedGlobals = {}) {
|
|
129
|
+
const windowGlobal = providedGlobals.window;
|
|
130
|
+
if (!windowGlobal || typeof windowGlobal !== "object") {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
const windowRecord = windowGlobal;
|
|
134
|
+
const globals = {};
|
|
135
|
+
RUNJS_BROWSER_GLOBAL_NAMES.forEach((name) => {
|
|
136
|
+
if (Object.prototype.hasOwnProperty.call(providedGlobals, name)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const value = windowRecord[name];
|
|
141
|
+
if (typeof value === "undefined") {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
globals[name] = name === "fetch" && typeof value === "function" ? value.bind(windowGlobal) : value;
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return globals;
|
|
149
|
+
}
|
|
150
|
+
__name(collectRunJSBrowserGlobals, "collectRunJSBrowserGlobals");
|
|
43
151
|
const BARE_CTX_TEMPLATE_RE = /(^|[=(:,[\s)])(\{\{\s*(ctx(?:\.|\[|\?\.)[^}]*)\s*\}\})/m;
|
|
44
152
|
function extractDeprecatedCtxTemplateUsage(code) {
|
|
45
153
|
const src = String(code || "");
|
|
@@ -78,7 +186,6 @@ const _JSRunner = class _JSRunner {
|
|
|
78
186
|
globals;
|
|
79
187
|
timeoutMs;
|
|
80
188
|
constructor(options = {}) {
|
|
81
|
-
var _a, _b;
|
|
82
189
|
const bindWindowFn = /* @__PURE__ */ __name((key) => {
|
|
83
190
|
if (typeof window !== "undefined" && typeof window[key] === "function") {
|
|
84
191
|
return window[key].bind(window);
|
|
@@ -87,25 +194,7 @@ const _JSRunner = class _JSRunner {
|
|
|
87
194
|
return typeof fn === "function" ? fn.bind(globalThis) : fn;
|
|
88
195
|
}, "bindWindowFn");
|
|
89
196
|
const providedGlobals = options.globals || {};
|
|
90
|
-
const liftedGlobals =
|
|
91
|
-
if (!Object.prototype.hasOwnProperty.call(providedGlobals, "Blob")) {
|
|
92
|
-
try {
|
|
93
|
-
const blobCtor = (_a = providedGlobals.window) == null ? void 0 : _a.Blob;
|
|
94
|
-
if (typeof blobCtor !== "undefined") {
|
|
95
|
-
liftedGlobals.Blob = blobCtor;
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (!Object.prototype.hasOwnProperty.call(providedGlobals, "URL")) {
|
|
101
|
-
try {
|
|
102
|
-
const urlCtor = (_b = providedGlobals.window) == null ? void 0 : _b.URL;
|
|
103
|
-
if (typeof urlCtor !== "undefined") {
|
|
104
|
-
liftedGlobals.URL = urlCtor;
|
|
105
|
-
}
|
|
106
|
-
} catch {
|
|
107
|
-
}
|
|
108
|
-
}
|
|
197
|
+
const liftedGlobals = collectRunJSBrowserGlobals(providedGlobals);
|
|
109
198
|
this.globals = {
|
|
110
199
|
console,
|
|
111
200
|
setTimeout: bindWindowFn("setTimeout"),
|
|
@@ -169,5 +258,6 @@ let JSRunner = _JSRunner;
|
|
|
169
258
|
// Annotate the CommonJS export names for ESM import in node:
|
|
170
259
|
0 && (module.exports = {
|
|
171
260
|
JSRunner,
|
|
261
|
+
RUNJS_ALLOWED_BARE_GLOBAL_NAMES,
|
|
172
262
|
shouldPreprocessRunJSTemplates
|
|
173
263
|
});
|
|
@@ -97,6 +97,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
97
97
|
open,
|
|
98
98
|
onlyLeafSelectable = false,
|
|
99
99
|
ignoreFieldNames,
|
|
100
|
+
dropdownFooter,
|
|
100
101
|
...cascaderProps
|
|
101
102
|
}) => {
|
|
102
103
|
const { token } = import_antd.theme.useToken();
|
|
@@ -289,12 +290,32 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
289
290
|
},
|
|
290
291
|
[cascaderOnDropdownVisibleChange, open]
|
|
291
292
|
);
|
|
293
|
+
const footerNode = (0, import_react.useMemo)(() => {
|
|
294
|
+
if (dropdownFooter !== void 0) {
|
|
295
|
+
return dropdownFooter;
|
|
296
|
+
}
|
|
297
|
+
if (onlyLeafSelectable) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
301
|
+
"div",
|
|
302
|
+
{
|
|
303
|
+
className: import_css.css`
|
|
304
|
+
padding: 6px 12px;
|
|
305
|
+
color: ${token.colorTextDescription};
|
|
306
|
+
border-top: 1px solid ${token.colorSplit};
|
|
307
|
+
font-size: ${token.fontSizeSM}px;
|
|
308
|
+
`
|
|
309
|
+
},
|
|
310
|
+
flowCtx.t("Double click to choose entire object")
|
|
311
|
+
);
|
|
312
|
+
}, [dropdownFooter, onlyLeafSelectable, token, flowCtx]);
|
|
292
313
|
const renderDropdown = (0, import_react.useCallback)(
|
|
293
314
|
(menu) => {
|
|
294
315
|
const cascaderMenuNode = cascaderDropdownRender ? cascaderDropdownRender(menu) : menu;
|
|
295
316
|
const cascaderMenu = import_react.default.isValidElement(cascaderMenuNode) ? cascaderMenuNode : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenuNode);
|
|
296
317
|
if (!isSearchEnabled || children === null) {
|
|
297
|
-
return cascaderMenu;
|
|
318
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, cascaderMenu, footerNode);
|
|
298
319
|
}
|
|
299
320
|
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
321
|
import_antd.Input,
|
|
@@ -306,9 +327,9 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
306
327
|
onChange: (e) => setSearchText(e.target.value),
|
|
307
328
|
onKeyDown: (e) => e.stopPropagation()
|
|
308
329
|
}
|
|
309
|
-
)), cascaderMenu);
|
|
330
|
+
)), cascaderMenu, footerNode);
|
|
310
331
|
},
|
|
311
|
-
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText]
|
|
332
|
+
[cascaderDropdownRender, cascaderSearchInputClassName, children, flowCtx, isSearchEnabled, searchText, footerNode]
|
|
312
333
|
);
|
|
313
334
|
const inlinePlaceholder = typeof restCascaderProps.placeholder === "string" ? restCascaderProps.placeholder : flowCtx.t("Search");
|
|
314
335
|
const hasSelectedPath = Array.isArray(effectivePath) && effectivePath.length > 0;
|
|
@@ -23,5 +23,13 @@ export interface VariableHybridInputProps {
|
|
|
23
23
|
converters?: VariableHybridInputConverters;
|
|
24
24
|
style?: React.CSSProperties;
|
|
25
25
|
className?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Validation status — turns the input border red (`error`) or amber
|
|
28
|
+
* (`warning`). Usually omitted: when rendered inside an antd `Form.Item`, the
|
|
29
|
+
* status is read automatically from `FormItemInputContext`, so dropping this
|
|
30
|
+
* into a `Form.Item` with failing rules colours the border with no extra
|
|
31
|
+
* wiring. An explicit prop wins over the inherited form status.
|
|
32
|
+
*/
|
|
33
|
+
status?: 'error' | 'warning';
|
|
26
34
|
}
|
|
27
35
|
export declare const VariableHybridInput: React.NamedExoticComponent<VariableHybridInputProps>;
|
|
@@ -42,6 +42,7 @@ __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");
|
|
45
46
|
var import_react = __toESM(require("react"));
|
|
46
47
|
var import_FlowContextProvider = require("../../FlowContextProvider");
|
|
47
48
|
var import_FlowContextSelector = require("../FlowContextSelector");
|
|
@@ -83,14 +84,28 @@ function normalizeVariableKey(value) {
|
|
|
83
84
|
return value.replace(/^\{\{\s*/, "").replace(/\s*\}\}$/, "").trim();
|
|
84
85
|
}
|
|
85
86
|
__name(normalizeVariableKey, "normalizeVariableKey");
|
|
86
|
-
function renderHTML(value,
|
|
87
|
+
function renderHTML(value, regExp, resolveLabel) {
|
|
87
88
|
const re = new RegExp(regExp.source, regExp.flags.includes("g") ? regExp.flags : `${regExp.flags}g`);
|
|
88
89
|
return escapeHtml(value || "").replace(re, (matched) => {
|
|
89
|
-
const label =
|
|
90
|
+
const label = resolveLabel(matched) || matched;
|
|
90
91
|
return createTagHTML(matched, label);
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
__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");
|
|
94
109
|
function buildLabelMap(nodes, ctxT, converters) {
|
|
95
110
|
const map = /* @__PURE__ */ new Map();
|
|
96
111
|
function walk(items = [], parentTitles = []) {
|
|
@@ -112,6 +127,34 @@ function buildLabelMap(nodes, ctxT, converters) {
|
|
|
112
127
|
return map;
|
|
113
128
|
}
|
|
114
129
|
__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");
|
|
115
158
|
function pasteHTML(container, html, indexes) {
|
|
116
159
|
var _a;
|
|
117
160
|
const selection = (_a = window.getSelection) == null ? void 0 : _a.call(window);
|
|
@@ -213,21 +256,64 @@ function getCurrentRange(element) {
|
|
|
213
256
|
}
|
|
214
257
|
__name(getCurrentRange, "getCurrentRange");
|
|
215
258
|
const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
259
|
+
var _a;
|
|
216
260
|
const { addonBefore, className, converters, disabled, metaTree, onChange, placeholder, style } = props;
|
|
217
261
|
const { token } = import_antd.theme.useToken();
|
|
218
262
|
const ctx = (0, import_FlowContextProvider.useFlowContext)();
|
|
219
263
|
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;
|
|
220
266
|
const inputRef = (0, import_react.useRef)(null);
|
|
221
267
|
const [isComposing, setIsComposing] = (0, import_react.useState)(false);
|
|
222
268
|
const [changed, setChanged] = (0, import_react.useState)(false);
|
|
223
269
|
const [range, setRange] = (0, import_react.useState)([-1, 0, -1, 0]);
|
|
224
270
|
const value = typeof props.value === "string" ? props.value : props.value == null ? "" : String(props.value);
|
|
225
271
|
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]);
|
|
226
299
|
const labelMap = (0, import_react.useMemo)(
|
|
227
300
|
() => buildLabelMap(resolvedMetaTree, ctx.t, converters),
|
|
228
|
-
|
|
301
|
+
// `loadedFlag` is read so the map recomputes after a lazy level resolves.
|
|
302
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
303
|
+
[resolvedMetaTree, ctx, converters, loadedFlag]
|
|
304
|
+
);
|
|
305
|
+
const resolveLabel = (0, import_react.useCallback)(
|
|
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]
|
|
229
315
|
);
|
|
230
|
-
const [html, setHtml] = (0, import_react.useState)(() => renderHTML(value,
|
|
316
|
+
const [html, setHtml] = (0, import_react.useState)(() => renderHTML(value, variableRegExp, resolveLabel));
|
|
231
317
|
const emitChange = (0, import_react.useCallback)(
|
|
232
318
|
(target) => {
|
|
233
319
|
onChange == null ? void 0 : onChange(getDomValue(target).trim());
|
|
@@ -235,20 +321,20 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
235
321
|
[onChange]
|
|
236
322
|
);
|
|
237
323
|
(0, import_react.useEffect)(() => {
|
|
238
|
-
setHtml(renderHTML(value,
|
|
324
|
+
setHtml(renderHTML(value, variableRegExp, resolveLabel));
|
|
239
325
|
if (!changed) {
|
|
240
326
|
setRange([-1, 0, -1, 0]);
|
|
241
327
|
}
|
|
242
|
-
}, [value,
|
|
328
|
+
}, [value, resolveLabel]);
|
|
243
329
|
(0, import_react.useEffect)(() => {
|
|
244
|
-
var
|
|
330
|
+
var _a2;
|
|
245
331
|
const element = inputRef.current;
|
|
246
332
|
if (!element) return;
|
|
247
333
|
if (document.activeElement !== element) return;
|
|
248
334
|
const nextRange = new Range();
|
|
249
335
|
if (changed) {
|
|
250
336
|
if (range.join() === "-1,0,-1,0") return;
|
|
251
|
-
const selection = (
|
|
337
|
+
const selection = (_a2 = window.getSelection) == null ? void 0 : _a2.call(window);
|
|
252
338
|
if (!selection) return;
|
|
253
339
|
try {
|
|
254
340
|
const children = Array.from(element.childNodes);
|
|
@@ -457,6 +543,34 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
457
543
|
border-color: ${token.colorBorder};
|
|
458
544
|
}
|
|
459
545
|
}
|
|
546
|
+
|
|
547
|
+
&.is-error {
|
|
548
|
+
border-color: ${token.colorError};
|
|
549
|
+
|
|
550
|
+
&:hover {
|
|
551
|
+
border-color: ${token.colorErrorBorderHover};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
&:focus,
|
|
555
|
+
&:focus-visible {
|
|
556
|
+
border-color: ${token.colorError};
|
|
557
|
+
box-shadow: 0 0 0 ${token.controlOutlineWidth}px ${token.colorErrorOutline};
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
&.is-warning {
|
|
562
|
+
border-color: ${token.colorWarning};
|
|
563
|
+
|
|
564
|
+
&:hover {
|
|
565
|
+
border-color: ${token.colorWarningBorderHover};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
&:focus,
|
|
569
|
+
&:focus-visible {
|
|
570
|
+
border-color: ${token.colorWarning};
|
|
571
|
+
box-shadow: 0 0 0 ${token.controlOutlineWidth}px ${token.colorWarningOutline};
|
|
572
|
+
}
|
|
573
|
+
}
|
|
460
574
|
`;
|
|
461
575
|
}, [token, addonBefore]);
|
|
462
576
|
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(
|
|
@@ -466,7 +580,9 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
466
580
|
role: "textbox",
|
|
467
581
|
"aria-label": "textbox",
|
|
468
582
|
className: (0, import_css.cx)(editorClassName, {
|
|
469
|
-
"is-disabled": disabled
|
|
583
|
+
"is-disabled": disabled,
|
|
584
|
+
"is-error": effectiveStatus === "error",
|
|
585
|
+
"is-warning": effectiveStatus === "warning"
|
|
470
586
|
}),
|
|
471
587
|
contentEditable: !disabled,
|
|
472
588
|
"data-placeholder": placeholder,
|
|
@@ -483,10 +599,10 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
|
|
|
483
599
|
{
|
|
484
600
|
metaTree,
|
|
485
601
|
disabled,
|
|
486
|
-
parseValueToPath
|
|
602
|
+
parseValueToPath,
|
|
487
603
|
formatPathToValue: (item) => {
|
|
488
|
-
var
|
|
489
|
-
return ((
|
|
604
|
+
var _a2;
|
|
605
|
+
return ((_a2 = converters == null ? void 0 : converters.formatPathToValue) == null ? void 0 : _a2.call(converters, item)) || (0, import_utils.formatPathToValue)(item);
|
|
490
606
|
},
|
|
491
607
|
onChange: handleSelectorChange
|
|
492
608
|
}
|
|
@@ -19,6 +19,14 @@ export interface FlowContextSelectorProps extends Omit<CascaderProps<ContextSele
|
|
|
19
19
|
open?: boolean;
|
|
20
20
|
onlyLeafSelectable?: boolean;
|
|
21
21
|
ignoreFieldNames?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Footer rendered at the bottom of the dropdown. Defaults to a muted
|
|
24
|
+
* "Double click to choose entire object" hint when non-leaf selection is
|
|
25
|
+
* allowed (`onlyLeafSelectable` is false) — since double-clicking a non-leaf
|
|
26
|
+
* node selects the whole object. Pass an explicit node to override, or `null`
|
|
27
|
+
* to hide it.
|
|
28
|
+
*/
|
|
29
|
+
dropdownFooter?: React.ReactNode;
|
|
22
30
|
}
|
|
23
31
|
export interface ContextSelectorItem {
|
|
24
32
|
label: React.ReactNode;
|
package/lib/flowContext.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { ISchema } from '@formily/json-schema';
|
|
10
|
-
import { APIClient, RequestOptions } from '@nocobase/sdk';
|
|
10
|
+
import type { APIClient, RequestOptions } from '@nocobase/sdk';
|
|
11
11
|
import type { Router } from '@remix-run/router';
|
|
12
12
|
import { MessageInstance } from 'antd/es/message/interface';
|
|
13
13
|
import type { HookAPI } from 'antd/es/modal/useModal';
|
package/lib/flowContext.js
CHANGED
|
@@ -75,6 +75,7 @@ var import_utils = require("./utils");
|
|
|
75
75
|
var import_exceptions = require("./utils/exceptions");
|
|
76
76
|
var import_params_resolvers = require("./utils/params-resolvers");
|
|
77
77
|
var import_serverContextParams = require("./utils/serverContextParams");
|
|
78
|
+
var import_dirtyAwareApiClient = require("./utils/dirtyAwareApiClient");
|
|
78
79
|
var import_variablesParams = require("./utils/variablesParams");
|
|
79
80
|
var import_registry = require("./runjs-context/registry");
|
|
80
81
|
var import_createEphemeralContext = require("./utils/createEphemeralContext");
|
|
@@ -1899,15 +1900,16 @@ const _FlowContext = class _FlowContext {
|
|
|
1899
1900
|
const options = this._props[key];
|
|
1900
1901
|
if (!options) return void 0;
|
|
1901
1902
|
if ("value" in options) {
|
|
1902
|
-
return options.value;
|
|
1903
|
+
return key === "api" ? (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(options.value, currentContext) : options.value;
|
|
1903
1904
|
}
|
|
1904
1905
|
if (options.get) {
|
|
1905
1906
|
if (options.cache === false) {
|
|
1906
|
-
|
|
1907
|
+
const value = options.get(currentContext);
|
|
1908
|
+
return key === "api" ? (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(value, currentContext) : value;
|
|
1907
1909
|
}
|
|
1908
1910
|
const cacheKey = options.observable ? "_observableCache" : "_cache";
|
|
1909
1911
|
if (key in this[cacheKey]) {
|
|
1910
|
-
return this[cacheKey][key];
|
|
1912
|
+
return key === "api" ? (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(this[cacheKey][key], currentContext) : this[cacheKey][key];
|
|
1911
1913
|
}
|
|
1912
1914
|
if (this._pending[key]) return this._pending[key];
|
|
1913
1915
|
const result = options.get(this.createProxy());
|
|
@@ -1917,7 +1919,7 @@ const _FlowContext = class _FlowContext {
|
|
|
1917
1919
|
(v) => {
|
|
1918
1920
|
this[cacheKey][key] = v;
|
|
1919
1921
|
delete this._pending[key];
|
|
1920
|
-
return v;
|
|
1922
|
+
return key === "api" ? (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(v, currentContext) : v;
|
|
1921
1923
|
},
|
|
1922
1924
|
(err) => {
|
|
1923
1925
|
delete this._pending[key];
|
|
@@ -1927,7 +1929,7 @@ const _FlowContext = class _FlowContext {
|
|
|
1927
1929
|
return this._pending[key];
|
|
1928
1930
|
}
|
|
1929
1931
|
this[cacheKey][key] = result;
|
|
1930
|
-
return result;
|
|
1932
|
+
return key === "api" ? (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(result, currentContext) : result;
|
|
1931
1933
|
}
|
|
1932
1934
|
return void 0;
|
|
1933
1935
|
}
|
|
@@ -2251,7 +2253,7 @@ const _BaseFlowEngineContext = class _BaseFlowEngineContext extends FlowContext
|
|
|
2251
2253
|
this.defineMethod("getModel", (modelName, searchInPreviousEngines) => {
|
|
2252
2254
|
return this.engine.getModel(modelName, searchInPreviousEngines);
|
|
2253
2255
|
});
|
|
2254
|
-
this.defineMethod("request", (options)
|
|
2256
|
+
this.defineMethod("request", function(options) {
|
|
2255
2257
|
const app = this.app;
|
|
2256
2258
|
if (typeof (options == null ? void 0 : options.url) === "string" && shouldBypassApiClient(options.url, app)) {
|
|
2257
2259
|
return import_axios.default.request(options);
|
|
@@ -2718,7 +2720,17 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2718
2720
|
doc = {};
|
|
2719
2721
|
}
|
|
2720
2722
|
const deprecatedCtx = createRunJSDeprecationProxy(runCtx, { doc });
|
|
2721
|
-
const
|
|
2723
|
+
const browserGlobals = {};
|
|
2724
|
+
if (typeof window !== "undefined") {
|
|
2725
|
+
browserGlobals.window = window;
|
|
2726
|
+
if (typeof navigator !== "undefined") {
|
|
2727
|
+
browserGlobals.navigator = navigator;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
if (typeof document !== "undefined") {
|
|
2731
|
+
browserGlobals.document = document;
|
|
2732
|
+
}
|
|
2733
|
+
const globals = { ctx: deprecatedCtx, ...browserGlobals, ...(options == null ? void 0 : options.globals) || {} };
|
|
2722
2734
|
const { timeoutMs } = options || {};
|
|
2723
2735
|
return new import_JSRunner.JSRunner({ globals, timeoutMs });
|
|
2724
2736
|
});
|
|
@@ -3500,6 +3512,20 @@ const _FlowRunJSContext = class _FlowRunJSContext extends FlowContext {
|
|
|
3500
3512
|
ReactDOMShim.__nbRunjsInternalShim = true;
|
|
3501
3513
|
this.defineProperty("ReactDOM", { value: ReactDOMShim });
|
|
3502
3514
|
(0, import_runjsLibs.setupRunJSLibs)(this);
|
|
3515
|
+
this.defineMethod("openView", async function(uid, options) {
|
|
3516
|
+
const delegateOpenView = delegate.openView;
|
|
3517
|
+
if (typeof delegateOpenView !== "function") {
|
|
3518
|
+
throw new Error("ctx.openView is not available in current context.");
|
|
3519
|
+
}
|
|
3520
|
+
const routeState = (0, import_utils.createOpenViewRouteState)(options);
|
|
3521
|
+
if (!routeState) {
|
|
3522
|
+
return delegateOpenView(uid, options);
|
|
3523
|
+
}
|
|
3524
|
+
return delegateOpenView(uid, {
|
|
3525
|
+
...options || {},
|
|
3526
|
+
[import_utils.RUNJS_OPEN_VIEW_ROUTE_STATE]: routeState
|
|
3527
|
+
});
|
|
3528
|
+
});
|
|
3503
3529
|
this.defineMethod(
|
|
3504
3530
|
"render",
|
|
3505
3531
|
function(vnode, container) {
|
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*(["'`])(?:\\.|(?!\1).)*?\1\s*(?:,\s*.*?)?\s*\)\s*\}\}/.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, key, optionsStr) => {
|
|
92
|
+
/\{\{\s*t\s*\(\s*(["'`])((?:\\.|(?!\1).)*?)\1\s*(?:,\s*((?:[^{}]|\{[^}]*\})*?))?\s*\)\s*\}\}/g,
|
|
93
|
+
(match, _quote, key, optionsStr) => {
|
|
94
94
|
try {
|
|
95
95
|
let templateOptions = {};
|
|
96
96
|
if (optionsStr) {
|
package/lib/locale/en-US.json
CHANGED
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"Replace current block with template?": "Replace current block with template?",
|
|
57
57
|
"Replaced with template block": "Replaced with template block",
|
|
58
58
|
"Render failed": "Render failed",
|
|
59
|
+
"Response record": "Response record",
|
|
59
60
|
"Step configuration": "Step configuration",
|
|
60
61
|
"Step parameter configuration": "Step parameter configuration",
|
|
61
62
|
"Step with key {{stepKey}} not found": "Step with key {{stepKey}} not found",
|
package/lib/locale/index.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ export declare const locales: {
|
|
|
65
65
|
"Replace current block with template?": string;
|
|
66
66
|
"Replaced with template block": string;
|
|
67
67
|
"Render failed": string;
|
|
68
|
+
"Response record": string;
|
|
68
69
|
"Step configuration": string;
|
|
69
70
|
"Step parameter configuration": string;
|
|
70
71
|
"Step with key {{stepKey}} not found": string;
|
|
@@ -150,6 +151,7 @@ export declare const locales: {
|
|
|
150
151
|
"Other blocks": string;
|
|
151
152
|
"Previous step": string;
|
|
152
153
|
"Render failed": string;
|
|
154
|
+
"Response record": string;
|
|
153
155
|
"Step configuration": string;
|
|
154
156
|
"Step parameter configuration": string;
|
|
155
157
|
"Step with key {{stepKey}} not found": string;
|
package/lib/locale/zh-CN.json
CHANGED
|
@@ -30,6 +30,7 @@ __export(apiResource_exports, {
|
|
|
30
30
|
APIResource: () => APIResource
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(apiResource_exports);
|
|
33
|
+
var import_dirtyAwareApiClient = require("../utils/dirtyAwareApiClient");
|
|
33
34
|
var import_flowResource = require("./flowResource");
|
|
34
35
|
const _APIResource = class _APIResource extends import_flowResource.FlowResource {
|
|
35
36
|
// 请求配置
|
|
@@ -49,7 +50,7 @@ const _APIResource = class _APIResource extends import_flowResource.FlowResource
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
setAPIClient(api) {
|
|
52
|
-
this.api = api;
|
|
53
|
+
this.api = (0, import_dirtyAwareApiClient.getDirtyAwareApiClient)(api, this.context);
|
|
53
54
|
return this;
|
|
54
55
|
}
|
|
55
56
|
getURL() {
|