@aatulwork/customform-renderer 1.9.0 → 1.11.0

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.
@@ -0,0 +1,1213 @@
1
+ import { Controller, useWatch } from 'react-hook-form';
2
+ import { FormControl, FormLabel, TextField as TextField$1, FormControlLabel, Checkbox, RadioGroup, Radio, Box, Typography, Switch, useTheme, FormHelperText, alpha, useMediaQuery, CircularProgress, Tooltip, IconButton, Select, ListSubheader, MenuItem, Chip, InputAdornment, OutlinedInput } from '@mui/material';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { useMemo, useRef, useEffect, useCallback, useState } from 'react';
5
+ import RefreshIcon from '@mui/icons-material/Refresh';
6
+ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
7
+ import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
8
+ import { TimePicker } from '@mui/x-date-pickers/TimePicker';
9
+ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
10
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
11
+ import dayjs from 'dayjs';
12
+ import { CKEditor } from '@ckeditor/ckeditor5-react';
13
+ import CloseIcon from '@mui/icons-material/Close';
14
+ import CloudUploadIcon from '@mui/icons-material/CloudUpload';
15
+ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
16
+
17
+ // src/components/fields/TextField.tsx
18
+ var TextField = ({ field, control, defaultValue, rules, errors }) => {
19
+ return /* @__PURE__ */ jsx(
20
+ Controller,
21
+ {
22
+ name: field.name,
23
+ control,
24
+ defaultValue,
25
+ rules,
26
+ render: ({ field: formField }) => /* @__PURE__ */ jsxs(
27
+ FormControl,
28
+ {
29
+ fullWidth: true,
30
+ required: field.required,
31
+ error: !!errors[field.name],
32
+ children: [
33
+ /* @__PURE__ */ jsxs(FormLabel, { children: [
34
+ " ",
35
+ field.label
36
+ ] }),
37
+ /* @__PURE__ */ jsx(
38
+ TextField$1,
39
+ {
40
+ ...formField,
41
+ type: field.type === "number" ? "number" : field.type,
42
+ placeholder: field.placeholder || "Enter value",
43
+ fullWidth: true,
44
+ size: "small",
45
+ required: field.required,
46
+ error: !!errors[field.name],
47
+ helperText: errors[field.name]?.message,
48
+ inputProps: field.type === "number" ? {
49
+ min: field.validation?.min,
50
+ max: field.validation?.max
51
+ } : void 0
52
+ }
53
+ )
54
+ ]
55
+ }
56
+ )
57
+ },
58
+ field.name
59
+ );
60
+ };
61
+
62
+ // src/utils/fieldHelpers.ts
63
+ var formatFileSize = (bytes) => {
64
+ if (bytes === 0) return "0 Bytes";
65
+ const k = 1024;
66
+ const sizes = ["Bytes", "KB", "MB", "GB"];
67
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
68
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
69
+ };
70
+ var validateFile = (file, field) => {
71
+ if (field.validation?.allowedFileTypes && field.validation.allowedFileTypes.length > 0) {
72
+ const fileExtension = file.name.split(".").pop()?.toLowerCase();
73
+ const allowedTypes = field.validation.allowedFileTypes.map((t) => t.toLowerCase().replace(".", ""));
74
+ if (!fileExtension || !allowedTypes.includes(fileExtension)) {
75
+ return `File type not allowed. Allowed types: ${field.validation.allowedFileTypes.join(", ")}`;
76
+ }
77
+ }
78
+ if (field.validation?.maxFileSize) {
79
+ if (file.size > field.validation.maxFileSize) {
80
+ const maxSizeFormatted = formatFileSize(field.validation.maxFileSize);
81
+ return `File size exceeds maximum allowed size of ${maxSizeFormatted}`;
82
+ }
83
+ }
84
+ return true;
85
+ };
86
+ var buildFieldRules = (field) => {
87
+ const rules = {};
88
+ if (field.required) {
89
+ rules.required = `${field.label} is required`;
90
+ }
91
+ if (field.type === "file") {
92
+ rules.validate = (value) => {
93
+ if (field.required) {
94
+ if (field.allowMultiple) {
95
+ const files = Array.isArray(value) ? value : [];
96
+ if (files.length === 0) {
97
+ return `${field.label} is required`;
98
+ }
99
+ const hasValidFiles = files.some(
100
+ (file) => file && typeof file === "object" && ("fileName" in file || "fileUrl" in file)
101
+ );
102
+ if (!hasValidFiles) {
103
+ return `${field.label} is required`;
104
+ }
105
+ } else {
106
+ if (!value || typeof value === "object" && !("fileName" in value || "fileUrl" in value)) {
107
+ return `${field.label} is required`;
108
+ }
109
+ }
110
+ }
111
+ return true;
112
+ };
113
+ }
114
+ if ((field.type === "select" || field.type === "formReference" || field.type === "apiReference") && field.allowMultiple) {
115
+ rules.validate = (value) => {
116
+ const values = value;
117
+ if (!values || values.length === 0) {
118
+ if (field.required) {
119
+ return `${field.label} is required`;
120
+ }
121
+ return true;
122
+ }
123
+ return true;
124
+ };
125
+ }
126
+ return rules;
127
+ };
128
+ var normalizeOptions = (options) => {
129
+ if (!options) return [];
130
+ return options.map((opt) => {
131
+ if (typeof opt === "string") {
132
+ return { label: opt, value: opt };
133
+ }
134
+ return opt;
135
+ });
136
+ };
137
+ var SimpleSelect = ({
138
+ label,
139
+ value,
140
+ onChange,
141
+ options,
142
+ placeholder,
143
+ helperText,
144
+ fullWidth = true,
145
+ size = "small",
146
+ required = false,
147
+ error = false,
148
+ disabled = false,
149
+ multiple = false,
150
+ isLoading = false,
151
+ filterable = true,
152
+ filterPlaceholder = "Filter...",
153
+ refreshable = false,
154
+ onRefresh
155
+ }) => {
156
+ const [filterText, setFilterText] = useState("");
157
+ const handleChange = (event) => {
158
+ const val = event.target.value;
159
+ onChange(val);
160
+ };
161
+ const filteredOptions = filterable && filterText ? options.filter(
162
+ (opt) => String(opt.label).toLowerCase().includes(filterText.toLowerCase())
163
+ ) : options;
164
+ const handleFilterChange = (e) => {
165
+ setFilterText(e.target.value);
166
+ };
167
+ const handleClose = () => {
168
+ setFilterText("");
169
+ };
170
+ return /* @__PURE__ */ jsxs(FormControl, { fullWidth, size, required, error, disabled, children: [
171
+ /* @__PURE__ */ jsx(FormLabel, { required, error, sx: { mb: 0.5, display: "block" }, children: label }),
172
+ /* @__PURE__ */ jsxs(
173
+ Select,
174
+ {
175
+ value: value ?? (multiple ? [] : ""),
176
+ onChange: handleChange,
177
+ onClose: handleClose,
178
+ input: /* @__PURE__ */ jsx(OutlinedInput, {}),
179
+ MenuProps: {
180
+ autoFocus: false
181
+ },
182
+ multiple,
183
+ disabled: disabled || isLoading,
184
+ endAdornment: isLoading ? /* @__PURE__ */ jsx(CircularProgress, { size: 20 }) : refreshable && onRefresh ? /* @__PURE__ */ jsx(InputAdornment, { position: "end", sx: { mr: 2 }, children: /* @__PURE__ */ jsx(
185
+ IconButton,
186
+ {
187
+ size: "small",
188
+ onClick: (e) => {
189
+ e.stopPropagation();
190
+ onRefresh();
191
+ },
192
+ disabled,
193
+ "aria-label": "Refresh options",
194
+ children: /* @__PURE__ */ jsx(RefreshIcon, { fontSize: "small" })
195
+ }
196
+ ) }) : void 0,
197
+ renderValue: (selected) => {
198
+ if (multiple) {
199
+ const selectedValues = selected;
200
+ if (selectedValues.length === 0) {
201
+ return /* @__PURE__ */ jsx("em", { children: placeholder || "Select..." });
202
+ }
203
+ return /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexWrap: "wrap", gap: 0.5 }, children: selectedValues.map((val) => {
204
+ const option2 = options.find((opt) => opt.value === val);
205
+ return /* @__PURE__ */ jsx(
206
+ Chip,
207
+ {
208
+ label: option2?.label || val,
209
+ size: "small"
210
+ },
211
+ val
212
+ );
213
+ }) });
214
+ }
215
+ const option = options.find((opt) => opt.value === selected);
216
+ return option?.label || selected || /* @__PURE__ */ jsx("em", { children: placeholder || "Select..." });
217
+ },
218
+ children: [
219
+ filterable && /* @__PURE__ */ jsx(
220
+ ListSubheader,
221
+ {
222
+ sx: { py: 1 },
223
+ onMouseDown: (e) => e.stopPropagation(),
224
+ onClick: (e) => e.stopPropagation(),
225
+ children: /* @__PURE__ */ jsx(
226
+ TextField$1,
227
+ {
228
+ size: "small",
229
+ fullWidth: true,
230
+ placeholder: filterPlaceholder,
231
+ value: filterText,
232
+ onChange: handleFilterChange,
233
+ variant: "outlined",
234
+ sx: { mt: -0.5 },
235
+ autoFocus: true
236
+ }
237
+ )
238
+ }
239
+ ),
240
+ filteredOptions.map((option) => /* @__PURE__ */ jsx(MenuItem, { value: option.value, children: option.label }, option.value))
241
+ ]
242
+ }
243
+ ),
244
+ helperText && /* @__PURE__ */ jsx(FormHelperText, { children: helperText })
245
+ ] });
246
+ };
247
+ var SelectField = ({ field, control, defaultValue, rules, errors }) => {
248
+ const isMultiple = field.allowMultiple || false;
249
+ const normalizedOptions = normalizeOptions(field.options);
250
+ const selectOptions = useMemo(() => {
251
+ return normalizedOptions.map((opt) => ({
252
+ value: String(opt.value),
253
+ label: opt.label
254
+ }));
255
+ }, [normalizedOptions]);
256
+ return /* @__PURE__ */ jsx(
257
+ Controller,
258
+ {
259
+ name: field.name,
260
+ control,
261
+ defaultValue,
262
+ rules,
263
+ render: ({ field: formField }) => {
264
+ return /* @__PURE__ */ jsx(
265
+ SimpleSelect,
266
+ {
267
+ label: field.label,
268
+ value: formField.value,
269
+ onChange: (value) => {
270
+ formField.onChange(value);
271
+ },
272
+ options: selectOptions,
273
+ placeholder: field.placeholder || "Select...",
274
+ helperText: errors[field.name]?.message,
275
+ fullWidth: true,
276
+ size: "small",
277
+ required: field.required,
278
+ error: !!errors[field.name],
279
+ disabled: false,
280
+ multiple: isMultiple
281
+ }
282
+ );
283
+ }
284
+ },
285
+ field.name
286
+ );
287
+ };
288
+ var CheckboxField = ({ field, control, defaultValue, rules }) => {
289
+ return /* @__PURE__ */ jsx(
290
+ Controller,
291
+ {
292
+ name: field.name,
293
+ control,
294
+ defaultValue,
295
+ rules,
296
+ render: ({ field: formField }) => /* @__PURE__ */ jsx(
297
+ FormControlLabel,
298
+ {
299
+ control: /* @__PURE__ */ jsx(
300
+ Checkbox,
301
+ {
302
+ ...formField,
303
+ checked: formField.value || false,
304
+ size: "small"
305
+ }
306
+ ),
307
+ label: field.label
308
+ }
309
+ )
310
+ },
311
+ field.name
312
+ );
313
+ };
314
+ var RadioField = ({ field, control, defaultValue, rules, errors }) => {
315
+ const normalizedOptions = normalizeOptions(field.options);
316
+ return /* @__PURE__ */ jsx(
317
+ Controller,
318
+ {
319
+ name: field.name,
320
+ control,
321
+ defaultValue,
322
+ rules,
323
+ render: ({ field: formField }) => /* @__PURE__ */ jsxs(FormControl, { component: "fieldset", required: field.required, error: !!errors[field.name], children: [
324
+ /* @__PURE__ */ jsx(FormLabel, { required: field.required, error: !!errors[field.name], children: field.label }),
325
+ /* @__PURE__ */ jsx(RadioGroup, { ...formField, row: true, children: normalizedOptions.map((option, index) => /* @__PURE__ */ jsx(
326
+ FormControlLabel,
327
+ {
328
+ value: option.value,
329
+ control: /* @__PURE__ */ jsx(Radio, { size: "small" }),
330
+ label: option.label
331
+ },
332
+ index
333
+ )) })
334
+ ] })
335
+ },
336
+ field.name
337
+ );
338
+ };
339
+ var ToggleField = ({ field, control, defaultValue, rules, errors }) => {
340
+ return /* @__PURE__ */ jsx(
341
+ Controller,
342
+ {
343
+ name: field.name,
344
+ control,
345
+ defaultValue,
346
+ rules,
347
+ render: ({ field: formField }) => /* @__PURE__ */ jsx(
348
+ FormControlLabel,
349
+ {
350
+ control: /* @__PURE__ */ jsx(
351
+ Switch,
352
+ {
353
+ ...formField,
354
+ checked: formField.value || false,
355
+ size: "medium"
356
+ }
357
+ ),
358
+ label: /* @__PURE__ */ jsxs(Box, { children: [
359
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
360
+ field.label,
361
+ " ",
362
+ field.required && "*"
363
+ ] }),
364
+ errors[field.name] && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", sx: { display: "block", mt: 0.5 }, children: errors[field.name]?.message })
365
+ ] })
366
+ }
367
+ )
368
+ },
369
+ field.name
370
+ );
371
+ };
372
+ var ColorField = ({ field, control, defaultValue, rules, errors }) => {
373
+ return /* @__PURE__ */ jsx(
374
+ Controller,
375
+ {
376
+ name: field.name,
377
+ control,
378
+ defaultValue,
379
+ rules,
380
+ render: ({ field: formField }) => {
381
+ const inputId = `color-field-${field.name}`;
382
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, width: "100%" }, children: [
383
+ /* @__PURE__ */ jsx(
384
+ FormLabel,
385
+ {
386
+ component: "label",
387
+ htmlFor: inputId,
388
+ required: field.required,
389
+ error: !!errors[field.name],
390
+ children: field.label
391
+ }
392
+ ),
393
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", flex: 1, maxWidth: 200 }, children: [
394
+ /* @__PURE__ */ jsx(
395
+ Box,
396
+ {
397
+ component: "input",
398
+ type: "color",
399
+ id: inputId,
400
+ ref: formField.ref,
401
+ name: formField.name,
402
+ value: formField.value ?? "#000000",
403
+ onChange: formField.onChange,
404
+ onBlur: formField.onBlur,
405
+ sx: {
406
+ width: 40,
407
+ height: 40,
408
+ padding: 0,
409
+ cursor: "pointer",
410
+ backgroundColor: "transparent",
411
+ "&::-webkit-color-swatch-wrapper": { padding: 0 },
412
+ "&::-webkit-color-swatch": { border: "none", borderRadius: "4px" },
413
+ "&::-moz-color-swatch": { border: "none", borderRadius: "4px" }
414
+ }
415
+ }
416
+ ),
417
+ errors[field.name] && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", sx: { mt: 0.5 }, children: errors[field.name]?.message })
418
+ ] })
419
+ ] });
420
+ }
421
+ },
422
+ field.name
423
+ );
424
+ };
425
+ var textFieldSlotProps = (field, errors) => ({
426
+ fullWidth: true,
427
+ size: "small",
428
+ required: field.required,
429
+ error: !!errors[field.name],
430
+ helperText: errors[field.name]?.message
431
+ });
432
+ function resolveDatePickerMode(field) {
433
+ if (field.datePickerMode) return field.datePickerMode;
434
+ return field?.displayTime ? "datetime" : "date";
435
+ }
436
+ var DateTimePickerField = ({ field, control, defaultValue, rules, errors }) => {
437
+ const mode = resolveDatePickerMode(field);
438
+ return /* @__PURE__ */ jsx(
439
+ Controller,
440
+ {
441
+ name: field.name,
442
+ control,
443
+ defaultValue,
444
+ rules,
445
+ render: ({ field: formField }) => /* @__PURE__ */ jsxs(Fragment, { children: [
446
+ /* @__PURE__ */ jsx(
447
+ FormLabel,
448
+ {
449
+ required: field.required,
450
+ error: !!errors[field.name],
451
+ children: field.label
452
+ }
453
+ ),
454
+ /* @__PURE__ */ jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [
455
+ mode === "date" && /* @__PURE__ */ jsx(
456
+ DatePicker,
457
+ {
458
+ format: "DD-MM-YYYY",
459
+ value: formField.value ? dayjs(formField.value) : null,
460
+ onChange: (date) => formField.onChange(date?.toISOString() ?? null),
461
+ slotProps: { textField: textFieldSlotProps(field, errors) }
462
+ }
463
+ ),
464
+ mode === "datetime" && /* @__PURE__ */ jsx(
465
+ DateTimePicker,
466
+ {
467
+ format: "DD-MM-YYYY hh:mm A",
468
+ value: formField.value ? dayjs(formField.value) : null,
469
+ onChange: (date) => formField.onChange(date?.toISOString() ?? null),
470
+ slotProps: { textField: textFieldSlotProps(field, errors) }
471
+ }
472
+ ),
473
+ mode === "time" && /* @__PURE__ */ jsx(
474
+ TimePicker,
475
+ {
476
+ format: "hh:mm A",
477
+ value: formField.value ? dayjs(formField.value) : null,
478
+ onChange: (date) => formField.onChange(date?.toISOString() ?? null),
479
+ slotProps: { textField: textFieldSlotProps(field, errors) }
480
+ }
481
+ )
482
+ ] })
483
+ ] })
484
+ },
485
+ field.name
486
+ );
487
+ };
488
+ var useFormColors = (customColors) => {
489
+ const theme = useTheme();
490
+ return {
491
+ primary: customColors?.primary || theme.palette.primary.main,
492
+ secondary: customColors?.secondary || theme.palette.secondary.main,
493
+ error: customColors?.error || theme.palette.error.main,
494
+ success: customColors?.success || theme.palette.success.main,
495
+ warning: customColors?.warning || theme.palette.warning.main,
496
+ info: customColors?.info || theme.palette.info.main,
497
+ textPrimary: customColors?.textPrimary || theme.palette.text.primary,
498
+ textSecondary: customColors?.textSecondary || theme.palette.text.secondary,
499
+ divider: customColors?.divider || theme.palette.divider,
500
+ background: customColors?.background || theme.palette.background.default,
501
+ backgroundPaper: customColors?.backgroundPaper || theme.palette.background.paper
502
+ };
503
+ };
504
+ var defaultFileUploadService = {
505
+ uploadFiles: async () => {
506
+ throw new Error("File upload service not provided. Please provide a fileUpload service in FormServices.");
507
+ }
508
+ };
509
+ var defaultFormReferenceService = {
510
+ fetchOptions: async () => {
511
+ throw new Error("Form reference service not provided. Please provide a formReference service in FormServices.");
512
+ }
513
+ };
514
+ var defaultApiReferenceService = {
515
+ fetchOptions: async () => {
516
+ throw new Error("API reference service not provided. Please provide an apiReference service in FormServices.");
517
+ }
518
+ };
519
+
520
+ // src/utils/ckeditorLoader.ts
521
+ var loadCKEditor = (scriptPath = "/lib/ckeditor/ckeditor.js") => {
522
+ return new Promise((resolve, reject) => {
523
+ if (window.ClassicEditor) {
524
+ resolve();
525
+ return;
526
+ }
527
+ const existingScript = document.querySelector(`script[src="${scriptPath}"]`);
528
+ if (existingScript) {
529
+ existingScript.addEventListener("load", () => {
530
+ if (window.ClassicEditor) {
531
+ resolve();
532
+ } else {
533
+ reject(new Error("CKEditor script loaded but ClassicEditor not found on window"));
534
+ }
535
+ });
536
+ existingScript.addEventListener("error", () => {
537
+ reject(new Error("Failed to load CKEditor script"));
538
+ });
539
+ return;
540
+ }
541
+ const script = document.createElement("script");
542
+ script.src = scriptPath;
543
+ script.async = true;
544
+ script.onload = () => {
545
+ const checkInterval = setInterval(() => {
546
+ if (window.ClassicEditor) {
547
+ clearInterval(checkInterval);
548
+ resolve();
549
+ }
550
+ }, 100);
551
+ setTimeout(() => {
552
+ clearInterval(checkInterval);
553
+ if (!window.ClassicEditor) {
554
+ reject(
555
+ new Error(
556
+ "CKEditor script loaded but ClassicEditor was not found on window. Ensure you are using the package-provided build from lib/ckeditor/ckeditor.js (see CKEDITOR_SETUP.md)."
557
+ )
558
+ );
559
+ }
560
+ }, 1e4);
561
+ };
562
+ script.onerror = () => {
563
+ reject(
564
+ new Error(
565
+ `Failed to load CKEditor from ${scriptPath}. Ensure the file exists at that URL (e.g. copy from node_modules/@aatulwork/customform-renderer/lib/ckeditor/ckeditor.js to public/lib/ckeditor/ckeditor.js) or set services.ckEditorScriptPath to a working URL.`
566
+ )
567
+ );
568
+ };
569
+ document.head.appendChild(script);
570
+ });
571
+ };
572
+ var isCKEditorAvailable = () => {
573
+ return typeof window !== "undefined" && typeof window.ClassicEditor !== "undefined";
574
+ };
575
+ var waitForCKEditor = (timeout = 1e4) => {
576
+ return new Promise((resolve, reject) => {
577
+ if (isCKEditorAvailable()) {
578
+ resolve();
579
+ return;
580
+ }
581
+ const startTime = Date.now();
582
+ const checkInterval = setInterval(() => {
583
+ if (isCKEditorAvailable()) {
584
+ clearInterval(checkInterval);
585
+ resolve();
586
+ } else if (Date.now() - startTime > timeout) {
587
+ clearInterval(checkInterval);
588
+ reject(new Error("CKEditor not available after timeout"));
589
+ }
590
+ }, 100);
591
+ });
592
+ };
593
+
594
+ // src/hooks/useCKEditor.ts
595
+ var useCKEditor = (options = {}) => {
596
+ const {
597
+ scriptPath = "/lib/ckeditor/ckeditor.js",
598
+ autoLoad = true,
599
+ timeout = 1e4
600
+ } = options;
601
+ const [isReady, setIsReady] = useState(false);
602
+ const [isLoading, setIsLoading] = useState(false);
603
+ const [error, setError] = useState(null);
604
+ const load = async () => {
605
+ if (isCKEditorAvailable()) {
606
+ setIsReady(true);
607
+ setIsLoading(false);
608
+ return;
609
+ }
610
+ setIsLoading(true);
611
+ setError(null);
612
+ try {
613
+ await loadCKEditor(scriptPath);
614
+ setIsReady(true);
615
+ } catch (err) {
616
+ const error2 = err instanceof Error ? err : new Error("Failed to load CKEditor");
617
+ setError(error2);
618
+ setIsReady(false);
619
+ } finally {
620
+ setIsLoading(false);
621
+ }
622
+ };
623
+ useEffect(() => {
624
+ if (isCKEditorAvailable()) {
625
+ setIsReady(true);
626
+ return;
627
+ }
628
+ if (autoLoad) {
629
+ load();
630
+ } else {
631
+ waitForCKEditor(timeout).then(() => setIsReady(true)).catch((err) => setError(err instanceof Error ? err : new Error("CKEditor not available")));
632
+ }
633
+ }, [scriptPath, autoLoad, timeout]);
634
+ return {
635
+ isReady,
636
+ isLoading,
637
+ error,
638
+ load
639
+ };
640
+ };
641
+ var CKEditorField = ({ field, control, defaultValue, rules, errors, setValue, formSchema, services, colors }) => {
642
+ const theme = useTheme();
643
+ const formColors = useFormColors(colors);
644
+ const editorContainerRef = useRef(null);
645
+ const editorInstanceRef = useRef(null);
646
+ const isUserTypingRef = useRef(false);
647
+ const fileUploadService = services?.fileUpload || defaultFileUploadService;
648
+ const fileBaseUrl = services?.fileBaseUrl || "";
649
+ const licenseKey = services?.ckEditorLicenseKey || "GPL";
650
+ const ckEditorScriptPath = services?.ckEditorScriptPath || "/lib/ckeditor/ckeditor.js";
651
+ const { isReady: isCKEditorReady, isLoading: isCKEditorLoading, error: ckEditorError } = useCKEditor({
652
+ scriptPath: ckEditorScriptPath,
653
+ autoLoad: true
654
+ });
655
+ const watchedValue = useWatch({
656
+ control,
657
+ name: field.name,
658
+ defaultValue: defaultValue || ""
659
+ });
660
+ useEffect(() => {
661
+ if (editorInstanceRef.current && isCKEditorReady && !isUserTypingRef.current) {
662
+ const currentEditorData = editorInstanceRef.current.getData();
663
+ const formValue = watchedValue || "";
664
+ if (currentEditorData !== formValue) {
665
+ editorInstanceRef.current.setData(formValue);
666
+ }
667
+ }
668
+ isUserTypingRef.current = false;
669
+ }, [watchedValue, isCKEditorReady]);
670
+ const createCustomUploadAdapter = useCallback((loader) => {
671
+ return {
672
+ upload: async () => {
673
+ if (!formSchema?.name) {
674
+ throw new Error("Form schema name is required for image uploads");
675
+ }
676
+ try {
677
+ const file = await loader.file;
678
+ if (!file) {
679
+ throw new Error("No file provided");
680
+ }
681
+ if (!file.type.startsWith("image/")) {
682
+ throw new Error("Only image files are allowed");
683
+ }
684
+ const uploadedFiles = await fileUploadService.uploadFiles(
685
+ formSchema.name,
686
+ field.name,
687
+ [file]
688
+ );
689
+ if (uploadedFiles && uploadedFiles.length > 0 && uploadedFiles[0].fileUrl) {
690
+ const fileUrl = uploadedFiles[0].fileUrl;
691
+ const isFullUrl = fileUrl.startsWith("http://") || fileUrl.startsWith("https://");
692
+ const url = isFullUrl ? fileUrl : fileBaseUrl + fileUrl;
693
+ return {
694
+ default: url
695
+ };
696
+ }
697
+ throw new Error("Upload failed: No file URL returned");
698
+ } catch (error) {
699
+ console.error("Upload error:", error);
700
+ const errorMessage = error.response?.data?.message || error.message || "Upload failed";
701
+ throw new Error(errorMessage);
702
+ }
703
+ },
704
+ abort: () => {
705
+ console.log("Upload aborted");
706
+ }
707
+ };
708
+ }, [formSchema?.name, field.name, fileUploadService, fileBaseUrl]);
709
+ if (isCKEditorLoading) {
710
+ return /* @__PURE__ */ jsxs(Box, { children: [
711
+ /* @__PURE__ */ jsx(FormLabel, { required: field.required, error: !!errors[field.name], children: field.label }),
712
+ /* @__PURE__ */ jsx(Box, { sx: { p: 2, textAlign: "center", color: formColors.textSecondary }, children: "Loading editor..." })
713
+ ] });
714
+ }
715
+ if (ckEditorError || !isCKEditorReady) {
716
+ const message = ckEditorError?.message || `CKEditor failed to load. Script path: ${ckEditorScriptPath}. Copy ckeditor.js to public/lib/ckeditor/ or set services.ckEditorScriptPath. See CKEDITOR_SETUP.md.`;
717
+ return /* @__PURE__ */ jsxs(Box, { children: [
718
+ /* @__PURE__ */ jsx(FormLabel, { required: field.required, error: !!errors[field.name], children: field.label }),
719
+ /* @__PURE__ */ jsx(Box, { sx: { p: 2, border: "1px solid", borderColor: formColors.error, borderRadius: 1 }, children: /* @__PURE__ */ jsx(FormHelperText, { error: true, children: message }) })
720
+ ] });
721
+ }
722
+ return /* @__PURE__ */ jsx(
723
+ Controller,
724
+ {
725
+ name: field.name,
726
+ control,
727
+ defaultValue: defaultValue || "",
728
+ rules,
729
+ render: ({ field: formField }) => /* @__PURE__ */ jsxs(Box, { children: [
730
+ /* @__PURE__ */ jsx(
731
+ FormLabel,
732
+ {
733
+ required: field.required,
734
+ error: !!errors[field.name],
735
+ children: field.label
736
+ }
737
+ ),
738
+ /* @__PURE__ */ jsx(
739
+ Box,
740
+ {
741
+ ref: editorContainerRef,
742
+ sx: {
743
+ "& .ck-editor": {
744
+ borderRadius: "4px",
745
+ "& .ck-toolbar": {
746
+ width: "100%",
747
+ maxWidth: "100%",
748
+ overflow: "visible",
749
+ display: "flex",
750
+ flexWrap: "wrap",
751
+ "& .ck-toolbar__items": {
752
+ display: "flex",
753
+ flexWrap: "wrap !important",
754
+ width: "100%",
755
+ maxWidth: "100%",
756
+ "& > *": {
757
+ flexShrink: 0
758
+ }
759
+ }
760
+ },
761
+ "& .ck-editor__editable": {
762
+ minHeight: "100px"
763
+ },
764
+ "&:hover": {
765
+ borderColor: errors[field.name] ? formColors.error : theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.87)" : formColors.primary
766
+ },
767
+ "& .ck-focused": {
768
+ border: errors[field.name] ? `1px solid ${formColors.error} !important` : `1px solid ${theme.palette.mode === "dark" ? "rgba(255, 255, 255, 0.23)" : formColors.primary} !important`,
769
+ boxShadow: errors[field.name] ? `0 0 0 1px ${formColors.error}` : `0 0 0 1px ${alpha(formColors.primary, 0.5)} !important`
770
+ }
771
+ }
772
+ },
773
+ children: window.ClassicEditor && isCKEditorReady && /* @__PURE__ */ jsx(
774
+ CKEditor,
775
+ {
776
+ editor: window.ClassicEditor,
777
+ config: {
778
+ licenseKey,
779
+ initialData: formField.value || ""
780
+ },
781
+ data: formField.value || "",
782
+ onReady: (editor) => {
783
+ editorInstanceRef.current = editor;
784
+ if (formSchema?.name) {
785
+ try {
786
+ const fileRepository = editor.plugins.get("FileRepository");
787
+ if (fileRepository) {
788
+ fileRepository.createUploadAdapter = (loader) => {
789
+ return createCustomUploadAdapter(loader);
790
+ };
791
+ } else {
792
+ console.warn("FileRepository plugin not found");
793
+ }
794
+ } catch (error) {
795
+ console.error("Error setting up upload adapter:", error);
796
+ }
797
+ } else {
798
+ console.warn("Form schema name not available, upload adapter not set");
799
+ }
800
+ if (defaultValue && !formField.value) {
801
+ editor.setData(defaultValue);
802
+ setValue?.(field.name, defaultValue);
803
+ }
804
+ },
805
+ onChange: (_event, editor) => {
806
+ const data = editor.getData();
807
+ isUserTypingRef.current = true;
808
+ formField.onChange(data);
809
+ },
810
+ onBlur: () => {
811
+ formField.onBlur();
812
+ }
813
+ }
814
+ )
815
+ }
816
+ ),
817
+ errors[field.name] && /* @__PURE__ */ jsx(FormHelperText, { error: true, sx: { mt: 0.5, mx: 0 }, children: errors[field.name]?.message })
818
+ ] })
819
+ },
820
+ field.name
821
+ );
822
+ };
823
+ var FileField = ({
824
+ field,
825
+ control,
826
+ defaultValue,
827
+ rules,
828
+ errors,
829
+ formSchema,
830
+ uploadingFiles = {},
831
+ setUploadingFiles,
832
+ setError,
833
+ clearErrors,
834
+ services,
835
+ colors
836
+ }) => {
837
+ const theme = useTheme();
838
+ const isMobile = useMediaQuery(theme.breakpoints.down("md"));
839
+ const formColors = useFormColors(colors);
840
+ const acceptTypes = field.validation?.allowedFileTypes ? field.validation.allowedFileTypes.map((type) => `.${type.replace(".", "")}`).join(",") : void 0;
841
+ const isMultiple = field.allowMultiple || false;
842
+ const fileUploadService = services?.fileUpload || defaultFileUploadService;
843
+ return /* @__PURE__ */ jsx(
844
+ Controller,
845
+ {
846
+ name: field.name,
847
+ control,
848
+ defaultValue,
849
+ rules,
850
+ render: ({ field: formField }) => {
851
+ const isUploading = uploadingFiles[field.name] || false;
852
+ let files = [];
853
+ if (isMultiple) {
854
+ files = Array.isArray(formField.value) ? formField.value : [];
855
+ } else {
856
+ files = formField.value ? [formField.value] : [];
857
+ }
858
+ const hasFiles = files.length > 0;
859
+ return /* @__PURE__ */ jsxs(Box, { children: [
860
+ /* @__PURE__ */ jsx(
861
+ FormLabel,
862
+ {
863
+ required: field.required,
864
+ error: !!errors[field.name],
865
+ children: field.label
866
+ }
867
+ ),
868
+ /* @__PURE__ */ jsxs(
869
+ Box,
870
+ {
871
+ component: "label",
872
+ htmlFor: `file-input-${field.name}`,
873
+ sx: {
874
+ display: "flex",
875
+ flexDirection: "column",
876
+ alignItems: "center",
877
+ justifyContent: "center",
878
+ border: "1px dashed",
879
+ borderColor: errors[field.name] ? formColors.error : isUploading ? formColors.primary : hasFiles ? formColors.primary : formColors.divider,
880
+ p: 1,
881
+ cursor: isUploading ? "wait" : "pointer",
882
+ transition: "all 0.2s ease-in-out",
883
+ backgroundColor: isUploading || hasFiles ? "action.hover" : formColors.backgroundPaper,
884
+ opacity: isUploading ? 0.7 : 1,
885
+ pointerEvents: isUploading ? "none" : "auto",
886
+ "&:hover": {
887
+ borderColor: isUploading ? formColors.primary : formColors.primary,
888
+ backgroundColor: "action.hover"
889
+ }
890
+ },
891
+ children: [
892
+ /* @__PURE__ */ jsx(
893
+ "input",
894
+ {
895
+ id: `file-input-${field.name}`,
896
+ type: "file",
897
+ hidden: true,
898
+ multiple: isMultiple,
899
+ accept: acceptTypes,
900
+ onChange: async (e) => {
901
+ const fileList = e.target.files;
902
+ if (!fileList || fileList.length === 0) return;
903
+ const newFiles = Array.from(fileList);
904
+ const isUploading2 = uploadingFiles[field.name] || false;
905
+ if (isUploading2) {
906
+ e.target.value = "";
907
+ return;
908
+ }
909
+ for (const file of newFiles) {
910
+ const validationResult = validateFile(file, field);
911
+ if (validationResult !== true) {
912
+ setError?.(field.name, {
913
+ type: "manual",
914
+ message: validationResult
915
+ });
916
+ e.target.value = "";
917
+ return;
918
+ }
919
+ }
920
+ try {
921
+ setUploadingFiles?.((prev) => ({ ...prev, [field.name]: true }));
922
+ clearErrors?.(field.name);
923
+ if (!formSchema?.name) {
924
+ throw new Error("Form schema name is required for file uploads");
925
+ }
926
+ const uploadedFiles = await fileUploadService.uploadFiles(
927
+ formSchema.name,
928
+ field.name,
929
+ newFiles
930
+ );
931
+ if (isMultiple) {
932
+ const currentValue = formField.value;
933
+ const existingFiles = Array.isArray(currentValue) ? currentValue.filter((item) => item && typeof item === "object" && "fileName" in item) : [];
934
+ const allUploadedFiles = [...existingFiles, ...uploadedFiles];
935
+ formField.onChange(allUploadedFiles);
936
+ } else {
937
+ formField.onChange(uploadedFiles[0] || null);
938
+ }
939
+ } catch (error) {
940
+ console.error(`Failed to upload files for field ${field.name}:`, error);
941
+ const errorMessage = error.response?.data?.message || error.message || "Failed to upload files";
942
+ setError?.(field.name, {
943
+ type: "manual",
944
+ message: `Failed to upload files: ${errorMessage}`
945
+ });
946
+ } finally {
947
+ setUploadingFiles?.((prev) => ({ ...prev, [field.name]: false }));
948
+ e.target.value = "";
949
+ }
950
+ },
951
+ disabled: uploadingFiles[field.name] || false
952
+ }
953
+ ),
954
+ isUploading ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1, py: 3, width: "100%" }, children: [
955
+ /* @__PURE__ */ jsx(CircularProgress, { size: 40 }),
956
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 500, color: formColors.primary }, children: "Uploading files..." }),
957
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: formColors.textSecondary }, children: "Please wait while files are being uploaded" })
958
+ ] }) : !hasFiles ? /* @__PURE__ */ jsxs(Fragment, { children: [
959
+ /* @__PURE__ */ jsx(CloudUploadIcon, { sx: { fontSize: 40, color: formColors.textSecondary, mb: 1 } }),
960
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: formColors.textSecondary }, children: [
961
+ "Click to upload or drag and drop",
962
+ isMultiple && " (multiple files allowed)"
963
+ ] }),
964
+ field.validation?.allowedFileTypes && field.validation.allowedFileTypes.length > 0 && /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { mt: 0.5, color: formColors.textSecondary }, children: [
965
+ "Allowed: ",
966
+ field.validation.allowedFileTypes.join(", ")
967
+ ] }),
968
+ field.validation?.maxFileSize && /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { mt: 0.5, color: formColors.textSecondary }, children: [
969
+ "Max size: ",
970
+ formatFileSize(field.validation.maxFileSize),
971
+ " per file"
972
+ ] })
973
+ ] }) : /* @__PURE__ */ jsxs(Box, { sx: { width: "100%" }, children: [
974
+ isMultiple && /* @__PURE__ */ jsxs(Typography, { variant: "body2", sx: { fontWeight: 500, mb: 1.5 }, children: [
975
+ files.length,
976
+ " file",
977
+ files.length !== 1 ? "s" : "",
978
+ " uploaded"
979
+ ] }),
980
+ files.length === 1 && !isMultiple ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, width: "100%" }, children: [
981
+ /* @__PURE__ */ jsx(InsertDriveFileIcon, { sx: { fontSize: 40, color: formColors.primary } }),
982
+ /* @__PURE__ */ jsxs(Box, { sx: { flexGrow: 1, minWidth: 0 }, children: [
983
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", maxWidth: isMobile ? "200px" : "300px" }, children: files[0] instanceof File ? files[0].name : files[0].originalName || files[0].fileName }),
984
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: formColors.textSecondary }, children: formatFileSize(files[0].size || 0) })
985
+ ] }),
986
+ /* @__PURE__ */ jsx(Tooltip, { title: "Remove file", placement: "bottom", arrow: true, children: /* @__PURE__ */ jsx(
987
+ IconButton,
988
+ {
989
+ size: "small",
990
+ onClick: (e) => {
991
+ e.preventDefault();
992
+ e.stopPropagation();
993
+ formField.onChange(null);
994
+ const fileInput = document.getElementById(`file-input-${field.name}`);
995
+ if (fileInput) {
996
+ fileInput.value = "";
997
+ }
998
+ },
999
+ sx: { color: formColors.error },
1000
+ children: /* @__PURE__ */ jsx(CloseIcon, { fontSize: "small" })
1001
+ }
1002
+ ) })
1003
+ ] }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 1, width: "100%" }, children: files.map((file, index) => {
1004
+ const fileName = file instanceof File ? file.name : file.originalName || file.fileName;
1005
+ return /* @__PURE__ */ jsxs(
1006
+ Box,
1007
+ {
1008
+ sx: {
1009
+ display: "flex",
1010
+ alignItems: "center",
1011
+ gap: 1.5,
1012
+ p: 1,
1013
+ borderRadius: 1,
1014
+ backgroundColor: formColors.background,
1015
+ border: "1px solid",
1016
+ borderColor: formColors.divider
1017
+ },
1018
+ children: [
1019
+ /* @__PURE__ */ jsx(InsertDriveFileIcon, { sx: { fontSize: 32, color: formColors.primary } }),
1020
+ /* @__PURE__ */ jsxs(Box, { sx: { flexGrow: 1, minWidth: 0 }, children: [
1021
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis" }, children: fileName }),
1022
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: formColors.textSecondary }, children: formatFileSize(file.size || 0) })
1023
+ ] }),
1024
+ /* @__PURE__ */ jsx(Tooltip, { title: "Remove file", placement: "bottom", arrow: true, children: /* @__PURE__ */ jsx(
1025
+ IconButton,
1026
+ {
1027
+ size: "small",
1028
+ onClick: (e) => {
1029
+ e.preventDefault();
1030
+ e.stopPropagation();
1031
+ if (isMultiple) {
1032
+ const updatedFiles = files.filter((_, i) => i !== index);
1033
+ formField.onChange(updatedFiles.length > 0 ? updatedFiles : []);
1034
+ } else {
1035
+ formField.onChange(null);
1036
+ }
1037
+ },
1038
+ sx: { color: formColors.error },
1039
+ children: /* @__PURE__ */ jsx(CloseIcon, { fontSize: "small" })
1040
+ }
1041
+ ) })
1042
+ ]
1043
+ },
1044
+ `${fileName}-${index}`
1045
+ );
1046
+ }) })
1047
+ ] })
1048
+ ]
1049
+ }
1050
+ ),
1051
+ errors[field.name] && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { mt: 1, display: "block", color: formColors.error }, children: errors[field.name]?.message })
1052
+ ] });
1053
+ }
1054
+ },
1055
+ field.name
1056
+ );
1057
+ };
1058
+
1059
+ // src/utils/formHelpers.ts
1060
+ var getDefaultValue = (field) => {
1061
+ switch (field.type) {
1062
+ case "checkbox":
1063
+ case "toggle":
1064
+ return false;
1065
+ case "datepicker":
1066
+ return null;
1067
+ case "file":
1068
+ return field.allowMultiple ? [] : null;
1069
+ case "select":
1070
+ case "formReference":
1071
+ case "apiReference":
1072
+ return field.allowMultiple ? [] : "";
1073
+ case "text":
1074
+ case "email":
1075
+ case "number":
1076
+ case "radio":
1077
+ case "ckeditor":
1078
+ default:
1079
+ return "";
1080
+ }
1081
+ };
1082
+ var FormReferenceField = ({ field, control, defaultValue, rules, errors, services }) => {
1083
+ const isMultiple = field.allowMultiple || false;
1084
+ const [options, setOptions] = useState([]);
1085
+ const [isLoading, setIsLoading] = useState(false);
1086
+ const formReferenceService = services?.formReference || defaultFormReferenceService;
1087
+ const fetchOptions = useCallback(() => {
1088
+ if (!field.referenceFormName || !field.referenceFieldName) return;
1089
+ setIsLoading(true);
1090
+ formReferenceService.fetchOptions(field.referenceFormName, field.referenceFieldName).then((opts) => {
1091
+ setOptions(opts);
1092
+ }).catch((error) => {
1093
+ console.error("Failed to fetch form reference options:", error);
1094
+ setOptions([]);
1095
+ }).finally(() => {
1096
+ setIsLoading(false);
1097
+ });
1098
+ }, [field.referenceFormName, field.referenceFieldName, formReferenceService]);
1099
+ useEffect(() => {
1100
+ fetchOptions();
1101
+ }, [fetchOptions]);
1102
+ const selectOptions = useMemo(() => {
1103
+ return options.map((opt) => ({
1104
+ value: String(opt.value),
1105
+ label: opt.label
1106
+ }));
1107
+ }, [options]);
1108
+ const fieldDefaultValue = defaultValue ?? getDefaultValue(field);
1109
+ const fieldRules = rules ?? buildFieldRules(field);
1110
+ const isDisabled = !field.referenceFormName || !field.referenceFieldName;
1111
+ return /* @__PURE__ */ jsx(
1112
+ Controller,
1113
+ {
1114
+ name: field.name,
1115
+ control,
1116
+ defaultValue: fieldDefaultValue,
1117
+ rules: fieldRules,
1118
+ render: ({ field: formField }) => {
1119
+ return /* @__PURE__ */ jsx(
1120
+ SimpleSelect,
1121
+ {
1122
+ label: field.label,
1123
+ value: formField.value,
1124
+ onChange: (value) => {
1125
+ formField.onChange(value);
1126
+ },
1127
+ options: selectOptions,
1128
+ placeholder: field.placeholder || "Search and select...",
1129
+ helperText: errors[field.name]?.message,
1130
+ fullWidth: true,
1131
+ size: "small",
1132
+ required: field.required,
1133
+ error: !!errors[field.name],
1134
+ disabled: isDisabled || isLoading,
1135
+ multiple: isMultiple,
1136
+ isLoading,
1137
+ refreshable: true,
1138
+ onRefresh: fetchOptions
1139
+ }
1140
+ );
1141
+ }
1142
+ },
1143
+ field.name
1144
+ );
1145
+ };
1146
+ var ApiReferenceField = ({ field, control, defaultValue, rules, errors, services }) => {
1147
+ const isMultiple = field.allowMultiple || false;
1148
+ const [options, setOptions] = useState([]);
1149
+ const [isLoading, setIsLoading] = useState(false);
1150
+ const apiReferenceService = services?.apiReference || defaultApiReferenceService;
1151
+ const fetchOptions = useCallback(() => {
1152
+ if (!field.apiEndpoint || !field.apiLabelField) return;
1153
+ setIsLoading(true);
1154
+ apiReferenceService.fetchOptions(field.apiEndpoint, field.apiLabelField, field.apiValueField || "_id").then((opts) => {
1155
+ setOptions(opts);
1156
+ }).catch((error) => {
1157
+ console.error("Failed to fetch API reference options:", error);
1158
+ setOptions([]);
1159
+ }).finally(() => {
1160
+ setIsLoading(false);
1161
+ });
1162
+ }, [field.apiEndpoint, field.apiLabelField, field.apiValueField, apiReferenceService]);
1163
+ useEffect(() => {
1164
+ fetchOptions();
1165
+ }, [fetchOptions]);
1166
+ const selectOptions = useMemo(() => {
1167
+ return options.map((opt) => ({
1168
+ value: String(opt.value),
1169
+ label: opt.label
1170
+ }));
1171
+ }, [options]);
1172
+ const fieldDefaultValue = defaultValue ?? getDefaultValue(field);
1173
+ const fieldRules = rules ?? buildFieldRules(field);
1174
+ const isDisabled = !field.apiEndpoint || !field.apiLabelField;
1175
+ return /* @__PURE__ */ jsx(
1176
+ Controller,
1177
+ {
1178
+ name: field.name,
1179
+ control,
1180
+ defaultValue: fieldDefaultValue,
1181
+ rules: fieldRules,
1182
+ render: ({ field: formField }) => {
1183
+ return /* @__PURE__ */ jsx(
1184
+ SimpleSelect,
1185
+ {
1186
+ label: field.label,
1187
+ value: formField.value,
1188
+ onChange: (value) => {
1189
+ formField.onChange(value);
1190
+ },
1191
+ options: selectOptions,
1192
+ placeholder: field.placeholder || "Search and select...",
1193
+ helperText: errors[field.name]?.message,
1194
+ fullWidth: true,
1195
+ size: "small",
1196
+ required: field.required,
1197
+ error: !!errors[field.name],
1198
+ disabled: isDisabled || isLoading,
1199
+ multiple: isMultiple,
1200
+ isLoading,
1201
+ refreshable: true,
1202
+ onRefresh: fetchOptions
1203
+ }
1204
+ );
1205
+ }
1206
+ },
1207
+ field.name
1208
+ );
1209
+ };
1210
+
1211
+ export { ApiReferenceField, CKEditorField, CheckboxField, ColorField, DateTimePickerField, FileField, FormReferenceField, RadioField, SelectField, TextField, ToggleField };
1212
+ //# sourceMappingURL=fields.mjs.map
1213
+ //# sourceMappingURL=fields.mjs.map