@nocobase/client-v2 2.2.0-beta.9 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/APIClient.d.ts +1 -3
- package/es/BaseApplication.d.ts +1 -0
- package/es/acl/ACLProvider.d.ts +10 -0
- package/es/authRedirect.d.ts +5 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- package/es/components/AttachmentUpload.d.ts +51 -0
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/index.d.ts +2 -0
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/actions/linkageRules.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/NocoBaseLogo.d.ts +10 -0
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/index.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/models/base/ActionModelCore.d.ts +4 -2
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +8 -1
- package/es/index.mjs +190 -141
- package/es/nocobase-buildin-plugin/currentUserAuthStatus.d.ts +11 -0
- package/es/nocobase-buildin-plugin/index.d.ts +9 -0
- package/es/settings-app/SettingsApplication.d.ts +18 -0
- package/es/settings-app/SettingsBrand.d.ts +16 -0
- package/es/settings-app/SettingsBuildInPlugin.d.ts +9 -0
- package/es/settings-app/SettingsDocumentRedirect.d.ts +10 -0
- package/es/settings-app/SettingsGroupNav.d.ts +17 -0
- package/es/settings-app/SettingsPluginSettingsManager.d.ts +14 -0
- package/es/settings-app/SettingsRouterManager.d.ts +15 -0
- package/es/settings-app/SettingsSearch.d.ts +17 -0
- package/es/settings-app/SettingsShell.d.ts +10 -0
- package/es/settings-app/isSettingsApp.d.ts +18 -0
- package/es/settings-app/runtimePaths.d.ts +18 -0
- package/es/settings-app/settingsDocumentPath.d.ts +14 -0
- package/es/settings-app/settingsTheme.d.ts +87 -0
- package/es/settings-app/useSettingsThemeConfig.d.ts +22 -0
- package/es/settings-center/AdminSettingsLayout.d.ts +2 -2
- package/es/settings-center/groups.d.ts +66 -0
- package/es/settings-center/useSettingsGroups.d.ts +36 -0
- package/es/settings-center/useSettingsSearch.d.ts +35 -0
- package/es/settings-center/utils.d.ts +24 -0
- package/es/theme/index.d.ts +1 -0
- package/es/theme/itemActive.d.ts +24 -0
- package/es/theme/type.d.ts +9 -0
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/lib/index.js +195 -146
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +11 -6
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/authRedirect.test.ts +137 -0
- package/src/__tests__/browserChecker.test.ts +185 -6
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/mockSettingsApplication.ts +29 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +79 -10
- package/src/__tests__/plugin-manager.test.tsx +7 -6
- package/src/__tests__/settings-application.test.ts +164 -0
- package/src/__tests__/settings-auth-routes.test.ts +71 -0
- package/src/__tests__/settings-center.test.tsx +38 -37
- package/src/__tests__/settings-layout-root.test.tsx +260 -0
- package/src/__tests__/settings-runtime-paths.test.ts +99 -0
- package/src/__tests__/settings-shell.test.tsx +200 -0
- package/src/acl/ACLProvider.tsx +21 -0
- package/src/authRedirect.ts +72 -3
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/components/AttachmentUpload.tsx +275 -0
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +4 -0
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/index.ts +2 -0
- package/src/flow/FlowPage.tsx +9 -1
- package/src/flow/__tests__/FlowPage.test.tsx +50 -3
- package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/index.ts +2 -0
- package/src/flow/actions/linkageRules.tsx +111 -16
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +1 -57
- package/src/flow/admin-shell/admin-layout/NocoBaseLogo.tsx +77 -0
- package/src/flow/admin-shell/admin-layout/TopbarActionsBar.tsx +37 -14
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +77 -13
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignValueInput.tsx +91 -20
- package/src/flow/components/FlowRoute.tsx +78 -32
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
- package/src/flow/index.ts +3 -0
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/models/base/ActionModelCore.tsx +11 -2
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +4 -2
- package/src/flow/models/blocks/table/TableColumnModel.tsx +30 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +2 -1
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
- package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +52 -180
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +14 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/currentUserAuthStatus.ts +58 -0
- package/src/nocobase-buildin-plugin/index.tsx +110 -70
- package/src/settings-app/SettingsApplication.ts +31 -0
- package/src/settings-app/SettingsBrand.tsx +108 -0
- package/src/settings-app/SettingsBuildInPlugin.ts +10 -0
- package/src/settings-app/SettingsDocumentRedirect.tsx +26 -0
- package/src/settings-app/SettingsGroupNav.tsx +124 -0
- package/src/settings-app/SettingsPluginSettingsManager.ts +38 -0
- package/src/settings-app/SettingsRouterManager.ts +92 -0
- package/src/settings-app/SettingsSearch.tsx +238 -0
- package/src/settings-app/SettingsShell.tsx +167 -0
- package/src/settings-app/isSettingsApp.ts +21 -0
- package/src/settings-app/runtimePaths.ts +104 -0
- package/src/settings-app/settingsDocumentPath.ts +66 -0
- package/src/settings-app/settingsTheme.ts +301 -0
- package/src/settings-app/useSettingsThemeConfig.ts +100 -0
- package/src/settings-center/AdminSettingsLayout.tsx +122 -141
- package/src/settings-center/SystemSettingsPage.tsx +4 -170
- package/src/settings-center/groups.ts +108 -0
- package/src/settings-center/useSettingsGroups.ts +105 -0
- package/src/settings-center/useSettingsSearch.ts +173 -0
- package/src/settings-center/utils.tsx +50 -1
- package/src/theme/index.tsx +1 -0
- package/src/theme/itemActive.ts +31 -0
- package/src/theme/type.ts +10 -0
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
import { CloseCircleFilled } from '@ant-design/icons';
|
|
11
11
|
import {
|
|
12
12
|
buildContextSelectorItems,
|
|
13
|
+
loadMetaTreeChildren,
|
|
13
14
|
useFlowContext,
|
|
14
15
|
type ContextSelectorItem,
|
|
15
16
|
type MetaTreeNode,
|
|
16
17
|
} from '@nocobase/flow-engine';
|
|
17
18
|
import { Button, Cascader, DatePicker, Input, InputNumber, Select, Space, Tag, theme, type CascaderProps } from 'antd';
|
|
18
19
|
import dayjs from 'dayjs';
|
|
19
|
-
import React, { useCallback, useMemo } from 'react';
|
|
20
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
21
|
+
import { css } from '@emotion/css';
|
|
20
22
|
import {
|
|
21
23
|
makeFormatVariablePath,
|
|
22
24
|
makeParseVariablePath,
|
|
@@ -25,11 +27,11 @@ import {
|
|
|
25
27
|
} from './VariableInput';
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
* Constant types this input can edit.
|
|
29
|
-
* `useTypedConstant`
|
|
30
|
-
*
|
|
30
|
+
* Constant types this input can edit. Matches v1 `Variable.Input`
|
|
31
|
+
* `useTypedConstant` minus `'null'` (handled by the dedicated `nullable` prop).
|
|
32
|
+
* `'object'` renders an inline JSON editor (textarea).
|
|
31
33
|
*/
|
|
32
|
-
export type TypedConstantType = 'string' | 'number' | 'boolean' | 'date';
|
|
34
|
+
export type TypedConstantType = 'string' | 'number' | 'boolean' | 'date' | 'object';
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* One allowed constant type. Either a bare type name (`'number'`) or a
|
|
@@ -50,25 +52,51 @@ export interface TypedVariableInputProps {
|
|
|
50
52
|
* Allowed constant types. The `Constant` switcher entry always exposes a
|
|
51
53
|
* typed submenu (matching v1 `Variable.Input`) so users can see what type
|
|
52
54
|
* the constant is, even when only one type is permitted. Default: all four
|
|
53
|
-
* supported types.
|
|
55
|
+
* supported types. Passing `[]` switches the component into a variable-only
|
|
56
|
+
* mode: constants/null are hidden, the empty state becomes a readonly
|
|
57
|
+
* placeholder, and clearing a selected variable resets to `null`.
|
|
54
58
|
*/
|
|
55
59
|
types?: TypedConstantSpec[];
|
|
56
60
|
/**
|
|
57
61
|
* Restrict the variable picker to specific top-level meta-tree namespaces
|
|
58
62
|
* (e.g. `['$env']`). When omitted, every registered top-level property is
|
|
59
63
|
* exposed — matching `VariableInput`'s default behaviour.
|
|
64
|
+
*
|
|
65
|
+
* Ignored when `metaTree` is supplied (an explicit tree wins).
|
|
60
66
|
*/
|
|
61
67
|
namespaces?: string[];
|
|
62
68
|
/** Additional leaves appended to the picker after the namespace-filtered nodes. */
|
|
63
69
|
extraNodes?: MetaTreeNode[];
|
|
70
|
+
/**
|
|
71
|
+
* Provide the variable tree explicitly instead of reading the global
|
|
72
|
+
* FlowContext meta tree. When set, `namespaces`/`extraNodes` are ignored and
|
|
73
|
+
* this tree is used verbatim — use for context-scoped variable sources that
|
|
74
|
+
* are not part of the global registry (e.g. a workflow node's upstream
|
|
75
|
+
* outputs). Lazy `children` thunks are resolved on demand as the user
|
|
76
|
+
* expands the cascader.
|
|
77
|
+
*/
|
|
78
|
+
metaTree?: MetaTreeNode[];
|
|
64
79
|
/**
|
|
65
80
|
* When true (default), the switcher exposes a `Null` option that resets the
|
|
66
81
|
* value to `null`. When false, the value is constrained to one of the
|
|
67
82
|
* allowed types or a variable reference.
|
|
68
83
|
*/
|
|
69
84
|
nullable?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Opt-in: when the incoming `value` is `undefined`, initialize the editor to
|
|
87
|
+
* the first allowed constant type instead of rendering the null-mode
|
|
88
|
+
* placeholder. `null` still keeps its explicit null semantics; only
|
|
89
|
+
* `undefined` triggers this path.
|
|
90
|
+
*/
|
|
91
|
+
defaultToFirstConstantTypeWhenUndefined?: boolean;
|
|
70
92
|
/** Variable-token delimiters. Default `['{{', '}}']` — see `VariableInput`. */
|
|
71
93
|
delimiters?: VariableDelimiters;
|
|
94
|
+
/**
|
|
95
|
+
* Hide variable choices from the switcher. In variable-only mode this hides
|
|
96
|
+
* the selector button entirely; in mixed constant/variable mode it keeps the
|
|
97
|
+
* null/constant choices but removes variable entries.
|
|
98
|
+
*/
|
|
99
|
+
hideVariable?: boolean;
|
|
72
100
|
disabled?: boolean;
|
|
73
101
|
placeholder?: string;
|
|
74
102
|
style?: React.CSSProperties;
|
|
@@ -82,6 +110,7 @@ const TYPE_LABEL_KEYS: Record<TypedConstantType, string> = {
|
|
|
82
110
|
number: 'Number',
|
|
83
111
|
boolean: 'Boolean',
|
|
84
112
|
date: 'Date',
|
|
113
|
+
object: 'JSON',
|
|
85
114
|
};
|
|
86
115
|
|
|
87
116
|
type NormalizedType = { type: TypedConstantType; props: Record<string, unknown> };
|
|
@@ -92,18 +121,23 @@ function normalizeTypes(types: TypedConstantSpec[]): NormalizedType[] {
|
|
|
92
121
|
);
|
|
93
122
|
}
|
|
94
123
|
|
|
95
|
-
function defaultValueFor(type: TypedConstantType): unknown {
|
|
124
|
+
function defaultValueFor(type: TypedConstantType, typedProps: Record<string, unknown> = {}): unknown {
|
|
96
125
|
switch (type) {
|
|
97
126
|
case 'string':
|
|
98
127
|
return '';
|
|
99
|
-
case 'number':
|
|
128
|
+
case 'number': {
|
|
129
|
+
const min = typeof typedProps.min === 'number' ? typedProps.min : undefined;
|
|
130
|
+
if (min !== undefined && min > 0) return min;
|
|
100
131
|
return 0;
|
|
132
|
+
}
|
|
101
133
|
case 'boolean':
|
|
102
134
|
return false;
|
|
103
135
|
case 'date': {
|
|
104
136
|
const now = new Date();
|
|
105
137
|
return new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
|
|
106
138
|
}
|
|
139
|
+
case 'object':
|
|
140
|
+
return {};
|
|
107
141
|
default:
|
|
108
142
|
return null;
|
|
109
143
|
}
|
|
@@ -121,6 +155,7 @@ function detectMode(value: unknown, parseVariablePath: (v?: string) => string[]
|
|
|
121
155
|
if (typeof value === 'number') return { mode: 'number' };
|
|
122
156
|
if (typeof value === 'boolean') return { mode: 'boolean' };
|
|
123
157
|
if (value instanceof Date) return { mode: 'date' };
|
|
158
|
+
if (typeof value === 'object') return { mode: 'object' };
|
|
124
159
|
return { mode: 'string' };
|
|
125
160
|
}
|
|
126
161
|
|
|
@@ -131,6 +166,7 @@ interface SwitcherOption {
|
|
|
131
166
|
value: string;
|
|
132
167
|
label: React.ReactNode;
|
|
133
168
|
isLeaf?: boolean;
|
|
169
|
+
loading?: boolean;
|
|
134
170
|
children?: SwitcherOption[];
|
|
135
171
|
meta?: MetaTreeNode;
|
|
136
172
|
paths?: string[];
|
|
@@ -175,9 +211,10 @@ function renderConstantEditor(
|
|
|
175
211
|
disabled?: boolean;
|
|
176
212
|
placeholder?: string;
|
|
177
213
|
t: (text: string) => string;
|
|
214
|
+
onJsonError?: (message: string | null) => void;
|
|
178
215
|
},
|
|
179
216
|
): React.ReactNode {
|
|
180
|
-
const { typedProps, disabled, placeholder, t } = options;
|
|
217
|
+
const { typedProps, disabled, placeholder, t, onJsonError } = options;
|
|
181
218
|
switch (type) {
|
|
182
219
|
case 'string':
|
|
183
220
|
return (
|
|
@@ -229,11 +266,126 @@ function renderConstantEditor(
|
|
|
229
266
|
/>
|
|
230
267
|
);
|
|
231
268
|
}
|
|
269
|
+
case 'object':
|
|
270
|
+
return (
|
|
271
|
+
<JsonConstantEditor
|
|
272
|
+
value={value}
|
|
273
|
+
onChange={onChange}
|
|
274
|
+
onError={onJsonError}
|
|
275
|
+
disabled={disabled}
|
|
276
|
+
typedProps={typedProps}
|
|
277
|
+
/>
|
|
278
|
+
);
|
|
232
279
|
default:
|
|
233
280
|
return null;
|
|
234
281
|
}
|
|
235
282
|
}
|
|
236
283
|
|
|
284
|
+
const jsonEditorClassName = css`
|
|
285
|
+
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
|
286
|
+
font-size: 80%;
|
|
287
|
+
`;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Inline JSON editor for the `'object'` constant type. Formily-free port of v1's
|
|
291
|
+
* `Json` input (which used a Formily field for error feedback): keeps the raw
|
|
292
|
+
* text as local draft state, parses on blur, and writes the parsed object up via
|
|
293
|
+
* `onChange`. A parse error is reported up through `onError` (the raw
|
|
294
|
+
* `JSON.parse` message, matching v1) so the parent can render it on its own row
|
|
295
|
+
* below the input — keeping the textarea + switcher button row intact.
|
|
296
|
+
*/
|
|
297
|
+
function JsonConstantEditor({
|
|
298
|
+
value,
|
|
299
|
+
onChange,
|
|
300
|
+
onError,
|
|
301
|
+
disabled,
|
|
302
|
+
typedProps,
|
|
303
|
+
}: {
|
|
304
|
+
value: unknown;
|
|
305
|
+
onChange?: (next: unknown) => void;
|
|
306
|
+
onError?: (message: string | null) => void;
|
|
307
|
+
disabled?: boolean;
|
|
308
|
+
typedProps: Record<string, unknown>;
|
|
309
|
+
}) {
|
|
310
|
+
const stringify = useCallback((v: unknown) => {
|
|
311
|
+
if (v == null) return '';
|
|
312
|
+
if (typeof v === 'string') return v;
|
|
313
|
+
try {
|
|
314
|
+
return JSON.stringify(v, null, 2);
|
|
315
|
+
} catch {
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
318
|
+
}, []);
|
|
319
|
+
|
|
320
|
+
const [text, setText] = useState<string>(() => stringify(value));
|
|
321
|
+
const [hasError, setHasError] = useState(false);
|
|
322
|
+
|
|
323
|
+
// Re-sync the draft when the external value changes (e.g. switching type).
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
setText(stringify(value));
|
|
326
|
+
setHasError(false);
|
|
327
|
+
onError?.(null);
|
|
328
|
+
// `onError` intentionally omitted — only resync on external value change.
|
|
329
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
330
|
+
}, [value, stringify]);
|
|
331
|
+
|
|
332
|
+
// Validate as the user types (live red border + error row), mirroring v1's `Json` which calls `setFeedback` on every
|
|
333
|
+
// change — not just on blur.
|
|
334
|
+
const handleChange = useCallback(
|
|
335
|
+
(raw: string) => {
|
|
336
|
+
setText(raw);
|
|
337
|
+
const trimmed = raw.trim();
|
|
338
|
+
if (trimmed === '') {
|
|
339
|
+
setHasError(false);
|
|
340
|
+
onError?.(null);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
JSON.parse(trimmed);
|
|
345
|
+
setHasError(false);
|
|
346
|
+
onError?.(null);
|
|
347
|
+
} catch (err) {
|
|
348
|
+
setHasError(true);
|
|
349
|
+
onError?.((err as Error).message);
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
[onError],
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// Commit the parsed object up on blur (v1 emits the value on blur). A still-invalid value keeps its error and is not
|
|
356
|
+
// emitted.
|
|
357
|
+
const commit = useCallback(
|
|
358
|
+
(raw: string) => {
|
|
359
|
+
const trimmed = raw.trim();
|
|
360
|
+
if (trimmed === '') {
|
|
361
|
+
onChange?.(null);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
onChange?.(JSON.parse(trimmed));
|
|
366
|
+
} catch {
|
|
367
|
+
/* error already shown live via handleChange */
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
[onChange],
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
// No `autoSize` — it would disable the resize grip. Default to 2 rows (mirrors v1's `Json`) and let the user
|
|
375
|
+
// drag-resize from the corner.
|
|
376
|
+
<Input.TextArea
|
|
377
|
+
className={jsonEditorClassName}
|
|
378
|
+
value={text}
|
|
379
|
+
onChange={(ev) => handleChange(ev.target.value)}
|
|
380
|
+
onBlur={(ev) => commit(ev.target.value)}
|
|
381
|
+
disabled={disabled}
|
|
382
|
+
rows={2}
|
|
383
|
+
status={hasError ? 'error' : undefined}
|
|
384
|
+
{...typedProps}
|
|
385
|
+
/>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
237
389
|
/**
|
|
238
390
|
* Constant-or-variable input. Port of v1 `Variable.Input` typed-constant
|
|
239
391
|
* Cascader pattern (the `[Null | Constant<type> | Variable<…namespaces>]`
|
|
@@ -255,8 +407,11 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
255
407
|
types = DEFAULT_TYPES,
|
|
256
408
|
namespaces,
|
|
257
409
|
extraNodes,
|
|
410
|
+
metaTree: metaTreeProp,
|
|
258
411
|
nullable = true,
|
|
412
|
+
defaultToFirstConstantTypeWhenUndefined = true,
|
|
259
413
|
delimiters,
|
|
414
|
+
hideVariable = false,
|
|
260
415
|
disabled,
|
|
261
416
|
placeholder,
|
|
262
417
|
style,
|
|
@@ -266,24 +421,86 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
266
421
|
const t = useCallback((text: string): string => (typeof ctx?.t === 'function' ? ctx.t(text) : text), [ctx]);
|
|
267
422
|
const { token } = theme.useToken();
|
|
268
423
|
|
|
269
|
-
|
|
424
|
+
// An explicit `metaTree` wins over the global FlowContext tree. The hook is still called unconditionally (Rules of
|
|
425
|
+
// Hooks); its result is simply unused when a tree is injected.
|
|
426
|
+
const filteredMetaTree = useFilteredMetaTree({ namespaces, extraNodes });
|
|
427
|
+
const metaTree = metaTreeProp ?? filteredMetaTree;
|
|
270
428
|
|
|
271
429
|
const parseVariablePath = useMemo(() => makeParseVariablePath(delimiters), [delimiters]);
|
|
272
430
|
const formatVariablePath = useMemo(() => makeFormatVariablePath(delimiters), [delimiters]);
|
|
273
431
|
|
|
274
432
|
const normalizedTypes = useMemo(() => normalizeTypes(types), [types]);
|
|
275
|
-
const
|
|
276
|
-
const
|
|
433
|
+
const variableOnly = normalizedTypes.length === 0;
|
|
434
|
+
const defaultedValue = useMemo(() => {
|
|
435
|
+
if (variableOnly || !defaultToFirstConstantTypeWhenUndefined || value !== undefined) {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
const firstType = normalizedTypes[0];
|
|
439
|
+
return firstType ? defaultValueFor(firstType.type, firstType.props) : undefined;
|
|
440
|
+
}, [defaultToFirstConstantTypeWhenUndefined, normalizedTypes, value, variableOnly]);
|
|
441
|
+
const effectiveValue = value === undefined && defaultedValue !== undefined ? defaultedValue : value;
|
|
442
|
+
const detected = useMemo(() => detectMode(effectiveValue, parseVariablePath), [effectiveValue, parseVariablePath]);
|
|
443
|
+
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
if (value === undefined && defaultedValue !== undefined) {
|
|
446
|
+
onChange?.(defaultedValue);
|
|
447
|
+
}
|
|
448
|
+
}, [defaultedValue, onChange, value]);
|
|
449
|
+
|
|
450
|
+
// rc-cascader caches its options by *reference* (useEntities), so lazily filling a node's children in place is
|
|
451
|
+
// invisible to it. We mirror `FlowContextSelector`: lazy `loadData` mutates the **meta tree** in place, bumps
|
|
452
|
+
// `updateFlag`, and the options are rebuilt from scratch (fresh references) on every bump — forcing rc-cascader to
|
|
453
|
+
// re-index the new column.
|
|
454
|
+
const [updateFlag, setUpdateFlag] = useState(0);
|
|
455
|
+
const triggerUpdate = useCallback(() => setUpdateFlag((prev) => prev + 1), []);
|
|
456
|
+
|
|
457
|
+
const loadData = useCallback(
|
|
458
|
+
(selectedOptions: SwitcherOption[]) => {
|
|
459
|
+
const target = selectedOptions[selectedOptions.length - 1];
|
|
460
|
+
// Only variable nodes lazy-load; the Null / Constant switcher entries never do.
|
|
461
|
+
if (!target || target.value === NULL_KEY || target.value === CONST_KEY) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const meta = target.meta;
|
|
465
|
+
if (!meta) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
// Already-resolved children (array): nothing to fetch.
|
|
469
|
+
if (Array.isArray(meta.children)) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (typeof meta.children !== 'function') {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
target.loading = true;
|
|
476
|
+
triggerUpdate();
|
|
477
|
+
// `loadMetaTreeChildren` already swallows its own errors (returns []), so a bare chain is safe; `return`
|
|
478
|
+
// satisfies the promise/catch-or-return rule.
|
|
479
|
+
return loadMetaTreeChildren(meta)
|
|
480
|
+
.then((childMetas) => {
|
|
481
|
+
// Cache resolved children on the meta node; the options tree is then rebuilt from the mutated meta tree on
|
|
482
|
+
// the next `updateFlag` bump.
|
|
483
|
+
meta.children = childMetas;
|
|
484
|
+
})
|
|
485
|
+
.finally(() => {
|
|
486
|
+
target.loading = false;
|
|
487
|
+
triggerUpdate();
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
[triggerUpdate],
|
|
491
|
+
);
|
|
277
492
|
|
|
278
493
|
const switcherOptions = useMemo<SwitcherOption[]>(() => {
|
|
494
|
+
// `updateFlag` is read so this recomputes (with fresh option references) after a lazy load mutates the meta tree.
|
|
495
|
+
void updateFlag;
|
|
279
496
|
const items: SwitcherOption[] = [];
|
|
280
|
-
if (nullable) {
|
|
497
|
+
if (!variableOnly && nullable) {
|
|
281
498
|
items.push({ value: NULL_KEY, label: t('Null'), isLeaf: true });
|
|
282
499
|
}
|
|
283
500
|
// Always render Constant with a typed submenu — even when only one type is
|
|
284
501
|
// allowed. Matches v1 `Variable.Input`, where clicking 常量 reveals 数字 /
|
|
285
502
|
// 逻辑值 / etc. so the user can see what type the constant actually is.
|
|
286
|
-
if (normalizedTypes.length > 0) {
|
|
503
|
+
if (!variableOnly && normalizedTypes.length > 0) {
|
|
287
504
|
items.push({
|
|
288
505
|
value: CONST_KEY,
|
|
289
506
|
label: t('Constant'),
|
|
@@ -294,9 +511,11 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
294
511
|
})),
|
|
295
512
|
});
|
|
296
513
|
}
|
|
297
|
-
|
|
514
|
+
if (!hideVariable) {
|
|
515
|
+
items.push(...buildContextSelectorItems(metaTree).map(fromContextItem));
|
|
516
|
+
}
|
|
298
517
|
return items;
|
|
299
|
-
}, [
|
|
518
|
+
}, [hideVariable, metaTree, normalizedTypes, nullable, t, updateFlag, variableOnly]);
|
|
300
519
|
|
|
301
520
|
const onSwitcherChange = useCallback<NonNullable<CascaderProps<SwitcherOption>['onChange']>>(
|
|
302
521
|
(path, selectedOptions) => {
|
|
@@ -313,7 +532,8 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
313
532
|
const targetType = (path[1] as TypedConstantType | undefined) ?? normalizedTypes[0]?.type;
|
|
314
533
|
if (!targetType) return;
|
|
315
534
|
if (detected.mode === targetType) return;
|
|
316
|
-
|
|
535
|
+
const target = normalizedTypes.find(({ type }) => type === targetType);
|
|
536
|
+
onChange?.(defaultValueFor(targetType, target?.props));
|
|
317
537
|
return;
|
|
318
538
|
}
|
|
319
539
|
const leaf = selectedOptions?.[selectedOptions.length - 1] as SwitcherOption | undefined;
|
|
@@ -326,17 +546,23 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
326
546
|
);
|
|
327
547
|
|
|
328
548
|
const onClearVariable = useCallback(() => {
|
|
329
|
-
if (
|
|
549
|
+
if (variableOnly) {
|
|
330
550
|
onChange?.(null);
|
|
331
551
|
return;
|
|
332
552
|
}
|
|
333
553
|
const first = normalizedTypes[0];
|
|
334
|
-
if (first)
|
|
335
|
-
|
|
554
|
+
if (first) {
|
|
555
|
+
onChange?.(defaultValueFor(first.type, first.props));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (nullable) {
|
|
559
|
+
onChange?.(null);
|
|
560
|
+
}
|
|
561
|
+
}, [nullable, normalizedTypes, onChange, variableOnly]);
|
|
336
562
|
|
|
337
563
|
const constantTypeForRendering: TypedConstantType = useMemo(() => {
|
|
338
564
|
const m = detected.mode;
|
|
339
|
-
if (m === 'string' || m === 'number' || m === 'boolean' || m === 'date') return m;
|
|
565
|
+
if (m === 'string' || m === 'number' || m === 'boolean' || m === 'date' || m === 'object') return m;
|
|
340
566
|
return normalizedTypes[0]?.type ?? 'string';
|
|
341
567
|
}, [detected.mode, normalizedTypes]);
|
|
342
568
|
|
|
@@ -347,94 +573,219 @@ export function TypedVariableInput(props: TypedVariableInputProps) {
|
|
|
347
573
|
|
|
348
574
|
const isVariable = detected.mode === 'variable';
|
|
349
575
|
const isNull = detected.mode === 'null';
|
|
576
|
+
const showSwitcher = switcherOptions.length > 0 && !(hideVariable && isVariable);
|
|
577
|
+
|
|
578
|
+
const variableLabels = useMemo(() => {
|
|
579
|
+
// `updateFlag` is read so this recomputes after the preload effect below resolves a lazy level in the tree (same
|
|
580
|
+
// pattern as `switcherOptions`).
|
|
581
|
+
void updateFlag;
|
|
582
|
+
return isVariable && detected.variablePath ? resolveVariableLabels(detected.variablePath, metaTree) : [];
|
|
583
|
+
}, [isVariable, detected.variablePath, metaTree, updateFlag]);
|
|
350
584
|
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
585
|
+
const switcherValue = useMemo(() => {
|
|
586
|
+
if (isVariable && detected.variablePath?.length) {
|
|
587
|
+
return detected.variablePath;
|
|
588
|
+
}
|
|
589
|
+
if (variableOnly) {
|
|
590
|
+
return undefined;
|
|
591
|
+
}
|
|
592
|
+
if (isNull && nullable) {
|
|
593
|
+
return [NULL_KEY];
|
|
594
|
+
}
|
|
595
|
+
return [CONST_KEY, constantTypeForRendering];
|
|
596
|
+
}, [constantTypeForRendering, detected.variablePath, isNull, isVariable, nullable, variableOnly]);
|
|
597
|
+
|
|
598
|
+
// Preload a saved variable's label path across lazy levels. `resolveVariableLabels` can only read already-loaded
|
|
599
|
+
// `children`; when a saved reference points below a node whose children are still a lazy thunk (e.g. a relation field
|
|
600
|
+
// that hasn't been expanded), the deep segments render as raw names. Walk the saved path on mount / value change,
|
|
601
|
+
// resolving each lazy level in place (then bump `updateFlag` so the labels recompute). Mirrors v1 `Variable.Input`'s
|
|
602
|
+
// preload effect.
|
|
603
|
+
useEffect(() => {
|
|
604
|
+
if (!isVariable || !detected.variablePath?.length) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
let cancelled = false;
|
|
608
|
+
const run = async () => {
|
|
609
|
+
let nodes: MetaTreeNode[] | undefined = metaTree;
|
|
610
|
+
let didLoad = false;
|
|
611
|
+
for (const segment of detected.variablePath as string[]) {
|
|
612
|
+
if (!nodes) {
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
const matched: MetaTreeNode | undefined = nodes.find((node) => node.name === segment);
|
|
616
|
+
if (!matched) {
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
if (typeof matched.children === 'function') {
|
|
620
|
+
const resolved = await loadMetaTreeChildren(matched);
|
|
621
|
+
if (cancelled) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
matched.children = resolved;
|
|
625
|
+
didLoad = true;
|
|
626
|
+
}
|
|
627
|
+
nodes = Array.isArray(matched.children) ? matched.children : undefined;
|
|
628
|
+
}
|
|
629
|
+
if (didLoad && !cancelled) {
|
|
630
|
+
triggerUpdate();
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
run();
|
|
634
|
+
return () => {
|
|
635
|
+
cancelled = true;
|
|
636
|
+
};
|
|
637
|
+
// `metaTree` identity is stable per structural change (see consumers); re-run when the saved path or the tree
|
|
638
|
+
// changes.
|
|
639
|
+
}, [isVariable, detected.variablePath, metaTree, triggerUpdate]);
|
|
640
|
+
|
|
641
|
+
// JSON parse error from the object editor, rendered on its own row below the input (not squeezed into the compact
|
|
642
|
+
// row). Cleared whenever the value is no longer an object literal.
|
|
643
|
+
const [jsonError, setJsonError] = useState<string | null>(null);
|
|
644
|
+
useEffect(() => {
|
|
645
|
+
if (constantTypeForRendering !== 'object') setJsonError(null);
|
|
646
|
+
}, [constantTypeForRendering]);
|
|
647
|
+
|
|
648
|
+
const variableValueClassName = useMemo(
|
|
649
|
+
() => css`
|
|
650
|
+
&:hover .clear-button,
|
|
651
|
+
&:focus-within .clear-button {
|
|
652
|
+
visibility: visible;
|
|
653
|
+
pointer-events: auto;
|
|
654
|
+
opacity: 1;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.clear-button {
|
|
658
|
+
display: inline-flex;
|
|
659
|
+
align-items: center;
|
|
660
|
+
justify-content: center;
|
|
661
|
+
width: ${token.fontSizeIcon}px;
|
|
662
|
+
height: ${token.fontSizeIcon}px;
|
|
663
|
+
padding: 0;
|
|
664
|
+
color: ${token.colorTextQuaternary};
|
|
665
|
+
background: transparent;
|
|
666
|
+
cursor: pointer;
|
|
667
|
+
visibility: hidden;
|
|
668
|
+
pointer-events: none;
|
|
669
|
+
opacity: 0;
|
|
670
|
+
transition:
|
|
671
|
+
visibility ${token.motionDurationMid} ease,
|
|
672
|
+
color ${token.motionDurationMid} ease,
|
|
673
|
+
opacity ${token.motionDurationSlow} ease;
|
|
674
|
+
|
|
675
|
+
&:hover {
|
|
676
|
+
color: ${token.colorTextTertiary};
|
|
677
|
+
background: transparent;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
`,
|
|
681
|
+
[token],
|
|
354
682
|
);
|
|
355
683
|
|
|
356
684
|
return (
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
border: `1px solid ${token.colorBorder}`,
|
|
370
|
-
borderRadius: token.borderRadius,
|
|
371
|
-
borderTopRightRadius: 0,
|
|
372
|
-
borderBottomRightRadius: 0,
|
|
373
|
-
background: disabled ? token.colorBgContainerDisabled : token.colorBgContainer,
|
|
374
|
-
overflow: 'hidden',
|
|
375
|
-
}}
|
|
376
|
-
>
|
|
377
|
-
<Tag
|
|
378
|
-
color="blue"
|
|
685
|
+
<div style={{ width: '100%' }}>
|
|
686
|
+
{/* Default `Space.Compact` (align-items: stretch) so the switcher button
|
|
687
|
+
grows to the value component's height — its `height: auto` (below) lets
|
|
688
|
+
a multi-line value (the JSON textarea) stretch the button to match,
|
|
689
|
+
joined like v1 (which sets `.ant-btn { height: auto }` likewise). */}
|
|
690
|
+
<Space.Compact style={{ display: 'flex', width: '100%', ...style }} className={className}>
|
|
691
|
+
<div style={{ flex: 1, minWidth: 0, overflow: 'hidden' }}>
|
|
692
|
+
{isVariable ? (
|
|
693
|
+
<div
|
|
694
|
+
className={variableValueClassName}
|
|
695
|
+
role="button"
|
|
696
|
+
aria-label="variable-tag"
|
|
379
697
|
style={{
|
|
380
|
-
|
|
381
|
-
|
|
698
|
+
display: 'flex',
|
|
699
|
+
alignItems: 'center',
|
|
700
|
+
gap: token.marginXXS,
|
|
701
|
+
padding: `0 ${token.paddingSM}px`,
|
|
702
|
+
minHeight: token.controlHeight,
|
|
703
|
+
border: `1px solid ${token.colorBorder}`,
|
|
704
|
+
borderRadius: token.borderRadius,
|
|
705
|
+
borderTopRightRadius: showSwitcher ? 0 : token.borderRadius,
|
|
706
|
+
borderBottomRightRadius: showSwitcher ? 0 : token.borderRadius,
|
|
707
|
+
background: disabled ? token.colorBgContainerDisabled : token.colorBgContainer,
|
|
382
708
|
overflow: 'hidden',
|
|
383
|
-
textOverflow: 'ellipsis',
|
|
384
|
-
whiteSpace: 'nowrap',
|
|
385
709
|
}}
|
|
386
710
|
>
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
711
|
+
<Tag
|
|
712
|
+
color="blue"
|
|
713
|
+
style={{
|
|
714
|
+
marginInlineEnd: 0,
|
|
715
|
+
maxWidth: '100%',
|
|
716
|
+
overflow: 'hidden',
|
|
717
|
+
textOverflow: 'ellipsis',
|
|
718
|
+
whiteSpace: 'nowrap',
|
|
719
|
+
}}
|
|
720
|
+
>
|
|
721
|
+
{variableLabels.map((label, index) => (
|
|
722
|
+
<React.Fragment key={`${label}-${index}`}>
|
|
723
|
+
{index ? ' / ' : ''}
|
|
724
|
+
{label}
|
|
725
|
+
</React.Fragment>
|
|
726
|
+
))}
|
|
727
|
+
</Tag>
|
|
728
|
+
{!disabled ? (
|
|
729
|
+
<Button
|
|
730
|
+
type="text"
|
|
731
|
+
size="small"
|
|
732
|
+
className="clear-button"
|
|
733
|
+
aria-label="icon-close"
|
|
734
|
+
onClick={onClearVariable}
|
|
735
|
+
icon={<CloseCircleFilled />}
|
|
736
|
+
/>
|
|
737
|
+
) : null}
|
|
738
|
+
</div>
|
|
739
|
+
) : variableOnly ? (
|
|
740
|
+
<Input placeholder={placeholder} readOnly disabled={disabled} style={{ width: '100%' }} />
|
|
741
|
+
) : isNull && nullable ? (
|
|
742
|
+
// v1 used the `placeholder` slot (not `value`) so the antd default placeholder colour applies — keeps the
|
|
743
|
+
// field looking visibly empty/inactive rather than holding a real text value.
|
|
744
|
+
<Input placeholder={`<${t('Null')}>`} readOnly disabled={disabled} style={{ width: '100%' }} />
|
|
745
|
+
) : (
|
|
746
|
+
renderConstantEditor(constantTypeForRendering, effectiveValue, onChange, {
|
|
747
|
+
typedProps,
|
|
748
|
+
disabled,
|
|
749
|
+
placeholder,
|
|
750
|
+
t,
|
|
751
|
+
onJsonError: setJsonError,
|
|
752
|
+
})
|
|
753
|
+
)}
|
|
754
|
+
</div>
|
|
755
|
+
{showSwitcher ? (
|
|
756
|
+
<Cascader<SwitcherOption>
|
|
757
|
+
options={switcherOptions}
|
|
758
|
+
value={switcherValue}
|
|
759
|
+
onChange={onSwitcherChange}
|
|
760
|
+
loadData={loadData}
|
|
761
|
+
disabled={disabled}
|
|
762
|
+
changeOnSelect
|
|
763
|
+
>
|
|
764
|
+
<Button
|
|
765
|
+
aria-label="variable-switcher"
|
|
766
|
+
disabled={disabled}
|
|
767
|
+
type={isVariable ? 'primary' : 'default'}
|
|
768
|
+
style={{
|
|
769
|
+
flexShrink: 0,
|
|
770
|
+
// `height: auto` (instead of antd's fixed control height) lets the button stretch to the value
|
|
771
|
+
// component's height under the compact row's default `align-items: stretch` — so it stays joined to a
|
|
772
|
+
// tall JSON textarea. Mirrors v1's `.ant-btn { height: auto }`.
|
|
773
|
+
height: 'auto',
|
|
774
|
+
fontStyle: 'italic',
|
|
775
|
+
fontFamily: '"New York", "Times New Roman", Times, serif',
|
|
776
|
+
}}
|
|
777
|
+
>
|
|
778
|
+
x
|
|
779
|
+
</Button>
|
|
780
|
+
</Cascader>
|
|
781
|
+
) : null}
|
|
782
|
+
</Space.Compact>
|
|
783
|
+
{jsonError ? (
|
|
784
|
+
<div style={{ marginTop: token.marginXXS, color: token.colorError, fontSize: token.fontSizeSM }}>
|
|
785
|
+
{jsonError}
|
|
786
|
+
</div>
|
|
787
|
+
) : null}
|
|
788
|
+
</div>
|
|
438
789
|
);
|
|
439
790
|
}
|
|
440
791
|
|