@petrarca/sonnet-forms 0.5.0 → 0.7.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/README.md +69 -244
- package/dist/index.d.ts +232 -6
- package/dist/index.js +755 -126
- package/dist/index.js.map +1 -1
- package/dist/widget-options.js +72 -1
- package/dist/widget-options.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1483,16 +1483,18 @@ function FormActions({
|
|
|
1483
1483
|
disabled = false,
|
|
1484
1484
|
hasChanges,
|
|
1485
1485
|
isValid,
|
|
1486
|
+
saveWhenInvalid = "reveal",
|
|
1486
1487
|
saveLabel = "Save",
|
|
1487
1488
|
cancelLabel = "Cancel",
|
|
1488
1489
|
showCancel = true,
|
|
1489
1490
|
leadingActions
|
|
1490
1491
|
}) {
|
|
1492
|
+
const saveDisabled = disabled || !hasChanges || saveWhenInvalid === "disable" && !isValid;
|
|
1491
1493
|
return /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between mt-4", children: [
|
|
1492
1494
|
/* @__PURE__ */ jsx10("div", { className: "flex items-center gap-2", children: leadingActions }),
|
|
1493
1495
|
/* @__PURE__ */ jsxs9(SimpleGroup, { justify: "flex-end", children: [
|
|
1494
1496
|
showCancel && /* @__PURE__ */ jsx10(Button3, { variant: "outline", onClick: onCancel, disabled, children: cancelLabel }),
|
|
1495
|
-
/* @__PURE__ */ jsx10(Button3, { onClick: onSave, disabled:
|
|
1497
|
+
/* @__PURE__ */ jsx10(Button3, { onClick: onSave, disabled: saveDisabled, children: saveLabel })
|
|
1496
1498
|
] })
|
|
1497
1499
|
] });
|
|
1498
1500
|
}
|
|
@@ -1533,12 +1535,13 @@ function ExtraPropertiesCard({ properties, schema }) {
|
|
|
1533
1535
|
var ExtraPropertiesCard_default = ExtraPropertiesCard;
|
|
1534
1536
|
|
|
1535
1537
|
// src/JsonSchemaFormRenderer.tsx
|
|
1536
|
-
import
|
|
1538
|
+
import React13, { useCallback as useCallback8, useContext as useContext2, useMemo as useMemo8 } from "react";
|
|
1537
1539
|
import { SimpleStack } from "@petrarca/sonnet-ui";
|
|
1540
|
+
import { AlertCircle as AlertCircle2 } from "lucide-react";
|
|
1538
1541
|
import {
|
|
1539
1542
|
WIDTH_TOKEN_REMS
|
|
1540
1543
|
} from "@petrarca/sonnet-core/schema";
|
|
1541
|
-
import { isFieldRequired } from "@petrarca/sonnet-core/schema";
|
|
1544
|
+
import { isFieldRequired, evaluateUiRule } from "@petrarca/sonnet-core/schema";
|
|
1542
1545
|
import { FORM_ITEM_ID_FIELD as FORM_ITEM_ID_FIELD2 } from "@petrarca/sonnet-core";
|
|
1543
1546
|
|
|
1544
1547
|
// src/widgets/types.ts
|
|
@@ -1794,6 +1797,7 @@ function SelectWidget(props) {
|
|
|
1794
1797
|
const enumNames = widgetOptions.enumNames || {};
|
|
1795
1798
|
const clearable = widgetOptions.clearable !== false && !required;
|
|
1796
1799
|
const isMultiple = widgetOptions.multiple ?? propertySchema.type === "array";
|
|
1800
|
+
const creatable = !!widgetOptions.creatable;
|
|
1797
1801
|
const selectOptions = buildSelectOptions(propertySchema, enumNames);
|
|
1798
1802
|
const error = firstError(rawErrors);
|
|
1799
1803
|
const compact = widgetOptions?.compact;
|
|
@@ -1844,7 +1848,9 @@ function SelectWidget(props) {
|
|
|
1844
1848
|
autoFocus: autofocus,
|
|
1845
1849
|
onBlur: handleBlur,
|
|
1846
1850
|
className: triggerClassName,
|
|
1847
|
-
compact
|
|
1851
|
+
compact,
|
|
1852
|
+
allowCreate: creatable,
|
|
1853
|
+
onCreate: (label2) => onChange(label2)
|
|
1848
1854
|
}
|
|
1849
1855
|
);
|
|
1850
1856
|
}
|
|
@@ -2190,9 +2196,11 @@ function resolveLayout(uiSchema, propertySchema) {
|
|
|
2190
2196
|
return uiSchema?.["x-ui-layout"] ?? propertySchema["x-ui-layout"];
|
|
2191
2197
|
}
|
|
2192
2198
|
function ObjectWidget({
|
|
2199
|
+
id,
|
|
2193
2200
|
propertySchema,
|
|
2194
2201
|
value,
|
|
2195
2202
|
onChange,
|
|
2203
|
+
onBlur,
|
|
2196
2204
|
registry,
|
|
2197
2205
|
uiSchema,
|
|
2198
2206
|
disabled,
|
|
@@ -2229,7 +2237,9 @@ function ObjectWidget({
|
|
|
2229
2237
|
readOnly: readonly,
|
|
2230
2238
|
widgets: registry,
|
|
2231
2239
|
uiSchema: effectiveUiSchema,
|
|
2232
|
-
displayContents
|
|
2240
|
+
displayContents,
|
|
2241
|
+
idPrefix: id,
|
|
2242
|
+
onBlur: onBlur ? () => onBlur(id, value) : void 0
|
|
2233
2243
|
}
|
|
2234
2244
|
);
|
|
2235
2245
|
if (displayContents) return /* @__PURE__ */ jsx21(Fragment3, { children: content });
|
|
@@ -2696,9 +2706,310 @@ function QuantityWidget({
|
|
|
2696
2706
|
);
|
|
2697
2707
|
}
|
|
2698
2708
|
|
|
2709
|
+
// src/widgets/DateWidget.tsx
|
|
2710
|
+
import { DateInput } from "@petrarca/sonnet-ui";
|
|
2711
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2712
|
+
function resolvePrecision(value) {
|
|
2713
|
+
return value === "year" || value === "month" ? value : "day";
|
|
2714
|
+
}
|
|
2715
|
+
function DateWidget({
|
|
2716
|
+
id,
|
|
2717
|
+
value,
|
|
2718
|
+
onChange,
|
|
2719
|
+
onBlur,
|
|
2720
|
+
required,
|
|
2721
|
+
disabled,
|
|
2722
|
+
rawErrors,
|
|
2723
|
+
label,
|
|
2724
|
+
description,
|
|
2725
|
+
placeholder,
|
|
2726
|
+
options
|
|
2727
|
+
}) {
|
|
2728
|
+
const error = firstError(rawErrors);
|
|
2729
|
+
const precision = resolvePrecision(options?.precision);
|
|
2730
|
+
const locale = typeof options?.locale === "string" ? options.locale : void 0;
|
|
2731
|
+
return /* @__PURE__ */ jsx26(
|
|
2732
|
+
FormFieldWrapper,
|
|
2733
|
+
{
|
|
2734
|
+
inputId: id,
|
|
2735
|
+
label,
|
|
2736
|
+
description,
|
|
2737
|
+
required,
|
|
2738
|
+
labelPlacement: options?.labelPlacement,
|
|
2739
|
+
children: /* @__PURE__ */ jsx26(
|
|
2740
|
+
DateInput,
|
|
2741
|
+
{
|
|
2742
|
+
id,
|
|
2743
|
+
value: typeof value === "string" ? value : "",
|
|
2744
|
+
onChange,
|
|
2745
|
+
onBlur: () => onBlur?.(id, value),
|
|
2746
|
+
precision,
|
|
2747
|
+
locale,
|
|
2748
|
+
placeholder,
|
|
2749
|
+
required,
|
|
2750
|
+
disabled,
|
|
2751
|
+
error
|
|
2752
|
+
}
|
|
2753
|
+
)
|
|
2754
|
+
}
|
|
2755
|
+
);
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
// src/widgets/TimeWidget.tsx
|
|
2759
|
+
import { TimeInput } from "@petrarca/sonnet-ui";
|
|
2760
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2761
|
+
function resolveGranularity(value) {
|
|
2762
|
+
return value === "second" ? "second" : "minute";
|
|
2763
|
+
}
|
|
2764
|
+
function TimeWidget({
|
|
2765
|
+
id,
|
|
2766
|
+
value,
|
|
2767
|
+
onChange,
|
|
2768
|
+
onBlur,
|
|
2769
|
+
required,
|
|
2770
|
+
disabled,
|
|
2771
|
+
rawErrors,
|
|
2772
|
+
label,
|
|
2773
|
+
description,
|
|
2774
|
+
placeholder,
|
|
2775
|
+
options
|
|
2776
|
+
}) {
|
|
2777
|
+
const error = firstError(rawErrors);
|
|
2778
|
+
const granularity = resolveGranularity(options?.granularity);
|
|
2779
|
+
return /* @__PURE__ */ jsx27(
|
|
2780
|
+
FormFieldWrapper,
|
|
2781
|
+
{
|
|
2782
|
+
inputId: id,
|
|
2783
|
+
label,
|
|
2784
|
+
description,
|
|
2785
|
+
required,
|
|
2786
|
+
labelPlacement: options?.labelPlacement,
|
|
2787
|
+
children: /* @__PURE__ */ jsx27(
|
|
2788
|
+
TimeInput,
|
|
2789
|
+
{
|
|
2790
|
+
id,
|
|
2791
|
+
value: typeof value === "string" ? value : "",
|
|
2792
|
+
onChange,
|
|
2793
|
+
onBlur: () => onBlur?.(id, value),
|
|
2794
|
+
granularity,
|
|
2795
|
+
placeholder,
|
|
2796
|
+
required,
|
|
2797
|
+
disabled,
|
|
2798
|
+
error
|
|
2799
|
+
}
|
|
2800
|
+
)
|
|
2801
|
+
}
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
// src/widgets/DateTimeWidget.tsx
|
|
2806
|
+
import { DateTimeInput } from "@petrarca/sonnet-ui";
|
|
2807
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2808
|
+
function resolveGranularity2(value) {
|
|
2809
|
+
return value === "second" ? "second" : "minute";
|
|
2810
|
+
}
|
|
2811
|
+
function DateTimeWidget({
|
|
2812
|
+
id,
|
|
2813
|
+
value,
|
|
2814
|
+
onChange,
|
|
2815
|
+
onBlur,
|
|
2816
|
+
required,
|
|
2817
|
+
disabled,
|
|
2818
|
+
rawErrors,
|
|
2819
|
+
label,
|
|
2820
|
+
description,
|
|
2821
|
+
options
|
|
2822
|
+
}) {
|
|
2823
|
+
const error = firstError(rawErrors);
|
|
2824
|
+
const granularity = resolveGranularity2(options?.granularity);
|
|
2825
|
+
const timeZone = typeof options?.timeZone === "string" ? options.timeZone : void 0;
|
|
2826
|
+
const locale = typeof options?.locale === "string" ? options.locale : void 0;
|
|
2827
|
+
return /* @__PURE__ */ jsx28(
|
|
2828
|
+
FormFieldWrapper,
|
|
2829
|
+
{
|
|
2830
|
+
inputId: id,
|
|
2831
|
+
label,
|
|
2832
|
+
description,
|
|
2833
|
+
required,
|
|
2834
|
+
labelPlacement: options?.labelPlacement,
|
|
2835
|
+
children: /* @__PURE__ */ jsx28(
|
|
2836
|
+
DateTimeInput,
|
|
2837
|
+
{
|
|
2838
|
+
idPrefix: id,
|
|
2839
|
+
value: typeof value === "string" ? value : "",
|
|
2840
|
+
onChange,
|
|
2841
|
+
onBlur: () => onBlur?.(id, value),
|
|
2842
|
+
granularity,
|
|
2843
|
+
timeZone,
|
|
2844
|
+
locale,
|
|
2845
|
+
required,
|
|
2846
|
+
disabled,
|
|
2847
|
+
error
|
|
2848
|
+
}
|
|
2849
|
+
)
|
|
2850
|
+
}
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
// src/widgets/DisplayWidget.tsx
|
|
2855
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2856
|
+
function DisplayWidget({
|
|
2857
|
+
label,
|
|
2858
|
+
description
|
|
2859
|
+
}) {
|
|
2860
|
+
if (!label && !description) return null;
|
|
2861
|
+
return /* @__PURE__ */ jsxs17("div", { className: "space-y-1", children: [
|
|
2862
|
+
label && /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground", children: label }),
|
|
2863
|
+
description && /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: description })
|
|
2864
|
+
] });
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// src/widgets/OneOfWidget.tsx
|
|
2868
|
+
import { useMemo as useMemo6 } from "react";
|
|
2869
|
+
import {
|
|
2870
|
+
getDiscriminator,
|
|
2871
|
+
getBranchValue,
|
|
2872
|
+
getBranchLabel,
|
|
2873
|
+
findActiveBranch
|
|
2874
|
+
} from "@petrarca/sonnet-core/schema";
|
|
2875
|
+
import {
|
|
2876
|
+
Label,
|
|
2877
|
+
Select,
|
|
2878
|
+
SelectTrigger,
|
|
2879
|
+
SelectValue,
|
|
2880
|
+
SelectContent,
|
|
2881
|
+
SelectItem
|
|
2882
|
+
} from "@petrarca/sonnet-ui";
|
|
2883
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2884
|
+
function buildChoices(branches, discriminator) {
|
|
2885
|
+
return branches.map((branch, index) => {
|
|
2886
|
+
const raw = discriminator ? getBranchValue(branch, discriminator) : void 0;
|
|
2887
|
+
return {
|
|
2888
|
+
index,
|
|
2889
|
+
value: raw !== void 0 ? String(raw) : String(index),
|
|
2890
|
+
label: getBranchLabel(branch, discriminator, index)
|
|
2891
|
+
};
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
function buildBranchSchema(branch) {
|
|
2895
|
+
if (!branch.properties) return null;
|
|
2896
|
+
return {
|
|
2897
|
+
title: branch.title || "",
|
|
2898
|
+
type: "object",
|
|
2899
|
+
properties: branch.properties,
|
|
2900
|
+
required: branch.required,
|
|
2901
|
+
...branch["x-ui-order"] ? { "x-ui-order": branch["x-ui-order"] } : {}
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
function branchChangeData(choices, branches, discriminator, selected) {
|
|
2905
|
+
const choice = choices.find((c) => c.value === selected);
|
|
2906
|
+
if (!choice || !discriminator) return {};
|
|
2907
|
+
const discValue = getBranchValue(branches[choice.index], discriminator) ?? selected;
|
|
2908
|
+
return { [discriminator]: discValue };
|
|
2909
|
+
}
|
|
2910
|
+
function BranchSelector({
|
|
2911
|
+
id,
|
|
2912
|
+
label,
|
|
2913
|
+
description,
|
|
2914
|
+
choices,
|
|
2915
|
+
activeValue,
|
|
2916
|
+
disabled,
|
|
2917
|
+
onSelect
|
|
2918
|
+
}) {
|
|
2919
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-1", children: [
|
|
2920
|
+
label && /* @__PURE__ */ jsx30(Label, { htmlFor: id, children: label }),
|
|
2921
|
+
/* @__PURE__ */ jsxs18(Select, { value: activeValue, onValueChange: onSelect, disabled, children: [
|
|
2922
|
+
/* @__PURE__ */ jsx30(SelectTrigger, { id, children: /* @__PURE__ */ jsx30(SelectValue, { placeholder: "Select an option" }) }),
|
|
2923
|
+
/* @__PURE__ */ jsx30(SelectContent, { children: choices.map((c) => /* @__PURE__ */ jsx30(SelectItem, { value: c.value, children: c.label }, c.value)) })
|
|
2924
|
+
] }),
|
|
2925
|
+
description && /* @__PURE__ */ jsx30("p", { className: "text-sm text-muted-foreground", children: description })
|
|
2926
|
+
] });
|
|
2927
|
+
}
|
|
2928
|
+
function resolveActive(propertySchema, branches, choices, discriminator, data) {
|
|
2929
|
+
const index = findActiveBranch(propertySchema, data, discriminator);
|
|
2930
|
+
if (index < 0) return { value: "", label: "", subSchema: null };
|
|
2931
|
+
return {
|
|
2932
|
+
value: choices[index]?.value ?? "",
|
|
2933
|
+
label: choices[index]?.label ?? "",
|
|
2934
|
+
subSchema: buildBranchSchema(branches[index])
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
function OneOfWidget({
|
|
2938
|
+
propertySchema,
|
|
2939
|
+
value,
|
|
2940
|
+
onChange,
|
|
2941
|
+
registry,
|
|
2942
|
+
uiSchema,
|
|
2943
|
+
disabled,
|
|
2944
|
+
readonly,
|
|
2945
|
+
label,
|
|
2946
|
+
description,
|
|
2947
|
+
id,
|
|
2948
|
+
options
|
|
2949
|
+
}) {
|
|
2950
|
+
const { renderer: Renderer, templates } = useNestedFormContext();
|
|
2951
|
+
const ContainerTemplate = templates.ObjectContainerTemplate ?? DefaultObjectContainerTemplate;
|
|
2952
|
+
const branches = useMemo6(() => propertySchema.oneOf ?? [], [propertySchema]);
|
|
2953
|
+
const discriminator = useMemo6(
|
|
2954
|
+
() => getDiscriminator(propertySchema),
|
|
2955
|
+
[propertySchema]
|
|
2956
|
+
);
|
|
2957
|
+
const choices = useMemo6(
|
|
2958
|
+
() => buildChoices(branches, discriminator),
|
|
2959
|
+
[branches, discriminator]
|
|
2960
|
+
);
|
|
2961
|
+
const data = value || {};
|
|
2962
|
+
const active = resolveActive(
|
|
2963
|
+
propertySchema,
|
|
2964
|
+
branches,
|
|
2965
|
+
choices,
|
|
2966
|
+
discriminator,
|
|
2967
|
+
data
|
|
2968
|
+
);
|
|
2969
|
+
if (branches.length === 0) return null;
|
|
2970
|
+
const subUiSchema = uiSchema?.["x-ui-fields"] ?? {};
|
|
2971
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-4", children: [
|
|
2972
|
+
/* @__PURE__ */ jsx30(
|
|
2973
|
+
BranchSelector,
|
|
2974
|
+
{
|
|
2975
|
+
id,
|
|
2976
|
+
label,
|
|
2977
|
+
description,
|
|
2978
|
+
choices,
|
|
2979
|
+
activeValue: active.value,
|
|
2980
|
+
disabled: disabled || readonly,
|
|
2981
|
+
onSelect: (selected) => onChange(branchChangeData(choices, branches, discriminator, selected))
|
|
2982
|
+
}
|
|
2983
|
+
),
|
|
2984
|
+
active.subSchema && /* @__PURE__ */ jsx30(
|
|
2985
|
+
ContainerTemplate,
|
|
2986
|
+
{
|
|
2987
|
+
label: active.label,
|
|
2988
|
+
description: void 0,
|
|
2989
|
+
variant: options?.variant || "section",
|
|
2990
|
+
defaultOpen: true,
|
|
2991
|
+
children: /* @__PURE__ */ jsx30(
|
|
2992
|
+
Renderer,
|
|
2993
|
+
{
|
|
2994
|
+
schema: active.subSchema,
|
|
2995
|
+
data,
|
|
2996
|
+
onChange,
|
|
2997
|
+
disabled,
|
|
2998
|
+
readOnly: readonly,
|
|
2999
|
+
widgets: registry,
|
|
3000
|
+
uiSchema: subUiSchema,
|
|
3001
|
+
idPrefix: id
|
|
3002
|
+
}
|
|
3003
|
+
)
|
|
3004
|
+
}
|
|
3005
|
+
)
|
|
3006
|
+
] });
|
|
3007
|
+
}
|
|
3008
|
+
|
|
2699
3009
|
// src/widgets/widgetResolver.ts
|
|
2700
3010
|
import {
|
|
2701
3011
|
isNullableBoolean as isNullableBoolean2,
|
|
3012
|
+
isOneOf,
|
|
2702
3013
|
extractEnumValues as extractEnumValues2,
|
|
2703
3014
|
getFieldType
|
|
2704
3015
|
} from "@petrarca/sonnet-core/schema";
|
|
@@ -2710,8 +3021,11 @@ var DEFAULT_WIDGET_MAP = {
|
|
|
2710
3021
|
// Uses email input type
|
|
2711
3022
|
uri: "TextInputWidget",
|
|
2712
3023
|
// Uses url input type
|
|
2713
|
-
url: "TextInputWidget"
|
|
3024
|
+
url: "TextInputWidget",
|
|
2714
3025
|
// Uses url input type
|
|
3026
|
+
date: "DateWidget",
|
|
3027
|
+
"date-time": "DateTimeWidget",
|
|
3028
|
+
time: "TimeWidget"
|
|
2715
3029
|
},
|
|
2716
3030
|
number: {
|
|
2717
3031
|
default: "NumberWidget"
|
|
@@ -2722,6 +3036,9 @@ var DEFAULT_WIDGET_MAP = {
|
|
|
2722
3036
|
},
|
|
2723
3037
|
boolean: {
|
|
2724
3038
|
default: "CheckboxWidget"
|
|
3039
|
+
},
|
|
3040
|
+
null: {
|
|
3041
|
+
default: "DisplayWidget"
|
|
2725
3042
|
}
|
|
2726
3043
|
};
|
|
2727
3044
|
function resolveWidgetByName(widgetName, propertySchema, registry) {
|
|
@@ -2771,6 +3088,7 @@ function registryLookup(registry, widgetName) {
|
|
|
2771
3088
|
return registry[widgetName] || registry["TextInputWidget"];
|
|
2772
3089
|
}
|
|
2773
3090
|
function resolveFromSchemaStructure(schema, registry) {
|
|
3091
|
+
if (isOneOf(schema)) return registryLookup(registry, "OneOfWidget");
|
|
2774
3092
|
if (isNullableBoolean2(schema))
|
|
2775
3093
|
return registryLookup(registry, "CheckboxWidget");
|
|
2776
3094
|
if (extractEnumValues2(schema).length > 0)
|
|
@@ -2814,6 +3132,11 @@ var DEFAULT_WIDGETS = {
|
|
|
2814
3132
|
ObjectWidget,
|
|
2815
3133
|
ArrayWidget,
|
|
2816
3134
|
QuantityWidget,
|
|
3135
|
+
DateWidget,
|
|
3136
|
+
TimeWidget,
|
|
3137
|
+
DateTimeWidget,
|
|
3138
|
+
DisplayWidget,
|
|
3139
|
+
OneOfWidget,
|
|
2817
3140
|
// Lowercase aliases for x-ui-widget convenience
|
|
2818
3141
|
text: TextInputWidget,
|
|
2819
3142
|
textarea: TextareaWidget,
|
|
@@ -2825,13 +3148,18 @@ var DEFAULT_WIDGETS = {
|
|
|
2825
3148
|
"entity-select": EntitySelectWidget,
|
|
2826
3149
|
object: ObjectWidget,
|
|
2827
3150
|
array: ArrayWidget,
|
|
2828
|
-
quantity: QuantityWidget
|
|
3151
|
+
quantity: QuantityWidget,
|
|
3152
|
+
date: DateWidget,
|
|
3153
|
+
time: TimeWidget,
|
|
3154
|
+
"date-time": DateTimeWidget,
|
|
3155
|
+
display: DisplayWidget,
|
|
3156
|
+
"one-of": OneOfWidget
|
|
2829
3157
|
};
|
|
2830
3158
|
|
|
2831
3159
|
// src/templates/HorizontalArrayItemTemplate.tsx
|
|
2832
3160
|
import { Trash2 as Trash22, ChevronUp as ChevronUp2, ChevronDown as ChevronDown4 } from "lucide-react";
|
|
2833
3161
|
import { Button as Button6 } from "@petrarca/sonnet-ui";
|
|
2834
|
-
import { jsx as
|
|
3162
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2835
3163
|
function HorizontalArrayItemTemplate({
|
|
2836
3164
|
children,
|
|
2837
3165
|
index,
|
|
@@ -2855,7 +3183,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2855
3183
|
);
|
|
2856
3184
|
const gap = resolvedLayout.gap ?? 8;
|
|
2857
3185
|
const alignItems = resolvedLayout.compact ? "center" : "start";
|
|
2858
|
-
return /* @__PURE__ */
|
|
3186
|
+
return /* @__PURE__ */ jsxs19(
|
|
2859
3187
|
"div",
|
|
2860
3188
|
{
|
|
2861
3189
|
"data-fid": fid,
|
|
@@ -2867,8 +3195,8 @@ function HorizontalArrayItemTemplate({
|
|
|
2867
3195
|
},
|
|
2868
3196
|
children: [
|
|
2869
3197
|
children,
|
|
2870
|
-
orderable && /* @__PURE__ */
|
|
2871
|
-
/* @__PURE__ */
|
|
3198
|
+
orderable && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center justify-center", children: [
|
|
3199
|
+
/* @__PURE__ */ jsx31(
|
|
2872
3200
|
Button6,
|
|
2873
3201
|
{
|
|
2874
3202
|
type: "button",
|
|
@@ -2878,10 +3206,10 @@ function HorizontalArrayItemTemplate({
|
|
|
2878
3206
|
onClick: onMoveUp,
|
|
2879
3207
|
title: "Move up",
|
|
2880
3208
|
className: "h-4 w-8 p-0",
|
|
2881
|
-
children: /* @__PURE__ */
|
|
3209
|
+
children: /* @__PURE__ */ jsx31(ChevronUp2, { className: "h-3 w-3" })
|
|
2882
3210
|
}
|
|
2883
3211
|
),
|
|
2884
|
-
/* @__PURE__ */
|
|
3212
|
+
/* @__PURE__ */ jsx31(
|
|
2885
3213
|
Button6,
|
|
2886
3214
|
{
|
|
2887
3215
|
type: "button",
|
|
@@ -2891,11 +3219,11 @@ function HorizontalArrayItemTemplate({
|
|
|
2891
3219
|
onClick: onMoveDown,
|
|
2892
3220
|
title: "Move down",
|
|
2893
3221
|
className: "h-4 w-8 p-0",
|
|
2894
|
-
children: /* @__PURE__ */
|
|
3222
|
+
children: /* @__PURE__ */ jsx31(ChevronDown4, { className: "h-3 w-3" })
|
|
2895
3223
|
}
|
|
2896
3224
|
)
|
|
2897
3225
|
] }),
|
|
2898
|
-
canRemove && /* @__PURE__ */
|
|
3226
|
+
canRemove && /* @__PURE__ */ jsx31(
|
|
2899
3227
|
Button6,
|
|
2900
3228
|
{
|
|
2901
3229
|
type: "button",
|
|
@@ -2905,7 +3233,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2905
3233
|
onClick: onRemove,
|
|
2906
3234
|
title: "Remove item",
|
|
2907
3235
|
className: "text-muted-foreground hover:text-destructive h-8 w-8 p-0",
|
|
2908
|
-
children: /* @__PURE__ */
|
|
3236
|
+
children: /* @__PURE__ */ jsx31(Trash22, { className: "h-3.5 w-3.5" })
|
|
2909
3237
|
}
|
|
2910
3238
|
)
|
|
2911
3239
|
]
|
|
@@ -2913,13 +3241,123 @@ function HorizontalArrayItemTemplate({
|
|
|
2913
3241
|
);
|
|
2914
3242
|
}
|
|
2915
3243
|
|
|
3244
|
+
// src/templates/TabsGroupTemplate.tsx
|
|
3245
|
+
import { useState as useState7 } from "react";
|
|
3246
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@petrarca/sonnet-ui";
|
|
3247
|
+
|
|
3248
|
+
// src/templates/GroupErrorBadge.tsx
|
|
3249
|
+
import { AlertCircle } from "lucide-react";
|
|
3250
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
3251
|
+
function GroupErrorBadge({
|
|
3252
|
+
count
|
|
3253
|
+
}) {
|
|
3254
|
+
if (count === 0) return null;
|
|
3255
|
+
const label = count === 1 ? "1 field needs attention" : `${count} fields need attention`;
|
|
3256
|
+
return /* @__PURE__ */ jsx32(
|
|
3257
|
+
"span",
|
|
3258
|
+
{
|
|
3259
|
+
role: "img",
|
|
3260
|
+
"aria-label": label,
|
|
3261
|
+
title: label,
|
|
3262
|
+
className: "inline-flex shrink-0 text-destructive",
|
|
3263
|
+
children: /* @__PURE__ */ jsx32(AlertCircle, { className: "h-3.5 w-3.5" })
|
|
3264
|
+
}
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
// src/templates/TabsGroupTemplate.tsx
|
|
3269
|
+
import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3270
|
+
function TabsGroupTemplate({
|
|
3271
|
+
groups
|
|
3272
|
+
}) {
|
|
3273
|
+
const [active, setActive] = useState7(groups[0].key);
|
|
3274
|
+
const value = groups.some((g) => g.key === active) ? active : groups[0].key;
|
|
3275
|
+
return /* @__PURE__ */ jsxs20(Tabs, { value, onValueChange: setActive, children: [
|
|
3276
|
+
/* @__PURE__ */ jsx33(TabsList, { className: "mb-4", children: groups.map((g) => /* @__PURE__ */ jsxs20(TabsTrigger, { value: g.key, className: "gap-1.5", children: [
|
|
3277
|
+
g.label,
|
|
3278
|
+
/* @__PURE__ */ jsx33(GroupErrorBadge, { count: g.errorCount })
|
|
3279
|
+
] }, g.key)) }),
|
|
3280
|
+
groups.map((g) => /* @__PURE__ */ jsx33(TabsContent, { value: g.key, className: "space-y-4", children: g.content }, g.key))
|
|
3281
|
+
] });
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
// src/templates/StepperGroupTemplate.tsx
|
|
3285
|
+
import { useState as useState8 } from "react";
|
|
3286
|
+
import {
|
|
3287
|
+
Button as Button7,
|
|
3288
|
+
Stepper,
|
|
3289
|
+
StepperList,
|
|
3290
|
+
StepperItem,
|
|
3291
|
+
StepperTrigger,
|
|
3292
|
+
StepperIndicator,
|
|
3293
|
+
StepperSeparator,
|
|
3294
|
+
StepperTitle,
|
|
3295
|
+
StepperContent,
|
|
3296
|
+
StepperPrev,
|
|
3297
|
+
StepperNext
|
|
3298
|
+
} from "@petrarca/sonnet-ui";
|
|
3299
|
+
import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3300
|
+
function StepperGroupTemplate({
|
|
3301
|
+
groups
|
|
3302
|
+
}) {
|
|
3303
|
+
const [active, setActive] = useState8(groups[0].key);
|
|
3304
|
+
const value = groups.some((g) => g.key === active) ? active : groups[0].key;
|
|
3305
|
+
const isLast = value === groups[groups.length - 1].key;
|
|
3306
|
+
return /* @__PURE__ */ jsxs21(Stepper, { value, onValueChange: setActive, children: [
|
|
3307
|
+
/* @__PURE__ */ jsx34(StepperList, { className: "mb-4 gap-2", children: groups.map((g) => /* @__PURE__ */ jsxs21(StepperItem, { value: g.key, children: [
|
|
3308
|
+
/* @__PURE__ */ jsxs21(StepperTrigger, { children: [
|
|
3309
|
+
/* @__PURE__ */ jsx34(StepperIndicator, {}),
|
|
3310
|
+
/* @__PURE__ */ jsxs21(StepperTitle, { className: "inline-flex items-center gap-1.5", children: [
|
|
3311
|
+
g.label,
|
|
3312
|
+
/* @__PURE__ */ jsx34(GroupErrorBadge, { count: g.errorCount })
|
|
3313
|
+
] })
|
|
3314
|
+
] }),
|
|
3315
|
+
/* @__PURE__ */ jsx34(StepperSeparator, {})
|
|
3316
|
+
] }, g.key)) }),
|
|
3317
|
+
groups.map((g) => /* @__PURE__ */ jsx34(StepperContent, { value: g.key, className: "space-y-4", children: g.content }, g.key)),
|
|
3318
|
+
/* @__PURE__ */ jsxs21("div", { className: "mt-4 flex justify-between", children: [
|
|
3319
|
+
/* @__PURE__ */ jsx34(StepperPrev, { asChild: true, children: /* @__PURE__ */ jsx34(Button7, { variant: "outline", size: "sm", children: "Back" }) }),
|
|
3320
|
+
!isLast && /* @__PURE__ */ jsx34(StepperNext, { asChild: true, children: /* @__PURE__ */ jsx34(Button7, { size: "sm", children: "Next" }) })
|
|
3321
|
+
] })
|
|
3322
|
+
] });
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
// src/templates/SectionsGroupTemplate.tsx
|
|
3326
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3327
|
+
function SectionsGroupTemplate({
|
|
3328
|
+
groups
|
|
3329
|
+
}) {
|
|
3330
|
+
return /* @__PURE__ */ jsx35("div", { className: "space-y-6", children: groups.map((g) => /* @__PURE__ */ jsxs22(
|
|
3331
|
+
"fieldset",
|
|
3332
|
+
{
|
|
3333
|
+
className: "rounded-md border border-border/60 px-3 pb-3 pt-1",
|
|
3334
|
+
children: [
|
|
3335
|
+
/* @__PURE__ */ jsxs22("legend", { className: "inline-flex items-center gap-1.5 px-1 text-sm font-medium text-muted-foreground", children: [
|
|
3336
|
+
g.label,
|
|
3337
|
+
/* @__PURE__ */ jsx35(GroupErrorBadge, { count: g.errorCount })
|
|
3338
|
+
] }),
|
|
3339
|
+
/* @__PURE__ */ jsx35("div", { className: "space-y-4", children: g.content })
|
|
3340
|
+
]
|
|
3341
|
+
},
|
|
3342
|
+
g.key
|
|
3343
|
+
)) });
|
|
3344
|
+
}
|
|
3345
|
+
|
|
2916
3346
|
// src/templates/index.ts
|
|
2917
3347
|
var DEFAULT_TEMPLATES = {
|
|
2918
3348
|
ArrayItemTemplate: DefaultArrayItemTemplate,
|
|
2919
3349
|
HorizontalArrayItemTemplate,
|
|
2920
3350
|
ArrayHeaderTemplate: DefaultArrayHeaderTemplate,
|
|
2921
|
-
ObjectContainerTemplate: DefaultObjectContainerTemplate
|
|
3351
|
+
ObjectContainerTemplate: DefaultObjectContainerTemplate,
|
|
3352
|
+
TabsGroupTemplate,
|
|
3353
|
+
StepperGroupTemplate,
|
|
3354
|
+
SectionsGroupTemplate
|
|
2922
3355
|
};
|
|
3356
|
+
function groupTemplateKey(variant) {
|
|
3357
|
+
if (!variant) return "TabsGroupTemplate";
|
|
3358
|
+
const name = variant.charAt(0).toUpperCase() + variant.slice(1);
|
|
3359
|
+
return `${name}GroupTemplate`;
|
|
3360
|
+
}
|
|
2923
3361
|
|
|
2924
3362
|
// src/FormFieldRenderer.tsx
|
|
2925
3363
|
import { warnLog } from "@petrarca/sonnet-core";
|
|
@@ -2928,7 +3366,7 @@ import {
|
|
|
2928
3366
|
getFieldHelpText,
|
|
2929
3367
|
getFieldPlaceholder
|
|
2930
3368
|
} from "@petrarca/sonnet-core/schema";
|
|
2931
|
-
import { jsx as
|
|
3369
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2932
3370
|
function mergeFieldOptions(globalOptions, property, uiSchema) {
|
|
2933
3371
|
const xWidgetOptions = property["x-ui-options"] || {};
|
|
2934
3372
|
const fieldOptions = uiSchema?.["x-ui-options"] || {};
|
|
@@ -2955,7 +3393,7 @@ function applyFieldWidth(widget, uiSchema, mergedOptions) {
|
|
|
2955
3393
|
}
|
|
2956
3394
|
const width = candidate && candidate in FIELD_WIDTH_CLASS ? candidate : "full";
|
|
2957
3395
|
if (width === "full") return widget;
|
|
2958
|
-
return /* @__PURE__ */
|
|
3396
|
+
return /* @__PURE__ */ jsx36("div", { className: FIELD_WIDTH_CLASS[width], children: widget });
|
|
2959
3397
|
}
|
|
2960
3398
|
function resolvePresentationText(property, uiSchema) {
|
|
2961
3399
|
return {
|
|
@@ -2983,7 +3421,8 @@ function FormFieldRenderer({
|
|
|
2983
3421
|
registry,
|
|
2984
3422
|
globalOptions = {},
|
|
2985
3423
|
formData,
|
|
2986
|
-
idPrefix = "field"
|
|
3424
|
+
idPrefix = "field",
|
|
3425
|
+
rawErrors
|
|
2987
3426
|
}) {
|
|
2988
3427
|
const label = getFieldTitle(fieldName, property);
|
|
2989
3428
|
const { description, placeholder } = resolvePresentationText(
|
|
@@ -3005,7 +3444,7 @@ function FormFieldRenderer({
|
|
|
3005
3444
|
uiSchema?.["x-ui-title"],
|
|
3006
3445
|
globalOptions.label
|
|
3007
3446
|
);
|
|
3008
|
-
const widget = /* @__PURE__ */
|
|
3447
|
+
const widget = /* @__PURE__ */ jsx36(
|
|
3009
3448
|
Widget,
|
|
3010
3449
|
{
|
|
3011
3450
|
id: `${idPrefix}-${fieldName}`,
|
|
@@ -3024,14 +3463,15 @@ function FormFieldRenderer({
|
|
|
3024
3463
|
disabled: flags.disabled,
|
|
3025
3464
|
readonly: flags.readOnly,
|
|
3026
3465
|
options: mergedOptions,
|
|
3027
|
-
formData
|
|
3466
|
+
formData,
|
|
3467
|
+
rawErrors
|
|
3028
3468
|
}
|
|
3029
3469
|
);
|
|
3030
3470
|
return applyFieldWidth(widget, uiSchema, mergedOptions);
|
|
3031
3471
|
}
|
|
3032
3472
|
|
|
3033
3473
|
// src/hooks/useFormEngine.ts
|
|
3034
|
-
import { useState as
|
|
3474
|
+
import { useState as useState9, useEffect as useEffect3, useMemo as useMemo7, useCallback as useCallback7, useRef as useRef5 } from "react";
|
|
3035
3475
|
import { isEqual } from "@petrarca/sonnet-core";
|
|
3036
3476
|
import {
|
|
3037
3477
|
getSchemaProperties,
|
|
@@ -3040,7 +3480,8 @@ import {
|
|
|
3040
3480
|
resolveSchema,
|
|
3041
3481
|
resolveRefs,
|
|
3042
3482
|
validateFormData,
|
|
3043
|
-
diffFormData
|
|
3483
|
+
diffFormData,
|
|
3484
|
+
ensureFormItemIds
|
|
3044
3485
|
} from "@petrarca/sonnet-core/schema";
|
|
3045
3486
|
|
|
3046
3487
|
// src/hooks/orderProperties.ts
|
|
@@ -3099,14 +3540,24 @@ function useFormEngine({
|
|
|
3099
3540
|
showActions = false,
|
|
3100
3541
|
deferChanges = false,
|
|
3101
3542
|
deferStateUpdates = false,
|
|
3102
|
-
allowPristineSave = false
|
|
3543
|
+
allowPristineSave = false,
|
|
3544
|
+
refResolver
|
|
3103
3545
|
}) {
|
|
3104
|
-
const refResolvedSchema =
|
|
3105
|
-
|
|
3106
|
-
|
|
3546
|
+
const refResolvedSchema = useMemo7(
|
|
3547
|
+
() => resolveRefs(schema, refResolver),
|
|
3548
|
+
[schema, refResolver]
|
|
3107
3549
|
);
|
|
3108
|
-
const
|
|
3109
|
-
|
|
3550
|
+
const seed = useState9(
|
|
3551
|
+
() => ensureFormItemIds(
|
|
3552
|
+
applySchemaDefaults2(data, refResolvedSchema),
|
|
3553
|
+
refResolvedSchema
|
|
3554
|
+
)
|
|
3555
|
+
)[0];
|
|
3556
|
+
const [originalData, setOriginalData] = useState9(
|
|
3557
|
+
() => seed
|
|
3558
|
+
);
|
|
3559
|
+
const [originalDataInitialized, setOriginalDataInitialized] = useState9(false);
|
|
3560
|
+
const [nonSchemaData, setNonSchemaData] = useState9(
|
|
3110
3561
|
() => {
|
|
3111
3562
|
const props = getSchemaProperties(refResolvedSchema);
|
|
3112
3563
|
const names = Object.keys(props);
|
|
@@ -3115,26 +3566,24 @@ function useFormEngine({
|
|
|
3115
3566
|
);
|
|
3116
3567
|
}
|
|
3117
3568
|
);
|
|
3118
|
-
const [formData, setFormData] =
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
const [pendingChanges, setPendingChanges] =
|
|
3122
|
-
const resolvedSchema =
|
|
3569
|
+
const [formData, setFormData] = useState9(() => seed);
|
|
3570
|
+
const formDataRef = useRef5(formData);
|
|
3571
|
+
formDataRef.current = formData;
|
|
3572
|
+
const [pendingChanges, setPendingChanges] = useState9({});
|
|
3573
|
+
const resolvedSchema = useMemo7(
|
|
3123
3574
|
() => resolveSchema(refResolvedSchema, formData),
|
|
3124
3575
|
[refResolvedSchema, formData]
|
|
3125
3576
|
);
|
|
3126
|
-
const orderedProperties =
|
|
3577
|
+
const orderedProperties = useMemo7(() => {
|
|
3127
3578
|
const properties2 = getSchemaProperties(resolvedSchema);
|
|
3128
3579
|
const rawOrder = uiSchema["x-ui-order"] ?? resolvedSchema["x-ui-order"];
|
|
3129
3580
|
const orderArray = Array.isArray(rawOrder) ? rawOrder : void 0;
|
|
3130
3581
|
return orderProperties(properties2, orderArray);
|
|
3131
3582
|
}, [resolvedSchema, uiSchema]);
|
|
3132
|
-
const [
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
});
|
|
3137
|
-
const [initialDefaultsSent, setInitialDefaultsSent] = useState7(false);
|
|
3583
|
+
const [initialDefaultsSent, setInitialDefaultsSent] = useState9(false);
|
|
3584
|
+
const [edited, setEdited] = useState9({});
|
|
3585
|
+
const [touched, setTouched] = useState9({});
|
|
3586
|
+
const [submitAttempted, setSubmitAttempted] = useState9(false);
|
|
3138
3587
|
const properties = getSchemaProperties(resolvedSchema);
|
|
3139
3588
|
useEffect3(() => {
|
|
3140
3589
|
if (!initialDefaultsSent) {
|
|
@@ -3165,7 +3614,10 @@ function useFormEngine({
|
|
|
3165
3614
|
useEffect3(() => {
|
|
3166
3615
|
if (isEqual(prevDataRef.current, data)) return;
|
|
3167
3616
|
prevDataRef.current = data;
|
|
3168
|
-
const newData =
|
|
3617
|
+
const newData = ensureFormItemIds(
|
|
3618
|
+
applySchemaDefaults2(data, refResolvedSchema),
|
|
3619
|
+
refResolvedSchema
|
|
3620
|
+
);
|
|
3169
3621
|
setFormData(newData);
|
|
3170
3622
|
const schemaPropertyNames = Object.keys(properties);
|
|
3171
3623
|
const result = computeBaselineUpdate(
|
|
@@ -3181,8 +3633,6 @@ function useFormEngine({
|
|
|
3181
3633
|
setOriginalDataInitialized(true);
|
|
3182
3634
|
}
|
|
3183
3635
|
}
|
|
3184
|
-
const validation = validateFormData(resolvedSchema, newData);
|
|
3185
|
-
setIsValid(validation.isValid);
|
|
3186
3636
|
}, [
|
|
3187
3637
|
data,
|
|
3188
3638
|
refResolvedSchema,
|
|
@@ -3191,21 +3641,34 @@ function useFormEngine({
|
|
|
3191
3641
|
originalData,
|
|
3192
3642
|
originalDataInitialized
|
|
3193
3643
|
]);
|
|
3194
|
-
const hasChanges =
|
|
3644
|
+
const hasChanges = useMemo7(
|
|
3195
3645
|
() => !isEqual(formData, originalData),
|
|
3196
3646
|
[formData, originalData]
|
|
3197
3647
|
);
|
|
3198
|
-
const displayData =
|
|
3648
|
+
const displayData = useMemo7(() => {
|
|
3199
3649
|
if (deferStateUpdates && Object.keys(pendingChanges).length > 0) {
|
|
3200
3650
|
return { ...formData, ...pendingChanges };
|
|
3201
3651
|
}
|
|
3202
3652
|
return formData;
|
|
3203
3653
|
}, [formData, pendingChanges, deferStateUpdates]);
|
|
3654
|
+
const validation = useMemo7(
|
|
3655
|
+
() => validateFormData(resolvedSchema, displayData),
|
|
3656
|
+
[resolvedSchema, displayData]
|
|
3657
|
+
);
|
|
3658
|
+
const { isValid, fieldErrors } = validation;
|
|
3659
|
+
const onValidationChangeRef = useRef5(onValidationChange);
|
|
3660
|
+
onValidationChangeRef.current = onValidationChange;
|
|
3661
|
+
useEffect3(() => {
|
|
3662
|
+
onValidationChangeRef.current?.(validation.errors, validation.isValid);
|
|
3663
|
+
}, [validation]);
|
|
3204
3664
|
const handleFieldChange = useCallback7(
|
|
3205
3665
|
(fieldName, value) => {
|
|
3206
3666
|
if (readOnly) return;
|
|
3667
|
+
setEdited(
|
|
3668
|
+
(prev) => prev[fieldName] ? prev : { ...prev, [fieldName]: true }
|
|
3669
|
+
);
|
|
3207
3670
|
const immediate = isImmediateField(fieldName, properties);
|
|
3208
|
-
if (
|
|
3671
|
+
if (deferStateUpdates && !immediate) {
|
|
3209
3672
|
setPendingChanges((prev) => ({ ...prev, [fieldName]: value }));
|
|
3210
3673
|
return;
|
|
3211
3674
|
}
|
|
@@ -3215,11 +3678,6 @@ function useFormEngine({
|
|
|
3215
3678
|
const shouldCallOnChange = !deferChanges || immediate;
|
|
3216
3679
|
if (shouldCallOnChange && onChange) onChange(newData);
|
|
3217
3680
|
}
|
|
3218
|
-
const validation = validateFormData(resolvedSchema, newData);
|
|
3219
|
-
setIsValid(validation.isValid);
|
|
3220
|
-
if (onValidationChange) {
|
|
3221
|
-
onValidationChange(validation.errors, validation.isValid);
|
|
3222
|
-
}
|
|
3223
3681
|
},
|
|
3224
3682
|
[
|
|
3225
3683
|
readOnly,
|
|
@@ -3228,14 +3686,12 @@ function useFormEngine({
|
|
|
3228
3686
|
deferStateUpdates,
|
|
3229
3687
|
showActions,
|
|
3230
3688
|
onChange,
|
|
3231
|
-
resolvedSchema,
|
|
3232
|
-
onValidationChange,
|
|
3233
3689
|
properties
|
|
3234
3690
|
]
|
|
3235
3691
|
);
|
|
3236
3692
|
const flushPendingField = useCallback7(
|
|
3237
3693
|
(fieldName) => {
|
|
3238
|
-
if (!
|
|
3694
|
+
if (!deferStateUpdates) return;
|
|
3239
3695
|
if (pendingChanges[fieldName] === void 0) return;
|
|
3240
3696
|
const newData = { ...formData, ...pendingChanges };
|
|
3241
3697
|
setFormData(newData);
|
|
@@ -3244,37 +3700,32 @@ function useFormEngine({
|
|
|
3244
3700
|
delete updated[fieldName];
|
|
3245
3701
|
return updated;
|
|
3246
3702
|
});
|
|
3247
|
-
|
|
3248
|
-
setIsValid(validation.isValid);
|
|
3249
|
-
if (onValidationChange) {
|
|
3250
|
-
onValidationChange(validation.errors, validation.isValid);
|
|
3251
|
-
}
|
|
3703
|
+
if (!showActions && onChange) onChange(newData);
|
|
3252
3704
|
},
|
|
3253
|
-
[
|
|
3254
|
-
showActions,
|
|
3255
|
-
deferStateUpdates,
|
|
3256
|
-
pendingChanges,
|
|
3257
|
-
formData,
|
|
3258
|
-
resolvedSchema,
|
|
3259
|
-
onValidationChange
|
|
3260
|
-
]
|
|
3705
|
+
[showActions, deferStateUpdates, pendingChanges, formData, onChange]
|
|
3261
3706
|
);
|
|
3262
3707
|
const handleFieldBlur = useCallback7(
|
|
3263
3708
|
(fieldName) => {
|
|
3264
3709
|
if (readOnly) return;
|
|
3710
|
+
if (edited[fieldName]) {
|
|
3711
|
+
setTouched(
|
|
3712
|
+
(prev) => prev[fieldName] ? prev : { ...prev, [fieldName]: true }
|
|
3713
|
+
);
|
|
3714
|
+
}
|
|
3265
3715
|
flushPendingField(fieldName);
|
|
3716
|
+
const latest = formDataRef.current;
|
|
3266
3717
|
if (!showActions && deferChanges && onChange) {
|
|
3267
|
-
onChange(
|
|
3718
|
+
onChange(latest);
|
|
3268
3719
|
}
|
|
3269
3720
|
if (onBlur) {
|
|
3270
3721
|
const hasPending = deferStateUpdates && pendingChanges[fieldName] !== void 0;
|
|
3271
|
-
const dataToPass = hasPending ? { ...
|
|
3722
|
+
const dataToPass = hasPending ? { ...latest, [fieldName]: pendingChanges[fieldName] } : latest;
|
|
3272
3723
|
onBlur(fieldName, dataToPass);
|
|
3273
3724
|
}
|
|
3274
3725
|
},
|
|
3275
3726
|
[
|
|
3276
3727
|
readOnly,
|
|
3277
|
-
|
|
3728
|
+
edited,
|
|
3278
3729
|
pendingChanges,
|
|
3279
3730
|
deferChanges,
|
|
3280
3731
|
deferStateUpdates,
|
|
@@ -3285,11 +3736,12 @@ function useFormEngine({
|
|
|
3285
3736
|
]
|
|
3286
3737
|
);
|
|
3287
3738
|
const handleSave = useCallback7(() => {
|
|
3739
|
+
setSubmitAttempted(true);
|
|
3288
3740
|
if (!hasChanges && !allowPristineSave) return;
|
|
3289
3741
|
const finalData = deferStateUpdates && Object.keys(pendingChanges).length > 0 ? { ...formData, ...pendingChanges } : formData;
|
|
3290
3742
|
const completeData = { ...nonSchemaData, ...finalData };
|
|
3291
|
-
const
|
|
3292
|
-
if (
|
|
3743
|
+
const validation2 = validateFormData(resolvedSchema, finalData);
|
|
3744
|
+
if (validation2.isValid && onUpdate) {
|
|
3293
3745
|
const changed = {};
|
|
3294
3746
|
for (const key of Object.keys(finalData)) {
|
|
3295
3747
|
if (!isEqual(finalData[key], originalData[key])) {
|
|
@@ -3314,6 +3766,9 @@ function useFormEngine({
|
|
|
3314
3766
|
const handleCancel = useCallback7(() => {
|
|
3315
3767
|
setFormData({ ...originalData });
|
|
3316
3768
|
setPendingChanges({});
|
|
3769
|
+
setEdited({});
|
|
3770
|
+
setTouched({});
|
|
3771
|
+
setSubmitAttempted(false);
|
|
3317
3772
|
onCancel?.();
|
|
3318
3773
|
}, [originalData, onCancel]);
|
|
3319
3774
|
return {
|
|
@@ -3323,6 +3778,9 @@ function useFormEngine({
|
|
|
3323
3778
|
orderedProperties,
|
|
3324
3779
|
hasChanges,
|
|
3325
3780
|
isValid,
|
|
3781
|
+
fieldErrors,
|
|
3782
|
+
touched,
|
|
3783
|
+
submitAttempted,
|
|
3326
3784
|
handleFieldChange,
|
|
3327
3785
|
handleFieldBlur,
|
|
3328
3786
|
handleSave,
|
|
@@ -3331,7 +3789,11 @@ function useFormEngine({
|
|
|
3331
3789
|
}
|
|
3332
3790
|
|
|
3333
3791
|
// src/JsonSchemaFormRenderer.tsx
|
|
3334
|
-
import {
|
|
3792
|
+
import {
|
|
3793
|
+
useValidationDisplay,
|
|
3794
|
+
useSaveWhenInvalid
|
|
3795
|
+
} from "@petrarca/sonnet-core/config";
|
|
3796
|
+
import { jsx as jsx37, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3335
3797
|
function stripFids(data) {
|
|
3336
3798
|
const result = {};
|
|
3337
3799
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -3398,7 +3860,7 @@ function useWrappedCallbacks(onChange, onBlur, onUpdate) {
|
|
|
3398
3860
|
function buildFieldMap(fields) {
|
|
3399
3861
|
const map = /* @__PURE__ */ new Map();
|
|
3400
3862
|
for (const field of fields) {
|
|
3401
|
-
if (
|
|
3863
|
+
if (React13.isValidElement(field) && field.key != null) {
|
|
3402
3864
|
map.set(String(field.key), field);
|
|
3403
3865
|
}
|
|
3404
3866
|
}
|
|
@@ -3417,12 +3879,12 @@ function resolveGridColumns(row, fieldCount) {
|
|
|
3417
3879
|
}
|
|
3418
3880
|
function wrapInGroup(group, key, content) {
|
|
3419
3881
|
if (!group) return content;
|
|
3420
|
-
return /* @__PURE__ */
|
|
3882
|
+
return /* @__PURE__ */ jsxs23(
|
|
3421
3883
|
"fieldset",
|
|
3422
3884
|
{
|
|
3423
3885
|
className: "rounded-md border border-border/60 px-3 pb-3 pt-1",
|
|
3424
3886
|
children: [
|
|
3425
|
-
/* @__PURE__ */
|
|
3887
|
+
/* @__PURE__ */ jsx37("legend", { className: "px-1 text-sm font-medium text-muted-foreground", children: group }),
|
|
3426
3888
|
content
|
|
3427
3889
|
]
|
|
3428
3890
|
},
|
|
@@ -3444,7 +3906,7 @@ function buildRowElement(row, fieldMap, defaultRowGap) {
|
|
|
3444
3906
|
gridTemplateColumns: resolveGridColumns(row, rowFields.length),
|
|
3445
3907
|
gap: `${rowGap}px`
|
|
3446
3908
|
};
|
|
3447
|
-
const inner = /* @__PURE__ */
|
|
3909
|
+
const inner = /* @__PURE__ */ jsx37("div", { style, children: rowFields }, `row:${rowKey}`);
|
|
3448
3910
|
return {
|
|
3449
3911
|
element: wrapInGroup(row.group, rowKey, inner),
|
|
3450
3912
|
placed: row.fields
|
|
@@ -3453,24 +3915,108 @@ function buildRowElement(row, fieldMap, defaultRowGap) {
|
|
|
3453
3915
|
function collectUnplacedFields(fields, placed) {
|
|
3454
3916
|
const result = [];
|
|
3455
3917
|
for (const field of fields) {
|
|
3456
|
-
if (
|
|
3918
|
+
if (React13.isValidElement(field) && field.key != null && !placed.has(String(field.key))) {
|
|
3457
3919
|
result.push(field);
|
|
3458
3920
|
}
|
|
3459
3921
|
}
|
|
3460
3922
|
return result;
|
|
3461
3923
|
}
|
|
3462
|
-
function
|
|
3463
|
-
const fieldMap = buildFieldMap(fields);
|
|
3464
|
-
const placed = /* @__PURE__ */ new Set();
|
|
3924
|
+
function buildRows(rowDefs, fieldMap, defaultRowGap, placed) {
|
|
3465
3925
|
const elements = [];
|
|
3466
3926
|
for (const row of rowDefs) {
|
|
3467
3927
|
const result = buildRowElement(row, fieldMap, defaultRowGap);
|
|
3468
|
-
|
|
3469
|
-
elements.push(result.element);
|
|
3470
|
-
for (const name of result.placed) placed.add(name);
|
|
3928
|
+
for (const name of row.fields) placed.add(name);
|
|
3929
|
+
if (result != null) elements.push(result.element);
|
|
3471
3930
|
}
|
|
3931
|
+
return elements;
|
|
3932
|
+
}
|
|
3933
|
+
function layoutFieldsInRows(fields, rowDefs, defaultRowGap) {
|
|
3934
|
+
const placed = /* @__PURE__ */ new Set();
|
|
3935
|
+
const elements = buildRows(
|
|
3936
|
+
rowDefs,
|
|
3937
|
+
buildFieldMap(fields),
|
|
3938
|
+
defaultRowGap,
|
|
3939
|
+
placed
|
|
3940
|
+
);
|
|
3472
3941
|
return elements.concat(collectUnplacedFields(fields, placed));
|
|
3473
3942
|
}
|
|
3943
|
+
function useRootFormData(ownData) {
|
|
3944
|
+
return useContext2(NestedFormContext)?.rootData ?? ownData;
|
|
3945
|
+
}
|
|
3946
|
+
function blurHandlerNeeded({
|
|
3947
|
+
deferStateUpdates,
|
|
3948
|
+
deferChanges,
|
|
3949
|
+
hasOnBlur,
|
|
3950
|
+
validationDisplay
|
|
3951
|
+
}) {
|
|
3952
|
+
return deferStateUpdates || deferChanges || hasOnBlur || validationDisplay === "onTouched";
|
|
3953
|
+
}
|
|
3954
|
+
function fieldErrorsVisible(validationDisplay, submitAttempted, isTouched) {
|
|
3955
|
+
if (validationDisplay === "always") return true;
|
|
3956
|
+
if (submitAttempted) return true;
|
|
3957
|
+
return validationDisplay === "onTouched" && !!isTouched;
|
|
3958
|
+
}
|
|
3959
|
+
function pointerRoot(pointer) {
|
|
3960
|
+
const token = pointer.split("/")[1] ?? "";
|
|
3961
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
3962
|
+
}
|
|
3963
|
+
function groupErrorCount(tab, fieldErrors) {
|
|
3964
|
+
if (fieldErrors.length === 0) return 0;
|
|
3965
|
+
const owned = new Set(tab.rows.flatMap((row) => row.fields));
|
|
3966
|
+
return fieldErrors.filter((e) => owned.has(pointerRoot(e.pointer))).length;
|
|
3967
|
+
}
|
|
3968
|
+
function groupVisibility(groupDefs, formData) {
|
|
3969
|
+
const visible = groupDefs.map(
|
|
3970
|
+
(tab) => evaluateUiRule(tab.rule, formData).visible
|
|
3971
|
+
);
|
|
3972
|
+
return { visible, allHidden: !visible.some(Boolean) };
|
|
3973
|
+
}
|
|
3974
|
+
function hiddenGroupFieldNames(groupDefs, formData) {
|
|
3975
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
3976
|
+
const { visible, allHidden } = groupVisibility(groupDefs, formData);
|
|
3977
|
+
if (allHidden) return hidden;
|
|
3978
|
+
groupDefs.forEach((tab, i) => {
|
|
3979
|
+
if (visible[i]) return;
|
|
3980
|
+
for (const row of tab.rows) for (const f of row.fields) hidden.add(f);
|
|
3981
|
+
});
|
|
3982
|
+
return hidden;
|
|
3983
|
+
}
|
|
3984
|
+
function resolveGroupTemplate(templates, variant) {
|
|
3985
|
+
const registered = templates[groupTemplateKey(variant)];
|
|
3986
|
+
if (typeof registered === "function") {
|
|
3987
|
+
return registered;
|
|
3988
|
+
}
|
|
3989
|
+
return DEFAULT_TEMPLATES.TabsGroupTemplate;
|
|
3990
|
+
}
|
|
3991
|
+
function HiddenErrorNotice({
|
|
3992
|
+
count
|
|
3993
|
+
}) {
|
|
3994
|
+
if (count === 0) return null;
|
|
3995
|
+
return /* @__PURE__ */ jsxs23("p", { className: "flex items-center gap-1.5 text-sm text-destructive", children: [
|
|
3996
|
+
/* @__PURE__ */ jsx37(AlertCircle2, { className: "h-4 w-4 shrink-0" }),
|
|
3997
|
+
count === 1 ? "1 required field is in a section that is not currently shown." : `${count} required fields are in sections that are not currently shown.`
|
|
3998
|
+
] });
|
|
3999
|
+
}
|
|
4000
|
+
function layoutFieldsInGroups(fields, groupDefs, defaultRowGap, fieldErrors, formData, GroupTemplate) {
|
|
4001
|
+
const fieldMap = buildFieldMap(fields);
|
|
4002
|
+
const placed = /* @__PURE__ */ new Set();
|
|
4003
|
+
const { visible, allHidden } = groupVisibility(groupDefs, formData);
|
|
4004
|
+
const built = groupDefs.map((tab, index) => ({
|
|
4005
|
+
index,
|
|
4006
|
+
rows: buildRows(tab.rows, fieldMap, defaultRowGap, placed)
|
|
4007
|
+
}));
|
|
4008
|
+
const unplaced = collectUnplacedFields(fields, placed);
|
|
4009
|
+
if (allHidden) {
|
|
4010
|
+
return [...built.flatMap((b) => b.rows), ...unplaced];
|
|
4011
|
+
}
|
|
4012
|
+
const groups = built.filter((b) => visible[b.index]).map((b) => ({
|
|
4013
|
+
key: `group-${b.index}`,
|
|
4014
|
+
label: groupDefs[b.index].label,
|
|
4015
|
+
content: b.rows,
|
|
4016
|
+
errorCount: groupErrorCount(groupDefs[b.index], fieldErrors)
|
|
4017
|
+
}));
|
|
4018
|
+
return [/* @__PURE__ */ jsx37(GroupTemplate, { groups }, "form:groups"), ...unplaced];
|
|
4019
|
+
}
|
|
3474
4020
|
function FormContainer({
|
|
3475
4021
|
displayContents,
|
|
3476
4022
|
showExtraProperties,
|
|
@@ -3482,19 +4028,22 @@ function FormContainer({
|
|
|
3482
4028
|
disabled,
|
|
3483
4029
|
hasChanges,
|
|
3484
4030
|
isValid,
|
|
4031
|
+
saveWhenInvalid,
|
|
3485
4032
|
saveLabel,
|
|
3486
4033
|
cancelLabel,
|
|
3487
4034
|
showCancel,
|
|
3488
4035
|
leadingActions,
|
|
4036
|
+
hiddenErrorCount,
|
|
3489
4037
|
children
|
|
3490
4038
|
}) {
|
|
3491
4039
|
if (displayContents) {
|
|
3492
|
-
return /* @__PURE__ */
|
|
4040
|
+
return /* @__PURE__ */ jsx37("div", { style: { display: "contents" }, children });
|
|
3493
4041
|
}
|
|
3494
|
-
return /* @__PURE__ */
|
|
4042
|
+
return /* @__PURE__ */ jsxs23(SimpleStack, { gap: "md", children: [
|
|
3495
4043
|
children,
|
|
3496
|
-
showExtraProperties && /* @__PURE__ */
|
|
3497
|
-
|
|
4044
|
+
showExtraProperties && /* @__PURE__ */ jsx37(ExtraPropertiesCard_default, { properties: formData, schema: resolvedSchema }),
|
|
4045
|
+
/* @__PURE__ */ jsx37(HiddenErrorNotice, { count: hiddenErrorCount }),
|
|
4046
|
+
showActions && /* @__PURE__ */ jsx37(
|
|
3498
4047
|
FormActions,
|
|
3499
4048
|
{
|
|
3500
4049
|
onSave: handleSave,
|
|
@@ -3502,6 +4051,7 @@ function FormContainer({
|
|
|
3502
4051
|
disabled,
|
|
3503
4052
|
hasChanges,
|
|
3504
4053
|
isValid,
|
|
4054
|
+
saveWhenInvalid,
|
|
3505
4055
|
saveLabel,
|
|
3506
4056
|
cancelLabel,
|
|
3507
4057
|
showCancel,
|
|
@@ -3543,6 +4093,9 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3543
4093
|
orderedProperties,
|
|
3544
4094
|
hasChanges,
|
|
3545
4095
|
isValid,
|
|
4096
|
+
fieldErrors,
|
|
4097
|
+
touched,
|
|
4098
|
+
submitAttempted,
|
|
3546
4099
|
handleFieldChange,
|
|
3547
4100
|
handleFieldBlur,
|
|
3548
4101
|
handleSave,
|
|
@@ -3560,22 +4113,23 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3560
4113
|
showActions,
|
|
3561
4114
|
deferChanges,
|
|
3562
4115
|
deferStateUpdates,
|
|
3563
|
-
allowPristineSave
|
|
4116
|
+
allowPristineSave,
|
|
4117
|
+
refResolver: props.refResolver
|
|
3564
4118
|
});
|
|
3565
4119
|
const layoutLabelPlacement = uiSchema["x-ui-layout"]?.labelPlacement;
|
|
3566
|
-
const globalOptions =
|
|
4120
|
+
const globalOptions = useMemo8(
|
|
3567
4121
|
() => ({
|
|
3568
4122
|
...layoutLabelPlacement ? { labelPlacement: layoutLabelPlacement } : {},
|
|
3569
4123
|
...uiSchema["x-ui-globalOptions"] || {}
|
|
3570
4124
|
}),
|
|
3571
4125
|
[layoutLabelPlacement, uiSchema]
|
|
3572
4126
|
);
|
|
3573
|
-
const resolvedTemplates =
|
|
4127
|
+
const resolvedTemplates = useMemo8(
|
|
3574
4128
|
() => ({ ...DEFAULT_TEMPLATES, ...templates }),
|
|
3575
4129
|
[templates]
|
|
3576
4130
|
);
|
|
3577
4131
|
const nestedRenderer = useCallback8(
|
|
3578
|
-
(props2) => /* @__PURE__ */
|
|
4132
|
+
(props2) => /* @__PURE__ */ jsx37(
|
|
3579
4133
|
JsonSchemaFormRenderer,
|
|
3580
4134
|
{
|
|
3581
4135
|
...props2,
|
|
@@ -3586,35 +4140,64 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3586
4140
|
),
|
|
3587
4141
|
[resolvedTemplates]
|
|
3588
4142
|
);
|
|
3589
|
-
const
|
|
3590
|
-
|
|
3591
|
-
|
|
4143
|
+
const rootData = useRootFormData(displayData);
|
|
4144
|
+
const contextValue = useMemo8(
|
|
4145
|
+
() => ({
|
|
4146
|
+
renderer: nestedRenderer,
|
|
4147
|
+
templates: resolvedTemplates,
|
|
4148
|
+
rootData
|
|
4149
|
+
}),
|
|
4150
|
+
[nestedRenderer, resolvedTemplates, rootData]
|
|
3592
4151
|
);
|
|
3593
|
-
const
|
|
3594
|
-
const
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
},
|
|
3613
|
-
|
|
3614
|
-
|
|
4152
|
+
const validationDisplay = useValidationDisplay(props.validationDisplay);
|
|
4153
|
+
const saveWhenInvalid = useSaveWhenInvalid(props.saveWhenInvalid);
|
|
4154
|
+
const needsBlurHandler = blurHandlerNeeded({
|
|
4155
|
+
deferStateUpdates,
|
|
4156
|
+
deferChanges,
|
|
4157
|
+
hasOnBlur: !!onBlur,
|
|
4158
|
+
validationDisplay
|
|
4159
|
+
});
|
|
4160
|
+
const showFieldErrors = useCallback8(
|
|
4161
|
+
(fieldName) => fieldErrorsVisible(
|
|
4162
|
+
validationDisplay,
|
|
4163
|
+
submitAttempted,
|
|
4164
|
+
touched[fieldName]
|
|
4165
|
+
),
|
|
4166
|
+
[validationDisplay, submitAttempted, touched]
|
|
4167
|
+
);
|
|
4168
|
+
const fields = useMemo8(
|
|
4169
|
+
() => orderedProperties.flatMap(([fieldName, property]) => {
|
|
4170
|
+
const rule = uiSchema[fieldName]?.["x-ui-rule"] ?? property["x-ui-rule"];
|
|
4171
|
+
const { visible, enabled } = evaluateUiRule(rule, rootData);
|
|
4172
|
+
if (!visible) return [];
|
|
4173
|
+
return [
|
|
4174
|
+
/* @__PURE__ */ jsx37(
|
|
4175
|
+
FormFieldRenderer,
|
|
4176
|
+
{
|
|
4177
|
+
fieldName,
|
|
4178
|
+
property,
|
|
4179
|
+
value: displayData[fieldName],
|
|
4180
|
+
onChange: (v) => handleFieldChange(fieldName, v),
|
|
4181
|
+
onBlur: needsBlurHandler ? () => handleFieldBlur(fieldName) : void 0,
|
|
4182
|
+
required: isFieldRequired(resolvedSchema, fieldName),
|
|
4183
|
+
disabled: disabled || !enabled,
|
|
4184
|
+
readOnly,
|
|
4185
|
+
schema,
|
|
4186
|
+
uiSchema: uiSchema[fieldName],
|
|
4187
|
+
registry: widgets,
|
|
4188
|
+
globalOptions,
|
|
4189
|
+
formData: displayData,
|
|
4190
|
+
idPrefix,
|
|
4191
|
+
rawErrors: showFieldErrors(fieldName) ? fieldErrors.filter((e) => pointerRoot(e.pointer) === fieldName).map((e) => e.message) : void 0
|
|
4192
|
+
},
|
|
4193
|
+
fieldName
|
|
4194
|
+
)
|
|
4195
|
+
];
|
|
4196
|
+
}),
|
|
3615
4197
|
[
|
|
3616
4198
|
orderedProperties,
|
|
3617
4199
|
displayData,
|
|
4200
|
+
rootData,
|
|
3618
4201
|
resolvedSchema,
|
|
3619
4202
|
disabled,
|
|
3620
4203
|
readOnly,
|
|
@@ -3624,18 +4207,52 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3624
4207
|
globalOptions,
|
|
3625
4208
|
idPrefix,
|
|
3626
4209
|
needsBlurHandler,
|
|
4210
|
+
showFieldErrors,
|
|
4211
|
+
fieldErrors,
|
|
3627
4212
|
handleFieldChange,
|
|
3628
4213
|
handleFieldBlur
|
|
3629
4214
|
]
|
|
3630
4215
|
);
|
|
3631
4216
|
const layoutConfig = uiSchema["x-ui-layout"];
|
|
3632
4217
|
const rowDefs = layoutConfig?.rows;
|
|
4218
|
+
const groupDefs = layoutConfig?.groups;
|
|
3633
4219
|
const defaultRowGap = layoutConfig?.gap ?? 24;
|
|
3634
|
-
const
|
|
3635
|
-
|
|
3636
|
-
|
|
4220
|
+
const groupTemplate = resolveGroupTemplate(
|
|
4221
|
+
resolvedTemplates,
|
|
4222
|
+
layoutConfig?.variant
|
|
3637
4223
|
);
|
|
3638
|
-
|
|
4224
|
+
const layoutFields = useMemo8(() => {
|
|
4225
|
+
if (groupDefs && groupDefs.length > 0) {
|
|
4226
|
+
return layoutFieldsInGroups(
|
|
4227
|
+
fields,
|
|
4228
|
+
groupDefs,
|
|
4229
|
+
defaultRowGap,
|
|
4230
|
+
fieldErrors,
|
|
4231
|
+
// Tab rules are x-ui-rule too: root-anchored like field rules.
|
|
4232
|
+
rootData,
|
|
4233
|
+
groupTemplate
|
|
4234
|
+
);
|
|
4235
|
+
}
|
|
4236
|
+
if (rowDefs && rowDefs.length > 0) {
|
|
4237
|
+
return layoutFieldsInRows(fields, rowDefs, defaultRowGap);
|
|
4238
|
+
}
|
|
4239
|
+
return fields;
|
|
4240
|
+
}, [
|
|
4241
|
+
fields,
|
|
4242
|
+
rowDefs,
|
|
4243
|
+
groupDefs,
|
|
4244
|
+
defaultRowGap,
|
|
4245
|
+
fieldErrors,
|
|
4246
|
+
rootData,
|
|
4247
|
+
groupTemplate
|
|
4248
|
+
]);
|
|
4249
|
+
const hiddenErrorCount = useMemo8(() => {
|
|
4250
|
+
if (!groupDefs?.length || fieldErrors.length === 0) return 0;
|
|
4251
|
+
const hidden = hiddenGroupFieldNames(groupDefs, rootData);
|
|
4252
|
+
if (hidden.size === 0) return 0;
|
|
4253
|
+
return fieldErrors.filter((e) => hidden.has(pointerRoot(e.pointer))).length;
|
|
4254
|
+
}, [groupDefs, fieldErrors, rootData]);
|
|
4255
|
+
return /* @__PURE__ */ jsx37(NestedFormContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx37(
|
|
3639
4256
|
FormContainer,
|
|
3640
4257
|
{
|
|
3641
4258
|
displayContents,
|
|
@@ -3648,17 +4265,19 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3648
4265
|
disabled,
|
|
3649
4266
|
hasChanges: hasChanges || allowPristineSave,
|
|
3650
4267
|
isValid,
|
|
4268
|
+
saveWhenInvalid,
|
|
3651
4269
|
saveLabel,
|
|
3652
4270
|
cancelLabel,
|
|
3653
4271
|
showCancel,
|
|
3654
4272
|
leadingActions,
|
|
4273
|
+
hiddenErrorCount,
|
|
3655
4274
|
children: layoutFields
|
|
3656
4275
|
}
|
|
3657
4276
|
) });
|
|
3658
4277
|
}
|
|
3659
4278
|
|
|
3660
4279
|
// src/hooks/useResolvedSchema.ts
|
|
3661
|
-
import { useMemo as
|
|
4280
|
+
import { useMemo as useMemo9 } from "react";
|
|
3662
4281
|
import {
|
|
3663
4282
|
getSchemaProperties as getSchemaProperties2,
|
|
3664
4283
|
resolveSchema as resolveSchema2
|
|
@@ -3668,11 +4287,11 @@ function useResolvedSchema({
|
|
|
3668
4287
|
formData,
|
|
3669
4288
|
uiSchema
|
|
3670
4289
|
}) {
|
|
3671
|
-
const resolvedSchema =
|
|
4290
|
+
const resolvedSchema = useMemo9(
|
|
3672
4291
|
() => resolveSchema2(schema, formData),
|
|
3673
4292
|
[schema, formData]
|
|
3674
4293
|
);
|
|
3675
|
-
const orderedProperties =
|
|
4294
|
+
const orderedProperties = useMemo9(() => {
|
|
3676
4295
|
const properties = getSchemaProperties2(resolvedSchema);
|
|
3677
4296
|
const rawOrder = uiSchema["x-ui-order"] ?? resolvedSchema["x-ui-order"];
|
|
3678
4297
|
const orderArray = Array.isArray(rawOrder) ? rawOrder : void 0;
|
|
@@ -3686,6 +4305,9 @@ export {
|
|
|
3686
4305
|
DEFAULT_TEMPLATES,
|
|
3687
4306
|
DEFAULT_WIDGETS,
|
|
3688
4307
|
DEFAULT_WIDGET_MAP,
|
|
4308
|
+
DateTimeWidget,
|
|
4309
|
+
DateWidget,
|
|
4310
|
+
DisplayWidget,
|
|
3689
4311
|
EntitySelectWidget,
|
|
3690
4312
|
ExtraPropertiesCard_default as ExtraPropertiesCard,
|
|
3691
4313
|
FormActions,
|
|
@@ -3699,18 +4321,25 @@ export {
|
|
|
3699
4321
|
FormSelect,
|
|
3700
4322
|
FormTagsInput,
|
|
3701
4323
|
FormTextarea,
|
|
4324
|
+
GroupErrorBadge,
|
|
3702
4325
|
JsonEditorWidget,
|
|
3703
4326
|
JsonSchemaFormRenderer,
|
|
3704
4327
|
NumberWidget,
|
|
3705
4328
|
ObjectWidget,
|
|
4329
|
+
OneOfWidget,
|
|
3706
4330
|
QuantityWidget,
|
|
4331
|
+
SectionsGroupTemplate,
|
|
3707
4332
|
SelectWidget,
|
|
4333
|
+
StepperGroupTemplate,
|
|
4334
|
+
TabsGroupTemplate,
|
|
3708
4335
|
TagsInputWidget,
|
|
3709
4336
|
TextInputWidget,
|
|
3710
4337
|
TextareaWidget,
|
|
4338
|
+
TimeWidget,
|
|
3711
4339
|
firstError,
|
|
3712
4340
|
getAriaDescribedBy,
|
|
3713
4341
|
getDefaultWidgetForType,
|
|
4342
|
+
groupTemplateKey,
|
|
3714
4343
|
resolveWidget,
|
|
3715
4344
|
useFormEngine,
|
|
3716
4345
|
useNestedFormContext,
|