@nocobase/flow-engine 2.2.0-beta.14 → 2.2.0-beta.15

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.
@@ -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 label = disabled ? /* @__PURE__ */ import_react.default.createElement("span", null, baseLabel, /* @__PURE__ */ import_react.default.createElement(
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: disabledReason || flowCtx.t("This variable is not available"),
118
- placement: "right",
119
- overlayClassName: "flow-variable-disabled-tip",
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(import_icons.QuestionCircleOutlined, { style: { marginLeft: 6, color: "rgba(0,0,0,0.35)" } })
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(rest).filter(([key]) => !formItemPropKeys.includes(key))
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 } = rest;
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
- ...rest,
140
- style: { ...formItemStyle, ...rest.style },
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: rest.extra && /* @__PURE__ */ import_react.default.createElement("span", { style: { whiteSpace: "pre-wrap" } }, rest.extra),
146
- tooltip: rest.tooltip && {
147
- title: rest.tooltip,
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
  },
@@ -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>;
@@ -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, labelMap, regExp) {
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 = labelMap.get(normalizeVariableKey(matched)) || matched;
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) => {
216
- const { addonBefore, className, converters, disabled, metaTree, onChange, placeholder, style } = props;
259
+ var _a;
260
+ const { addonBefore, className, converters, disabled, metaTree, onChange, placeholder, readOnly, 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
- [resolvedMetaTree, ctx, converters]
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]
229
304
  );
230
- const [html, setHtml] = (0, import_react.useState)(() => renderHTML(value, labelMap, variableRegExp));
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]
315
+ );
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, labelMap, variableRegExp));
324
+ setHtml(renderHTML(value, variableRegExp, resolveLabel));
239
325
  if (!changed) {
240
326
  setRange([-1, 0, -1, 0]);
241
327
  }
242
- }, [value, labelMap]);
328
+ }, [value, resolveLabel]);
243
329
  (0, import_react.useEffect)(() => {
244
- var _a;
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 = (_a = window.getSelection) == null ? void 0 : _a.call(window);
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);
@@ -304,12 +390,13 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
304
390
  );
305
391
  const handleInput = (0, import_react.useCallback)(
306
392
  ({ currentTarget }) => {
393
+ if (readOnly) return;
307
394
  if (isComposing) return;
308
395
  setChanged(true);
309
396
  setRange(getCurrentRange(currentTarget));
310
397
  emitChange(currentTarget);
311
398
  },
312
- [emitChange, isComposing]
399
+ [emitChange, isComposing, readOnly]
313
400
  );
314
401
  const handleBlur = (0, import_react.useCallback)(({ currentTarget }) => {
315
402
  setRange(getCurrentRange(currentTarget));
@@ -322,6 +409,7 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
322
409
  const handlePaste = (0, import_react.useCallback)(
323
410
  (event) => {
324
411
  event.preventDefault();
412
+ if (readOnly) return;
325
413
  const text = event.clipboardData.getData("text/plain").replace(/\n/g, " ");
326
414
  if (!text) return;
327
415
  setChanged(true);
@@ -329,17 +417,18 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
329
417
  setRange(getCurrentRange(event.currentTarget));
330
418
  emitChange(event.currentTarget);
331
419
  },
332
- [emitChange]
420
+ [emitChange, readOnly]
333
421
  );
334
422
  const handleCompositionStart = (0, import_react.useCallback)(() => setIsComposing(true), []);
335
423
  const handleCompositionEnd = (0, import_react.useCallback)(
336
424
  ({ currentTarget }) => {
337
425
  setIsComposing(false);
426
+ if (readOnly) return;
338
427
  setChanged(true);
339
428
  setRange(getCurrentRange(currentTarget));
340
429
  emitChange(currentTarget);
341
430
  },
342
- [emitChange]
431
+ [emitChange, readOnly]
343
432
  );
344
433
  const wrapperClassName = (0, import_react.useMemo)(
345
434
  () => import_css.css`
@@ -457,6 +546,42 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
457
546
  border-color: ${token.colorBorder};
458
547
  }
459
548
  }
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
+ }
460
585
  `;
461
586
  }, [token, addonBefore]);
462
587
  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,9 +591,13 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
466
591
  role: "textbox",
467
592
  "aria-label": "textbox",
468
593
  className: (0, import_css.cx)(editorClassName, {
469
- "is-disabled": disabled
594
+ "is-disabled": disabled,
595
+ "is-readonly": readOnly,
596
+ "is-error": effectiveStatus === "error",
597
+ "is-warning": effectiveStatus === "warning"
470
598
  }),
471
- contentEditable: !disabled,
599
+ contentEditable: !disabled && !readOnly,
600
+ "aria-readonly": readOnly,
472
601
  "data-placeholder": placeholder,
473
602
  onInput: handleInput,
474
603
  onBlur: handleBlur,
@@ -483,10 +612,10 @@ const VariableHybridInputComponent = /* @__PURE__ */ __name((props) => {
483
612
  {
484
613
  metaTree,
485
614
  disabled,
486
- parseValueToPath: (converters == null ? void 0 : converters.parseValueToPath) ?? import_utils.parseValueToPath,
615
+ parseValueToPath,
487
616
  formatPathToValue: (item) => {
488
- var _a;
489
- return ((_a = converters == null ? void 0 : converters.formatPathToValue) == null ? void 0 : _a.call(converters, item)) || (0, import_utils.formatPathToValue)(item);
617
+ var _a2;
618
+ return ((_a2 = converters == null ? void 0 : converters.formatPathToValue) == null ? void 0 : _a2.call(converters, item)) || (0, import_utils.formatPathToValue)(item);
490
619
  },
491
620
  onChange: handleSelectorChange
492
621
  }
@@ -132,16 +132,18 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
132
132
  },
133
133
  [onChange]
134
134
  );
135
+ const resolvedPath = (0, import_react.useMemo)(() => {
136
+ return resolvePathFromValue == null ? void 0 : resolvePathFromValue(innerValue);
137
+ }, [innerValue, resolvePathFromValue]);
135
138
  const resolvedMetaTreeNode = (0, import_react.useMemo)(() => {
136
139
  if (currentMetaTreeNode) return currentMetaTreeNode;
137
140
  if (Array.isArray(resolvedMetaTree)) {
138
- const path = resolvePathFromValue == null ? void 0 : resolvePathFromValue(innerValue);
139
- if (path) {
140
- return findMetaTreeNodeByPath(resolvedMetaTree, path);
141
+ if (resolvedPath) {
142
+ return findMetaTreeNodeByPath(resolvedMetaTree, resolvedPath);
141
143
  }
142
144
  }
143
145
  return null;
144
- }, [currentMetaTreeNode, innerValue, resolvedMetaTree, resolvePathFromValue]);
146
+ }, [currentMetaTreeNode, resolvedMetaTree, resolvedPath]);
145
147
  (0, import_react.useEffect)(() => {
146
148
  const restoreFromValue = /* @__PURE__ */ __name(async () => {
147
149
  if (!Array.isArray(resolvedMetaTree) || value == null) return;
@@ -188,13 +190,17 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
188
190
  }
189
191
  }, "restoreFromValue");
190
192
  restoreFromValue();
191
- }, [resolvedMetaTree, innerValue, resolvePathFromValue, currentMetaTreeNode]);
193
+ }, [resolvedMetaTree, innerValue, resolvePathFromValue, currentMetaTreeNode, value]);
192
194
  const ValueComponent = (0, import_react.useMemo)(() => {
193
195
  const Component = renderInputComponent == null ? void 0 : renderInputComponent(resolvedMetaTreeNode);
194
196
  const CustomComponent = resolvedMetaTreeNode == null ? void 0 : resolvedMetaTreeNode.render;
195
- const finalComponent = (0, import_utils.isVariableValue)(innerValue) ? import_VariableTag.VariableTag : Component || CustomComponent || import_antd.Input;
197
+ const shouldRenderVariableTag = (0, import_utils.isVariableValue)(innerValue) || Array.isArray(resolvedPath) && resolvedPath.length > 0 && !Component && !CustomComponent;
198
+ const finalComponent = shouldRenderVariableTag ? import_VariableTag.VariableTag : Component || CustomComponent || import_antd.Input;
196
199
  return finalComponent;
197
- }, [renderInputComponent, resolvedMetaTreeNode, innerValue]);
200
+ }, [renderInputComponent, resolvedMetaTreeNode, innerValue, resolvedPath]);
201
+ const isVariableActive = (0, import_react.useMemo)(() => {
202
+ return (0, import_utils.isVariableValue)(innerValue) || Array.isArray(resolvedPath) && resolvedPath.length > 0 && !(renderInputComponent == null ? void 0 : renderInputComponent(resolvedMetaTreeNode)) && !(resolvedMetaTreeNode == null ? void 0 : resolvedMetaTreeNode.render);
203
+ }, [innerValue, renderInputComponent, resolvedMetaTreeNode, resolvedPath]);
198
204
  (0, import_react.useEffect)(() => {
199
205
  if (!resolvedMetaTreeNode) return;
200
206
  if (!Array.isArray(resolvedMetaTree) || innerValue == null) return;
@@ -278,8 +284,11 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
278
284
  return {
279
285
  ...baseProps,
280
286
  onClear: handleClear,
287
+ disabled,
288
+ allowCustomTagInput: false,
281
289
  metaTreeNode: resolvedMetaTreeNode,
282
290
  metaTree,
291
+ resolvedPath,
283
292
  style: stableProps.style
284
293
  };
285
294
  }
@@ -300,6 +309,7 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
300
309
  disabled,
301
310
  handleClear,
302
311
  resolvedMetaTreeNode,
312
+ resolvedPath,
303
313
  metaTree,
304
314
  ValueComponent,
305
315
  stableProps
@@ -325,6 +335,8 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
325
335
  {
326
336
  metaTree: resolvedMetaTree,
327
337
  value: innerValue,
338
+ active: isVariableActive,
339
+ disabled,
328
340
  onChange: handleVariableSelect,
329
341
  parseValueToPath: resolvePathFromValue,
330
342
  formatPathToValue: resolveValueFromPath,