@vendure/dashboard 3.4.1-master-202508210231 → 3.4.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.
Files changed (58) hide show
  1. package/package.json +152 -152
  2. package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +2 -1
  3. package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +1 -0
  4. package/src/lib/components/data-input/affixed-input.tsx +19 -5
  5. package/src/lib/components/data-input/boolean-input.tsx +9 -0
  6. package/src/lib/components/data-input/checkbox-input.tsx +8 -0
  7. package/src/lib/components/data-input/combination-mode-input.tsx +11 -2
  8. package/src/lib/components/data-input/configurable-operation-list-input.tsx +26 -401
  9. package/src/lib/components/data-input/custom-field-list-input.tsx +18 -25
  10. package/src/lib/components/data-input/customer-group-input.tsx +7 -11
  11. package/src/lib/components/data-input/datetime-input.tsx +9 -13
  12. package/src/lib/components/data-input/default-relation-input.tsx +29 -9
  13. package/src/lib/components/data-input/facet-value-input.tsx +15 -13
  14. package/src/lib/components/data-input/index.ts +2 -2
  15. package/src/lib/components/data-input/money-input.tsx +27 -20
  16. package/src/lib/components/data-input/number-input.tsx +48 -0
  17. package/src/lib/components/data-input/password-input.tsx +16 -0
  18. package/src/lib/components/data-input/{product-multi-selector.tsx → product-multi-selector-input.tsx} +8 -15
  19. package/src/lib/components/data-input/relation-input.tsx +7 -6
  20. package/src/lib/components/data-input/rich-text-input.tsx +10 -13
  21. package/src/lib/components/data-input/select-with-options.tsx +29 -17
  22. package/src/lib/components/data-input/struct-form-input.tsx +54 -59
  23. package/src/lib/components/data-input/text-input.tsx +9 -0
  24. package/src/lib/components/data-input/textarea-input.tsx +16 -0
  25. package/src/lib/components/data-table/filters/data-table-number-filter.tsx +3 -0
  26. package/src/lib/components/data-table/use-generated-columns.tsx +16 -5
  27. package/src/lib/components/shared/configurable-operation-arg-input.tsx +3 -10
  28. package/src/lib/components/shared/configurable-operation-input.tsx +1 -6
  29. package/src/lib/components/shared/configurable-operation-multi-selector.tsx +8 -5
  30. package/src/lib/components/shared/configurable-operation-selector.tsx +5 -5
  31. package/src/lib/components/shared/custom-fields-form.tsx +20 -49
  32. package/src/lib/components/shared/multi-select.tsx +1 -1
  33. package/src/lib/framework/component-registry/component-registry.tsx +9 -32
  34. package/src/lib/framework/component-registry/display-component.tsx +28 -0
  35. package/src/lib/framework/extension-api/display-component-extensions.tsx +0 -14
  36. package/src/lib/framework/extension-api/input-component-extensions.tsx +52 -34
  37. package/src/lib/framework/extension-api/logic/data-table.ts +4 -27
  38. package/src/lib/framework/extension-api/logic/form-components.ts +3 -2
  39. package/src/lib/framework/extension-api/types/detail-forms.ts +2 -38
  40. package/src/lib/framework/extension-api/types/form-components.ts +2 -4
  41. package/src/lib/framework/form-engine/custom-form-component-extensions.ts +0 -23
  42. package/src/lib/framework/form-engine/custom-form-component.tsx +8 -25
  43. package/src/lib/framework/form-engine/default-input-for-type.tsx +35 -0
  44. package/src/lib/framework/form-engine/form-control-adapter.tsx +192 -0
  45. package/src/lib/framework/form-engine/form-engine-types.ts +163 -0
  46. package/src/lib/framework/form-engine/form-schema-tools.ts +55 -71
  47. package/src/lib/framework/form-engine/overridden-form-component.tsx +2 -2
  48. package/src/lib/framework/form-engine/utils.ts +223 -0
  49. package/src/lib/{components/shared → framework/form-engine}/value-transformers.ts +9 -9
  50. package/src/lib/framework/registry/registry-types.ts +3 -5
  51. package/src/lib/graphql/graphql-env.d.ts +11 -7
  52. package/src/lib/index.ts +28 -1
  53. package/src/lib/providers/server-config.tsx +1 -0
  54. package/src/lib/components/shared/direct-form-component-map.tsx +0 -393
  55. package/src/lib/components/shared/universal-field-definition.ts +0 -118
  56. package/src/lib/components/shared/universal-form-input.tsx +0 -175
  57. package/src/lib/components/shared/universal-input-components.tsx +0 -291
  58. package/src/lib/framework/component-registry/dynamic-component.tsx +0 -58
@@ -1,393 +0,0 @@
1
- import { DefaultFormComponentId } from '@vendure/common/lib/shared-types';
2
- import React from 'react';
3
-
4
- import { AffixedInput } from '@/vdb/components/data-input/affixed-input.js';
5
- import { CombinationModeInput } from '@/vdb/components/data-input/combination-mode-input.js';
6
- import { DateTimeInput } from '@/vdb/components/data-input/datetime-input.js';
7
- import { DefaultRelationInput } from '@/vdb/components/data-input/default-relation-input.js';
8
- import { FacetValueInput } from '@/vdb/components/data-input/facet-value-input.js';
9
- import { MoneyInput } from '@/vdb/components/data-input/money-input.js';
10
- import { ProductMultiInput } from '@/vdb/components/data-input/product-multi-selector.js';
11
- import { RichTextInput } from '@/vdb/components/data-input/rich-text-input.js';
12
- import { Input } from '@/vdb/components/ui/input.js';
13
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/vdb/components/ui/select.js';
14
- import { Switch } from '@/vdb/components/ui/switch.js';
15
- import { Textarea } from '@/vdb/components/ui/textarea.js';
16
-
17
- import { UniversalFieldDefinition } from './universal-field-definition.js';
18
- import { transformValue, ValueMode } from './value-transformers.js';
19
-
20
- /**
21
- * Custom hook to handle value transformation between native and JSON string modes
22
- * Eliminates duplication across form input components
23
- */
24
- function useValueTransformation(
25
- field: { value: any; onChange: (value: any) => void },
26
- fieldDef: UniversalFieldDefinition,
27
- valueMode: ValueMode,
28
- ) {
29
- const transformedValue = React.useMemo(() => {
30
- return valueMode === 'json-string'
31
- ? transformValue(field.value, fieldDef, valueMode, 'parse')
32
- : field.value;
33
- }, [field.value, fieldDef, valueMode]);
34
-
35
- const handleChange = React.useCallback(
36
- (newValue: any) => {
37
- const serializedValue =
38
- valueMode === 'json-string'
39
- ? transformValue(newValue, fieldDef, valueMode, 'serialize')
40
- : newValue;
41
- field.onChange(serializedValue);
42
- },
43
- [field.onChange, fieldDef, valueMode],
44
- );
45
-
46
- return { transformedValue, handleChange };
47
- }
48
-
49
- export interface DirectFormComponentProps {
50
- fieldDef: UniversalFieldDefinition;
51
- field: {
52
- value: any;
53
- onChange: (value: any) => void;
54
- onBlur?: () => void;
55
- name: string;
56
- ref?: any;
57
- };
58
- valueMode: ValueMode;
59
- disabled?: boolean;
60
- }
61
-
62
- /**
63
- * Text input wrapper for config args
64
- */
65
- const TextFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
66
- const handleChange = React.useCallback(
67
- (e: React.ChangeEvent<HTMLInputElement>) => {
68
- // For both modes, text values are stored as strings
69
- field.onChange(e.target.value);
70
- },
71
- [field.onChange],
72
- );
73
-
74
- const value = field.value || '';
75
-
76
- return (
77
- <Input
78
- type="text"
79
- value={value}
80
- onChange={handleChange}
81
- onBlur={field.onBlur}
82
- name={field.name}
83
- disabled={disabled}
84
- className={valueMode === 'json-string' ? 'bg-background' : undefined}
85
- />
86
- );
87
- };
88
-
89
- /**
90
- * Number input wrapper for config args
91
- */
92
- const NumberFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
93
- const ui = fieldDef.ui;
94
- const isFloat = fieldDef.type === 'float';
95
- const min = ui?.min;
96
- const max = ui?.max;
97
- const step = ui?.step || (isFloat ? 0.01 : 1);
98
- const prefix = ui?.prefix;
99
- const suffix = ui?.suffix;
100
-
101
- const handleChange = React.useCallback(
102
- (newValue: number | '') => {
103
- if (valueMode === 'json-string') {
104
- // For config args, store as string
105
- field.onChange(newValue === '' ? '' : newValue.toString());
106
- } else {
107
- // For custom fields, store as number or undefined
108
- field.onChange(newValue === '' ? undefined : newValue);
109
- }
110
- },
111
- [field.onChange, valueMode],
112
- );
113
-
114
- // Parse current value to number
115
- const numericValue = React.useMemo(() => {
116
- if (field.value === undefined || field.value === null || field.value === '') {
117
- return '';
118
- }
119
- const parsed = typeof field.value === 'number' ? field.value : parseFloat(field.value);
120
- return isNaN(parsed) ? '' : parsed;
121
- }, [field.value]);
122
-
123
- // Use AffixedInput if we have prefix/suffix or for config args mode
124
- if (prefix || suffix || valueMode === 'json-string') {
125
- return (
126
- <AffixedInput
127
- type="number"
128
- value={numericValue}
129
- onChange={e => {
130
- const val = e.target.valueAsNumber;
131
- handleChange(isNaN(val) ? '' : val);
132
- }}
133
- disabled={disabled}
134
- min={min}
135
- max={max}
136
- step={step}
137
- prefix={prefix}
138
- suffix={suffix}
139
- className="bg-background"
140
- />
141
- );
142
- }
143
-
144
- return (
145
- <Input
146
- type="number"
147
- value={numericValue}
148
- onChange={e => {
149
- const val = e.target.valueAsNumber;
150
- handleChange(isNaN(val) ? '' : val);
151
- }}
152
- onBlur={field.onBlur}
153
- name={field.name}
154
- disabled={disabled}
155
- min={min}
156
- max={max}
157
- step={step}
158
- />
159
- );
160
- };
161
-
162
- /**
163
- * Boolean input wrapper
164
- */
165
- const BooleanFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
166
- // Parse the current value to boolean
167
- const currentValue = React.useMemo(() => {
168
- if (valueMode === 'json-string') {
169
- return field.value === 'true' || field.value === true;
170
- } else {
171
- return Boolean(field.value);
172
- }
173
- }, [field.value, valueMode]);
174
-
175
- // Simple change handler - directly call field.onChange
176
- const handleChange = React.useCallback(
177
- (newValue: boolean) => {
178
- if (valueMode === 'json-string') {
179
- field.onChange(newValue.toString());
180
- } else {
181
- field.onChange(newValue);
182
- }
183
- },
184
- [field.onChange, valueMode],
185
- );
186
-
187
- return <Switch checked={currentValue} onCheckedChange={handleChange} disabled={disabled} />;
188
- };
189
-
190
- /**
191
- * Currency input wrapper (uses MoneyInput)
192
- */
193
- const CurrencyFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
194
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
195
-
196
- return <MoneyInput value={transformedValue} onChange={handleChange} disabled={disabled} />;
197
- };
198
-
199
- /**
200
- * Date input wrapper
201
- */
202
- const DateFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
203
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
204
-
205
- return <DateTimeInput value={transformedValue} onChange={handleChange} disabled={disabled} />;
206
- };
207
-
208
- /**
209
- * Select input wrapper
210
- */
211
- const SelectFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
212
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
213
- const options = fieldDef.ui?.options || [];
214
-
215
- return (
216
- <Select value={transformedValue || ''} onValueChange={handleChange} disabled={disabled}>
217
- <SelectTrigger className="bg-background mb-0">
218
- <SelectValue placeholder="Select an option..." />
219
- </SelectTrigger>
220
- <SelectContent>
221
- {options.map(option => (
222
- <SelectItem key={option.value} value={option.value}>
223
- {typeof option.label === 'string'
224
- ? option.label
225
- : Array.isArray(option.label)
226
- ? option.label[0]?.value || option.value
227
- : option.value}
228
- </SelectItem>
229
- ))}
230
- </SelectContent>
231
- </Select>
232
- );
233
- };
234
-
235
- /**
236
- * Textarea input wrapper
237
- */
238
- const TextareaFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
239
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
240
-
241
- const handleTextareaChange = React.useCallback(
242
- (e: React.ChangeEvent<HTMLTextAreaElement>) => {
243
- handleChange(e.target.value);
244
- },
245
- [handleChange],
246
- );
247
-
248
- return (
249
- <Textarea
250
- value={transformedValue || ''}
251
- onChange={handleTextareaChange}
252
- disabled={disabled}
253
- spellCheck={fieldDef.ui?.spellcheck ?? true}
254
- placeholder="Enter text..."
255
- rows={4}
256
- className="bg-background"
257
- />
258
- );
259
- };
260
-
261
- /**
262
- * Product selector wrapper (uses DefaultRelationInput)
263
- */
264
- const ProductSelectorFormInput: React.FC<DirectFormComponentProps> = ({
265
- field,
266
- disabled,
267
- fieldDef,
268
- valueMode,
269
- }) => {
270
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
271
- const entityType = fieldDef.ui?.selectionMode === 'variant' ? 'ProductVariant' : 'Product';
272
-
273
- return (
274
- <DefaultRelationInput
275
- fieldDef={
276
- {
277
- entity: entityType,
278
- list: fieldDef.list,
279
- } as any
280
- }
281
- field={{
282
- ...field,
283
- value: transformedValue,
284
- onChange: handleChange,
285
- }}
286
- disabled={disabled}
287
- />
288
- );
289
- };
290
-
291
- /**
292
- * Customer group input wrapper
293
- */
294
- const CustomerGroupFormInput: React.FC<DirectFormComponentProps> = ({
295
- field,
296
- disabled,
297
- fieldDef,
298
- valueMode,
299
- }) => {
300
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
301
-
302
- return (
303
- <DefaultRelationInput
304
- fieldDef={
305
- {
306
- entity: 'CustomerGroup',
307
- list: fieldDef.list,
308
- } as any
309
- }
310
- field={{
311
- ...field,
312
- value: transformedValue,
313
- onChange: handleChange,
314
- }}
315
- disabled={disabled}
316
- />
317
- );
318
- };
319
-
320
- /**
321
- * Password input wrapper (uses regular Input with type="password")
322
- */
323
- const PasswordFormInput: React.FC<DirectFormComponentProps> = ({ field, disabled, fieldDef, valueMode }) => {
324
- const { transformedValue, handleChange } = useValueTransformation(field, fieldDef, valueMode);
325
-
326
- const handleInputChange = React.useCallback(
327
- (e: React.ChangeEvent<HTMLInputElement>) => {
328
- handleChange(e.target.value);
329
- },
330
- [handleChange],
331
- );
332
-
333
- return (
334
- <Input
335
- type="password"
336
- value={transformedValue || ''}
337
- onChange={handleInputChange}
338
- onBlur={field.onBlur}
339
- name={field.name}
340
- disabled={disabled}
341
- className={valueMode === 'json-string' ? 'bg-background' : undefined}
342
- />
343
- );
344
- };
345
-
346
- /**
347
- * Direct mapping from DefaultFormComponentId to React components
348
- * This eliminates the need for intermediate registry IDs
349
- */
350
- export const DIRECT_FORM_COMPONENT_MAP: Record<DefaultFormComponentId, React.FC<DirectFormComponentProps>> = {
351
- 'boolean-form-input': BooleanFormInput,
352
- 'currency-form-input': CurrencyFormInput,
353
- 'customer-group-form-input': CustomerGroupFormInput,
354
- 'date-form-input': DateFormInput,
355
- 'facet-value-form-input': ({ field, disabled }) => (
356
- <FacetValueInput value={field.value} onChange={field.onChange} readOnly={disabled} />
357
- ),
358
- 'json-editor-form-input': TextareaFormInput, // Fallback to textarea for now
359
- 'html-editor-form-input': ({ field, disabled }) => (
360
- <RichTextInput value={field.value} onChange={field.onChange} disabled={disabled} />
361
- ),
362
- 'number-form-input': NumberFormInput,
363
- 'password-form-input': PasswordFormInput,
364
- 'product-selector-form-input': ProductSelectorFormInput,
365
- 'relation-form-input': ProductSelectorFormInput, // Uses same relation logic
366
- 'rich-text-form-input': ({ field, disabled }) => (
367
- <RichTextInput value={field.value} onChange={field.onChange} disabled={disabled} />
368
- ),
369
- 'select-form-input': SelectFormInput,
370
- 'text-form-input': TextFormInput,
371
- 'textarea-form-input': TextareaFormInput,
372
- 'product-multi-form-input': ({ field, disabled, fieldDef }) => (
373
- <ProductMultiInput
374
- value={field.value}
375
- onChange={field.onChange}
376
- disabled={disabled}
377
- selectionMode={fieldDef.ui?.selectionMode as any}
378
- />
379
- ),
380
- 'combination-mode-form-input': ({ field, disabled }) => (
381
- <CombinationModeInput value={field.value} onChange={field.onChange} disabled={disabled} />
382
- ),
383
- 'struct-form-input': TextareaFormInput, // Fallback for now
384
- };
385
-
386
- /**
387
- * Get a direct form component by ID
388
- */
389
- export function getDirectFormComponent(
390
- componentId: DefaultFormComponentId,
391
- ): React.FC<DirectFormComponentProps> | undefined {
392
- return DIRECT_FORM_COMPONENT_MAP[componentId];
393
- }
@@ -1,118 +0,0 @@
1
- import { ConfigurableOperationDefFragment } from '@/vdb/graphql/fragments.js';
2
- import { CustomFieldConfig } from '@vendure/common/lib/generated-types';
3
- import { ConfigArgType, CustomFieldType, DefaultFormComponentId } from '@vendure/common/lib/shared-types';
4
-
5
- /**
6
- * Universal field definition that can represent both custom fields and configurable operation args
7
- */
8
- export interface UniversalFieldDefinition {
9
- name: string;
10
- type: CustomFieldType | 'ID'; // Extends CustomFieldType with ID for config args
11
- list?: boolean;
12
- readonly?: boolean;
13
- ui?: {
14
- component?: DefaultFormComponentId | string;
15
- options?: Array<{ value: string; label: string | Array<{ languageCode: string; value: string }> }>;
16
- min?: number;
17
- max?: number;
18
- step?: number;
19
- prefix?: string;
20
- suffix?: string;
21
- tab?: string;
22
- fullWidth?: boolean;
23
- spellcheck?: boolean;
24
- selectionMode?: string;
25
- };
26
- entity?: string; // for relations
27
- label?: string | Array<{ languageCode: string; value: string }>;
28
- description?: string | Array<{ languageCode: string; value: string }>;
29
- }
30
-
31
- /**
32
- * Convert a custom field config to universal field definition
33
- */
34
- export function customFieldToUniversal(fieldDef: CustomFieldConfig): UniversalFieldDefinition {
35
- const hasOptions = (fieldDef as any).options;
36
- const hasUi = fieldDef.ui;
37
- const hasNumericConfig =
38
- (fieldDef as any).min !== undefined ||
39
- (fieldDef as any).max !== undefined ||
40
- (fieldDef as any).step !== undefined;
41
-
42
- return {
43
- name: fieldDef.name,
44
- type: fieldDef.type as any,
45
- list: fieldDef.list ?? false,
46
- readonly: fieldDef.readonly ?? false,
47
- ui:
48
- hasUi || hasOptions || hasNumericConfig
49
- ? {
50
- component: fieldDef.ui?.component,
51
- options: (fieldDef as any).options,
52
- ...((fieldDef as any).min != null && {
53
- min: (fieldDef as any).min,
54
- }),
55
- ...((fieldDef as any).max != null && {
56
- max: (fieldDef as any).max,
57
- }),
58
- ...((fieldDef as any).step != null && {
59
- step: (fieldDef as any).step,
60
- }),
61
- tab: fieldDef.ui?.tab,
62
- fullWidth: fieldDef.ui?.fullWidth,
63
- }
64
- : undefined,
65
- entity: (fieldDef as any).entity,
66
- label: fieldDef.label,
67
- description: fieldDef.description,
68
- };
69
- }
70
-
71
- /**
72
- * Convert a configurable operation arg definition to universal field definition
73
- */
74
- export function configArgToUniversal(
75
- definition: ConfigurableOperationDefFragment['args'][number],
76
- ): UniversalFieldDefinition {
77
- const ui = definition.ui;
78
-
79
- return {
80
- name: definition.name,
81
- type: mapConfigArgType(definition.type as ConfigArgType),
82
- list: definition.list ?? false,
83
- readonly: false,
84
- ui: ui
85
- ? {
86
- component: ui.component,
87
- options: ui.options,
88
- min: ui.min ?? undefined,
89
- max: ui.max ?? undefined,
90
- step: ui.step ?? undefined,
91
- prefix: ui.prefix,
92
- suffix: ui.suffix,
93
- spellcheck: ui.spellcheck,
94
- selectionMode: ui.selectionMode,
95
- }
96
- : undefined,
97
- entity: getEntityFromUiComponent(ui?.component),
98
- label: definition.label,
99
- description: definition.description,
100
- };
101
- }
102
-
103
- function mapConfigArgType(configArgType: ConfigArgType): UniversalFieldDefinition['type'] {
104
- // All ConfigArgType values are compatible with our extended type
105
- return configArgType as UniversalFieldDefinition['type'];
106
- }
107
-
108
- function getEntityFromUiComponent(component?: string): string | undefined {
109
- switch (component) {
110
- case 'product-selector-form-input':
111
- case 'product-multi-form-input':
112
- return 'Product';
113
- case 'customer-group-form-input':
114
- return 'CustomerGroup';
115
- default:
116
- return undefined;
117
- }
118
- }
@@ -1,175 +0,0 @@
1
- import { DefaultFormComponentId } from '@vendure/common/lib/shared-types';
2
- import { ControllerRenderProps } from 'react-hook-form';
3
-
4
- import { CustomFieldListInput } from '@/vdb/components/data-input/custom-field-list-input.js';
5
- import { StructFormInput } from '@/vdb/components/data-input/struct-form-input.js';
6
- import {
7
- CustomFormComponent,
8
- CustomFormComponentInputProps,
9
- } from '@/vdb/framework/form-engine/custom-form-component.js';
10
-
11
- import { ConfigurableOperationListInput } from '../data-input/configurable-operation-list-input.js';
12
- import { FacetValueInput } from '../data-input/facet-value-input.js';
13
- import { getDirectFormComponent } from './direct-form-component-map.js';
14
- import { UniversalFieldDefinition } from './universal-field-definition.js';
15
- import { UniversalInputComponent } from './universal-input-components.js';
16
- import { ValueMode } from './value-transformers.js';
17
-
18
- export interface UniversalFormInputProps {
19
- fieldDef: UniversalFieldDefinition;
20
- field: ControllerRenderProps<any, any>;
21
- valueMode: ValueMode;
22
- disabled?: boolean;
23
- // Additional props for config args mode
24
- position?: number;
25
- // Additional props for custom fields mode
26
- control?: any;
27
- getTranslation?: (
28
- input: Array<{ languageCode: string; value: string }> | null | undefined,
29
- ) => string | undefined;
30
- }
31
-
32
- /**
33
- * Universal form input component that handles both custom fields and configurable operation args
34
- * Maintains full backward compatibility with existing APIs while eliminating duplication
35
- */
36
- export function UniversalFormInput({
37
- fieldDef,
38
- field,
39
- valueMode,
40
- disabled = false,
41
- position,
42
- control,
43
- getTranslation,
44
- }: Readonly<UniversalFormInputProps>) {
45
- const uiComponent = fieldDef.ui?.component;
46
- const isList = fieldDef.list ?? false;
47
- const isReadonly = disabled || fieldDef.readonly;
48
-
49
- // Handle special case: facet-value-form-input (only in config args)
50
- if (uiComponent === 'facet-value-form-input' && valueMode === 'json-string') {
51
- return <FacetValueInput value={field.value} onChange={field.onChange} readOnly={isReadonly} />;
52
- }
53
-
54
- // Handle custom form components (custom fields mode)
55
- if (uiComponent && valueMode === 'native') {
56
- const fieldProps: CustomFormComponentInputProps = {
57
- field: {
58
- ...field,
59
- disabled: isReadonly,
60
- },
61
- fieldState: {} as any, // This would be passed from the parent FormField
62
- formState: {} as any, // This would be passed from the parent FormField
63
- };
64
-
65
- return <CustomFormComponent fieldDef={fieldDef as any} fieldProps={fieldProps} />;
66
- }
67
-
68
- // Handle direct component mapping (config args mode)
69
- if (uiComponent && valueMode === 'json-string') {
70
- const DirectComponent = getDirectFormComponent(uiComponent as DefaultFormComponentId);
71
- if (DirectComponent) {
72
- return (
73
- <DirectComponent
74
- fieldDef={fieldDef}
75
- field={field}
76
- valueMode={valueMode}
77
- disabled={isReadonly}
78
- />
79
- );
80
- }
81
- }
82
-
83
- // Handle struct fields (custom fields mode only)
84
- if (fieldDef.type === 'struct' && valueMode === 'native') {
85
- if (isList) {
86
- return (
87
- <CustomFieldListInput
88
- field={field}
89
- disabled={isReadonly}
90
- renderInput={(index, inputField) => (
91
- <StructFormInput
92
- field={inputField}
93
- fieldDef={fieldDef as any}
94
- control={control}
95
- getTranslation={getTranslation}
96
- />
97
- )}
98
- defaultValue={{}}
99
- isFullWidth={true}
100
- />
101
- );
102
- }
103
-
104
- return (
105
- <StructFormInput
106
- field={field}
107
- fieldDef={fieldDef as any}
108
- control={control}
109
- getTranslation={getTranslation}
110
- />
111
- );
112
- }
113
-
114
- // Handle list fields
115
- if (isList) {
116
- if (valueMode === 'json-string') {
117
- // Use ConfigurableOperationListInput for config args
118
- return (
119
- <ConfigurableOperationListInput
120
- definition={fieldDef as any}
121
- value={field.value}
122
- onChange={field.onChange}
123
- readOnly={isReadonly}
124
- />
125
- );
126
- } else {
127
- // Use CustomFieldListInput for custom fields
128
- const getDefaultValue = () => {
129
- switch (fieldDef.type) {
130
- case 'string':
131
- case 'localeString':
132
- case 'localeText':
133
- return '';
134
- case 'int':
135
- case 'float':
136
- return 0;
137
- case 'boolean':
138
- return false;
139
- case 'datetime':
140
- return '';
141
- case 'relation':
142
- return '';
143
- default:
144
- return '';
145
- }
146
- };
147
-
148
- return (
149
- <CustomFieldListInput
150
- field={field}
151
- disabled={isReadonly}
152
- renderInput={(index, inputField) => (
153
- <UniversalInputComponent
154
- fieldDef={{ ...fieldDef, list: false }}
155
- field={inputField}
156
- valueMode={valueMode}
157
- disabled={isReadonly}
158
- />
159
- )}
160
- defaultValue={getDefaultValue()}
161
- />
162
- );
163
- }
164
- }
165
-
166
- // Fall back to consolidated input component
167
- return (
168
- <UniversalInputComponent
169
- fieldDef={fieldDef}
170
- field={field}
171
- valueMode={valueMode}
172
- disabled={isReadonly}
173
- />
174
- );
175
- }