@petrarca/sonnet-forms 0.4.1 → 0.5.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.
- package/dist/index.d.ts +64 -3
- package/dist/index.js +345 -159
- package/dist/index.js.map +1 -1
- package/dist/widget-options.d.ts +61 -0
- package/dist/widget-options.js +166 -0
- package/dist/widget-options.js.map +1 -0
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -8,39 +8,39 @@ function FormFieldWrapper({
|
|
|
8
8
|
error,
|
|
9
9
|
required,
|
|
10
10
|
compact,
|
|
11
|
+
labelPlacement = "top",
|
|
11
12
|
className,
|
|
12
13
|
children
|
|
13
14
|
}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
if (compact) return /* @__PURE__ */ jsx("div", { className, children });
|
|
16
|
+
const labelEl = !!label && /* @__PURE__ */ jsxs(
|
|
17
|
+
"label",
|
|
18
|
+
{
|
|
19
|
+
htmlFor: inputId,
|
|
20
|
+
className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
21
|
+
children: [
|
|
22
|
+
label,
|
|
23
|
+
required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
const descriptionEl = description && /* @__PURE__ */ jsx("p", { id: `${inputId}-description`, className: "text-sm text-muted-foreground", children: description });
|
|
28
|
+
const errorEl = error && /* @__PURE__ */ jsx("p", { id: `${inputId}-error`, className: "text-sm font-medium text-destructive", children: error });
|
|
29
|
+
if (labelPlacement === "start" && !!label) {
|
|
30
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), children: [
|
|
31
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 whitespace-nowrap", children: labelEl }),
|
|
32
|
+
/* @__PURE__ */ jsxs("div", { className: "w-full min-w-0 space-y-1", children: [
|
|
33
|
+
descriptionEl,
|
|
34
|
+
children,
|
|
35
|
+
errorEl
|
|
36
|
+
] })
|
|
37
|
+
] });
|
|
38
|
+
}
|
|
39
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
|
|
40
|
+
labelEl,
|
|
41
|
+
descriptionEl,
|
|
35
42
|
children,
|
|
36
|
-
|
|
37
|
-
"p",
|
|
38
|
-
{
|
|
39
|
-
id: `${inputId}-error`,
|
|
40
|
-
className: "text-sm font-medium text-destructive",
|
|
41
|
-
children: error
|
|
42
|
-
}
|
|
43
|
-
)
|
|
43
|
+
errorEl
|
|
44
44
|
] });
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -84,6 +84,7 @@ var FormInput = React.forwardRef(
|
|
|
84
84
|
rightSection,
|
|
85
85
|
rightSectionWidth,
|
|
86
86
|
compact,
|
|
87
|
+
labelPlacement,
|
|
87
88
|
...props
|
|
88
89
|
}, ref) => {
|
|
89
90
|
const generatedId = useId();
|
|
@@ -98,6 +99,7 @@ var FormInput = React.forwardRef(
|
|
|
98
99
|
error,
|
|
99
100
|
required,
|
|
100
101
|
compact,
|
|
102
|
+
labelPlacement,
|
|
101
103
|
className: wrapperClassName,
|
|
102
104
|
children: /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
103
105
|
/* @__PURE__ */ jsx2(
|
|
@@ -170,16 +172,21 @@ var FormTextarea = React2.forwardRef((allProps, ref) => {
|
|
|
170
172
|
error,
|
|
171
173
|
required,
|
|
172
174
|
wrapperClassName,
|
|
175
|
+
labelPlacement,
|
|
173
176
|
className,
|
|
174
177
|
id,
|
|
175
178
|
value,
|
|
176
179
|
onChange,
|
|
177
180
|
rightSection,
|
|
178
181
|
rightSectionWidth,
|
|
182
|
+
// Destructured out so they are not spread onto the DOM textarea below;
|
|
183
|
+
// these are component-level props, not valid HTML attributes.
|
|
184
|
+
autosize: autosizeProp,
|
|
185
|
+
minRows: minRowsProp,
|
|
179
186
|
...props
|
|
180
187
|
} = allProps;
|
|
181
|
-
const autosize =
|
|
182
|
-
const minRows =
|
|
188
|
+
const autosize = autosizeProp ?? false;
|
|
189
|
+
const minRows = minRowsProp ?? 3;
|
|
183
190
|
const generatedId = useId2();
|
|
184
191
|
const textareaId = id ?? generatedId;
|
|
185
192
|
const internalRef = useRef(null);
|
|
@@ -196,6 +203,7 @@ var FormTextarea = React2.forwardRef((allProps, ref) => {
|
|
|
196
203
|
description,
|
|
197
204
|
error,
|
|
198
205
|
required,
|
|
206
|
+
labelPlacement,
|
|
199
207
|
className: cn3("flex flex-col", wrapperClassName),
|
|
200
208
|
children: /* @__PURE__ */ jsxs3("div", { className: "relative flex-1 min-h-0", children: [
|
|
201
209
|
/* @__PURE__ */ jsx3(
|
|
@@ -246,7 +254,8 @@ var FormNumberInput = React3.forwardRef(
|
|
|
246
254
|
wrapperClassName,
|
|
247
255
|
className,
|
|
248
256
|
id,
|
|
249
|
-
name
|
|
257
|
+
name,
|
|
258
|
+
labelPlacement
|
|
250
259
|
}, ref) => {
|
|
251
260
|
const generatedId = useId3();
|
|
252
261
|
const inputId = id ?? generatedId;
|
|
@@ -269,6 +278,7 @@ var FormNumberInput = React3.forwardRef(
|
|
|
269
278
|
description,
|
|
270
279
|
error,
|
|
271
280
|
required,
|
|
281
|
+
labelPlacement,
|
|
272
282
|
className: wrapperClassName,
|
|
273
283
|
children: /* @__PURE__ */ jsx4(
|
|
274
284
|
Input2,
|
|
@@ -422,7 +432,7 @@ FormCheckbox.displayName = "FormCheckbox";
|
|
|
422
432
|
|
|
423
433
|
// src/FormSelect.tsx
|
|
424
434
|
import * as React5 from "react";
|
|
425
|
-
import { Check, ChevronsUpDown, X } from "lucide-react";
|
|
435
|
+
import { Check, ChevronsUpDown, Plus, X } from "lucide-react";
|
|
426
436
|
import { cn as cn6 } from "@petrarca/sonnet-core";
|
|
427
437
|
import { Button } from "@petrarca/sonnet-ui";
|
|
428
438
|
import {
|
|
@@ -434,12 +444,28 @@ import {
|
|
|
434
444
|
CommandList
|
|
435
445
|
} from "@petrarca/sonnet-ui";
|
|
436
446
|
import { Popover, PopoverContent, PopoverTrigger } from "@petrarca/sonnet-ui";
|
|
447
|
+
|
|
448
|
+
// src/formSelectUtils.ts
|
|
449
|
+
function canCreateFromSearch(allowCreate, searchQuery, options) {
|
|
450
|
+
const trimmed = searchQuery.trim();
|
|
451
|
+
if (!allowCreate || trimmed.length === 0) return false;
|
|
452
|
+
const lower = trimmed.toLowerCase();
|
|
453
|
+
return !options.some((o) => o.label.toLowerCase() === lower);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/FormSelect.tsx
|
|
437
457
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
438
|
-
function useSelectState(value, onChange, onBlur) {
|
|
458
|
+
function useSelectState(value, onChange, onBlur, onCreate, clearable) {
|
|
439
459
|
const [open, setOpen] = React5.useState(false);
|
|
440
460
|
const [searchQuery, setSearchQuery] = React5.useState("");
|
|
441
461
|
const handleSelect = (selectedValue) => {
|
|
442
|
-
|
|
462
|
+
const next = clearable && selectedValue === value ? null : selectedValue;
|
|
463
|
+
onChange?.(next);
|
|
464
|
+
setOpen(false);
|
|
465
|
+
setSearchQuery("");
|
|
466
|
+
};
|
|
467
|
+
const handleCreate = (label) => {
|
|
468
|
+
onCreate?.(label);
|
|
443
469
|
setOpen(false);
|
|
444
470
|
setSearchQuery("");
|
|
445
471
|
};
|
|
@@ -458,6 +484,7 @@ function useSelectState(value, onChange, onBlur) {
|
|
|
458
484
|
searchQuery,
|
|
459
485
|
setSearchQuery,
|
|
460
486
|
handleSelect,
|
|
487
|
+
handleCreate,
|
|
461
488
|
handleClear,
|
|
462
489
|
handleOpenChange
|
|
463
490
|
};
|
|
@@ -479,8 +506,11 @@ function SelectDropdown({
|
|
|
479
506
|
emptyMessage,
|
|
480
507
|
options,
|
|
481
508
|
value,
|
|
482
|
-
onSelect
|
|
509
|
+
onSelect,
|
|
510
|
+
allowCreate = false,
|
|
511
|
+
onCreate
|
|
483
512
|
}) {
|
|
513
|
+
const showCreate = canCreateFromSearch(allowCreate, searchQuery, options);
|
|
484
514
|
return /* @__PURE__ */ jsxs5(Command, { shouldFilter: false, children: [
|
|
485
515
|
/* @__PURE__ */ jsx6(
|
|
486
516
|
CommandInput,
|
|
@@ -491,29 +521,45 @@ function SelectDropdown({
|
|
|
491
521
|
}
|
|
492
522
|
),
|
|
493
523
|
/* @__PURE__ */ jsxs5(CommandList, { children: [
|
|
494
|
-
/* @__PURE__ */ jsx6(CommandEmpty, { children: loading ? "Loading..." : emptyMessage }),
|
|
495
|
-
/* @__PURE__ */
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
524
|
+
!showCreate && /* @__PURE__ */ jsx6(CommandEmpty, { children: loading ? "Loading..." : emptyMessage }),
|
|
525
|
+
/* @__PURE__ */ jsxs5(CommandGroup, { children: [
|
|
526
|
+
options.filter(
|
|
527
|
+
(option) => option.label.toLowerCase().includes(searchQuery.toLowerCase())
|
|
528
|
+
).map((option) => /* @__PURE__ */ jsxs5(
|
|
529
|
+
CommandItem,
|
|
530
|
+
{
|
|
531
|
+
value: option.value,
|
|
532
|
+
onSelect,
|
|
533
|
+
children: [
|
|
534
|
+
/* @__PURE__ */ jsx6(
|
|
535
|
+
Check,
|
|
536
|
+
{
|
|
537
|
+
className: cn6(
|
|
538
|
+
"mr-2 h-4 w-4",
|
|
539
|
+
value === option.value ? "opacity-100" : "opacity-0"
|
|
540
|
+
)
|
|
541
|
+
}
|
|
542
|
+
),
|
|
543
|
+
option.label
|
|
544
|
+
]
|
|
545
|
+
},
|
|
546
|
+
option.value
|
|
547
|
+
)),
|
|
548
|
+
showCreate && /* @__PURE__ */ jsxs5(
|
|
549
|
+
CommandItem,
|
|
550
|
+
{
|
|
551
|
+
value: `__create__${searchQuery}`,
|
|
552
|
+
onSelect: () => onCreate?.(searchQuery.trim()),
|
|
553
|
+
children: [
|
|
554
|
+
/* @__PURE__ */ jsx6(Plus, { className: "mr-2 h-4 w-4" }),
|
|
555
|
+
"Create \u201C",
|
|
556
|
+
searchQuery.trim(),
|
|
557
|
+
"\u201D"
|
|
558
|
+
]
|
|
559
|
+
},
|
|
560
|
+
"__create__"
|
|
561
|
+
)
|
|
562
|
+
] })
|
|
517
563
|
] })
|
|
518
564
|
] });
|
|
519
565
|
}
|
|
@@ -536,13 +582,16 @@ function FormSelect(props) {
|
|
|
536
582
|
required,
|
|
537
583
|
wrapperClassName,
|
|
538
584
|
compact,
|
|
585
|
+
labelPlacement,
|
|
539
586
|
options,
|
|
540
587
|
value,
|
|
541
588
|
onChange,
|
|
542
589
|
onBlur,
|
|
543
590
|
className,
|
|
544
591
|
id,
|
|
545
|
-
autoFocus
|
|
592
|
+
autoFocus,
|
|
593
|
+
allowCreate,
|
|
594
|
+
onCreate
|
|
546
595
|
} = props;
|
|
547
596
|
const {
|
|
548
597
|
placeholder,
|
|
@@ -560,9 +609,10 @@ function FormSelect(props) {
|
|
|
560
609
|
searchQuery,
|
|
561
610
|
setSearchQuery,
|
|
562
611
|
handleSelect,
|
|
612
|
+
handleCreate,
|
|
563
613
|
handleClear,
|
|
564
614
|
handleOpenChange
|
|
565
|
-
} = useSelectState(value, onChange, onBlur);
|
|
615
|
+
} = useSelectState(value, onChange, onBlur, onCreate, clearable);
|
|
566
616
|
const selectedOption = options.find((opt) => opt.value === value);
|
|
567
617
|
const isDisabled = disabled || readOnly || loading;
|
|
568
618
|
return /* @__PURE__ */ jsx6(
|
|
@@ -574,6 +624,7 @@ function FormSelect(props) {
|
|
|
574
624
|
error,
|
|
575
625
|
required,
|
|
576
626
|
compact,
|
|
627
|
+
labelPlacement,
|
|
577
628
|
className: wrapperClassName,
|
|
578
629
|
children: /* @__PURE__ */ jsxs5(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
579
630
|
/* @__PURE__ */ jsx6(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
|
|
@@ -627,7 +678,9 @@ function FormSelect(props) {
|
|
|
627
678
|
emptyMessage,
|
|
628
679
|
options,
|
|
629
680
|
value,
|
|
630
|
-
onSelect: handleSelect
|
|
681
|
+
onSelect: handleSelect,
|
|
682
|
+
allowCreate,
|
|
683
|
+
onCreate: handleCreate
|
|
631
684
|
}
|
|
632
685
|
)
|
|
633
686
|
}
|
|
@@ -661,13 +714,22 @@ function RemovableBadge({
|
|
|
661
714
|
}) {
|
|
662
715
|
return /* @__PURE__ */ jsxs6(Badge, { variant: "secondary", badgeColor, className: "mr-1", children: [
|
|
663
716
|
option.label,
|
|
664
|
-
!isDisabled &&
|
|
665
|
-
|
|
717
|
+
!isDisabled && // Rendered as a role="button" span, not a <button>: this badge lives
|
|
718
|
+
// inside the combobox trigger <button>, and a button cannot be nested
|
|
719
|
+
// in a button (invalid HTML / hydration error). The span keeps click +
|
|
720
|
+
// Enter/Space activation and a focus ring.
|
|
721
|
+
/* @__PURE__ */ jsx7(
|
|
722
|
+
"span",
|
|
666
723
|
{
|
|
667
|
-
|
|
668
|
-
|
|
724
|
+
role: "button",
|
|
725
|
+
tabIndex: 0,
|
|
726
|
+
"aria-label": `Remove ${option.label}`,
|
|
727
|
+
className: "ml-1 inline-flex cursor-pointer ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
669
728
|
onKeyDown: (e) => {
|
|
670
|
-
if (e.key === "Enter"
|
|
729
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
730
|
+
e.preventDefault();
|
|
731
|
+
onRemove(option.value, e);
|
|
732
|
+
}
|
|
671
733
|
},
|
|
672
734
|
onMouseDown: (e) => {
|
|
673
735
|
e.preventDefault();
|
|
@@ -852,6 +914,7 @@ function FormMultiSelect(props) {
|
|
|
852
914
|
error,
|
|
853
915
|
required,
|
|
854
916
|
wrapperClassName,
|
|
917
|
+
labelPlacement,
|
|
855
918
|
options,
|
|
856
919
|
onChange,
|
|
857
920
|
onBlur,
|
|
@@ -897,6 +960,7 @@ function FormMultiSelect(props) {
|
|
|
897
960
|
description,
|
|
898
961
|
error,
|
|
899
962
|
required,
|
|
963
|
+
labelPlacement,
|
|
900
964
|
className: wrapperClassName,
|
|
901
965
|
children: [
|
|
902
966
|
/* @__PURE__ */ jsxs6(Popover2, { open, onOpenChange: handleOpenChange, children: [
|
|
@@ -1063,6 +1127,7 @@ var FormTagsInput = React7.forwardRef((props, ref) => {
|
|
|
1063
1127
|
description,
|
|
1064
1128
|
error,
|
|
1065
1129
|
wrapperClassName,
|
|
1130
|
+
labelPlacement,
|
|
1066
1131
|
id,
|
|
1067
1132
|
name,
|
|
1068
1133
|
value = [],
|
|
@@ -1089,6 +1154,7 @@ var FormTagsInput = React7.forwardRef((props, ref) => {
|
|
|
1089
1154
|
description,
|
|
1090
1155
|
error,
|
|
1091
1156
|
required,
|
|
1157
|
+
labelPlacement,
|
|
1092
1158
|
className: wrapperClassName,
|
|
1093
1159
|
children: /* @__PURE__ */ jsxs7(
|
|
1094
1160
|
"div",
|
|
@@ -1292,6 +1358,7 @@ var FormQuantityInput = React8.forwardRef((allProps, ref) => {
|
|
|
1292
1358
|
readOnly,
|
|
1293
1359
|
autoFocus,
|
|
1294
1360
|
wrapperClassName,
|
|
1361
|
+
labelPlacement,
|
|
1295
1362
|
className,
|
|
1296
1363
|
name
|
|
1297
1364
|
} = allProps;
|
|
@@ -1361,6 +1428,7 @@ var FormQuantityInput = React8.forwardRef((allProps, ref) => {
|
|
|
1361
1428
|
description,
|
|
1362
1429
|
error: resolvedError,
|
|
1363
1430
|
required,
|
|
1431
|
+
labelPlacement,
|
|
1364
1432
|
className: wrapperClassName,
|
|
1365
1433
|
children: /* @__PURE__ */ jsxs8(
|
|
1366
1434
|
InputGroup,
|
|
@@ -1467,6 +1535,9 @@ var ExtraPropertiesCard_default = ExtraPropertiesCard;
|
|
|
1467
1535
|
// src/JsonSchemaFormRenderer.tsx
|
|
1468
1536
|
import React11, { useCallback as useCallback8, useMemo as useMemo7 } from "react";
|
|
1469
1537
|
import { SimpleStack } from "@petrarca/sonnet-ui";
|
|
1538
|
+
import {
|
|
1539
|
+
WIDTH_TOKEN_REMS
|
|
1540
|
+
} from "@petrarca/sonnet-core/schema";
|
|
1470
1541
|
import { isFieldRequired } from "@petrarca/sonnet-core/schema";
|
|
1471
1542
|
import { FORM_ITEM_ID_FIELD as FORM_ITEM_ID_FIELD2 } from "@petrarca/sonnet-core";
|
|
1472
1543
|
|
|
@@ -1510,6 +1581,7 @@ function TextInputWidget({
|
|
|
1510
1581
|
onChange: (event) => onChange(event.currentTarget.value),
|
|
1511
1582
|
onBlur: () => onBlur?.(id, value),
|
|
1512
1583
|
label,
|
|
1584
|
+
labelPlacement: options?.labelPlacement,
|
|
1513
1585
|
description,
|
|
1514
1586
|
placeholder,
|
|
1515
1587
|
required,
|
|
@@ -1557,6 +1629,7 @@ function TextareaWidget({
|
|
|
1557
1629
|
onChange: (event) => onChange(event.currentTarget.value),
|
|
1558
1630
|
onBlur: () => onBlur?.(id, value),
|
|
1559
1631
|
label,
|
|
1632
|
+
labelPlacement: options?.labelPlacement,
|
|
1560
1633
|
description,
|
|
1561
1634
|
placeholder,
|
|
1562
1635
|
required,
|
|
@@ -1604,6 +1677,7 @@ function NumberWidget({
|
|
|
1604
1677
|
onChange: (val) => onChange(val),
|
|
1605
1678
|
onBlur: () => onBlur?.(id, value),
|
|
1606
1679
|
label,
|
|
1680
|
+
labelPlacement: options.labelPlacement,
|
|
1607
1681
|
description,
|
|
1608
1682
|
placeholder,
|
|
1609
1683
|
required,
|
|
@@ -1735,6 +1809,7 @@ function SelectWidget(props) {
|
|
|
1735
1809
|
value: normalizeMultiValue(value),
|
|
1736
1810
|
onChange: (newValue) => onChange(newValue || []),
|
|
1737
1811
|
label,
|
|
1812
|
+
labelPlacement: widgetOptions.labelPlacement,
|
|
1738
1813
|
description,
|
|
1739
1814
|
placeholder,
|
|
1740
1815
|
required,
|
|
@@ -1758,6 +1833,7 @@ function SelectWidget(props) {
|
|
|
1758
1833
|
value: normalizeSingleValue(value),
|
|
1759
1834
|
onChange: (val) => onChange(val || ""),
|
|
1760
1835
|
label,
|
|
1836
|
+
labelPlacement: widgetOptions.labelPlacement,
|
|
1761
1837
|
description,
|
|
1762
1838
|
placeholder,
|
|
1763
1839
|
required,
|
|
@@ -1799,6 +1875,7 @@ function TagsInputWidget({
|
|
|
1799
1875
|
value: tags,
|
|
1800
1876
|
onChange: (newTags) => onChange(newTags),
|
|
1801
1877
|
label,
|
|
1878
|
+
labelPlacement: options?.labelPlacement,
|
|
1802
1879
|
description,
|
|
1803
1880
|
placeholder: placeholder ?? "Type and press Enter",
|
|
1804
1881
|
required,
|
|
@@ -1880,11 +1957,14 @@ function resolveMinChars(options) {
|
|
|
1880
1957
|
}
|
|
1881
1958
|
function buildRestConfig(options) {
|
|
1882
1959
|
if (!("endpoint" in options) || !options.endpoint) return null;
|
|
1960
|
+
const rawExtra = options.extraParams;
|
|
1961
|
+
const extraParams = rawExtra && typeof rawExtra === "object" ? rawExtra : void 0;
|
|
1883
1962
|
return {
|
|
1884
1963
|
endpoint: String(options.endpoint),
|
|
1885
1964
|
valueKey: String(options.valueKey ?? "id"),
|
|
1886
1965
|
labelKey: String(options.labelKey ?? "name"),
|
|
1887
|
-
searchKey: options.searchKey ? String(options.searchKey) : void 0
|
|
1966
|
+
searchKey: options.searchKey ? String(options.searchKey) : void 0,
|
|
1967
|
+
extraParams
|
|
1888
1968
|
};
|
|
1889
1969
|
}
|
|
1890
1970
|
function buildGraphqlConfig(options) {
|
|
@@ -1948,12 +2028,13 @@ function EntitySelectWidget({
|
|
|
1948
2028
|
mode: selectMode,
|
|
1949
2029
|
minChars: selectMinChars,
|
|
1950
2030
|
value: typeof value === "string" ? value : null,
|
|
1951
|
-
onChange: (v) => onChange(v ??
|
|
2031
|
+
onChange: (v) => onChange(v ?? null),
|
|
1952
2032
|
onBlur: () => onBlur?.(id, value),
|
|
1953
2033
|
label: label || false,
|
|
1954
2034
|
description,
|
|
1955
2035
|
placeholder,
|
|
1956
2036
|
required,
|
|
2037
|
+
clearable: !required,
|
|
1957
2038
|
disabled: disabled || readonly,
|
|
1958
2039
|
error: firstError(rawErrors)
|
|
1959
2040
|
}
|
|
@@ -2166,7 +2247,7 @@ function ObjectWidget({
|
|
|
2166
2247
|
|
|
2167
2248
|
// src/widgets/ArrayWidget.tsx
|
|
2168
2249
|
import { useCallback as useCallback5, useMemo as useMemo5 } from "react";
|
|
2169
|
-
import { Plus } from "lucide-react";
|
|
2250
|
+
import { Plus as Plus2 } from "lucide-react";
|
|
2170
2251
|
import { Button as Button5 } from "@petrarca/sonnet-ui";
|
|
2171
2252
|
import { applySchemaDefaults } from "@petrarca/sonnet-core/schema";
|
|
2172
2253
|
import { FORM_ITEM_ID_FIELD, generateId } from "@petrarca/sonnet-core";
|
|
@@ -2182,53 +2263,61 @@ function DefaultArrayItemTemplate({
|
|
|
2182
2263
|
canRemove,
|
|
2183
2264
|
orderable,
|
|
2184
2265
|
disabled,
|
|
2266
|
+
fid,
|
|
2185
2267
|
onRemove,
|
|
2186
2268
|
onMoveUp,
|
|
2187
2269
|
onMoveDown
|
|
2188
2270
|
}) {
|
|
2189
2271
|
const showControls = canRemove || orderable;
|
|
2190
|
-
return /* @__PURE__ */ jsxs14(
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2272
|
+
return /* @__PURE__ */ jsxs14(
|
|
2273
|
+
"div",
|
|
2274
|
+
{
|
|
2275
|
+
"data-fid": fid,
|
|
2276
|
+
className: "relative flex gap-2 rounded-md border border-border/50 bg-muted/20 p-3",
|
|
2277
|
+
children: [
|
|
2278
|
+
/* @__PURE__ */ jsx22("div", { className: "flex-1 min-w-0", children }),
|
|
2279
|
+
showControls && /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1 pt-0.5", children: [
|
|
2280
|
+
orderable && index > 0 && /* @__PURE__ */ jsx22(
|
|
2281
|
+
Button4,
|
|
2282
|
+
{
|
|
2283
|
+
type: "button",
|
|
2284
|
+
variant: "ghost",
|
|
2285
|
+
size: "compact",
|
|
2286
|
+
disabled,
|
|
2287
|
+
onClick: onMoveUp,
|
|
2288
|
+
title: "Move up",
|
|
2289
|
+
children: /* @__PURE__ */ jsx22(ChevronUp, { className: "h-3.5 w-3.5" })
|
|
2290
|
+
}
|
|
2291
|
+
),
|
|
2292
|
+
orderable && index < total - 1 && /* @__PURE__ */ jsx22(
|
|
2293
|
+
Button4,
|
|
2294
|
+
{
|
|
2295
|
+
type: "button",
|
|
2296
|
+
variant: "ghost",
|
|
2297
|
+
size: "compact",
|
|
2298
|
+
disabled,
|
|
2299
|
+
onClick: onMoveDown,
|
|
2300
|
+
title: "Move down",
|
|
2301
|
+
children: /* @__PURE__ */ jsx22(ChevronDown3, { className: "h-3.5 w-3.5" })
|
|
2302
|
+
}
|
|
2303
|
+
),
|
|
2304
|
+
canRemove && /* @__PURE__ */ jsx22(
|
|
2305
|
+
Button4,
|
|
2306
|
+
{
|
|
2307
|
+
type: "button",
|
|
2308
|
+
variant: "ghost",
|
|
2309
|
+
size: "compact",
|
|
2310
|
+
disabled,
|
|
2311
|
+
onClick: onRemove,
|
|
2312
|
+
title: "Remove item",
|
|
2313
|
+
className: "text-muted-foreground hover:text-destructive",
|
|
2314
|
+
children: /* @__PURE__ */ jsx22(Trash2, { className: "h-3.5 w-3.5" })
|
|
2315
|
+
}
|
|
2316
|
+
)
|
|
2317
|
+
] })
|
|
2318
|
+
]
|
|
2319
|
+
}
|
|
2320
|
+
);
|
|
2232
2321
|
}
|
|
2233
2322
|
|
|
2234
2323
|
// src/templates/gridUtils.ts
|
|
@@ -2494,6 +2583,7 @@ function ArrayWidgetInner({
|
|
|
2494
2583
|
disabled,
|
|
2495
2584
|
layout,
|
|
2496
2585
|
fieldCount,
|
|
2586
|
+
fid: item[FORM_ITEM_ID_FIELD],
|
|
2497
2587
|
onRemove: () => onRemove(index),
|
|
2498
2588
|
onMoveUp: index > 0 ? () => onMove(index, index - 1) : void 0,
|
|
2499
2589
|
onMoveDown: index < items.length - 1 ? () => onMove(index, index + 1) : void 0,
|
|
@@ -2528,7 +2618,7 @@ function ArrayWidgetInner({
|
|
|
2528
2618
|
disabled,
|
|
2529
2619
|
className: "gap-1.5",
|
|
2530
2620
|
children: [
|
|
2531
|
-
/* @__PURE__ */ jsx24(
|
|
2621
|
+
/* @__PURE__ */ jsx24(Plus2, { className: "h-3.5 w-3.5" }),
|
|
2532
2622
|
display.addLabel
|
|
2533
2623
|
]
|
|
2534
2624
|
}
|
|
@@ -2542,6 +2632,13 @@ import { jsx as jsx25 } from "react/jsx-runtime";
|
|
|
2542
2632
|
function isUnitOption(u) {
|
|
2543
2633
|
return typeof u === "object" && u !== null && typeof u.value === "string" && typeof u.label === "string";
|
|
2544
2634
|
}
|
|
2635
|
+
function parseQuantityOptions(options) {
|
|
2636
|
+
const units = Array.isArray(options.units) ? options.units.filter(isUnitOption) : [];
|
|
2637
|
+
const precision = typeof options.precision === "number" ? options.precision : void 0;
|
|
2638
|
+
const step = typeof options.step === "number" ? options.step : void 0;
|
|
2639
|
+
const unitPosition = options.unitPosition === "prefix" || options.unitPosition === "suffix" ? options.unitPosition : void 0;
|
|
2640
|
+
return { units, precision, step, unitPosition };
|
|
2641
|
+
}
|
|
2545
2642
|
function QuantityWidget({
|
|
2546
2643
|
id,
|
|
2547
2644
|
name,
|
|
@@ -2560,10 +2657,12 @@ function QuantityWidget({
|
|
|
2560
2657
|
options = {}
|
|
2561
2658
|
}) {
|
|
2562
2659
|
const error = firstError(rawErrors);
|
|
2563
|
-
const
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2660
|
+
const {
|
|
2661
|
+
units,
|
|
2662
|
+
precision,
|
|
2663
|
+
step,
|
|
2664
|
+
unitPosition: unitPositionOpt
|
|
2665
|
+
} = parseQuantityOptions(options);
|
|
2567
2666
|
const quantityValue = value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2568
2667
|
const valueRef = useRef4(value);
|
|
2569
2668
|
valueRef.current = value;
|
|
@@ -2585,6 +2684,7 @@ function QuantityWidget({
|
|
|
2585
2684
|
min: propertySchema.minimum,
|
|
2586
2685
|
max: propertySchema.maximum,
|
|
2587
2686
|
label,
|
|
2687
|
+
labelPlacement: options?.labelPlacement,
|
|
2588
2688
|
description,
|
|
2589
2689
|
error,
|
|
2590
2690
|
placeholder,
|
|
@@ -2741,6 +2841,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2741
2841
|
disabled,
|
|
2742
2842
|
layout,
|
|
2743
2843
|
fieldCount,
|
|
2844
|
+
fid,
|
|
2744
2845
|
onRemove,
|
|
2745
2846
|
onMoveUp,
|
|
2746
2847
|
onMoveDown
|
|
@@ -2757,6 +2858,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2757
2858
|
return /* @__PURE__ */ jsxs17(
|
|
2758
2859
|
"div",
|
|
2759
2860
|
{
|
|
2861
|
+
"data-fid": fid,
|
|
2760
2862
|
style: {
|
|
2761
2863
|
display: "grid",
|
|
2762
2864
|
gridTemplateColumns: template,
|
|
@@ -2820,6 +2922,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
2820
2922
|
};
|
|
2821
2923
|
|
|
2822
2924
|
// src/FormFieldRenderer.tsx
|
|
2925
|
+
import { warnLog } from "@petrarca/sonnet-core";
|
|
2823
2926
|
import {
|
|
2824
2927
|
getFieldTitle,
|
|
2825
2928
|
getFieldHelpText,
|
|
@@ -2837,6 +2940,29 @@ function resolveEffectiveLabel(label, uiTitle, globalLabelOption) {
|
|
|
2837
2940
|
if (uiTitle === false) return "";
|
|
2838
2941
|
return label;
|
|
2839
2942
|
}
|
|
2943
|
+
var FIELD_WIDTH_CLASS = {
|
|
2944
|
+
sm: "w-32",
|
|
2945
|
+
md: "w-48",
|
|
2946
|
+
lg: "w-64",
|
|
2947
|
+
full: "w-full"
|
|
2948
|
+
};
|
|
2949
|
+
function applyFieldWidth(widget, uiSchema, mergedOptions) {
|
|
2950
|
+
const candidate = uiSchema?.["x-ui-width"] ?? mergedOptions.width;
|
|
2951
|
+
if (candidate && !(candidate in FIELD_WIDTH_CLASS)) {
|
|
2952
|
+
warnLog(
|
|
2953
|
+
`[FormFieldRenderer] Unknown x-ui-width value: "${candidate}". Falling back to "full".`
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2956
|
+
const width = candidate && candidate in FIELD_WIDTH_CLASS ? candidate : "full";
|
|
2957
|
+
if (width === "full") return widget;
|
|
2958
|
+
return /* @__PURE__ */ jsx27("div", { className: FIELD_WIDTH_CLASS[width], children: widget });
|
|
2959
|
+
}
|
|
2960
|
+
function resolvePresentationText(property, uiSchema) {
|
|
2961
|
+
return {
|
|
2962
|
+
description: uiSchema?.["x-ui-description"] ?? getFieldHelpText(property),
|
|
2963
|
+
placeholder: uiSchema?.["x-ui-placeholder"] ?? getFieldPlaceholder(property)
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2840
2966
|
function resolveFieldFlags(fieldOptions, globalOptions, disabled, readOnly) {
|
|
2841
2967
|
return {
|
|
2842
2968
|
disabled: fieldOptions.disabled ?? globalOptions.disabled ?? disabled,
|
|
@@ -2856,11 +2982,14 @@ function FormFieldRenderer({
|
|
|
2856
2982
|
uiSchema,
|
|
2857
2983
|
registry,
|
|
2858
2984
|
globalOptions = {},
|
|
2859
|
-
formData
|
|
2985
|
+
formData,
|
|
2986
|
+
idPrefix = "field"
|
|
2860
2987
|
}) {
|
|
2861
2988
|
const label = getFieldTitle(fieldName, property);
|
|
2862
|
-
const description
|
|
2863
|
-
|
|
2989
|
+
const { description, placeholder } = resolvePresentationText(
|
|
2990
|
+
property,
|
|
2991
|
+
uiSchema
|
|
2992
|
+
);
|
|
2864
2993
|
const Widget = resolveWidget(property, uiSchema, registry);
|
|
2865
2994
|
const mergedOptions = mergeFieldOptions(globalOptions, property, uiSchema);
|
|
2866
2995
|
if (mergedOptions.hidden) return null;
|
|
@@ -2876,10 +3005,10 @@ function FormFieldRenderer({
|
|
|
2876
3005
|
uiSchema?.["x-ui-title"],
|
|
2877
3006
|
globalOptions.label
|
|
2878
3007
|
);
|
|
2879
|
-
|
|
3008
|
+
const widget = /* @__PURE__ */ jsx27(
|
|
2880
3009
|
Widget,
|
|
2881
3010
|
{
|
|
2882
|
-
id:
|
|
3011
|
+
id: `${idPrefix}-${fieldName}`,
|
|
2883
3012
|
name: fieldName,
|
|
2884
3013
|
value,
|
|
2885
3014
|
onChange,
|
|
@@ -2898,6 +3027,7 @@ function FormFieldRenderer({
|
|
|
2898
3027
|
formData
|
|
2899
3028
|
}
|
|
2900
3029
|
);
|
|
3030
|
+
return applyFieldWidth(widget, uiSchema, mergedOptions);
|
|
2901
3031
|
}
|
|
2902
3032
|
|
|
2903
3033
|
// src/hooks/useFormEngine.ts
|
|
@@ -2968,7 +3098,8 @@ function useFormEngine({
|
|
|
2968
3098
|
readOnly = false,
|
|
2969
3099
|
showActions = false,
|
|
2970
3100
|
deferChanges = false,
|
|
2971
|
-
deferStateUpdates = false
|
|
3101
|
+
deferStateUpdates = false,
|
|
3102
|
+
allowPristineSave = false
|
|
2972
3103
|
}) {
|
|
2973
3104
|
const refResolvedSchema = useMemo6(() => resolveRefs(schema), [schema]);
|
|
2974
3105
|
const [originalData, setOriginalData] = useState7(
|
|
@@ -3154,7 +3285,7 @@ function useFormEngine({
|
|
|
3154
3285
|
]
|
|
3155
3286
|
);
|
|
3156
3287
|
const handleSave = useCallback7(() => {
|
|
3157
|
-
if (!hasChanges) return;
|
|
3288
|
+
if (!hasChanges && !allowPristineSave) return;
|
|
3158
3289
|
const finalData = deferStateUpdates && Object.keys(pendingChanges).length > 0 ? { ...formData, ...pendingChanges } : formData;
|
|
3159
3290
|
const completeData = { ...nonSchemaData, ...finalData };
|
|
3160
3291
|
const validation = validateFormData(resolvedSchema, finalData);
|
|
@@ -3171,6 +3302,7 @@ function useFormEngine({
|
|
|
3171
3302
|
}
|
|
3172
3303
|
}, [
|
|
3173
3304
|
hasChanges,
|
|
3305
|
+
allowPristineSave,
|
|
3174
3306
|
resolvedSchema,
|
|
3175
3307
|
formData,
|
|
3176
3308
|
originalData,
|
|
@@ -3232,7 +3364,8 @@ function resolveFlagDefaults(props) {
|
|
|
3232
3364
|
deferStateUpdates: props.deferStateUpdates ?? false,
|
|
3233
3365
|
showCancel: props.showCancel ?? true,
|
|
3234
3366
|
showExtraProperties: props.showExtraProperties ?? true,
|
|
3235
|
-
displayContents: props.displayContents ?? false
|
|
3367
|
+
displayContents: props.displayContents ?? false,
|
|
3368
|
+
allowPristineSave: props.allowPristineSave ?? false
|
|
3236
3369
|
};
|
|
3237
3370
|
}
|
|
3238
3371
|
function resolveValueDefaults(props) {
|
|
@@ -3271,24 +3404,49 @@ function buildFieldMap(fields) {
|
|
|
3271
3404
|
}
|
|
3272
3405
|
return map;
|
|
3273
3406
|
}
|
|
3407
|
+
var COLUMN_TRACK = WIDTH_TOKEN_REMS;
|
|
3408
|
+
function resolveGridColumns(row, fieldCount) {
|
|
3409
|
+
if (row.columns) return row.columns;
|
|
3410
|
+
if (row.widths && row.widths.length > 0) {
|
|
3411
|
+
return Array.from(
|
|
3412
|
+
{ length: fieldCount },
|
|
3413
|
+
(_, i) => COLUMN_TRACK[row.widths?.[i] ?? "full"]
|
|
3414
|
+
).join(" ");
|
|
3415
|
+
}
|
|
3416
|
+
return `repeat(${fieldCount}, 1fr)`;
|
|
3417
|
+
}
|
|
3418
|
+
function wrapInGroup(group, key, content) {
|
|
3419
|
+
if (!group) return content;
|
|
3420
|
+
return /* @__PURE__ */ jsxs18(
|
|
3421
|
+
"fieldset",
|
|
3422
|
+
{
|
|
3423
|
+
className: "rounded-md border border-border/60 px-3 pb-3 pt-1",
|
|
3424
|
+
children: [
|
|
3425
|
+
/* @__PURE__ */ jsx28("legend", { className: "px-1 text-sm font-medium text-muted-foreground", children: group }),
|
|
3426
|
+
content
|
|
3427
|
+
]
|
|
3428
|
+
},
|
|
3429
|
+
`group:${key}`
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3274
3432
|
function buildRowElement(row, fieldMap, defaultRowGap) {
|
|
3275
3433
|
const rowFields = row.fields.map((name) => fieldMap.get(name)).filter((el) => el != null);
|
|
3276
3434
|
if (rowFields.length === 0) return null;
|
|
3277
|
-
const gridColumns = row.columns ?? `repeat(${rowFields.length}, 1fr)`;
|
|
3278
3435
|
const rowGap = row.gap ?? defaultRowGap;
|
|
3436
|
+
const rowKey = row.fields.join(",");
|
|
3437
|
+
const style = row.arrange === "flow" ? {
|
|
3438
|
+
display: "flex",
|
|
3439
|
+
flexWrap: "wrap",
|
|
3440
|
+
alignItems: "center",
|
|
3441
|
+
gap: `${rowGap}px`
|
|
3442
|
+
} : {
|
|
3443
|
+
display: "grid",
|
|
3444
|
+
gridTemplateColumns: resolveGridColumns(row, rowFields.length),
|
|
3445
|
+
gap: `${rowGap}px`
|
|
3446
|
+
};
|
|
3447
|
+
const inner = /* @__PURE__ */ jsx28("div", { style, children: rowFields }, `row:${rowKey}`);
|
|
3279
3448
|
return {
|
|
3280
|
-
element:
|
|
3281
|
-
"div",
|
|
3282
|
-
{
|
|
3283
|
-
style: {
|
|
3284
|
-
display: "grid",
|
|
3285
|
-
gridTemplateColumns: gridColumns,
|
|
3286
|
-
gap: `${rowGap}px`
|
|
3287
|
-
},
|
|
3288
|
-
children: rowFields
|
|
3289
|
-
},
|
|
3290
|
-
`row:${row.fields.join(",")}`
|
|
3291
|
-
),
|
|
3449
|
+
element: wrapInGroup(row.group, rowKey, inner),
|
|
3292
3450
|
placed: row.fields
|
|
3293
3451
|
};
|
|
3294
3452
|
}
|
|
@@ -3333,7 +3491,7 @@ function FormContainer({
|
|
|
3333
3491
|
if (displayContents) {
|
|
3334
3492
|
return /* @__PURE__ */ jsx28("div", { style: { display: "contents" }, children });
|
|
3335
3493
|
}
|
|
3336
|
-
return /* @__PURE__ */ jsxs18(SimpleStack, { gap:
|
|
3494
|
+
return /* @__PURE__ */ jsxs18(SimpleStack, { gap: "md", children: [
|
|
3337
3495
|
children,
|
|
3338
3496
|
showExtraProperties && /* @__PURE__ */ jsx28(ExtraPropertiesCard_default, { properties: formData, schema: resolvedSchema }),
|
|
3339
3497
|
showActions && /* @__PURE__ */ jsx28(
|
|
@@ -3362,7 +3520,8 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3362
3520
|
onUpdate,
|
|
3363
3521
|
onCancel,
|
|
3364
3522
|
templates,
|
|
3365
|
-
leadingActions
|
|
3523
|
+
leadingActions,
|
|
3524
|
+
idPrefix = "field"
|
|
3366
3525
|
} = props;
|
|
3367
3526
|
const {
|
|
3368
3527
|
disabled,
|
|
@@ -3372,7 +3531,8 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3372
3531
|
deferStateUpdates,
|
|
3373
3532
|
showCancel,
|
|
3374
3533
|
showExtraProperties,
|
|
3375
|
-
displayContents
|
|
3534
|
+
displayContents,
|
|
3535
|
+
allowPristineSave
|
|
3376
3536
|
} = resolveFlagDefaults(props);
|
|
3377
3537
|
const { saveLabel, cancelLabel, widgets, uiSchema } = resolveValueDefaults(props);
|
|
3378
3538
|
const { wrappedOnChange, wrappedOnBlur, wrappedOnUpdate } = useWrappedCallbacks(onChange, onBlur, onUpdate);
|
|
@@ -3399,9 +3559,17 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3399
3559
|
readOnly,
|
|
3400
3560
|
showActions,
|
|
3401
3561
|
deferChanges,
|
|
3402
|
-
deferStateUpdates
|
|
3562
|
+
deferStateUpdates,
|
|
3563
|
+
allowPristineSave
|
|
3403
3564
|
});
|
|
3404
|
-
const
|
|
3565
|
+
const layoutLabelPlacement = uiSchema["x-ui-layout"]?.labelPlacement;
|
|
3566
|
+
const globalOptions = useMemo7(
|
|
3567
|
+
() => ({
|
|
3568
|
+
...layoutLabelPlacement ? { labelPlacement: layoutLabelPlacement } : {},
|
|
3569
|
+
...uiSchema["x-ui-globalOptions"] || {}
|
|
3570
|
+
}),
|
|
3571
|
+
[layoutLabelPlacement, uiSchema]
|
|
3572
|
+
);
|
|
3405
3573
|
const resolvedTemplates = useMemo7(
|
|
3406
3574
|
() => ({ ...DEFAULT_TEMPLATES, ...templates }),
|
|
3407
3575
|
[templates]
|
|
@@ -3423,25 +3591,43 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3423
3591
|
[nestedRenderer, resolvedTemplates]
|
|
3424
3592
|
);
|
|
3425
3593
|
const needsBlurHandler = deferStateUpdates || !!onBlur;
|
|
3426
|
-
const fields =
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3594
|
+
const fields = useMemo7(
|
|
3595
|
+
() => orderedProperties.map(([fieldName, property]) => /* @__PURE__ */ jsx28(
|
|
3596
|
+
FormFieldRenderer,
|
|
3597
|
+
{
|
|
3598
|
+
fieldName,
|
|
3599
|
+
property,
|
|
3600
|
+
value: displayData[fieldName],
|
|
3601
|
+
onChange: (v) => handleFieldChange(fieldName, v),
|
|
3602
|
+
onBlur: needsBlurHandler ? () => handleFieldBlur(fieldName) : void 0,
|
|
3603
|
+
required: isFieldRequired(resolvedSchema, fieldName),
|
|
3604
|
+
disabled,
|
|
3605
|
+
readOnly,
|
|
3606
|
+
schema,
|
|
3607
|
+
uiSchema: uiSchema[fieldName],
|
|
3608
|
+
registry: widgets,
|
|
3609
|
+
globalOptions,
|
|
3610
|
+
formData: displayData,
|
|
3611
|
+
idPrefix
|
|
3612
|
+
},
|
|
3613
|
+
fieldName
|
|
3614
|
+
)),
|
|
3615
|
+
[
|
|
3616
|
+
orderedProperties,
|
|
3617
|
+
displayData,
|
|
3618
|
+
resolvedSchema,
|
|
3435
3619
|
disabled,
|
|
3436
3620
|
readOnly,
|
|
3437
3621
|
schema,
|
|
3438
|
-
uiSchema
|
|
3439
|
-
|
|
3622
|
+
uiSchema,
|
|
3623
|
+
widgets,
|
|
3440
3624
|
globalOptions,
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3625
|
+
idPrefix,
|
|
3626
|
+
needsBlurHandler,
|
|
3627
|
+
handleFieldChange,
|
|
3628
|
+
handleFieldBlur
|
|
3629
|
+
]
|
|
3630
|
+
);
|
|
3445
3631
|
const layoutConfig = uiSchema["x-ui-layout"];
|
|
3446
3632
|
const rowDefs = layoutConfig?.rows;
|
|
3447
3633
|
const defaultRowGap = layoutConfig?.gap ?? 24;
|
|
@@ -3460,7 +3646,7 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3460
3646
|
handleSave,
|
|
3461
3647
|
handleCancel,
|
|
3462
3648
|
disabled,
|
|
3463
|
-
hasChanges,
|
|
3649
|
+
hasChanges: hasChanges || allowPristineSave,
|
|
3464
3650
|
isValid,
|
|
3465
3651
|
saveLabel,
|
|
3466
3652
|
cancelLabel,
|