@flanksource/clicky-ui 0.2.15 → 0.2.16

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.
Files changed (84) hide show
  1. package/dist/components/Combobox.cjs +15 -7
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts +8 -0
  4. package/dist/components/Combobox.d.ts.map +1 -1
  5. package/dist/components/Combobox.js +15 -7
  6. package/dist/components/Combobox.js.map +1 -1
  7. package/dist/components/DateTimePicker.cjs +3 -0
  8. package/dist/components/DateTimePicker.cjs.map +1 -1
  9. package/dist/components/DateTimePicker.d.ts +6 -2
  10. package/dist/components/DateTimePicker.d.ts.map +1 -1
  11. package/dist/components/DateTimePicker.js +3 -0
  12. package/dist/components/DateTimePicker.js.map +1 -1
  13. package/dist/components/Field.cjs +33 -0
  14. package/dist/components/Field.cjs.map +1 -0
  15. package/dist/components/Field.d.ts +27 -0
  16. package/dist/components/Field.d.ts.map +1 -0
  17. package/dist/components/Field.js +33 -0
  18. package/dist/components/Field.js.map +1 -0
  19. package/dist/components/FilterBar.cjs +41 -14
  20. package/dist/components/FilterBar.cjs.map +1 -1
  21. package/dist/components/FilterBar.d.ts +2 -0
  22. package/dist/components/FilterBar.d.ts.map +1 -1
  23. package/dist/components/FilterBar.js +43 -16
  24. package/dist/components/FilterBar.js.map +1 -1
  25. package/dist/components/MultiSelect.cjs +2 -1
  26. package/dist/components/MultiSelect.cjs.map +1 -1
  27. package/dist/components/MultiSelect.d.ts +5 -1
  28. package/dist/components/MultiSelect.d.ts.map +1 -1
  29. package/dist/components/MultiSelect.js +2 -1
  30. package/dist/components/MultiSelect.js.map +1 -1
  31. package/dist/components/json-schema-form-fields.cjs +15 -8
  32. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  33. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  34. package/dist/components/json-schema-form-fields.js +15 -8
  35. package/dist/components/json-schema-form-fields.js.map +1 -1
  36. package/dist/components/json-schema-form-types.d.ts +1 -0
  37. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  38. package/dist/components/loading.cjs +4 -4
  39. package/dist/components/loading.cjs.map +1 -1
  40. package/dist/components/loading.d.ts +6 -4
  41. package/dist/components/loading.d.ts.map +1 -1
  42. package/dist/components/loading.js +4 -4
  43. package/dist/components/loading.js.map +1 -1
  44. package/dist/components/use-form.cjs +27 -0
  45. package/dist/components/use-form.cjs.map +1 -0
  46. package/dist/components/use-form.d.ts +30 -0
  47. package/dist/components/use-form.d.ts.map +1 -0
  48. package/dist/components/use-form.js +27 -0
  49. package/dist/components/use-form.js.map +1 -0
  50. package/dist/components.cjs +7 -0
  51. package/dist/components.cjs.map +1 -1
  52. package/dist/components.d.ts +4 -1
  53. package/dist/components.d.ts.map +1 -1
  54. package/dist/components.js +8 -1
  55. package/dist/components.js.map +1 -1
  56. package/dist/data/version-info.cjs +3 -3
  57. package/dist/data/version-info.js +3 -3
  58. package/dist/index.cjs +7 -0
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.js +8 -1
  61. package/dist/index.js.map +1 -1
  62. package/dist/overlay/Modal.cjs +27 -23
  63. package/dist/overlay/Modal.cjs.map +1 -1
  64. package/dist/overlay/Modal.d.ts +7 -1
  65. package/dist/overlay/Modal.d.ts.map +1 -1
  66. package/dist/overlay/Modal.js +27 -23
  67. package/dist/overlay/Modal.js.map +1 -1
  68. package/dist/rpc/FilterForm.cjs +4 -4
  69. package/dist/rpc/FilterForm.cjs.map +1 -1
  70. package/dist/rpc/FilterForm.js +4 -4
  71. package/dist/rpc/FilterForm.js.map +1 -1
  72. package/dist/rpc/OperationCatalog.cjs +17 -21
  73. package/dist/rpc/OperationCatalog.cjs.map +1 -1
  74. package/dist/rpc/OperationCatalog.d.ts +3 -3
  75. package/dist/rpc/OperationCatalog.d.ts.map +1 -1
  76. package/dist/rpc/OperationCatalog.js +18 -22
  77. package/dist/rpc/OperationCatalog.js.map +1 -1
  78. package/dist/rpc/formMetadata.cjs +5 -3
  79. package/dist/rpc/formMetadata.cjs.map +1 -1
  80. package/dist/rpc/formMetadata.d.ts.map +1 -1
  81. package/dist/rpc/formMetadata.js +5 -3
  82. package/dist/rpc/formMetadata.js.map +1 -1
  83. package/dist/styles.css +1 -1
  84. package/package.json +1 -1
@@ -12,6 +12,7 @@ function MultiSelect({
12
12
  value,
13
13
  onChange,
14
14
  placeholder = "Select options",
15
+ ariaLabel,
15
16
  disabled,
16
17
  className,
17
18
  triggerClassName,
@@ -66,7 +67,7 @@ function MultiSelect({
66
67
  variant: "outline",
67
68
  size: "sm",
68
69
  disabled,
69
- "aria-label": `${placeholder} filter`,
70
+ "aria-label": ariaLabel ?? `${placeholder} filter`,
70
71
  "aria-haspopup": "menu",
71
72
  "aria-expanded": open,
72
73
  onClick: () => setOpen((current) => !current),
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.cjs","sources":["../../src/components/MultiSelect.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Button } from \"./button\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\n\nexport type MultiSelectOption = {\n /** Stable option value written into the selected value array. */\n value: string;\n /** Visible option label in the menu and trigger summary. */\n label: ReactNode;\n /** Prevents selecting this option while still showing it in the menu. */\n disabled?: boolean;\n /** Optional browser tooltip for truncated or explanatory labels. */\n title?: string;\n};\n\nexport type MultiSelectProps = {\n /** Available options, rendered in the order provided. */\n options: MultiSelectOption[];\n /** Controlled selected option values. */\n value: string[];\n /** Called with the complete next value array after each toggle. */\n onChange: (next: string[]) => void;\n /** Trigger text when nothing is selected. Also labels the menu. */\n placeholder?: string;\n /** Disables opening and changing selections. */\n disabled?: boolean;\n /** Classes applied to the root wrapper. */\n className?: string;\n /** Classes applied to the trigger button. */\n triggerClassName?: string;\n /** Classes applied to the popover menu. */\n menuClassName?: string;\n};\n\nexport function MultiSelect({\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n disabled,\n className,\n triggerClassName,\n menuClassName,\n}: MultiSelectProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setOpen(false);\n triggerRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const selected = useMemo(() => {\n const selectedOptions = options.filter((option) => value.includes(option.value));\n const labels = selectedOptions\n .map((option) => option.label)\n .filter((label): label is string => typeof label === \"string\");\n\n if (selectedOptions.length === 0) return placeholder;\n if (labels.length === 0) return `${selectedOptions.length} selected`;\n if (labels.length <= 2) return labels.join(\", \");\n return `${labels.length} selected`;\n }, [options, placeholder, value]);\n\n function toggleOption(nextValue: string) {\n if (value.includes(nextValue)) {\n onChange(value.filter((current) => current !== nextValue));\n return;\n }\n onChange([...value, nextValue]);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled}\n aria-label={`${placeholder} filter`}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n className={cn(\n \"w-fit max-w-[15rem] min-w-0 shrink-0 justify-between gap-3 text-left font-normal\",\n triggerClassName,\n value.length === 0 && \"text-muted-foreground\",\n )}\n >\n <span className=\"truncate\">{selected}</span>\n <Icon icon={open ? UiChevronUp : UiChevronDown} className=\"text-muted-foreground\" />\n </Button>\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[14rem] max-w-[20rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n <div className=\"mb-1 flex items-center justify-between gap-2 px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n <span>{placeholder}</span>\n <button\n type=\"button\"\n className=\"text-[10px] text-primary disabled:text-muted-foreground\"\n onClick={() => onChange([])}\n disabled={value.length === 0}\n >\n Clear all\n </button>\n </div>\n <div className=\"max-h-64 overflow-auto\">\n {options.map((option) => {\n const checked = value.includes(option.value);\n return (\n <label\n key={option.value}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <input\n type=\"checkbox\"\n role=\"menuitemcheckbox\"\n className=\"size-4 rounded border border-input\"\n checked={checked}\n disabled={option.disabled}\n onChange={() => toggleOption(option.value)}\n />\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </label>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["useRef","useState","useEffect","useMemo","jsxs","cn","Button","jsx","Icon","UiChevronUp","UiChevronDown"],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.cjs","sources":["../../src/components/MultiSelect.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Button } from \"./button\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\n\nexport type MultiSelectOption = {\n /** Stable option value written into the selected value array. */\n value: string;\n /** Visible option label in the menu and trigger summary. */\n label: ReactNode;\n /** Prevents selecting this option while still showing it in the menu. */\n disabled?: boolean;\n /** Optional browser tooltip for truncated or explanatory labels. */\n title?: string;\n};\n\nexport type MultiSelectProps = {\n /** Available options, rendered in the order provided. */\n options: MultiSelectOption[];\n /** Controlled selected option values. */\n value: string[];\n /** Called with the complete next value array after each toggle. */\n onChange: (next: string[]) => void;\n /** Trigger text when nothing is selected. Also labels the menu. */\n placeholder?: string;\n /** Accessible name for the trigger. Use when the field is labelled\n * elsewhere so the trigger doesn't have to borrow `placeholder` for its\n * name (which would force a synthesized placeholder). */\n ariaLabel?: string;\n /** Disables opening and changing selections. */\n disabled?: boolean;\n /** Classes applied to the root wrapper. */\n className?: string;\n /** Classes applied to the trigger button. */\n triggerClassName?: string;\n /** Classes applied to the popover menu. */\n menuClassName?: string;\n};\n\nexport function MultiSelect({\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n ariaLabel,\n disabled,\n className,\n triggerClassName,\n menuClassName,\n}: MultiSelectProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setOpen(false);\n triggerRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const selected = useMemo(() => {\n const selectedOptions = options.filter((option) => value.includes(option.value));\n const labels = selectedOptions\n .map((option) => option.label)\n .filter((label): label is string => typeof label === \"string\");\n\n if (selectedOptions.length === 0) return placeholder;\n if (labels.length === 0) return `${selectedOptions.length} selected`;\n if (labels.length <= 2) return labels.join(\", \");\n return `${labels.length} selected`;\n }, [options, placeholder, value]);\n\n function toggleOption(nextValue: string) {\n if (value.includes(nextValue)) {\n onChange(value.filter((current) => current !== nextValue));\n return;\n }\n onChange([...value, nextValue]);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled}\n aria-label={ariaLabel ?? `${placeholder} filter`}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n className={cn(\n \"w-fit max-w-[15rem] min-w-0 shrink-0 justify-between gap-3 text-left font-normal\",\n triggerClassName,\n value.length === 0 && \"text-muted-foreground\",\n )}\n >\n <span className=\"truncate\">{selected}</span>\n <Icon icon={open ? UiChevronUp : UiChevronDown} className=\"text-muted-foreground\" />\n </Button>\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[14rem] max-w-[20rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n <div className=\"mb-1 flex items-center justify-between gap-2 px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n <span>{placeholder}</span>\n <button\n type=\"button\"\n className=\"text-[10px] text-primary disabled:text-muted-foreground\"\n onClick={() => onChange([])}\n disabled={value.length === 0}\n >\n Clear all\n </button>\n </div>\n <div className=\"max-h-64 overflow-auto\">\n {options.map((option) => {\n const checked = value.includes(option.value);\n return (\n <label\n key={option.value}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <input\n type=\"checkbox\"\n role=\"menuitemcheckbox\"\n className=\"size-4 rounded border border-input\"\n checked={checked}\n disabled={option.disabled}\n onChange={() => toggleOption(option.value)}\n />\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </label>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["useRef","useState","useEffect","useMemo","jsxs","cn","Button","jsx","Icon","UiChevronUp","UiChevronDown"],"mappings":"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"}
@@ -18,6 +18,10 @@ export type MultiSelectProps = {
18
18
  onChange: (next: string[]) => void;
19
19
  /** Trigger text when nothing is selected. Also labels the menu. */
20
20
  placeholder?: string;
21
+ /** Accessible name for the trigger. Use when the field is labelled
22
+ * elsewhere so the trigger doesn't have to borrow `placeholder` for its
23
+ * name (which would force a synthesized placeholder). */
24
+ ariaLabel?: string;
21
25
  /** Disables opening and changing selections. */
22
26
  disabled?: boolean;
23
27
  /** Classes applied to the root wrapper. */
@@ -27,5 +31,5 @@ export type MultiSelectProps = {
27
31
  /** Classes applied to the popover menu. */
28
32
  menuClassName?: string;
29
33
  };
30
- export declare function MultiSelect({ options, value, onChange, placeholder, disabled, className, triggerClassName, menuClassName, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
34
+ export declare function MultiSelect({ options, value, onChange, placeholder, ariaLabel, disabled, className, triggerClassName, menuClassName, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
31
35
  //# sourceMappingURL=MultiSelect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.d.ts","sourceRoot":"","sources":["../../src/components/MultiSelect.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,KAAK,EAAE,SAAS,CAAC;IACjB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yDAAyD;IACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA8B,EAC9B,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,aAAa,GACd,EAAE,gBAAgB,2CAqHlB"}
1
+ {"version":3,"file":"MultiSelect.d.ts","sourceRoot":"","sources":["../../src/components/MultiSelect.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,KAAK,EAAE,SAAS,CAAC;IACjB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yDAAyD;IACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;8DAE0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA8B,EAC9B,SAAS,EACT,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,aAAa,GACd,EAAE,gBAAgB,2CAqHlB"}
@@ -10,6 +10,7 @@ function MultiSelect({
10
10
  value,
11
11
  onChange,
12
12
  placeholder = "Select options",
13
+ ariaLabel,
13
14
  disabled,
14
15
  className,
15
16
  triggerClassName,
@@ -64,7 +65,7 @@ function MultiSelect({
64
65
  variant: "outline",
65
66
  size: "sm",
66
67
  disabled,
67
- "aria-label": `${placeholder} filter`,
68
+ "aria-label": ariaLabel ?? `${placeholder} filter`,
68
69
  "aria-haspopup": "menu",
69
70
  "aria-expanded": open,
70
71
  onClick: () => setOpen((current) => !current),
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.js","sources":["../../src/components/MultiSelect.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Button } from \"./button\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\n\nexport type MultiSelectOption = {\n /** Stable option value written into the selected value array. */\n value: string;\n /** Visible option label in the menu and trigger summary. */\n label: ReactNode;\n /** Prevents selecting this option while still showing it in the menu. */\n disabled?: boolean;\n /** Optional browser tooltip for truncated or explanatory labels. */\n title?: string;\n};\n\nexport type MultiSelectProps = {\n /** Available options, rendered in the order provided. */\n options: MultiSelectOption[];\n /** Controlled selected option values. */\n value: string[];\n /** Called with the complete next value array after each toggle. */\n onChange: (next: string[]) => void;\n /** Trigger text when nothing is selected. Also labels the menu. */\n placeholder?: string;\n /** Disables opening and changing selections. */\n disabled?: boolean;\n /** Classes applied to the root wrapper. */\n className?: string;\n /** Classes applied to the trigger button. */\n triggerClassName?: string;\n /** Classes applied to the popover menu. */\n menuClassName?: string;\n};\n\nexport function MultiSelect({\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n disabled,\n className,\n triggerClassName,\n menuClassName,\n}: MultiSelectProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setOpen(false);\n triggerRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const selected = useMemo(() => {\n const selectedOptions = options.filter((option) => value.includes(option.value));\n const labels = selectedOptions\n .map((option) => option.label)\n .filter((label): label is string => typeof label === \"string\");\n\n if (selectedOptions.length === 0) return placeholder;\n if (labels.length === 0) return `${selectedOptions.length} selected`;\n if (labels.length <= 2) return labels.join(\", \");\n return `${labels.length} selected`;\n }, [options, placeholder, value]);\n\n function toggleOption(nextValue: string) {\n if (value.includes(nextValue)) {\n onChange(value.filter((current) => current !== nextValue));\n return;\n }\n onChange([...value, nextValue]);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled}\n aria-label={`${placeholder} filter`}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n className={cn(\n \"w-fit max-w-[15rem] min-w-0 shrink-0 justify-between gap-3 text-left font-normal\",\n triggerClassName,\n value.length === 0 && \"text-muted-foreground\",\n )}\n >\n <span className=\"truncate\">{selected}</span>\n <Icon icon={open ? UiChevronUp : UiChevronDown} className=\"text-muted-foreground\" />\n </Button>\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[14rem] max-w-[20rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n <div className=\"mb-1 flex items-center justify-between gap-2 px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n <span>{placeholder}</span>\n <button\n type=\"button\"\n className=\"text-[10px] text-primary disabled:text-muted-foreground\"\n onClick={() => onChange([])}\n disabled={value.length === 0}\n >\n Clear all\n </button>\n </div>\n <div className=\"max-h-64 overflow-auto\">\n {options.map((option) => {\n const checked = value.includes(option.value);\n return (\n <label\n key={option.value}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <input\n type=\"checkbox\"\n role=\"menuitemcheckbox\"\n className=\"size-4 rounded border border-input\"\n checked={checked}\n disabled={option.disabled}\n onChange={() => toggleOption(option.value)}\n />\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </label>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.js","sources":["../../src/components/MultiSelect.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Button } from \"./button\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronUp } from \"../icons\";\n\nexport type MultiSelectOption = {\n /** Stable option value written into the selected value array. */\n value: string;\n /** Visible option label in the menu and trigger summary. */\n label: ReactNode;\n /** Prevents selecting this option while still showing it in the menu. */\n disabled?: boolean;\n /** Optional browser tooltip for truncated or explanatory labels. */\n title?: string;\n};\n\nexport type MultiSelectProps = {\n /** Available options, rendered in the order provided. */\n options: MultiSelectOption[];\n /** Controlled selected option values. */\n value: string[];\n /** Called with the complete next value array after each toggle. */\n onChange: (next: string[]) => void;\n /** Trigger text when nothing is selected. Also labels the menu. */\n placeholder?: string;\n /** Accessible name for the trigger. Use when the field is labelled\n * elsewhere so the trigger doesn't have to borrow `placeholder` for its\n * name (which would force a synthesized placeholder). */\n ariaLabel?: string;\n /** Disables opening and changing selections. */\n disabled?: boolean;\n /** Classes applied to the root wrapper. */\n className?: string;\n /** Classes applied to the trigger button. */\n triggerClassName?: string;\n /** Classes applied to the popover menu. */\n menuClassName?: string;\n};\n\nexport function MultiSelect({\n options,\n value,\n onChange,\n placeholder = \"Select options\",\n ariaLabel,\n disabled,\n className,\n triggerClassName,\n menuClassName,\n}: MultiSelectProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setOpen(false);\n triggerRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const selected = useMemo(() => {\n const selectedOptions = options.filter((option) => value.includes(option.value));\n const labels = selectedOptions\n .map((option) => option.label)\n .filter((label): label is string => typeof label === \"string\");\n\n if (selectedOptions.length === 0) return placeholder;\n if (labels.length === 0) return `${selectedOptions.length} selected`;\n if (labels.length <= 2) return labels.join(\", \");\n return `${labels.length} selected`;\n }, [options, placeholder, value]);\n\n function toggleOption(nextValue: string) {\n if (value.includes(nextValue)) {\n onChange(value.filter((current) => current !== nextValue));\n return;\n }\n onChange([...value, nextValue]);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled}\n aria-label={ariaLabel ?? `${placeholder} filter`}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n className={cn(\n \"w-fit max-w-[15rem] min-w-0 shrink-0 justify-between gap-3 text-left font-normal\",\n triggerClassName,\n value.length === 0 && \"text-muted-foreground\",\n )}\n >\n <span className=\"truncate\">{selected}</span>\n <Icon icon={open ? UiChevronUp : UiChevronDown} className=\"text-muted-foreground\" />\n </Button>\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[14rem] max-w-[20rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n <div className=\"mb-1 flex items-center justify-between gap-2 px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n <span>{placeholder}</span>\n <button\n type=\"button\"\n className=\"text-[10px] text-primary disabled:text-muted-foreground\"\n onClick={() => onChange([])}\n disabled={value.length === 0}\n >\n Clear all\n </button>\n </div>\n <div className=\"max-h-64 overflow-auto\">\n {options.map((option) => {\n const checked = value.includes(option.value);\n return (\n <label\n key={option.value}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <input\n type=\"checkbox\"\n role=\"menuitemcheckbox\"\n className=\"size-4 rounded border border-input\"\n checked={checked}\n disabled={option.disabled}\n onChange={() => toggleOption(option.value)}\n />\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </label>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -97,11 +97,16 @@ function ReadOnlyValue({ field, fieldId, size }) {
97
97
  }
98
98
  );
99
99
  }
100
- function FieldSuffixWrapper({ suffix, children }) {
101
- if (!suffix) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
100
+ function FieldAdornmentWrapper({
101
+ prefix,
102
+ suffix,
103
+ children
104
+ }) {
105
+ if (!prefix && !suffix) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
102
106
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-jsf-control": true, className: "relative", children: [
107
+ prefix && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-1.5 flex items-center", children: prefix }),
103
108
  children,
104
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-1.5 flex items-center", children: suffix })
109
+ suffix && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-1.5 flex items-center", children: suffix })
105
110
  ] });
106
111
  }
107
112
  function StringControl({
@@ -110,13 +115,13 @@ function StringControl({
110
115
  readOnly,
111
116
  size
112
117
  }) {
113
- return /* @__PURE__ */ jsxRuntime.jsx(FieldSuffixWrapper, { suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
118
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
114
119
  "input",
115
120
  {
116
121
  id: fieldId,
117
122
  type: "text",
118
123
  "data-jsf-input": true,
119
- className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.suffix && "pr-8"),
124
+ className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
120
125
  value: jsonSchemaFormUtils.toText(field.value),
121
126
  disabled: readOnly,
122
127
  placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema),
@@ -130,14 +135,14 @@ function NumberControl({
130
135
  readOnly,
131
136
  size
132
137
  }) {
133
- return /* @__PURE__ */ jsxRuntime.jsx(FieldSuffixWrapper, { suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
138
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
134
139
  "input",
135
140
  {
136
141
  id: fieldId,
137
142
  type: "text",
138
143
  inputMode: "decimal",
139
144
  "data-jsf-input": true,
140
- className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.suffix && "pr-8"),
145
+ className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
141
146
  value: jsonSchemaFormUtils.toText(field.value),
142
147
  disabled: readOnly,
143
148
  placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema),
@@ -178,10 +183,11 @@ function DateControl({
178
183
  id: fieldId,
179
184
  "aria-label": field.label,
180
185
  "data-jsf-input": true,
181
- inputClassName: utils.cn(jsonSchemaFormUtils.inputClass(size), field.suffix ? "pr-14" : "pr-8"),
186
+ inputClassName: utils.cn(jsonSchemaFormUtils.inputClass(size), field.prefix && "pl-8", field.suffix ? "pr-14" : "pr-8"),
182
187
  value: text,
183
188
  onChange: (next) => field.onChange(next),
184
189
  placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema),
190
+ prefix: field.prefix,
185
191
  suffix: field.suffix
186
192
  }
187
193
  );
@@ -228,6 +234,7 @@ function EnumControl({
228
234
  size,
229
235
  allowCustomValue: field.allowCustomValue ?? false,
230
236
  onChange: (v) => field.onChange(v),
237
+ prefix: field.prefix,
231
238
  suffix: field.suffix,
232
239
  ...jsonSchemaFormUtils.defaultPlaceholder(field.schema) ? { placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema) } : {}
233
240
  }
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.cjs","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldSuffixWrapper positions a field's trailing in-field adornment\n// (FieldControl.suffix) at the right edge of the input. The container is tagged\n// data-jsf-control so the adornment can locate its sibling input[data-jsf-input]\n// (e.g. for caret-aware insertion). With no suffix it renders the input bare, so\n// fields without an adornment are byte-for-byte unchanged.\nfunction FieldSuffixWrapper({ suffix, children }: { suffix?: ReactNode; children: ReactNode }) {\n if (!suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {children}\n <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldSuffixWrapper suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldSuffixWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n return (\n <FieldSuffixWrapper suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n const coerce = field.coerceNumber !== false;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n />\n </FieldSuffixWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":["jsx","cn","inlineRowGapClass","stackedRowGapClass","jsxs","controlMinHeightClass","labelSizeClass","LabelIcon","fieldInnerGapClass","toText","formatDateTimeRelative","controlHeightClass","Fragment","inputClass","defaultPlaceholder","DateTimePicker","withSyntheticValue","Combobox"],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-fields.cjs","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n const coerce = field.coerceNumber !== false;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":["jsx","cn","inlineRowGapClass","stackedRowGapClass","jsxs","controlMinHeightClass","labelSizeClass","LabelIcon","fieldInnerGapClass","toText","formatDateTimeRelative","controlHeightClass","inputClass","defaultPlaceholder","DateTimePicker","withSyntheticValue","Combobox"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-fields.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAI7D,OAAO,KAAK,EAAE,YAAY,EAAe,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAOtF,OAAO,EAOL,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAajC,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAcjH;AAOD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,IAAI,GACL,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CAsBA;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,2CAe5G;AAQD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAiBA;AAMD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,2CAc/G;AAiBD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CAeA;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CA0BA;AAMD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CA0BA;AAED,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CAmBA;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB,2CAqBA"}
1
+ {"version":3,"file":"json-schema-form-fields.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-fields.tsx"],"names":[],"mappings":"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"}
@@ -95,11 +95,16 @@ function ReadOnlyValue({ field, fieldId, size }) {
95
95
  }
96
96
  );
97
97
  }
98
- function FieldSuffixWrapper({ suffix, children }) {
99
- if (!suffix) return /* @__PURE__ */ jsx(Fragment, { children });
98
+ function FieldAdornmentWrapper({
99
+ prefix,
100
+ suffix,
101
+ children
102
+ }) {
103
+ if (!prefix && !suffix) return /* @__PURE__ */ jsx(Fragment, { children });
100
104
  return /* @__PURE__ */ jsxs("div", { "data-jsf-control": true, className: "relative", children: [
105
+ prefix && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-1.5 flex items-center", children: prefix }),
101
106
  children,
102
- /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-1.5 flex items-center", children: suffix })
107
+ suffix && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-1.5 flex items-center", children: suffix })
103
108
  ] });
104
109
  }
105
110
  function StringControl({
@@ -108,13 +113,13 @@ function StringControl({
108
113
  readOnly,
109
114
  size
110
115
  }) {
111
- return /* @__PURE__ */ jsx(FieldSuffixWrapper, { suffix: field.suffix, children: /* @__PURE__ */ jsx(
116
+ return /* @__PURE__ */ jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsx(
112
117
  "input",
113
118
  {
114
119
  id: fieldId,
115
120
  type: "text",
116
121
  "data-jsf-input": true,
117
- className: cn(inputClass(size), field.suffix && "pr-8"),
122
+ className: cn(inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
118
123
  value: toText(field.value),
119
124
  disabled: readOnly,
120
125
  placeholder: defaultPlaceholder(field.schema),
@@ -128,14 +133,14 @@ function NumberControl({
128
133
  readOnly,
129
134
  size
130
135
  }) {
131
- return /* @__PURE__ */ jsx(FieldSuffixWrapper, { suffix: field.suffix, children: /* @__PURE__ */ jsx(
136
+ return /* @__PURE__ */ jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsx(
132
137
  "input",
133
138
  {
134
139
  id: fieldId,
135
140
  type: "text",
136
141
  inputMode: "decimal",
137
142
  "data-jsf-input": true,
138
- className: cn(inputClass(size), field.suffix && "pr-8"),
143
+ className: cn(inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
139
144
  value: toText(field.value),
140
145
  disabled: readOnly,
141
146
  placeholder: defaultPlaceholder(field.schema),
@@ -176,10 +181,11 @@ function DateControl({
176
181
  id: fieldId,
177
182
  "aria-label": field.label,
178
183
  "data-jsf-input": true,
179
- inputClassName: cn(inputClass(size), field.suffix ? "pr-14" : "pr-8"),
184
+ inputClassName: cn(inputClass(size), field.prefix && "pl-8", field.suffix ? "pr-14" : "pr-8"),
180
185
  value: text,
181
186
  onChange: (next) => field.onChange(next),
182
187
  placeholder: defaultPlaceholder(field.schema),
188
+ prefix: field.prefix,
183
189
  suffix: field.suffix
184
190
  }
185
191
  );
@@ -226,6 +232,7 @@ function EnumControl({
226
232
  size,
227
233
  allowCustomValue: field.allowCustomValue ?? false,
228
234
  onChange: (v) => field.onChange(v),
235
+ prefix: field.prefix,
229
236
  suffix: field.suffix,
230
237
  ...defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {}
231
238
  }
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.js","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldSuffixWrapper positions a field's trailing in-field adornment\n// (FieldControl.suffix) at the right edge of the input. The container is tagged\n// data-jsf-control so the adornment can locate its sibling input[data-jsf-input]\n// (e.g. for caret-aware insertion). With no suffix it renders the input bare, so\n// fields without an adornment are byte-for-byte unchanged.\nfunction FieldSuffixWrapper({ suffix, children }: { suffix?: ReactNode; children: ReactNode }) {\n if (!suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {children}\n <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldSuffixWrapper suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldSuffixWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n return (\n <FieldSuffixWrapper suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n const coerce = field.coerceNumber !== false;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n />\n </FieldSuffixWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-fields.js","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n const coerce = field.coerceNumber !== false;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":[],"mappings":"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"}
@@ -60,6 +60,7 @@ export interface FieldControl {
60
60
  badge?: string;
61
61
  helper?: string;
62
62
  suffix?: ReactNode;
63
+ prefix?: ReactNode;
63
64
  minimum?: number;
64
65
  coerceNumber?: boolean;
65
66
  dateFormat?: "date" | "date-time";
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-types.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-types.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-types.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,8 @@ const FIXED_DOT_SIZE = {
8
8
  md: "size-6",
9
9
  lg: "size-10"
10
10
  };
11
- const RESPONSIVE_DOT_SIZE = "size-[clamp(1.5rem,12cqi,4.5rem)]";
12
- const RESPONSIVE_LABEL_SIZE = "text-[clamp(0.7rem,4cqi,0.95rem)]";
11
+ const RESPONSIVE_DOT_SIZE = "size-[clamp(1.25rem,18cqmin,2.5rem)]";
12
+ const RESPONSIVE_LABEL_SIZE = "text-[clamp(0.72rem,4.5cqmin,0.9rem)]";
13
13
  const LoadingDots = react.forwardRef(
14
14
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
15
15
  "svg",
@@ -72,8 +72,8 @@ const Loading = react.forwardRef(
72
72
  role: "status",
73
73
  "aria-label": "Loading",
74
74
  className: utils.cn(
75
- "flex min-h-32 w-full flex-col items-center justify-center gap-3 text-muted-foreground",
76
- "[container-type:inline-size]",
75
+ "flex h-full min-h-32 w-full flex-col items-center justify-center gap-3 text-muted-foreground",
76
+ "[container-type:size]",
77
77
  className
78
78
  ),
79
79
  ...props,