@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.
- package/package.json +152 -152
- package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +2 -1
- package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +1 -0
- package/src/lib/components/data-input/affixed-input.tsx +19 -5
- package/src/lib/components/data-input/boolean-input.tsx +9 -0
- package/src/lib/components/data-input/checkbox-input.tsx +8 -0
- package/src/lib/components/data-input/combination-mode-input.tsx +11 -2
- package/src/lib/components/data-input/configurable-operation-list-input.tsx +26 -401
- package/src/lib/components/data-input/custom-field-list-input.tsx +18 -25
- package/src/lib/components/data-input/customer-group-input.tsx +7 -11
- package/src/lib/components/data-input/datetime-input.tsx +9 -13
- package/src/lib/components/data-input/default-relation-input.tsx +29 -9
- package/src/lib/components/data-input/facet-value-input.tsx +15 -13
- package/src/lib/components/data-input/index.ts +2 -2
- package/src/lib/components/data-input/money-input.tsx +27 -20
- package/src/lib/components/data-input/number-input.tsx +48 -0
- package/src/lib/components/data-input/password-input.tsx +16 -0
- package/src/lib/components/data-input/{product-multi-selector.tsx → product-multi-selector-input.tsx} +8 -15
- package/src/lib/components/data-input/relation-input.tsx +7 -6
- package/src/lib/components/data-input/rich-text-input.tsx +10 -13
- package/src/lib/components/data-input/select-with-options.tsx +29 -17
- package/src/lib/components/data-input/struct-form-input.tsx +54 -59
- package/src/lib/components/data-input/text-input.tsx +9 -0
- package/src/lib/components/data-input/textarea-input.tsx +16 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +3 -0
- package/src/lib/components/data-table/use-generated-columns.tsx +16 -5
- package/src/lib/components/shared/configurable-operation-arg-input.tsx +3 -10
- package/src/lib/components/shared/configurable-operation-input.tsx +1 -6
- package/src/lib/components/shared/configurable-operation-multi-selector.tsx +8 -5
- package/src/lib/components/shared/configurable-operation-selector.tsx +5 -5
- package/src/lib/components/shared/custom-fields-form.tsx +20 -49
- package/src/lib/components/shared/multi-select.tsx +1 -1
- package/src/lib/framework/component-registry/component-registry.tsx +9 -32
- package/src/lib/framework/component-registry/display-component.tsx +28 -0
- package/src/lib/framework/extension-api/display-component-extensions.tsx +0 -14
- package/src/lib/framework/extension-api/input-component-extensions.tsx +52 -34
- package/src/lib/framework/extension-api/logic/data-table.ts +4 -27
- package/src/lib/framework/extension-api/logic/form-components.ts +3 -2
- package/src/lib/framework/extension-api/types/detail-forms.ts +2 -38
- package/src/lib/framework/extension-api/types/form-components.ts +2 -4
- package/src/lib/framework/form-engine/custom-form-component-extensions.ts +0 -23
- package/src/lib/framework/form-engine/custom-form-component.tsx +8 -25
- package/src/lib/framework/form-engine/default-input-for-type.tsx +35 -0
- package/src/lib/framework/form-engine/form-control-adapter.tsx +192 -0
- package/src/lib/framework/form-engine/form-engine-types.ts +163 -0
- package/src/lib/framework/form-engine/form-schema-tools.ts +55 -71
- package/src/lib/framework/form-engine/overridden-form-component.tsx +2 -2
- package/src/lib/framework/form-engine/utils.ts +223 -0
- package/src/lib/{components/shared → framework/form-engine}/value-transformers.ts +9 -9
- package/src/lib/framework/registry/registry-types.ts +3 -5
- package/src/lib/graphql/graphql-env.d.ts +11 -7
- package/src/lib/index.ts +28 -1
- package/src/lib/providers/server-config.tsx +1 -0
- package/src/lib/components/shared/direct-form-component-map.tsx +0 -393
- package/src/lib/components/shared/universal-field-definition.ts +0 -118
- package/src/lib/components/shared/universal-form-input.tsx +0 -175
- package/src/lib/components/shared/universal-input-components.tsx +0 -291
- 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
|
-
}
|