jsonforms-nuxt-ui-renderers 0.1.5 → 0.1.7

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 CHANGED
@@ -24,15 +24,42 @@ import { nuxtUiRenderers } from 'jsonforms-nuxt-ui-renderers'
24
24
 
25
25
  ### Important
26
26
 
27
- - These renderers **resolve Nuxt UI components by name** (e.g. `UInput`, `UFormField`, `UButton`).
27
+ - These renderers **resolve Nuxt UI components by name** (e.g. `UFormField`, `UInput`, `UTextarea`, `USelectMenu`, `USwitch`, `UButton`).
28
28
  Your app must register Nuxt UI components globally (Nuxt does this when you use the `@nuxt/ui` module).
29
29
  - If you override component names or use a different UI library, these renderers will not work out of the box.
30
30
 
31
31
  ## Supported UI schema / schema constructs
32
32
 
33
- - **Controls**: string, multiline string, number, integer, boolean, enum
34
- - **Layouts**: `VerticalLayout`, `HorizontalLayout`, `Group`, `Label`, `Category`, `Categorization`
35
- - **Complex**: arrays (list UI), objects (delegates to generated/registered detail UI schema)
33
+ This package is intentionally small and opinionated: it ships a **single** renderer registry (`nuxtUiRenderers`) covering the following UI schema elements / JSON Schema patterns.
34
+
35
+ ### Controls (field types)
36
+
37
+ - **String**: JSON Schema `type: "string"` → `UInput`
38
+ - **Multiline string**: JSONForms “multiline” control (e.g. `uischema.options.multi: true`) → `UTextarea`
39
+ - **Password**: JSON Schema `type: "string"` + `format: "password"` → `UInput type="password"` with show/hide toggle button
40
+ - **Number**: JSON Schema `type: "number"` → `UInput type="number"` (parses to `number`, empty becomes `undefined`)
41
+ - **Integer**: JSON Schema `type: "integer"` → `UInput type="number" step="1"` (parses to `integer`, empty becomes `undefined`)
42
+ - **Boolean**: JSON Schema `type: "boolean"` → `USwitch`
43
+ - **Enum (single-select)**: JSON Schema `enum: [...]` (or `oneOf: [{ const, title? }, ...]`) → `USelectMenu`
44
+ - **Enum (multi-select)**: JSON Schema `type: "array"` with `items` being an enum schema (supports `$ref`’d `items`) → `USelectMenu multiple`
45
+
46
+ ### Layouts
47
+
48
+ - **`VerticalLayout`**
49
+ - **`HorizontalLayout`** (responsive: stacks on small screens, wraps into columns on larger screens)
50
+ - **`Group`**
51
+ - **`Label`**
52
+ - **`Categorization` / `Category`**
53
+
54
+ ### Complex types
55
+
56
+ - **Arrays (list UI)**: any schema with `type: "array"` renders as a list with **Add / Remove / Up / Down**
57
+ - Respects `minItems` / `maxItems` (disables buttons accordingly)
58
+ - Item label can be customized via `uischema.options.childLabelProp`; otherwise it uses JSONForms’ first primitive property as a best-effort label
59
+ - “Add” uses JSONForms `createDefaultValue(...)` for the item value
60
+ - **Objects**: object controls delegate to a **detail UI schema**
61
+ - If no matching detail UI schema is registered, a default one is generated via JSONForms `Generate.uiSchema(...)`
62
+ - The root object is rendered as a `VerticalLayout`; nested objects default to a `Group` using the control’s label
36
63
 
37
64
  ## Contributing
38
65
 
package/dist/index.cjs CHANGED
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(index_exports);
26
26
 
27
27
  // src/nuxtUiRenderers.ts
28
28
  var import_core3 = require("@jsonforms/core");
29
- var import_vue32 = require("vue");
29
+ var import_vue34 = require("vue");
30
30
 
31
31
  // src/renderers/complex/NuxtUiArrayListRenderer.ts
32
32
  var import_core = require("@jsonforms/core");
@@ -600,21 +600,24 @@ var NuxtUiNumberControl = (0, import_vue15.defineComponent)({
600
600
  }
601
601
  });
602
602
 
603
- // src/renderers/controls/NuxtUiStringControl.ts
603
+ // src/renderers/controls/NuxtUiPasswordControl.ts
604
604
  var import_vue16 = require("@jsonforms/vue");
605
605
  var import_vue17 = require("vue");
606
- var NuxtUiStringControl = (0, import_vue17.defineComponent)({
607
- name: "NuxtUiStringControl",
606
+ var NuxtUiPasswordControl = (0, import_vue17.defineComponent)({
607
+ name: "NuxtUiPasswordControl",
608
608
  props: (0, import_vue16.rendererProps)(),
609
609
  setup(props) {
610
610
  const { control, handleChange } = (0, import_vue16.useJsonFormsControl)(
611
611
  props
612
612
  );
613
613
  const errorMessage = (0, import_vue17.computed)(() => trimmedOrUndefined(control.value.errors));
614
+ const showPassword = (0, import_vue17.ref)(false);
615
+ const inputType = (0, import_vue17.computed)(() => showPassword.value ? "text" : "password");
614
616
  return () => {
615
617
  if (!control.value.visible) return null;
616
618
  const UFormField = (0, import_vue17.resolveComponent)("UFormField");
617
619
  const UInput = (0, import_vue17.resolveComponent)("UInput");
620
+ const UButton = (0, import_vue17.resolveComponent)("UButton");
618
621
  return (0, import_vue17.h)(
619
622
  "div",
620
623
  {},
@@ -627,7 +630,69 @@ var NuxtUiStringControl = (0, import_vue17.defineComponent)({
627
630
  error: errorMessage.value
628
631
  },
629
632
  {
630
- default: () => (0, import_vue17.h)(UInput, {
633
+ default: () => (0, import_vue17.h)(
634
+ UInput,
635
+ {
636
+ modelValue: control.value.data ?? "",
637
+ class: "w-full",
638
+ type: inputType.value,
639
+ autocomplete: "current-password",
640
+ disabled: !control.value.enabled,
641
+ color: errorMessage.value ? "error" : void 0,
642
+ "aria-invalid": Boolean(errorMessage.value),
643
+ "onUpdate:modelValue": (v) => handleChange(control.value.path, v)
644
+ },
645
+ {
646
+ trailing: () => (0, import_vue17.h)(UButton, {
647
+ type: "button",
648
+ color: "neutral",
649
+ variant: "ghost",
650
+ square: true,
651
+ icon: showPassword.value ? "i-heroicons-eye-slash" : "i-heroicons-eye",
652
+ "aria-pressed": showPassword.value,
653
+ "aria-label": showPassword.value ? "Hide password" : "Show password",
654
+ disabled: !control.value.enabled,
655
+ onClick: () => {
656
+ showPassword.value = !showPassword.value;
657
+ }
658
+ })
659
+ }
660
+ )
661
+ }
662
+ )
663
+ );
664
+ };
665
+ }
666
+ });
667
+
668
+ // src/renderers/controls/NuxtUiStringControl.ts
669
+ var import_vue18 = require("@jsonforms/vue");
670
+ var import_vue19 = require("vue");
671
+ var NuxtUiStringControl = (0, import_vue19.defineComponent)({
672
+ name: "NuxtUiStringControl",
673
+ props: (0, import_vue18.rendererProps)(),
674
+ setup(props) {
675
+ const { control, handleChange } = (0, import_vue18.useJsonFormsControl)(
676
+ props
677
+ );
678
+ const errorMessage = (0, import_vue19.computed)(() => trimmedOrUndefined(control.value.errors));
679
+ return () => {
680
+ if (!control.value.visible) return null;
681
+ const UFormField = (0, import_vue19.resolveComponent)("UFormField");
682
+ const UInput = (0, import_vue19.resolveComponent)("UInput");
683
+ return (0, import_vue19.h)(
684
+ "div",
685
+ {},
686
+ (0, import_vue19.h)(
687
+ UFormField,
688
+ {
689
+ label: control.value.label,
690
+ description: control.value.description,
691
+ required: control.value.required,
692
+ error: errorMessage.value
693
+ },
694
+ {
695
+ default: () => (0, import_vue19.h)(UInput, {
631
696
  modelValue: control.value.data ?? "",
632
697
  class: "w-full",
633
698
  disabled: !control.value.enabled,
@@ -643,24 +708,24 @@ var NuxtUiStringControl = (0, import_vue17.defineComponent)({
643
708
  });
644
709
 
645
710
  // src/renderers/controls/NuxtUiTextareaControl.ts
646
- var import_vue18 = require("@jsonforms/vue");
647
- var import_vue19 = require("vue");
648
- var NuxtUiTextareaControl = (0, import_vue19.defineComponent)({
711
+ var import_vue20 = require("@jsonforms/vue");
712
+ var import_vue21 = require("vue");
713
+ var NuxtUiTextareaControl = (0, import_vue21.defineComponent)({
649
714
  name: "NuxtUiTextareaControl",
650
- props: (0, import_vue18.rendererProps)(),
715
+ props: (0, import_vue20.rendererProps)(),
651
716
  setup(props) {
652
- const { control, handleChange } = (0, import_vue18.useJsonFormsControl)(
717
+ const { control, handleChange } = (0, import_vue20.useJsonFormsControl)(
653
718
  props
654
719
  );
655
- const errorMessage = (0, import_vue19.computed)(() => trimmedOrUndefined(control.value.errors));
720
+ const errorMessage = (0, import_vue21.computed)(() => trimmedOrUndefined(control.value.errors));
656
721
  return () => {
657
722
  if (!control.value.visible) return null;
658
- const UFormField = (0, import_vue19.resolveComponent)("UFormField");
659
- const UTextarea = (0, import_vue19.resolveComponent)("UTextarea");
660
- return (0, import_vue19.h)(
723
+ const UFormField = (0, import_vue21.resolveComponent)("UFormField");
724
+ const UTextarea = (0, import_vue21.resolveComponent)("UTextarea");
725
+ return (0, import_vue21.h)(
661
726
  "div",
662
727
  {},
663
- (0, import_vue19.h)(
728
+ (0, import_vue21.h)(
664
729
  UFormField,
665
730
  {
666
731
  label: control.value.label,
@@ -669,7 +734,7 @@ var NuxtUiTextareaControl = (0, import_vue19.defineComponent)({
669
734
  error: errorMessage.value
670
735
  },
671
736
  {
672
- default: () => (0, import_vue19.h)(UTextarea, {
737
+ default: () => (0, import_vue21.h)(UTextarea, {
673
738
  modelValue: control.value.data ?? "",
674
739
  class: "w-full",
675
740
  disabled: !control.value.enabled,
@@ -686,37 +751,37 @@ var NuxtUiTextareaControl = (0, import_vue19.defineComponent)({
686
751
  });
687
752
 
688
753
  // src/renderers/layouts/NuxtUiCategorizationRenderer.ts
689
- var import_vue20 = require("@jsonforms/vue");
690
- var import_vue21 = require("vue");
691
- var NuxtUiCategorizationRenderer = (0, import_vue21.defineComponent)({
754
+ var import_vue22 = require("@jsonforms/vue");
755
+ var import_vue23 = require("vue");
756
+ var NuxtUiCategorizationRenderer = (0, import_vue23.defineComponent)({
692
757
  name: "NuxtUiCategorizationRenderer",
693
- components: { DispatchRenderer: import_vue20.DispatchRenderer },
694
- props: (0, import_vue20.rendererProps)(),
758
+ components: { DispatchRenderer: import_vue22.DispatchRenderer },
759
+ props: (0, import_vue22.rendererProps)(),
695
760
  setup(props) {
696
- const { layout, categories } = (0, import_vue20.useJsonFormsCategorization)(
761
+ const { layout, categories } = (0, import_vue22.useJsonFormsCategorization)(
697
762
  props
698
763
  );
699
764
  return () => {
700
765
  if (!layout.value.visible) return null;
701
- return (0, import_vue21.h)(
766
+ return (0, import_vue23.h)(
702
767
  "div",
703
768
  { class: "flex flex-col gap-6" },
704
769
  categories.map((categoryRef, catIndex) => {
705
770
  const category = categoryRef.value;
706
771
  const elements = category.uischema.elements ?? [];
707
- return (0, import_vue21.h)(
772
+ return (0, import_vue23.h)(
708
773
  "div",
709
774
  { key: `${layout.value.path}-cat-${catIndex}`, class: "flex flex-col gap-3" },
710
775
  [
711
- category.label ? (0, import_vue21.h)("div", { class: "text-sm font-semibold" }, category.label) : null,
712
- (0, import_vue21.h)(
776
+ category.label ? (0, import_vue23.h)("div", { class: "text-sm font-semibold" }, category.label) : null,
777
+ (0, import_vue23.h)(
713
778
  "div",
714
779
  { class: "flex flex-col gap-3" },
715
780
  elements.map(
716
- (element, index) => (0, import_vue21.h)(
781
+ (element, index) => (0, import_vue23.h)(
717
782
  "div",
718
783
  { key: `${category.path}-${index}` },
719
- (0, import_vue21.h)(import_vue20.DispatchRenderer, {
784
+ (0, import_vue23.h)(import_vue22.DispatchRenderer, {
720
785
  schema: category.schema,
721
786
  uischema: element,
722
787
  path: category.path,
@@ -736,29 +801,29 @@ var NuxtUiCategorizationRenderer = (0, import_vue21.defineComponent)({
736
801
  });
737
802
 
738
803
  // src/renderers/layouts/NuxtUiCategoryRenderer.ts
739
- var import_vue22 = require("@jsonforms/vue");
740
- var import_vue23 = require("vue");
741
- var NuxtUiCategoryRenderer = (0, import_vue23.defineComponent)({
804
+ var import_vue24 = require("@jsonforms/vue");
805
+ var import_vue25 = require("vue");
806
+ var NuxtUiCategoryRenderer = (0, import_vue25.defineComponent)({
742
807
  name: "NuxtUiCategoryRenderer",
743
- components: { DispatchRenderer: import_vue22.DispatchRenderer },
744
- props: (0, import_vue22.rendererProps)(),
808
+ components: { DispatchRenderer: import_vue24.DispatchRenderer },
809
+ props: (0, import_vue24.rendererProps)(),
745
810
  setup(props) {
746
- const { layout } = (0, import_vue22.useJsonFormsLayout)(
811
+ const { layout } = (0, import_vue24.useJsonFormsLayout)(
747
812
  props
748
813
  );
749
814
  return () => {
750
815
  if (!layout.value.visible) return null;
751
816
  const elements = layout.value.uischema.elements ?? [];
752
- return (0, import_vue23.h)("div", { class: "flex flex-col gap-3" }, [
753
- layout.value.label ? (0, import_vue23.h)("div", { class: "text-sm font-semibold" }, layout.value.label) : null,
754
- (0, import_vue23.h)(
817
+ return (0, import_vue25.h)("div", { class: "flex flex-col gap-3" }, [
818
+ layout.value.label ? (0, import_vue25.h)("div", { class: "text-sm font-semibold" }, layout.value.label) : null,
819
+ (0, import_vue25.h)(
755
820
  "div",
756
821
  { class: "flex flex-col gap-3" },
757
822
  elements.map(
758
- (element, index) => (0, import_vue23.h)(
823
+ (element, index) => (0, import_vue25.h)(
759
824
  "div",
760
825
  { key: `${layout.value.path}-${index}` },
761
- (0, import_vue23.h)(import_vue22.DispatchRenderer, {
826
+ (0, import_vue25.h)(import_vue24.DispatchRenderer, {
762
827
  schema: layout.value.schema,
763
828
  uischema: element,
764
829
  path: layout.value.path,
@@ -775,29 +840,29 @@ var NuxtUiCategoryRenderer = (0, import_vue23.defineComponent)({
775
840
  });
776
841
 
777
842
  // src/renderers/layouts/NuxtUiGroupRenderer.ts
778
- var import_vue24 = require("@jsonforms/vue");
779
- var import_vue25 = require("vue");
780
- var NuxtUiGroupRenderer = (0, import_vue25.defineComponent)({
843
+ var import_vue26 = require("@jsonforms/vue");
844
+ var import_vue27 = require("vue");
845
+ var NuxtUiGroupRenderer = (0, import_vue27.defineComponent)({
781
846
  name: "NuxtUiGroupRenderer",
782
- components: { DispatchRenderer: import_vue24.DispatchRenderer },
783
- props: (0, import_vue24.rendererProps)(),
847
+ components: { DispatchRenderer: import_vue26.DispatchRenderer },
848
+ props: (0, import_vue26.rendererProps)(),
784
849
  setup(props) {
785
- const { layout } = (0, import_vue24.useJsonFormsLayout)(
850
+ const { layout } = (0, import_vue26.useJsonFormsLayout)(
786
851
  props
787
852
  );
788
853
  return () => {
789
854
  if (!layout.value.visible) return null;
790
855
  const elements = layout.value.uischema.elements ?? [];
791
- return (0, import_vue25.h)("div", { class: "rounded border p-3" }, [
792
- layout.value.label ? (0, import_vue25.h)("div", { class: "mb-3 text-sm font-semibold" }, layout.value.label) : null,
793
- (0, import_vue25.h)(
856
+ return (0, import_vue27.h)("div", { class: "rounded border p-3" }, [
857
+ layout.value.label ? (0, import_vue27.h)("div", { class: "mb-3 text-sm font-semibold" }, layout.value.label) : null,
858
+ (0, import_vue27.h)(
794
859
  "div",
795
860
  { class: "flex flex-col gap-3" },
796
861
  elements.map(
797
- (element, index) => (0, import_vue25.h)(
862
+ (element, index) => (0, import_vue27.h)(
798
863
  "div",
799
864
  { key: `${layout.value.path}-${index}` },
800
- (0, import_vue25.h)(import_vue24.DispatchRenderer, {
865
+ (0, import_vue27.h)(import_vue26.DispatchRenderer, {
801
866
  schema: layout.value.schema,
802
867
  uischema: element,
803
868
  path: layout.value.path,
@@ -814,27 +879,27 @@ var NuxtUiGroupRenderer = (0, import_vue25.defineComponent)({
814
879
  });
815
880
 
816
881
  // src/renderers/layouts/NuxtUiHorizontalLayoutRenderer.ts
817
- var import_vue26 = require("@jsonforms/vue");
818
- var import_vue27 = require("vue");
819
- var NuxtUiHorizontalLayoutRenderer = (0, import_vue27.defineComponent)({
882
+ var import_vue28 = require("@jsonforms/vue");
883
+ var import_vue29 = require("vue");
884
+ var NuxtUiHorizontalLayoutRenderer = (0, import_vue29.defineComponent)({
820
885
  name: "NuxtUiHorizontalLayoutRenderer",
821
- components: { DispatchRenderer: import_vue26.DispatchRenderer },
822
- props: (0, import_vue26.rendererProps)(),
886
+ components: { DispatchRenderer: import_vue28.DispatchRenderer },
887
+ props: (0, import_vue28.rendererProps)(),
823
888
  setup(props) {
824
- const { layout } = (0, import_vue26.useJsonFormsLayout)(
889
+ const { layout } = (0, import_vue28.useJsonFormsLayout)(
825
890
  props
826
891
  );
827
892
  return () => {
828
893
  if (!layout.value.visible) return null;
829
894
  const elements = layout.value.uischema.elements ?? [];
830
- return (0, import_vue27.h)(
895
+ return (0, import_vue29.h)(
831
896
  "div",
832
897
  { class: "flex flex-col gap-3 md:flex-row md:flex-wrap" },
833
898
  elements.map(
834
- (element, index) => (0, import_vue27.h)(
899
+ (element, index) => (0, import_vue29.h)(
835
900
  "div",
836
901
  { key: `${layout.value.path}-${index}`, class: "min-w-0 flex-1" },
837
- (0, import_vue27.h)(import_vue26.DispatchRenderer, {
902
+ (0, import_vue29.h)(import_vue28.DispatchRenderer, {
838
903
  schema: layout.value.schema,
839
904
  uischema: element,
840
905
  path: layout.value.path,
@@ -850,18 +915,18 @@ var NuxtUiHorizontalLayoutRenderer = (0, import_vue27.defineComponent)({
850
915
  });
851
916
 
852
917
  // src/renderers/layouts/NuxtUiLabelRenderer.ts
853
- var import_vue28 = require("@jsonforms/vue");
854
- var import_vue29 = require("vue");
855
- var NuxtUiLabelRenderer = (0, import_vue29.defineComponent)({
918
+ var import_vue30 = require("@jsonforms/vue");
919
+ var import_vue31 = require("vue");
920
+ var NuxtUiLabelRenderer = (0, import_vue31.defineComponent)({
856
921
  name: "NuxtUiLabelRenderer",
857
- props: (0, import_vue28.rendererProps)(),
922
+ props: (0, import_vue30.rendererProps)(),
858
923
  setup(props) {
859
- const { label } = (0, import_vue28.useJsonFormsLabel)(
924
+ const { label } = (0, import_vue30.useJsonFormsLabel)(
860
925
  props
861
926
  );
862
927
  return () => {
863
928
  if (!label.value.visible) return null;
864
- return (0, import_vue29.h)(
929
+ return (0, import_vue31.h)(
865
930
  "div",
866
931
  { class: "text-sm text-gray-600 dark:text-gray-300" },
867
932
  label.value.text
@@ -871,27 +936,27 @@ var NuxtUiLabelRenderer = (0, import_vue29.defineComponent)({
871
936
  });
872
937
 
873
938
  // src/renderers/layouts/NuxtUiVerticalLayoutRenderer.ts
874
- var import_vue30 = require("@jsonforms/vue");
875
- var import_vue31 = require("vue");
876
- var NuxtUiVerticalLayoutRenderer = (0, import_vue31.defineComponent)({
939
+ var import_vue32 = require("@jsonforms/vue");
940
+ var import_vue33 = require("vue");
941
+ var NuxtUiVerticalLayoutRenderer = (0, import_vue33.defineComponent)({
877
942
  name: "NuxtUiVerticalLayoutRenderer",
878
- components: { DispatchRenderer: import_vue30.DispatchRenderer },
879
- props: (0, import_vue30.rendererProps)(),
943
+ components: { DispatchRenderer: import_vue32.DispatchRenderer },
944
+ props: (0, import_vue32.rendererProps)(),
880
945
  setup(props) {
881
- const { layout } = (0, import_vue30.useJsonFormsLayout)(
946
+ const { layout } = (0, import_vue32.useJsonFormsLayout)(
882
947
  props
883
948
  );
884
949
  return () => {
885
950
  if (!layout.value.visible) return null;
886
951
  const elements = layout.value.uischema.elements ?? [];
887
- return (0, import_vue31.h)(
952
+ return (0, import_vue33.h)(
888
953
  "div",
889
954
  { class: "flex flex-col gap-3" },
890
955
  elements.map(
891
- (element, index) => (0, import_vue31.h)(
956
+ (element, index) => (0, import_vue33.h)(
892
957
  "div",
893
958
  { key: `${layout.value.path}-${index}` },
894
- (0, import_vue31.h)(import_vue30.DispatchRenderer, {
959
+ (0, import_vue33.h)(import_vue32.DispatchRenderer, {
895
960
  schema: layout.value.schema,
896
961
  uischema: element,
897
962
  path: layout.value.path,
@@ -909,6 +974,7 @@ var NuxtUiVerticalLayoutRenderer = (0, import_vue31.defineComponent)({
909
974
  // src/nuxtUiRenderers.ts
910
975
  var RANK = 10;
911
976
  var ENUM_RANK = RANK + 1;
977
+ var PASSWORD_RANK = ENUM_RANK + 1;
912
978
  var isMultiEnumControl = (uischema, schema, context) => {
913
979
  if (!(0, import_core3.uiTypeIs)("Control")(uischema, schema, context)) {
914
980
  return false;
@@ -930,72 +996,102 @@ var isMultiEnumControl = (uischema, schema, context) => {
930
996
  const resolvedItems = "$ref" in items && typeof items.$ref === "string" ? import_core3.Resolve.schema(rootSchema, items.$ref, rootSchema) : items;
931
997
  return (0, import_core3.isEnumSchema)(resolvedItems);
932
998
  };
999
+ var isOneOfEnumControl = (uischema, schema, context) => {
1000
+ if (!(0, import_core3.uiTypeIs)("Control")(uischema, schema, context)) {
1001
+ return false;
1002
+ }
1003
+ const scope = uischema?.scope;
1004
+ if (typeof scope !== "string") return false;
1005
+ const rootSchema = context?.rootSchema ?? schema;
1006
+ let resolved;
1007
+ try {
1008
+ resolved = import_core3.Resolve.schema(schema, scope, rootSchema);
1009
+ } catch {
1010
+ return false;
1011
+ }
1012
+ const oneOf = resolved?.oneOf;
1013
+ if (!Array.isArray(oneOf) || oneOf.length === 0) return false;
1014
+ for (const entry of oneOf) {
1015
+ if (typeof entry !== "object" || entry === null) continue;
1016
+ if (!("const" in entry)) return false;
1017
+ }
1018
+ return true;
1019
+ };
933
1020
  var nuxtUiRenderers = [
934
1021
  // Layouts
935
1022
  {
936
1023
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("VerticalLayout")),
937
- renderer: (0, import_vue32.markRaw)(NuxtUiVerticalLayoutRenderer)
1024
+ renderer: (0, import_vue34.markRaw)(NuxtUiVerticalLayoutRenderer)
938
1025
  },
939
1026
  {
940
1027
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("HorizontalLayout")),
941
- renderer: (0, import_vue32.markRaw)(NuxtUiHorizontalLayoutRenderer)
1028
+ renderer: (0, import_vue34.markRaw)(NuxtUiHorizontalLayoutRenderer)
942
1029
  },
943
1030
  {
944
1031
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("Group")),
945
- renderer: (0, import_vue32.markRaw)(NuxtUiGroupRenderer)
1032
+ renderer: (0, import_vue34.markRaw)(NuxtUiGroupRenderer)
946
1033
  },
947
1034
  {
948
1035
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("Categorization")),
949
- renderer: (0, import_vue32.markRaw)(NuxtUiCategorizationRenderer)
1036
+ renderer: (0, import_vue34.markRaw)(NuxtUiCategorizationRenderer)
950
1037
  },
951
1038
  {
952
1039
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("Category")),
953
- renderer: (0, import_vue32.markRaw)(NuxtUiCategoryRenderer)
1040
+ renderer: (0, import_vue34.markRaw)(NuxtUiCategoryRenderer)
954
1041
  },
955
1042
  {
956
1043
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.uiTypeIs)("Label")),
957
- renderer: (0, import_vue32.markRaw)(NuxtUiLabelRenderer)
1044
+ renderer: (0, import_vue34.markRaw)(NuxtUiLabelRenderer)
958
1045
  },
959
1046
  // Complex schemas
960
1047
  {
961
1048
  tester: (0, import_core3.rankWith)(RANK, (0, import_core3.schemaTypeIs)("array")),
962
- renderer: (0, import_vue32.markRaw)(NuxtUiArrayListRenderer)
1049
+ renderer: (0, import_vue34.markRaw)(NuxtUiArrayListRenderer)
963
1050
  },
964
1051
  {
965
1052
  tester: (0, import_core3.rankWith)(RANK, import_core3.isObjectControl),
966
- renderer: (0, import_vue32.markRaw)(NuxtUiObjectRenderer)
1053
+ renderer: (0, import_vue34.markRaw)(NuxtUiObjectRenderer)
967
1054
  },
968
1055
  // Primitive controls
969
1056
  {
970
1057
  tester: (0, import_core3.rankWith)(RANK, import_core3.isMultiLineControl),
971
- renderer: (0, import_vue32.markRaw)(NuxtUiTextareaControl)
1058
+ renderer: (0, import_vue34.markRaw)(NuxtUiTextareaControl)
972
1059
  },
973
1060
  {
974
1061
  tester: (0, import_core3.rankWith)(RANK, import_core3.isNumberControl),
975
- renderer: (0, import_vue32.markRaw)(NuxtUiNumberControl)
1062
+ renderer: (0, import_vue34.markRaw)(NuxtUiNumberControl)
976
1063
  },
977
1064
  {
978
1065
  tester: (0, import_core3.rankWith)(RANK, import_core3.isIntegerControl),
979
- renderer: (0, import_vue32.markRaw)(NuxtUiIntegerControl)
1066
+ renderer: (0, import_vue34.markRaw)(NuxtUiIntegerControl)
980
1067
  },
981
1068
  {
982
1069
  tester: (0, import_core3.rankWith)(RANK, import_core3.isBooleanControl),
983
- renderer: (0, import_vue32.markRaw)(NuxtUiBooleanControl)
1070
+ renderer: (0, import_vue34.markRaw)(NuxtUiBooleanControl)
984
1071
  },
985
1072
  {
986
1073
  // Multi-enum must outrank generic array renderer and string renderer.
987
1074
  tester: (0, import_core3.rankWith)(ENUM_RANK, isMultiEnumControl),
988
- renderer: (0, import_vue32.markRaw)(NuxtUiMultiEnumControl)
1075
+ renderer: (0, import_vue34.markRaw)(NuxtUiMultiEnumControl)
1076
+ },
1077
+ {
1078
+ // oneOf with const+title (display labels) - same as enum for rendering.
1079
+ tester: (0, import_core3.rankWith)(ENUM_RANK, isOneOfEnumControl),
1080
+ renderer: (0, import_vue34.markRaw)(NuxtUiEnumControl)
989
1081
  },
990
1082
  {
991
1083
  // Enum must outrank the generic string control, otherwise enums can render
992
1084
  // as freeform text inputs.
993
1085
  tester: (0, import_core3.rankWith)(ENUM_RANK, import_core3.isEnumControl),
994
- renderer: (0, import_vue32.markRaw)(NuxtUiEnumControl)
1086
+ renderer: (0, import_vue34.markRaw)(NuxtUiEnumControl)
1087
+ },
1088
+ {
1089
+ tester: (0, import_core3.rankWith)(PASSWORD_RANK, (0, import_core3.and)(import_core3.isStringControl, (0, import_core3.formatIs)("password"))),
1090
+ renderer: (0, import_vue34.markRaw)(NuxtUiPasswordControl)
995
1091
  },
996
1092
  {
997
1093
  tester: (0, import_core3.rankWith)(RANK, import_core3.isStringControl),
998
- renderer: (0, import_vue32.markRaw)(NuxtUiStringControl)
1094
+ renderer: (0, import_vue34.markRaw)(NuxtUiStringControl)
999
1095
  }
1000
1096
  ];
1001
1097
  // Annotate the CommonJS export names for ESM import in node: