@petrarca/sonnet-forms 0.5.0 → 0.6.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 +192 -3
- package/dist/index.js +658 -114
- 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
|
@@ -1533,12 +1533,13 @@ function ExtraPropertiesCard({ properties, schema }) {
|
|
|
1533
1533
|
var ExtraPropertiesCard_default = ExtraPropertiesCard;
|
|
1534
1534
|
|
|
1535
1535
|
// src/JsonSchemaFormRenderer.tsx
|
|
1536
|
-
import
|
|
1536
|
+
import React13, { useCallback as useCallback8, useMemo as useMemo8 } from "react";
|
|
1537
1537
|
import { SimpleStack } from "@petrarca/sonnet-ui";
|
|
1538
|
+
import { AlertCircle as AlertCircle2 } from "lucide-react";
|
|
1538
1539
|
import {
|
|
1539
1540
|
WIDTH_TOKEN_REMS
|
|
1540
1541
|
} from "@petrarca/sonnet-core/schema";
|
|
1541
|
-
import { isFieldRequired } from "@petrarca/sonnet-core/schema";
|
|
1542
|
+
import { isFieldRequired, evaluateUiRule } from "@petrarca/sonnet-core/schema";
|
|
1542
1543
|
import { FORM_ITEM_ID_FIELD as FORM_ITEM_ID_FIELD2 } from "@petrarca/sonnet-core";
|
|
1543
1544
|
|
|
1544
1545
|
// src/widgets/types.ts
|
|
@@ -1794,6 +1795,7 @@ function SelectWidget(props) {
|
|
|
1794
1795
|
const enumNames = widgetOptions.enumNames || {};
|
|
1795
1796
|
const clearable = widgetOptions.clearable !== false && !required;
|
|
1796
1797
|
const isMultiple = widgetOptions.multiple ?? propertySchema.type === "array";
|
|
1798
|
+
const creatable = !!widgetOptions.creatable;
|
|
1797
1799
|
const selectOptions = buildSelectOptions(propertySchema, enumNames);
|
|
1798
1800
|
const error = firstError(rawErrors);
|
|
1799
1801
|
const compact = widgetOptions?.compact;
|
|
@@ -1844,7 +1846,9 @@ function SelectWidget(props) {
|
|
|
1844
1846
|
autoFocus: autofocus,
|
|
1845
1847
|
onBlur: handleBlur,
|
|
1846
1848
|
className: triggerClassName,
|
|
1847
|
-
compact
|
|
1849
|
+
compact,
|
|
1850
|
+
allowCreate: creatable,
|
|
1851
|
+
onCreate: (label2) => onChange(label2)
|
|
1848
1852
|
}
|
|
1849
1853
|
);
|
|
1850
1854
|
}
|
|
@@ -2696,9 +2700,309 @@ function QuantityWidget({
|
|
|
2696
2700
|
);
|
|
2697
2701
|
}
|
|
2698
2702
|
|
|
2703
|
+
// src/widgets/DateWidget.tsx
|
|
2704
|
+
import { DateInput } from "@petrarca/sonnet-ui";
|
|
2705
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2706
|
+
function resolvePrecision(value) {
|
|
2707
|
+
return value === "year" || value === "month" ? value : "day";
|
|
2708
|
+
}
|
|
2709
|
+
function DateWidget({
|
|
2710
|
+
id,
|
|
2711
|
+
value,
|
|
2712
|
+
onChange,
|
|
2713
|
+
onBlur,
|
|
2714
|
+
required,
|
|
2715
|
+
disabled,
|
|
2716
|
+
rawErrors,
|
|
2717
|
+
label,
|
|
2718
|
+
description,
|
|
2719
|
+
placeholder,
|
|
2720
|
+
options
|
|
2721
|
+
}) {
|
|
2722
|
+
const error = firstError(rawErrors);
|
|
2723
|
+
const precision = resolvePrecision(options?.precision);
|
|
2724
|
+
const locale = typeof options?.locale === "string" ? options.locale : void 0;
|
|
2725
|
+
return /* @__PURE__ */ jsx26(
|
|
2726
|
+
FormFieldWrapper,
|
|
2727
|
+
{
|
|
2728
|
+
inputId: id,
|
|
2729
|
+
label,
|
|
2730
|
+
description,
|
|
2731
|
+
required,
|
|
2732
|
+
labelPlacement: options?.labelPlacement,
|
|
2733
|
+
children: /* @__PURE__ */ jsx26(
|
|
2734
|
+
DateInput,
|
|
2735
|
+
{
|
|
2736
|
+
id,
|
|
2737
|
+
value: typeof value === "string" ? value : "",
|
|
2738
|
+
onChange,
|
|
2739
|
+
onBlur: () => onBlur?.(id, value),
|
|
2740
|
+
precision,
|
|
2741
|
+
locale,
|
|
2742
|
+
placeholder,
|
|
2743
|
+
required,
|
|
2744
|
+
disabled,
|
|
2745
|
+
error
|
|
2746
|
+
}
|
|
2747
|
+
)
|
|
2748
|
+
}
|
|
2749
|
+
);
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/widgets/TimeWidget.tsx
|
|
2753
|
+
import { TimeInput } from "@petrarca/sonnet-ui";
|
|
2754
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2755
|
+
function resolveGranularity(value) {
|
|
2756
|
+
return value === "second" ? "second" : "minute";
|
|
2757
|
+
}
|
|
2758
|
+
function TimeWidget({
|
|
2759
|
+
id,
|
|
2760
|
+
value,
|
|
2761
|
+
onChange,
|
|
2762
|
+
onBlur,
|
|
2763
|
+
required,
|
|
2764
|
+
disabled,
|
|
2765
|
+
rawErrors,
|
|
2766
|
+
label,
|
|
2767
|
+
description,
|
|
2768
|
+
placeholder,
|
|
2769
|
+
options
|
|
2770
|
+
}) {
|
|
2771
|
+
const error = firstError(rawErrors);
|
|
2772
|
+
const granularity = resolveGranularity(options?.granularity);
|
|
2773
|
+
return /* @__PURE__ */ jsx27(
|
|
2774
|
+
FormFieldWrapper,
|
|
2775
|
+
{
|
|
2776
|
+
inputId: id,
|
|
2777
|
+
label,
|
|
2778
|
+
description,
|
|
2779
|
+
required,
|
|
2780
|
+
labelPlacement: options?.labelPlacement,
|
|
2781
|
+
children: /* @__PURE__ */ jsx27(
|
|
2782
|
+
TimeInput,
|
|
2783
|
+
{
|
|
2784
|
+
id,
|
|
2785
|
+
value: typeof value === "string" ? value : "",
|
|
2786
|
+
onChange,
|
|
2787
|
+
onBlur: () => onBlur?.(id, value),
|
|
2788
|
+
granularity,
|
|
2789
|
+
placeholder,
|
|
2790
|
+
required,
|
|
2791
|
+
disabled,
|
|
2792
|
+
error
|
|
2793
|
+
}
|
|
2794
|
+
)
|
|
2795
|
+
}
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// src/widgets/DateTimeWidget.tsx
|
|
2800
|
+
import { DateTimeInput } from "@petrarca/sonnet-ui";
|
|
2801
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2802
|
+
function resolveGranularity2(value) {
|
|
2803
|
+
return value === "second" ? "second" : "minute";
|
|
2804
|
+
}
|
|
2805
|
+
function DateTimeWidget({
|
|
2806
|
+
id,
|
|
2807
|
+
value,
|
|
2808
|
+
onChange,
|
|
2809
|
+
onBlur,
|
|
2810
|
+
required,
|
|
2811
|
+
disabled,
|
|
2812
|
+
rawErrors,
|
|
2813
|
+
label,
|
|
2814
|
+
description,
|
|
2815
|
+
options
|
|
2816
|
+
}) {
|
|
2817
|
+
const error = firstError(rawErrors);
|
|
2818
|
+
const granularity = resolveGranularity2(options?.granularity);
|
|
2819
|
+
const timeZone = typeof options?.timeZone === "string" ? options.timeZone : void 0;
|
|
2820
|
+
const locale = typeof options?.locale === "string" ? options.locale : void 0;
|
|
2821
|
+
return /* @__PURE__ */ jsx28(
|
|
2822
|
+
FormFieldWrapper,
|
|
2823
|
+
{
|
|
2824
|
+
inputId: id,
|
|
2825
|
+
label,
|
|
2826
|
+
description,
|
|
2827
|
+
required,
|
|
2828
|
+
labelPlacement: options?.labelPlacement,
|
|
2829
|
+
children: /* @__PURE__ */ jsx28(
|
|
2830
|
+
DateTimeInput,
|
|
2831
|
+
{
|
|
2832
|
+
idPrefix: id,
|
|
2833
|
+
value: typeof value === "string" ? value : "",
|
|
2834
|
+
onChange,
|
|
2835
|
+
onBlur: () => onBlur?.(id, value),
|
|
2836
|
+
granularity,
|
|
2837
|
+
timeZone,
|
|
2838
|
+
locale,
|
|
2839
|
+
required,
|
|
2840
|
+
disabled,
|
|
2841
|
+
error
|
|
2842
|
+
}
|
|
2843
|
+
)
|
|
2844
|
+
}
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// src/widgets/DisplayWidget.tsx
|
|
2849
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2850
|
+
function DisplayWidget({
|
|
2851
|
+
label,
|
|
2852
|
+
description
|
|
2853
|
+
}) {
|
|
2854
|
+
if (!label && !description) return null;
|
|
2855
|
+
return /* @__PURE__ */ jsxs17("div", { className: "space-y-1", children: [
|
|
2856
|
+
label && /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground", children: label }),
|
|
2857
|
+
description && /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: description })
|
|
2858
|
+
] });
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
// src/widgets/OneOfWidget.tsx
|
|
2862
|
+
import { useMemo as useMemo6 } from "react";
|
|
2863
|
+
import {
|
|
2864
|
+
getDiscriminator,
|
|
2865
|
+
getBranchValue,
|
|
2866
|
+
getBranchLabel,
|
|
2867
|
+
findActiveBranch
|
|
2868
|
+
} from "@petrarca/sonnet-core/schema";
|
|
2869
|
+
import {
|
|
2870
|
+
Label,
|
|
2871
|
+
Select,
|
|
2872
|
+
SelectTrigger,
|
|
2873
|
+
SelectValue,
|
|
2874
|
+
SelectContent,
|
|
2875
|
+
SelectItem
|
|
2876
|
+
} from "@petrarca/sonnet-ui";
|
|
2877
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2878
|
+
function buildChoices(branches, discriminator) {
|
|
2879
|
+
return branches.map((branch, index) => {
|
|
2880
|
+
const raw = discriminator ? getBranchValue(branch, discriminator) : void 0;
|
|
2881
|
+
return {
|
|
2882
|
+
index,
|
|
2883
|
+
value: raw !== void 0 ? String(raw) : String(index),
|
|
2884
|
+
label: getBranchLabel(branch, discriminator, index)
|
|
2885
|
+
};
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
function buildBranchSchema(branch) {
|
|
2889
|
+
if (!branch.properties) return null;
|
|
2890
|
+
return {
|
|
2891
|
+
title: branch.title || "",
|
|
2892
|
+
type: "object",
|
|
2893
|
+
properties: branch.properties,
|
|
2894
|
+
required: branch.required,
|
|
2895
|
+
...branch["x-ui-order"] ? { "x-ui-order": branch["x-ui-order"] } : {}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
function branchChangeData(choices, branches, discriminator, selected) {
|
|
2899
|
+
const choice = choices.find((c) => c.value === selected);
|
|
2900
|
+
if (!choice || !discriminator) return {};
|
|
2901
|
+
const discValue = getBranchValue(branches[choice.index], discriminator) ?? selected;
|
|
2902
|
+
return { [discriminator]: discValue };
|
|
2903
|
+
}
|
|
2904
|
+
function BranchSelector({
|
|
2905
|
+
id,
|
|
2906
|
+
label,
|
|
2907
|
+
description,
|
|
2908
|
+
choices,
|
|
2909
|
+
activeValue,
|
|
2910
|
+
disabled,
|
|
2911
|
+
onSelect
|
|
2912
|
+
}) {
|
|
2913
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-1", children: [
|
|
2914
|
+
label && /* @__PURE__ */ jsx30(Label, { htmlFor: id, children: label }),
|
|
2915
|
+
/* @__PURE__ */ jsxs18(Select, { value: activeValue, onValueChange: onSelect, disabled, children: [
|
|
2916
|
+
/* @__PURE__ */ jsx30(SelectTrigger, { id, children: /* @__PURE__ */ jsx30(SelectValue, { placeholder: "Select an option" }) }),
|
|
2917
|
+
/* @__PURE__ */ jsx30(SelectContent, { children: choices.map((c) => /* @__PURE__ */ jsx30(SelectItem, { value: c.value, children: c.label }, c.value)) })
|
|
2918
|
+
] }),
|
|
2919
|
+
description && /* @__PURE__ */ jsx30("p", { className: "text-sm text-muted-foreground", children: description })
|
|
2920
|
+
] });
|
|
2921
|
+
}
|
|
2922
|
+
function resolveActive(propertySchema, branches, choices, discriminator, data) {
|
|
2923
|
+
const index = findActiveBranch(propertySchema, data, discriminator);
|
|
2924
|
+
if (index < 0) return { value: "", label: "", subSchema: null };
|
|
2925
|
+
return {
|
|
2926
|
+
value: choices[index]?.value ?? "",
|
|
2927
|
+
label: choices[index]?.label ?? "",
|
|
2928
|
+
subSchema: buildBranchSchema(branches[index])
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
function OneOfWidget({
|
|
2932
|
+
propertySchema,
|
|
2933
|
+
value,
|
|
2934
|
+
onChange,
|
|
2935
|
+
registry,
|
|
2936
|
+
uiSchema,
|
|
2937
|
+
disabled,
|
|
2938
|
+
readonly,
|
|
2939
|
+
label,
|
|
2940
|
+
description,
|
|
2941
|
+
id,
|
|
2942
|
+
options
|
|
2943
|
+
}) {
|
|
2944
|
+
const { renderer: Renderer, templates } = useNestedFormContext();
|
|
2945
|
+
const ContainerTemplate = templates.ObjectContainerTemplate ?? DefaultObjectContainerTemplate;
|
|
2946
|
+
const branches = useMemo6(() => propertySchema.oneOf ?? [], [propertySchema]);
|
|
2947
|
+
const discriminator = useMemo6(
|
|
2948
|
+
() => getDiscriminator(propertySchema),
|
|
2949
|
+
[propertySchema]
|
|
2950
|
+
);
|
|
2951
|
+
const choices = useMemo6(
|
|
2952
|
+
() => buildChoices(branches, discriminator),
|
|
2953
|
+
[branches, discriminator]
|
|
2954
|
+
);
|
|
2955
|
+
const data = value || {};
|
|
2956
|
+
const active = resolveActive(
|
|
2957
|
+
propertySchema,
|
|
2958
|
+
branches,
|
|
2959
|
+
choices,
|
|
2960
|
+
discriminator,
|
|
2961
|
+
data
|
|
2962
|
+
);
|
|
2963
|
+
if (branches.length === 0) return null;
|
|
2964
|
+
const subUiSchema = uiSchema?.["x-ui-fields"] ?? {};
|
|
2965
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-4", children: [
|
|
2966
|
+
/* @__PURE__ */ jsx30(
|
|
2967
|
+
BranchSelector,
|
|
2968
|
+
{
|
|
2969
|
+
id,
|
|
2970
|
+
label,
|
|
2971
|
+
description,
|
|
2972
|
+
choices,
|
|
2973
|
+
activeValue: active.value,
|
|
2974
|
+
disabled: disabled || readonly,
|
|
2975
|
+
onSelect: (selected) => onChange(branchChangeData(choices, branches, discriminator, selected))
|
|
2976
|
+
}
|
|
2977
|
+
),
|
|
2978
|
+
active.subSchema && /* @__PURE__ */ jsx30(
|
|
2979
|
+
ContainerTemplate,
|
|
2980
|
+
{
|
|
2981
|
+
label: active.label,
|
|
2982
|
+
description: void 0,
|
|
2983
|
+
variant: options?.variant || "section",
|
|
2984
|
+
defaultOpen: true,
|
|
2985
|
+
children: /* @__PURE__ */ jsx30(
|
|
2986
|
+
Renderer,
|
|
2987
|
+
{
|
|
2988
|
+
schema: active.subSchema,
|
|
2989
|
+
data,
|
|
2990
|
+
onChange,
|
|
2991
|
+
disabled,
|
|
2992
|
+
readOnly: readonly,
|
|
2993
|
+
widgets: registry,
|
|
2994
|
+
uiSchema: subUiSchema
|
|
2995
|
+
}
|
|
2996
|
+
)
|
|
2997
|
+
}
|
|
2998
|
+
)
|
|
2999
|
+
] });
|
|
3000
|
+
}
|
|
3001
|
+
|
|
2699
3002
|
// src/widgets/widgetResolver.ts
|
|
2700
3003
|
import {
|
|
2701
3004
|
isNullableBoolean as isNullableBoolean2,
|
|
3005
|
+
isOneOf,
|
|
2702
3006
|
extractEnumValues as extractEnumValues2,
|
|
2703
3007
|
getFieldType
|
|
2704
3008
|
} from "@petrarca/sonnet-core/schema";
|
|
@@ -2710,8 +3014,11 @@ var DEFAULT_WIDGET_MAP = {
|
|
|
2710
3014
|
// Uses email input type
|
|
2711
3015
|
uri: "TextInputWidget",
|
|
2712
3016
|
// Uses url input type
|
|
2713
|
-
url: "TextInputWidget"
|
|
3017
|
+
url: "TextInputWidget",
|
|
2714
3018
|
// Uses url input type
|
|
3019
|
+
date: "DateWidget",
|
|
3020
|
+
"date-time": "DateTimeWidget",
|
|
3021
|
+
time: "TimeWidget"
|
|
2715
3022
|
},
|
|
2716
3023
|
number: {
|
|
2717
3024
|
default: "NumberWidget"
|
|
@@ -2722,6 +3029,9 @@ var DEFAULT_WIDGET_MAP = {
|
|
|
2722
3029
|
},
|
|
2723
3030
|
boolean: {
|
|
2724
3031
|
default: "CheckboxWidget"
|
|
3032
|
+
},
|
|
3033
|
+
null: {
|
|
3034
|
+
default: "DisplayWidget"
|
|
2725
3035
|
}
|
|
2726
3036
|
};
|
|
2727
3037
|
function resolveWidgetByName(widgetName, propertySchema, registry) {
|
|
@@ -2771,6 +3081,7 @@ function registryLookup(registry, widgetName) {
|
|
|
2771
3081
|
return registry[widgetName] || registry["TextInputWidget"];
|
|
2772
3082
|
}
|
|
2773
3083
|
function resolveFromSchemaStructure(schema, registry) {
|
|
3084
|
+
if (isOneOf(schema)) return registryLookup(registry, "OneOfWidget");
|
|
2774
3085
|
if (isNullableBoolean2(schema))
|
|
2775
3086
|
return registryLookup(registry, "CheckboxWidget");
|
|
2776
3087
|
if (extractEnumValues2(schema).length > 0)
|
|
@@ -2814,6 +3125,11 @@ var DEFAULT_WIDGETS = {
|
|
|
2814
3125
|
ObjectWidget,
|
|
2815
3126
|
ArrayWidget,
|
|
2816
3127
|
QuantityWidget,
|
|
3128
|
+
DateWidget,
|
|
3129
|
+
TimeWidget,
|
|
3130
|
+
DateTimeWidget,
|
|
3131
|
+
DisplayWidget,
|
|
3132
|
+
OneOfWidget,
|
|
2817
3133
|
// Lowercase aliases for x-ui-widget convenience
|
|
2818
3134
|
text: TextInputWidget,
|
|
2819
3135
|
textarea: TextareaWidget,
|
|
@@ -2825,13 +3141,18 @@ var DEFAULT_WIDGETS = {
|
|
|
2825
3141
|
"entity-select": EntitySelectWidget,
|
|
2826
3142
|
object: ObjectWidget,
|
|
2827
3143
|
array: ArrayWidget,
|
|
2828
|
-
quantity: QuantityWidget
|
|
3144
|
+
quantity: QuantityWidget,
|
|
3145
|
+
date: DateWidget,
|
|
3146
|
+
time: TimeWidget,
|
|
3147
|
+
"date-time": DateTimeWidget,
|
|
3148
|
+
display: DisplayWidget,
|
|
3149
|
+
"one-of": OneOfWidget
|
|
2829
3150
|
};
|
|
2830
3151
|
|
|
2831
3152
|
// src/templates/HorizontalArrayItemTemplate.tsx
|
|
2832
3153
|
import { Trash2 as Trash22, ChevronUp as ChevronUp2, ChevronDown as ChevronDown4 } from "lucide-react";
|
|
2833
3154
|
import { Button as Button6 } from "@petrarca/sonnet-ui";
|
|
2834
|
-
import { jsx as
|
|
3155
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2835
3156
|
function HorizontalArrayItemTemplate({
|
|
2836
3157
|
children,
|
|
2837
3158
|
index,
|
|
@@ -2855,7 +3176,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2855
3176
|
);
|
|
2856
3177
|
const gap = resolvedLayout.gap ?? 8;
|
|
2857
3178
|
const alignItems = resolvedLayout.compact ? "center" : "start";
|
|
2858
|
-
return /* @__PURE__ */
|
|
3179
|
+
return /* @__PURE__ */ jsxs19(
|
|
2859
3180
|
"div",
|
|
2860
3181
|
{
|
|
2861
3182
|
"data-fid": fid,
|
|
@@ -2867,8 +3188,8 @@ function HorizontalArrayItemTemplate({
|
|
|
2867
3188
|
},
|
|
2868
3189
|
children: [
|
|
2869
3190
|
children,
|
|
2870
|
-
orderable && /* @__PURE__ */
|
|
2871
|
-
/* @__PURE__ */
|
|
3191
|
+
orderable && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center justify-center", children: [
|
|
3192
|
+
/* @__PURE__ */ jsx31(
|
|
2872
3193
|
Button6,
|
|
2873
3194
|
{
|
|
2874
3195
|
type: "button",
|
|
@@ -2878,10 +3199,10 @@ function HorizontalArrayItemTemplate({
|
|
|
2878
3199
|
onClick: onMoveUp,
|
|
2879
3200
|
title: "Move up",
|
|
2880
3201
|
className: "h-4 w-8 p-0",
|
|
2881
|
-
children: /* @__PURE__ */
|
|
3202
|
+
children: /* @__PURE__ */ jsx31(ChevronUp2, { className: "h-3 w-3" })
|
|
2882
3203
|
}
|
|
2883
3204
|
),
|
|
2884
|
-
/* @__PURE__ */
|
|
3205
|
+
/* @__PURE__ */ jsx31(
|
|
2885
3206
|
Button6,
|
|
2886
3207
|
{
|
|
2887
3208
|
type: "button",
|
|
@@ -2891,11 +3212,11 @@ function HorizontalArrayItemTemplate({
|
|
|
2891
3212
|
onClick: onMoveDown,
|
|
2892
3213
|
title: "Move down",
|
|
2893
3214
|
className: "h-4 w-8 p-0",
|
|
2894
|
-
children: /* @__PURE__ */
|
|
3215
|
+
children: /* @__PURE__ */ jsx31(ChevronDown4, { className: "h-3 w-3" })
|
|
2895
3216
|
}
|
|
2896
3217
|
)
|
|
2897
3218
|
] }),
|
|
2898
|
-
canRemove && /* @__PURE__ */
|
|
3219
|
+
canRemove && /* @__PURE__ */ jsx31(
|
|
2899
3220
|
Button6,
|
|
2900
3221
|
{
|
|
2901
3222
|
type: "button",
|
|
@@ -2905,7 +3226,7 @@ function HorizontalArrayItemTemplate({
|
|
|
2905
3226
|
onClick: onRemove,
|
|
2906
3227
|
title: "Remove item",
|
|
2907
3228
|
className: "text-muted-foreground hover:text-destructive h-8 w-8 p-0",
|
|
2908
|
-
children: /* @__PURE__ */
|
|
3229
|
+
children: /* @__PURE__ */ jsx31(Trash22, { className: "h-3.5 w-3.5" })
|
|
2909
3230
|
}
|
|
2910
3231
|
)
|
|
2911
3232
|
]
|
|
@@ -2913,13 +3234,123 @@ function HorizontalArrayItemTemplate({
|
|
|
2913
3234
|
);
|
|
2914
3235
|
}
|
|
2915
3236
|
|
|
3237
|
+
// src/templates/TabsGroupTemplate.tsx
|
|
3238
|
+
import { useState as useState7 } from "react";
|
|
3239
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@petrarca/sonnet-ui";
|
|
3240
|
+
|
|
3241
|
+
// src/templates/GroupErrorBadge.tsx
|
|
3242
|
+
import { AlertCircle } from "lucide-react";
|
|
3243
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
3244
|
+
function GroupErrorBadge({
|
|
3245
|
+
count
|
|
3246
|
+
}) {
|
|
3247
|
+
if (count === 0) return null;
|
|
3248
|
+
const label = count === 1 ? "1 field needs attention" : `${count} fields need attention`;
|
|
3249
|
+
return /* @__PURE__ */ jsx32(
|
|
3250
|
+
"span",
|
|
3251
|
+
{
|
|
3252
|
+
role: "img",
|
|
3253
|
+
"aria-label": label,
|
|
3254
|
+
title: label,
|
|
3255
|
+
className: "inline-flex shrink-0 text-destructive",
|
|
3256
|
+
children: /* @__PURE__ */ jsx32(AlertCircle, { className: "h-3.5 w-3.5" })
|
|
3257
|
+
}
|
|
3258
|
+
);
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
// src/templates/TabsGroupTemplate.tsx
|
|
3262
|
+
import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3263
|
+
function TabsGroupTemplate({
|
|
3264
|
+
groups
|
|
3265
|
+
}) {
|
|
3266
|
+
const [active, setActive] = useState7(groups[0].key);
|
|
3267
|
+
const value = groups.some((g) => g.key === active) ? active : groups[0].key;
|
|
3268
|
+
return /* @__PURE__ */ jsxs20(Tabs, { value, onValueChange: setActive, children: [
|
|
3269
|
+
/* @__PURE__ */ jsx33(TabsList, { className: "mb-4", children: groups.map((g) => /* @__PURE__ */ jsxs20(TabsTrigger, { value: g.key, className: "gap-1.5", children: [
|
|
3270
|
+
g.label,
|
|
3271
|
+
/* @__PURE__ */ jsx33(GroupErrorBadge, { count: g.errorCount })
|
|
3272
|
+
] }, g.key)) }),
|
|
3273
|
+
groups.map((g) => /* @__PURE__ */ jsx33(TabsContent, { value: g.key, className: "space-y-4", children: g.content }, g.key))
|
|
3274
|
+
] });
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
// src/templates/StepperGroupTemplate.tsx
|
|
3278
|
+
import { useState as useState8 } from "react";
|
|
3279
|
+
import {
|
|
3280
|
+
Button as Button7,
|
|
3281
|
+
Stepper,
|
|
3282
|
+
StepperList,
|
|
3283
|
+
StepperItem,
|
|
3284
|
+
StepperTrigger,
|
|
3285
|
+
StepperIndicator,
|
|
3286
|
+
StepperSeparator,
|
|
3287
|
+
StepperTitle,
|
|
3288
|
+
StepperContent,
|
|
3289
|
+
StepperPrev,
|
|
3290
|
+
StepperNext
|
|
3291
|
+
} from "@petrarca/sonnet-ui";
|
|
3292
|
+
import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3293
|
+
function StepperGroupTemplate({
|
|
3294
|
+
groups
|
|
3295
|
+
}) {
|
|
3296
|
+
const [active, setActive] = useState8(groups[0].key);
|
|
3297
|
+
const value = groups.some((g) => g.key === active) ? active : groups[0].key;
|
|
3298
|
+
const isLast = value === groups[groups.length - 1].key;
|
|
3299
|
+
return /* @__PURE__ */ jsxs21(Stepper, { value, onValueChange: setActive, children: [
|
|
3300
|
+
/* @__PURE__ */ jsx34(StepperList, { className: "mb-4 gap-2", children: groups.map((g) => /* @__PURE__ */ jsxs21(StepperItem, { value: g.key, children: [
|
|
3301
|
+
/* @__PURE__ */ jsxs21(StepperTrigger, { children: [
|
|
3302
|
+
/* @__PURE__ */ jsx34(StepperIndicator, {}),
|
|
3303
|
+
/* @__PURE__ */ jsxs21(StepperTitle, { className: "inline-flex items-center gap-1.5", children: [
|
|
3304
|
+
g.label,
|
|
3305
|
+
/* @__PURE__ */ jsx34(GroupErrorBadge, { count: g.errorCount })
|
|
3306
|
+
] })
|
|
3307
|
+
] }),
|
|
3308
|
+
/* @__PURE__ */ jsx34(StepperSeparator, {})
|
|
3309
|
+
] }, g.key)) }),
|
|
3310
|
+
groups.map((g) => /* @__PURE__ */ jsx34(StepperContent, { value: g.key, className: "space-y-4", children: g.content }, g.key)),
|
|
3311
|
+
/* @__PURE__ */ jsxs21("div", { className: "mt-4 flex justify-between", children: [
|
|
3312
|
+
/* @__PURE__ */ jsx34(StepperPrev, { asChild: true, children: /* @__PURE__ */ jsx34(Button7, { variant: "outline", size: "sm", children: "Back" }) }),
|
|
3313
|
+
!isLast && /* @__PURE__ */ jsx34(StepperNext, { asChild: true, children: /* @__PURE__ */ jsx34(Button7, { size: "sm", children: "Next" }) })
|
|
3314
|
+
] })
|
|
3315
|
+
] });
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
// src/templates/SectionsGroupTemplate.tsx
|
|
3319
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3320
|
+
function SectionsGroupTemplate({
|
|
3321
|
+
groups
|
|
3322
|
+
}) {
|
|
3323
|
+
return /* @__PURE__ */ jsx35("div", { className: "space-y-6", children: groups.map((g) => /* @__PURE__ */ jsxs22(
|
|
3324
|
+
"fieldset",
|
|
3325
|
+
{
|
|
3326
|
+
className: "rounded-md border border-border/60 px-3 pb-3 pt-1",
|
|
3327
|
+
children: [
|
|
3328
|
+
/* @__PURE__ */ jsxs22("legend", { className: "inline-flex items-center gap-1.5 px-1 text-sm font-medium text-muted-foreground", children: [
|
|
3329
|
+
g.label,
|
|
3330
|
+
/* @__PURE__ */ jsx35(GroupErrorBadge, { count: g.errorCount })
|
|
3331
|
+
] }),
|
|
3332
|
+
/* @__PURE__ */ jsx35("div", { className: "space-y-4", children: g.content })
|
|
3333
|
+
]
|
|
3334
|
+
},
|
|
3335
|
+
g.key
|
|
3336
|
+
)) });
|
|
3337
|
+
}
|
|
3338
|
+
|
|
2916
3339
|
// src/templates/index.ts
|
|
2917
3340
|
var DEFAULT_TEMPLATES = {
|
|
2918
3341
|
ArrayItemTemplate: DefaultArrayItemTemplate,
|
|
2919
3342
|
HorizontalArrayItemTemplate,
|
|
2920
3343
|
ArrayHeaderTemplate: DefaultArrayHeaderTemplate,
|
|
2921
|
-
ObjectContainerTemplate: DefaultObjectContainerTemplate
|
|
3344
|
+
ObjectContainerTemplate: DefaultObjectContainerTemplate,
|
|
3345
|
+
TabsGroupTemplate,
|
|
3346
|
+
StepperGroupTemplate,
|
|
3347
|
+
SectionsGroupTemplate
|
|
2922
3348
|
};
|
|
3349
|
+
function groupTemplateKey(variant) {
|
|
3350
|
+
if (!variant) return "TabsGroupTemplate";
|
|
3351
|
+
const name = variant.charAt(0).toUpperCase() + variant.slice(1);
|
|
3352
|
+
return `${name}GroupTemplate`;
|
|
3353
|
+
}
|
|
2923
3354
|
|
|
2924
3355
|
// src/FormFieldRenderer.tsx
|
|
2925
3356
|
import { warnLog } from "@petrarca/sonnet-core";
|
|
@@ -2928,7 +3359,7 @@ import {
|
|
|
2928
3359
|
getFieldHelpText,
|
|
2929
3360
|
getFieldPlaceholder
|
|
2930
3361
|
} from "@petrarca/sonnet-core/schema";
|
|
2931
|
-
import { jsx as
|
|
3362
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2932
3363
|
function mergeFieldOptions(globalOptions, property, uiSchema) {
|
|
2933
3364
|
const xWidgetOptions = property["x-ui-options"] || {};
|
|
2934
3365
|
const fieldOptions = uiSchema?.["x-ui-options"] || {};
|
|
@@ -2955,7 +3386,7 @@ function applyFieldWidth(widget, uiSchema, mergedOptions) {
|
|
|
2955
3386
|
}
|
|
2956
3387
|
const width = candidate && candidate in FIELD_WIDTH_CLASS ? candidate : "full";
|
|
2957
3388
|
if (width === "full") return widget;
|
|
2958
|
-
return /* @__PURE__ */
|
|
3389
|
+
return /* @__PURE__ */ jsx36("div", { className: FIELD_WIDTH_CLASS[width], children: widget });
|
|
2959
3390
|
}
|
|
2960
3391
|
function resolvePresentationText(property, uiSchema) {
|
|
2961
3392
|
return {
|
|
@@ -3005,7 +3436,7 @@ function FormFieldRenderer({
|
|
|
3005
3436
|
uiSchema?.["x-ui-title"],
|
|
3006
3437
|
globalOptions.label
|
|
3007
3438
|
);
|
|
3008
|
-
const widget = /* @__PURE__ */
|
|
3439
|
+
const widget = /* @__PURE__ */ jsx36(
|
|
3009
3440
|
Widget,
|
|
3010
3441
|
{
|
|
3011
3442
|
id: `${idPrefix}-${fieldName}`,
|
|
@@ -3031,7 +3462,7 @@ function FormFieldRenderer({
|
|
|
3031
3462
|
}
|
|
3032
3463
|
|
|
3033
3464
|
// src/hooks/useFormEngine.ts
|
|
3034
|
-
import { useState as
|
|
3465
|
+
import { useState as useState9, useEffect as useEffect3, useMemo as useMemo7, useCallback as useCallback7, useRef as useRef5 } from "react";
|
|
3035
3466
|
import { isEqual } from "@petrarca/sonnet-core";
|
|
3036
3467
|
import {
|
|
3037
3468
|
getSchemaProperties,
|
|
@@ -3099,14 +3530,18 @@ function useFormEngine({
|
|
|
3099
3530
|
showActions = false,
|
|
3100
3531
|
deferChanges = false,
|
|
3101
3532
|
deferStateUpdates = false,
|
|
3102
|
-
allowPristineSave = false
|
|
3533
|
+
allowPristineSave = false,
|
|
3534
|
+
refResolver
|
|
3103
3535
|
}) {
|
|
3104
|
-
const refResolvedSchema =
|
|
3105
|
-
|
|
3536
|
+
const refResolvedSchema = useMemo7(
|
|
3537
|
+
() => resolveRefs(schema, refResolver),
|
|
3538
|
+
[schema, refResolver]
|
|
3539
|
+
);
|
|
3540
|
+
const [originalData, setOriginalData] = useState9(
|
|
3106
3541
|
() => applySchemaDefaults2(data, refResolvedSchema)
|
|
3107
3542
|
);
|
|
3108
|
-
const [originalDataInitialized, setOriginalDataInitialized] =
|
|
3109
|
-
const [nonSchemaData, setNonSchemaData] =
|
|
3543
|
+
const [originalDataInitialized, setOriginalDataInitialized] = useState9(false);
|
|
3544
|
+
const [nonSchemaData, setNonSchemaData] = useState9(
|
|
3110
3545
|
() => {
|
|
3111
3546
|
const props = getSchemaProperties(refResolvedSchema);
|
|
3112
3547
|
const names = Object.keys(props);
|
|
@@ -3115,26 +3550,23 @@ function useFormEngine({
|
|
|
3115
3550
|
);
|
|
3116
3551
|
}
|
|
3117
3552
|
);
|
|
3118
|
-
const [formData, setFormData] =
|
|
3553
|
+
const [formData, setFormData] = useState9(
|
|
3119
3554
|
() => applySchemaDefaults2(data, refResolvedSchema)
|
|
3120
3555
|
);
|
|
3121
|
-
const
|
|
3122
|
-
|
|
3556
|
+
const formDataRef = useRef5(formData);
|
|
3557
|
+
formDataRef.current = formData;
|
|
3558
|
+
const [pendingChanges, setPendingChanges] = useState9({});
|
|
3559
|
+
const resolvedSchema = useMemo7(
|
|
3123
3560
|
() => resolveSchema(refResolvedSchema, formData),
|
|
3124
3561
|
[refResolvedSchema, formData]
|
|
3125
3562
|
);
|
|
3126
|
-
const orderedProperties =
|
|
3563
|
+
const orderedProperties = useMemo7(() => {
|
|
3127
3564
|
const properties2 = getSchemaProperties(resolvedSchema);
|
|
3128
3565
|
const rawOrder = uiSchema["x-ui-order"] ?? resolvedSchema["x-ui-order"];
|
|
3129
3566
|
const orderArray = Array.isArray(rawOrder) ? rawOrder : void 0;
|
|
3130
3567
|
return orderProperties(properties2, orderArray);
|
|
3131
3568
|
}, [resolvedSchema, uiSchema]);
|
|
3132
|
-
const [
|
|
3133
|
-
const initResolved = resolveSchema(refResolvedSchema, data);
|
|
3134
|
-
const validation = validateFormData(initResolved, data);
|
|
3135
|
-
return validation.isValid;
|
|
3136
|
-
});
|
|
3137
|
-
const [initialDefaultsSent, setInitialDefaultsSent] = useState7(false);
|
|
3569
|
+
const [initialDefaultsSent, setInitialDefaultsSent] = useState9(false);
|
|
3138
3570
|
const properties = getSchemaProperties(resolvedSchema);
|
|
3139
3571
|
useEffect3(() => {
|
|
3140
3572
|
if (!initialDefaultsSent) {
|
|
@@ -3181,8 +3613,6 @@ function useFormEngine({
|
|
|
3181
3613
|
setOriginalDataInitialized(true);
|
|
3182
3614
|
}
|
|
3183
3615
|
}
|
|
3184
|
-
const validation = validateFormData(resolvedSchema, newData);
|
|
3185
|
-
setIsValid(validation.isValid);
|
|
3186
3616
|
}, [
|
|
3187
3617
|
data,
|
|
3188
3618
|
refResolvedSchema,
|
|
@@ -3191,21 +3621,31 @@ function useFormEngine({
|
|
|
3191
3621
|
originalData,
|
|
3192
3622
|
originalDataInitialized
|
|
3193
3623
|
]);
|
|
3194
|
-
const hasChanges =
|
|
3624
|
+
const hasChanges = useMemo7(
|
|
3195
3625
|
() => !isEqual(formData, originalData),
|
|
3196
3626
|
[formData, originalData]
|
|
3197
3627
|
);
|
|
3198
|
-
const displayData =
|
|
3628
|
+
const displayData = useMemo7(() => {
|
|
3199
3629
|
if (deferStateUpdates && Object.keys(pendingChanges).length > 0) {
|
|
3200
3630
|
return { ...formData, ...pendingChanges };
|
|
3201
3631
|
}
|
|
3202
3632
|
return formData;
|
|
3203
3633
|
}, [formData, pendingChanges, deferStateUpdates]);
|
|
3634
|
+
const validation = useMemo7(
|
|
3635
|
+
() => validateFormData(resolvedSchema, displayData),
|
|
3636
|
+
[resolvedSchema, displayData]
|
|
3637
|
+
);
|
|
3638
|
+
const { isValid, fieldErrors } = validation;
|
|
3639
|
+
const onValidationChangeRef = useRef5(onValidationChange);
|
|
3640
|
+
onValidationChangeRef.current = onValidationChange;
|
|
3641
|
+
useEffect3(() => {
|
|
3642
|
+
onValidationChangeRef.current?.(validation.errors, validation.isValid);
|
|
3643
|
+
}, [validation]);
|
|
3204
3644
|
const handleFieldChange = useCallback7(
|
|
3205
3645
|
(fieldName, value) => {
|
|
3206
3646
|
if (readOnly) return;
|
|
3207
3647
|
const immediate = isImmediateField(fieldName, properties);
|
|
3208
|
-
if (
|
|
3648
|
+
if (deferStateUpdates && !immediate) {
|
|
3209
3649
|
setPendingChanges((prev) => ({ ...prev, [fieldName]: value }));
|
|
3210
3650
|
return;
|
|
3211
3651
|
}
|
|
@@ -3215,11 +3655,6 @@ function useFormEngine({
|
|
|
3215
3655
|
const shouldCallOnChange = !deferChanges || immediate;
|
|
3216
3656
|
if (shouldCallOnChange && onChange) onChange(newData);
|
|
3217
3657
|
}
|
|
3218
|
-
const validation = validateFormData(resolvedSchema, newData);
|
|
3219
|
-
setIsValid(validation.isValid);
|
|
3220
|
-
if (onValidationChange) {
|
|
3221
|
-
onValidationChange(validation.errors, validation.isValid);
|
|
3222
|
-
}
|
|
3223
3658
|
},
|
|
3224
3659
|
[
|
|
3225
3660
|
readOnly,
|
|
@@ -3228,14 +3663,12 @@ function useFormEngine({
|
|
|
3228
3663
|
deferStateUpdates,
|
|
3229
3664
|
showActions,
|
|
3230
3665
|
onChange,
|
|
3231
|
-
resolvedSchema,
|
|
3232
|
-
onValidationChange,
|
|
3233
3666
|
properties
|
|
3234
3667
|
]
|
|
3235
3668
|
);
|
|
3236
3669
|
const flushPendingField = useCallback7(
|
|
3237
3670
|
(fieldName) => {
|
|
3238
|
-
if (!
|
|
3671
|
+
if (!deferStateUpdates) return;
|
|
3239
3672
|
if (pendingChanges[fieldName] === void 0) return;
|
|
3240
3673
|
const newData = { ...formData, ...pendingChanges };
|
|
3241
3674
|
setFormData(newData);
|
|
@@ -3244,37 +3677,26 @@ function useFormEngine({
|
|
|
3244
3677
|
delete updated[fieldName];
|
|
3245
3678
|
return updated;
|
|
3246
3679
|
});
|
|
3247
|
-
|
|
3248
|
-
setIsValid(validation.isValid);
|
|
3249
|
-
if (onValidationChange) {
|
|
3250
|
-
onValidationChange(validation.errors, validation.isValid);
|
|
3251
|
-
}
|
|
3680
|
+
if (!showActions && onChange) onChange(newData);
|
|
3252
3681
|
},
|
|
3253
|
-
[
|
|
3254
|
-
showActions,
|
|
3255
|
-
deferStateUpdates,
|
|
3256
|
-
pendingChanges,
|
|
3257
|
-
formData,
|
|
3258
|
-
resolvedSchema,
|
|
3259
|
-
onValidationChange
|
|
3260
|
-
]
|
|
3682
|
+
[showActions, deferStateUpdates, pendingChanges, formData, onChange]
|
|
3261
3683
|
);
|
|
3262
3684
|
const handleFieldBlur = useCallback7(
|
|
3263
3685
|
(fieldName) => {
|
|
3264
3686
|
if (readOnly) return;
|
|
3265
3687
|
flushPendingField(fieldName);
|
|
3688
|
+
const latest = formDataRef.current;
|
|
3266
3689
|
if (!showActions && deferChanges && onChange) {
|
|
3267
|
-
onChange(
|
|
3690
|
+
onChange(latest);
|
|
3268
3691
|
}
|
|
3269
3692
|
if (onBlur) {
|
|
3270
3693
|
const hasPending = deferStateUpdates && pendingChanges[fieldName] !== void 0;
|
|
3271
|
-
const dataToPass = hasPending ? { ...
|
|
3694
|
+
const dataToPass = hasPending ? { ...latest, [fieldName]: pendingChanges[fieldName] } : latest;
|
|
3272
3695
|
onBlur(fieldName, dataToPass);
|
|
3273
3696
|
}
|
|
3274
3697
|
},
|
|
3275
3698
|
[
|
|
3276
3699
|
readOnly,
|
|
3277
|
-
formData,
|
|
3278
3700
|
pendingChanges,
|
|
3279
3701
|
deferChanges,
|
|
3280
3702
|
deferStateUpdates,
|
|
@@ -3288,8 +3710,8 @@ function useFormEngine({
|
|
|
3288
3710
|
if (!hasChanges && !allowPristineSave) return;
|
|
3289
3711
|
const finalData = deferStateUpdates && Object.keys(pendingChanges).length > 0 ? { ...formData, ...pendingChanges } : formData;
|
|
3290
3712
|
const completeData = { ...nonSchemaData, ...finalData };
|
|
3291
|
-
const
|
|
3292
|
-
if (
|
|
3713
|
+
const validation2 = validateFormData(resolvedSchema, finalData);
|
|
3714
|
+
if (validation2.isValid && onUpdate) {
|
|
3293
3715
|
const changed = {};
|
|
3294
3716
|
for (const key of Object.keys(finalData)) {
|
|
3295
3717
|
if (!isEqual(finalData[key], originalData[key])) {
|
|
@@ -3323,6 +3745,7 @@ function useFormEngine({
|
|
|
3323
3745
|
orderedProperties,
|
|
3324
3746
|
hasChanges,
|
|
3325
3747
|
isValid,
|
|
3748
|
+
fieldErrors,
|
|
3326
3749
|
handleFieldChange,
|
|
3327
3750
|
handleFieldBlur,
|
|
3328
3751
|
handleSave,
|
|
@@ -3331,7 +3754,7 @@ function useFormEngine({
|
|
|
3331
3754
|
}
|
|
3332
3755
|
|
|
3333
3756
|
// src/JsonSchemaFormRenderer.tsx
|
|
3334
|
-
import { jsx as
|
|
3757
|
+
import { jsx as jsx37, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3335
3758
|
function stripFids(data) {
|
|
3336
3759
|
const result = {};
|
|
3337
3760
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -3398,7 +3821,7 @@ function useWrappedCallbacks(onChange, onBlur, onUpdate) {
|
|
|
3398
3821
|
function buildFieldMap(fields) {
|
|
3399
3822
|
const map = /* @__PURE__ */ new Map();
|
|
3400
3823
|
for (const field of fields) {
|
|
3401
|
-
if (
|
|
3824
|
+
if (React13.isValidElement(field) && field.key != null) {
|
|
3402
3825
|
map.set(String(field.key), field);
|
|
3403
3826
|
}
|
|
3404
3827
|
}
|
|
@@ -3417,12 +3840,12 @@ function resolveGridColumns(row, fieldCount) {
|
|
|
3417
3840
|
}
|
|
3418
3841
|
function wrapInGroup(group, key, content) {
|
|
3419
3842
|
if (!group) return content;
|
|
3420
|
-
return /* @__PURE__ */
|
|
3843
|
+
return /* @__PURE__ */ jsxs23(
|
|
3421
3844
|
"fieldset",
|
|
3422
3845
|
{
|
|
3423
3846
|
className: "rounded-md border border-border/60 px-3 pb-3 pt-1",
|
|
3424
3847
|
children: [
|
|
3425
|
-
/* @__PURE__ */
|
|
3848
|
+
/* @__PURE__ */ jsx37("legend", { className: "px-1 text-sm font-medium text-muted-foreground", children: group }),
|
|
3426
3849
|
content
|
|
3427
3850
|
]
|
|
3428
3851
|
},
|
|
@@ -3444,7 +3867,7 @@ function buildRowElement(row, fieldMap, defaultRowGap) {
|
|
|
3444
3867
|
gridTemplateColumns: resolveGridColumns(row, rowFields.length),
|
|
3445
3868
|
gap: `${rowGap}px`
|
|
3446
3869
|
};
|
|
3447
|
-
const inner = /* @__PURE__ */
|
|
3870
|
+
const inner = /* @__PURE__ */ jsx37("div", { style, children: rowFields }, `row:${rowKey}`);
|
|
3448
3871
|
return {
|
|
3449
3872
|
element: wrapInGroup(row.group, rowKey, inner),
|
|
3450
3873
|
placed: row.fields
|
|
@@ -3453,24 +3876,92 @@ function buildRowElement(row, fieldMap, defaultRowGap) {
|
|
|
3453
3876
|
function collectUnplacedFields(fields, placed) {
|
|
3454
3877
|
const result = [];
|
|
3455
3878
|
for (const field of fields) {
|
|
3456
|
-
if (
|
|
3879
|
+
if (React13.isValidElement(field) && field.key != null && !placed.has(String(field.key))) {
|
|
3457
3880
|
result.push(field);
|
|
3458
3881
|
}
|
|
3459
3882
|
}
|
|
3460
3883
|
return result;
|
|
3461
3884
|
}
|
|
3462
|
-
function
|
|
3463
|
-
const fieldMap = buildFieldMap(fields);
|
|
3464
|
-
const placed = /* @__PURE__ */ new Set();
|
|
3885
|
+
function buildRows(rowDefs, fieldMap, defaultRowGap, placed) {
|
|
3465
3886
|
const elements = [];
|
|
3466
3887
|
for (const row of rowDefs) {
|
|
3467
3888
|
const result = buildRowElement(row, fieldMap, defaultRowGap);
|
|
3468
|
-
|
|
3469
|
-
elements.push(result.element);
|
|
3470
|
-
for (const name of result.placed) placed.add(name);
|
|
3889
|
+
for (const name of row.fields) placed.add(name);
|
|
3890
|
+
if (result != null) elements.push(result.element);
|
|
3471
3891
|
}
|
|
3892
|
+
return elements;
|
|
3893
|
+
}
|
|
3894
|
+
function layoutFieldsInRows(fields, rowDefs, defaultRowGap) {
|
|
3895
|
+
const placed = /* @__PURE__ */ new Set();
|
|
3896
|
+
const elements = buildRows(
|
|
3897
|
+
rowDefs,
|
|
3898
|
+
buildFieldMap(fields),
|
|
3899
|
+
defaultRowGap,
|
|
3900
|
+
placed
|
|
3901
|
+
);
|
|
3472
3902
|
return elements.concat(collectUnplacedFields(fields, placed));
|
|
3473
3903
|
}
|
|
3904
|
+
function pointerRoot(pointer) {
|
|
3905
|
+
const token = pointer.split("/")[1] ?? "";
|
|
3906
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
3907
|
+
}
|
|
3908
|
+
function groupErrorCount(tab, fieldErrors) {
|
|
3909
|
+
if (fieldErrors.length === 0) return 0;
|
|
3910
|
+
const owned = new Set(tab.rows.flatMap((row) => row.fields));
|
|
3911
|
+
return fieldErrors.filter((e) => owned.has(pointerRoot(e.pointer))).length;
|
|
3912
|
+
}
|
|
3913
|
+
function groupVisibility(groupDefs, formData) {
|
|
3914
|
+
const visible = groupDefs.map(
|
|
3915
|
+
(tab) => evaluateUiRule(tab.rule, formData).visible
|
|
3916
|
+
);
|
|
3917
|
+
return { visible, allHidden: !visible.some(Boolean) };
|
|
3918
|
+
}
|
|
3919
|
+
function hiddenGroupFieldNames(groupDefs, formData) {
|
|
3920
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
3921
|
+
const { visible, allHidden } = groupVisibility(groupDefs, formData);
|
|
3922
|
+
if (allHidden) return hidden;
|
|
3923
|
+
groupDefs.forEach((tab, i) => {
|
|
3924
|
+
if (visible[i]) return;
|
|
3925
|
+
for (const row of tab.rows) for (const f of row.fields) hidden.add(f);
|
|
3926
|
+
});
|
|
3927
|
+
return hidden;
|
|
3928
|
+
}
|
|
3929
|
+
function resolveGroupTemplate(templates, variant) {
|
|
3930
|
+
const registered = templates[groupTemplateKey(variant)];
|
|
3931
|
+
if (typeof registered === "function") {
|
|
3932
|
+
return registered;
|
|
3933
|
+
}
|
|
3934
|
+
return DEFAULT_TEMPLATES.TabsGroupTemplate;
|
|
3935
|
+
}
|
|
3936
|
+
function HiddenErrorNotice({
|
|
3937
|
+
count
|
|
3938
|
+
}) {
|
|
3939
|
+
if (count === 0) return null;
|
|
3940
|
+
return /* @__PURE__ */ jsxs23("p", { className: "flex items-center gap-1.5 text-sm text-destructive", children: [
|
|
3941
|
+
/* @__PURE__ */ jsx37(AlertCircle2, { className: "h-4 w-4 shrink-0" }),
|
|
3942
|
+
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.`
|
|
3943
|
+
] });
|
|
3944
|
+
}
|
|
3945
|
+
function layoutFieldsInGroups(fields, groupDefs, defaultRowGap, fieldErrors, formData, GroupTemplate) {
|
|
3946
|
+
const fieldMap = buildFieldMap(fields);
|
|
3947
|
+
const placed = /* @__PURE__ */ new Set();
|
|
3948
|
+
const { visible, allHidden } = groupVisibility(groupDefs, formData);
|
|
3949
|
+
const built = groupDefs.map((tab, index) => ({
|
|
3950
|
+
index,
|
|
3951
|
+
rows: buildRows(tab.rows, fieldMap, defaultRowGap, placed)
|
|
3952
|
+
}));
|
|
3953
|
+
const unplaced = collectUnplacedFields(fields, placed);
|
|
3954
|
+
if (allHidden) {
|
|
3955
|
+
return [...built.flatMap((b) => b.rows), ...unplaced];
|
|
3956
|
+
}
|
|
3957
|
+
const groups = built.filter((b) => visible[b.index]).map((b) => ({
|
|
3958
|
+
key: `group-${b.index}`,
|
|
3959
|
+
label: groupDefs[b.index].label,
|
|
3960
|
+
content: b.rows,
|
|
3961
|
+
errorCount: groupErrorCount(groupDefs[b.index], fieldErrors)
|
|
3962
|
+
}));
|
|
3963
|
+
return [/* @__PURE__ */ jsx37(GroupTemplate, { groups }, "form:groups"), ...unplaced];
|
|
3964
|
+
}
|
|
3474
3965
|
function FormContainer({
|
|
3475
3966
|
displayContents,
|
|
3476
3967
|
showExtraProperties,
|
|
@@ -3486,15 +3977,17 @@ function FormContainer({
|
|
|
3486
3977
|
cancelLabel,
|
|
3487
3978
|
showCancel,
|
|
3488
3979
|
leadingActions,
|
|
3980
|
+
hiddenErrorCount,
|
|
3489
3981
|
children
|
|
3490
3982
|
}) {
|
|
3491
3983
|
if (displayContents) {
|
|
3492
|
-
return /* @__PURE__ */
|
|
3984
|
+
return /* @__PURE__ */ jsx37("div", { style: { display: "contents" }, children });
|
|
3493
3985
|
}
|
|
3494
|
-
return /* @__PURE__ */
|
|
3986
|
+
return /* @__PURE__ */ jsxs23(SimpleStack, { gap: "md", children: [
|
|
3495
3987
|
children,
|
|
3496
|
-
showExtraProperties && /* @__PURE__ */
|
|
3497
|
-
|
|
3988
|
+
showExtraProperties && /* @__PURE__ */ jsx37(ExtraPropertiesCard_default, { properties: formData, schema: resolvedSchema }),
|
|
3989
|
+
/* @__PURE__ */ jsx37(HiddenErrorNotice, { count: hiddenErrorCount }),
|
|
3990
|
+
showActions && /* @__PURE__ */ jsx37(
|
|
3498
3991
|
FormActions,
|
|
3499
3992
|
{
|
|
3500
3993
|
onSave: handleSave,
|
|
@@ -3543,6 +4036,7 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3543
4036
|
orderedProperties,
|
|
3544
4037
|
hasChanges,
|
|
3545
4038
|
isValid,
|
|
4039
|
+
fieldErrors,
|
|
3546
4040
|
handleFieldChange,
|
|
3547
4041
|
handleFieldBlur,
|
|
3548
4042
|
handleSave,
|
|
@@ -3560,22 +4054,23 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3560
4054
|
showActions,
|
|
3561
4055
|
deferChanges,
|
|
3562
4056
|
deferStateUpdates,
|
|
3563
|
-
allowPristineSave
|
|
4057
|
+
allowPristineSave,
|
|
4058
|
+
refResolver: props.refResolver
|
|
3564
4059
|
});
|
|
3565
4060
|
const layoutLabelPlacement = uiSchema["x-ui-layout"]?.labelPlacement;
|
|
3566
|
-
const globalOptions =
|
|
4061
|
+
const globalOptions = useMemo8(
|
|
3567
4062
|
() => ({
|
|
3568
4063
|
...layoutLabelPlacement ? { labelPlacement: layoutLabelPlacement } : {},
|
|
3569
4064
|
...uiSchema["x-ui-globalOptions"] || {}
|
|
3570
4065
|
}),
|
|
3571
4066
|
[layoutLabelPlacement, uiSchema]
|
|
3572
4067
|
);
|
|
3573
|
-
const resolvedTemplates =
|
|
4068
|
+
const resolvedTemplates = useMemo8(
|
|
3574
4069
|
() => ({ ...DEFAULT_TEMPLATES, ...templates }),
|
|
3575
4070
|
[templates]
|
|
3576
4071
|
);
|
|
3577
4072
|
const nestedRenderer = useCallback8(
|
|
3578
|
-
(props2) => /* @__PURE__ */
|
|
4073
|
+
(props2) => /* @__PURE__ */ jsx37(
|
|
3579
4074
|
JsonSchemaFormRenderer,
|
|
3580
4075
|
{
|
|
3581
4076
|
...props2,
|
|
@@ -3586,32 +4081,39 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3586
4081
|
),
|
|
3587
4082
|
[resolvedTemplates]
|
|
3588
4083
|
);
|
|
3589
|
-
const contextValue =
|
|
4084
|
+
const contextValue = useMemo8(
|
|
3590
4085
|
() => ({ renderer: nestedRenderer, templates: resolvedTemplates }),
|
|
3591
4086
|
[nestedRenderer, resolvedTemplates]
|
|
3592
4087
|
);
|
|
3593
4088
|
const needsBlurHandler = deferStateUpdates || !!onBlur;
|
|
3594
|
-
const fields =
|
|
3595
|
-
() => orderedProperties.
|
|
3596
|
-
|
|
3597
|
-
{
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
4089
|
+
const fields = useMemo8(
|
|
4090
|
+
() => orderedProperties.flatMap(([fieldName, property]) => {
|
|
4091
|
+
const rule = uiSchema[fieldName]?.["x-ui-rule"] ?? property["x-ui-rule"];
|
|
4092
|
+
const { visible, enabled } = evaluateUiRule(rule, displayData);
|
|
4093
|
+
if (!visible) return [];
|
|
4094
|
+
return [
|
|
4095
|
+
/* @__PURE__ */ jsx37(
|
|
4096
|
+
FormFieldRenderer,
|
|
4097
|
+
{
|
|
4098
|
+
fieldName,
|
|
4099
|
+
property,
|
|
4100
|
+
value: displayData[fieldName],
|
|
4101
|
+
onChange: (v) => handleFieldChange(fieldName, v),
|
|
4102
|
+
onBlur: needsBlurHandler ? () => handleFieldBlur(fieldName) : void 0,
|
|
4103
|
+
required: isFieldRequired(resolvedSchema, fieldName),
|
|
4104
|
+
disabled: disabled || !enabled,
|
|
4105
|
+
readOnly,
|
|
4106
|
+
schema,
|
|
4107
|
+
uiSchema: uiSchema[fieldName],
|
|
4108
|
+
registry: widgets,
|
|
4109
|
+
globalOptions,
|
|
4110
|
+
formData: displayData,
|
|
4111
|
+
idPrefix
|
|
4112
|
+
},
|
|
4113
|
+
fieldName
|
|
4114
|
+
)
|
|
4115
|
+
];
|
|
4116
|
+
}),
|
|
3615
4117
|
[
|
|
3616
4118
|
orderedProperties,
|
|
3617
4119
|
displayData,
|
|
@@ -3630,12 +4132,43 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3630
4132
|
);
|
|
3631
4133
|
const layoutConfig = uiSchema["x-ui-layout"];
|
|
3632
4134
|
const rowDefs = layoutConfig?.rows;
|
|
4135
|
+
const groupDefs = layoutConfig?.groups;
|
|
3633
4136
|
const defaultRowGap = layoutConfig?.gap ?? 24;
|
|
3634
|
-
const
|
|
3635
|
-
|
|
3636
|
-
|
|
4137
|
+
const groupTemplate = resolveGroupTemplate(
|
|
4138
|
+
resolvedTemplates,
|
|
4139
|
+
layoutConfig?.variant
|
|
3637
4140
|
);
|
|
3638
|
-
|
|
4141
|
+
const layoutFields = useMemo8(() => {
|
|
4142
|
+
if (groupDefs && groupDefs.length > 0) {
|
|
4143
|
+
return layoutFieldsInGroups(
|
|
4144
|
+
fields,
|
|
4145
|
+
groupDefs,
|
|
4146
|
+
defaultRowGap,
|
|
4147
|
+
fieldErrors,
|
|
4148
|
+
displayData,
|
|
4149
|
+
groupTemplate
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
if (rowDefs && rowDefs.length > 0) {
|
|
4153
|
+
return layoutFieldsInRows(fields, rowDefs, defaultRowGap);
|
|
4154
|
+
}
|
|
4155
|
+
return fields;
|
|
4156
|
+
}, [
|
|
4157
|
+
fields,
|
|
4158
|
+
rowDefs,
|
|
4159
|
+
groupDefs,
|
|
4160
|
+
defaultRowGap,
|
|
4161
|
+
fieldErrors,
|
|
4162
|
+
displayData,
|
|
4163
|
+
groupTemplate
|
|
4164
|
+
]);
|
|
4165
|
+
const hiddenErrorCount = useMemo8(() => {
|
|
4166
|
+
if (!groupDefs?.length || fieldErrors.length === 0) return 0;
|
|
4167
|
+
const hidden = hiddenGroupFieldNames(groupDefs, displayData);
|
|
4168
|
+
if (hidden.size === 0) return 0;
|
|
4169
|
+
return fieldErrors.filter((e) => hidden.has(pointerRoot(e.pointer))).length;
|
|
4170
|
+
}, [groupDefs, fieldErrors, displayData]);
|
|
4171
|
+
return /* @__PURE__ */ jsx37(NestedFormContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx37(
|
|
3639
4172
|
FormContainer,
|
|
3640
4173
|
{
|
|
3641
4174
|
displayContents,
|
|
@@ -3652,13 +4185,14 @@ function JsonSchemaFormRenderer(props) {
|
|
|
3652
4185
|
cancelLabel,
|
|
3653
4186
|
showCancel,
|
|
3654
4187
|
leadingActions,
|
|
4188
|
+
hiddenErrorCount,
|
|
3655
4189
|
children: layoutFields
|
|
3656
4190
|
}
|
|
3657
4191
|
) });
|
|
3658
4192
|
}
|
|
3659
4193
|
|
|
3660
4194
|
// src/hooks/useResolvedSchema.ts
|
|
3661
|
-
import { useMemo as
|
|
4195
|
+
import { useMemo as useMemo9 } from "react";
|
|
3662
4196
|
import {
|
|
3663
4197
|
getSchemaProperties as getSchemaProperties2,
|
|
3664
4198
|
resolveSchema as resolveSchema2
|
|
@@ -3668,11 +4202,11 @@ function useResolvedSchema({
|
|
|
3668
4202
|
formData,
|
|
3669
4203
|
uiSchema
|
|
3670
4204
|
}) {
|
|
3671
|
-
const resolvedSchema =
|
|
4205
|
+
const resolvedSchema = useMemo9(
|
|
3672
4206
|
() => resolveSchema2(schema, formData),
|
|
3673
4207
|
[schema, formData]
|
|
3674
4208
|
);
|
|
3675
|
-
const orderedProperties =
|
|
4209
|
+
const orderedProperties = useMemo9(() => {
|
|
3676
4210
|
const properties = getSchemaProperties2(resolvedSchema);
|
|
3677
4211
|
const rawOrder = uiSchema["x-ui-order"] ?? resolvedSchema["x-ui-order"];
|
|
3678
4212
|
const orderArray = Array.isArray(rawOrder) ? rawOrder : void 0;
|
|
@@ -3686,6 +4220,9 @@ export {
|
|
|
3686
4220
|
DEFAULT_TEMPLATES,
|
|
3687
4221
|
DEFAULT_WIDGETS,
|
|
3688
4222
|
DEFAULT_WIDGET_MAP,
|
|
4223
|
+
DateTimeWidget,
|
|
4224
|
+
DateWidget,
|
|
4225
|
+
DisplayWidget,
|
|
3689
4226
|
EntitySelectWidget,
|
|
3690
4227
|
ExtraPropertiesCard_default as ExtraPropertiesCard,
|
|
3691
4228
|
FormActions,
|
|
@@ -3699,18 +4236,25 @@ export {
|
|
|
3699
4236
|
FormSelect,
|
|
3700
4237
|
FormTagsInput,
|
|
3701
4238
|
FormTextarea,
|
|
4239
|
+
GroupErrorBadge,
|
|
3702
4240
|
JsonEditorWidget,
|
|
3703
4241
|
JsonSchemaFormRenderer,
|
|
3704
4242
|
NumberWidget,
|
|
3705
4243
|
ObjectWidget,
|
|
4244
|
+
OneOfWidget,
|
|
3706
4245
|
QuantityWidget,
|
|
4246
|
+
SectionsGroupTemplate,
|
|
3707
4247
|
SelectWidget,
|
|
4248
|
+
StepperGroupTemplate,
|
|
4249
|
+
TabsGroupTemplate,
|
|
3708
4250
|
TagsInputWidget,
|
|
3709
4251
|
TextInputWidget,
|
|
3710
4252
|
TextareaWidget,
|
|
4253
|
+
TimeWidget,
|
|
3711
4254
|
firstError,
|
|
3712
4255
|
getAriaDescribedBy,
|
|
3713
4256
|
getDefaultWidgetForType,
|
|
4257
|
+
groupTemplateKey,
|
|
3714
4258
|
resolveWidget,
|
|
3715
4259
|
useFormEngine,
|
|
3716
4260
|
useNestedFormContext,
|