@flanksource/clicky-ui 0.2.9 → 0.2.11

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 (105) hide show
  1. package/dist/components/Combobox.cjs +4 -1
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts +6 -0
  4. package/dist/components/Combobox.d.ts.map +1 -1
  5. package/dist/components/Combobox.js +4 -1
  6. package/dist/components/Combobox.js.map +1 -1
  7. package/dist/components/JsonSchemaForm.cjs +24 -5
  8. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  9. package/dist/components/JsonSchemaForm.d.ts +1 -1
  10. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  11. package/dist/components/JsonSchemaForm.js +24 -5
  12. package/dist/components/JsonSchemaForm.js.map +1 -1
  13. package/dist/components/json-schema-form-array.cjs +117 -37
  14. package/dist/components/json-schema-form-array.cjs.map +1 -1
  15. package/dist/components/json-schema-form-array.d.ts.map +1 -1
  16. package/dist/components/json-schema-form-array.js +118 -38
  17. package/dist/components/json-schema-form-array.js.map +1 -1
  18. package/dist/components/json-schema-form-fields.cjs +121 -51
  19. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  20. package/dist/components/json-schema-form-fields.d.ts +9 -6
  21. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  22. package/dist/components/json-schema-form-fields.js +121 -51
  23. package/dist/components/json-schema-form-fields.js.map +1 -1
  24. package/dist/components/json-schema-form-render.cjs +15 -8
  25. package/dist/components/json-schema-form-render.cjs.map +1 -1
  26. package/dist/components/json-schema-form-render.d.ts +5 -2
  27. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  28. package/dist/components/json-schema-form-render.js +15 -8
  29. package/dist/components/json-schema-form-render.js.map +1 -1
  30. package/dist/components/json-schema-form-resolve.cjs +41 -6
  31. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  32. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  33. package/dist/components/json-schema-form-resolve.js +41 -6
  34. package/dist/components/json-schema-form-resolve.js.map +1 -1
  35. package/dist/components/json-schema-form-size.cjs +61 -0
  36. package/dist/components/json-schema-form-size.cjs.map +1 -0
  37. package/dist/components/json-schema-form-size.d.ts +11 -0
  38. package/dist/components/json-schema-form-size.d.ts.map +1 -0
  39. package/dist/components/json-schema-form-size.js +61 -0
  40. package/dist/components/json-schema-form-size.js.map +1 -0
  41. package/dist/components/json-schema-form-types.d.ts +36 -1
  42. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  43. package/dist/components.cjs +2 -0
  44. package/dist/components.cjs.map +1 -1
  45. package/dist/components.d.ts +3 -1
  46. package/dist/components.d.ts.map +1 -1
  47. package/dist/components.js +2 -0
  48. package/dist/components.js.map +1 -1
  49. package/dist/data/Clicky.cjs +412 -90
  50. package/dist/data/Clicky.cjs.map +1 -1
  51. package/dist/data/Clicky.d.ts +4 -2
  52. package/dist/data/Clicky.d.ts.map +1 -1
  53. package/dist/data/Clicky.js +413 -91
  54. package/dist/data/Clicky.js.map +1 -1
  55. package/dist/data/DataTable.cjs +534 -293
  56. package/dist/data/DataTable.cjs.map +1 -1
  57. package/dist/data/DataTable.d.ts +12 -0
  58. package/dist/data/DataTable.d.ts.map +1 -1
  59. package/dist/data/DataTable.js +534 -293
  60. package/dist/data/DataTable.js.map +1 -1
  61. package/dist/data.d.ts +1 -1
  62. package/dist/data.d.ts.map +1 -1
  63. package/dist/index.cjs +2 -0
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.js +2 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/rpc/CommandOutput.cjs +101 -20
  68. package/dist/rpc/CommandOutput.cjs.map +1 -1
  69. package/dist/rpc/CommandOutput.d.ts +12 -4
  70. package/dist/rpc/CommandOutput.d.ts.map +1 -1
  71. package/dist/rpc/CommandOutput.js +102 -21
  72. package/dist/rpc/CommandOutput.js.map +1 -1
  73. package/dist/rpc/ExecutionResult.cjs +4 -76
  74. package/dist/rpc/ExecutionResult.cjs.map +1 -1
  75. package/dist/rpc/ExecutionResult.d.ts +3 -22
  76. package/dist/rpc/ExecutionResult.d.ts.map +1 -1
  77. package/dist/rpc/ExecutionResult.js +4 -76
  78. package/dist/rpc/ExecutionResult.js.map +1 -1
  79. package/dist/rpc/OperationCatalog.cjs +22 -13
  80. package/dist/rpc/OperationCatalog.cjs.map +1 -1
  81. package/dist/rpc/OperationCatalog.d.ts.map +1 -1
  82. package/dist/rpc/OperationCatalog.js +24 -15
  83. package/dist/rpc/OperationCatalog.js.map +1 -1
  84. package/dist/rpc/OperationCommandPage.cjs +10 -20
  85. package/dist/rpc/OperationCommandPage.cjs.map +1 -1
  86. package/dist/rpc/OperationCommandPage.d.ts.map +1 -1
  87. package/dist/rpc/OperationCommandPage.js +11 -21
  88. package/dist/rpc/OperationCommandPage.js.map +1 -1
  89. package/dist/rpc/apiClient.cjs +18 -0
  90. package/dist/rpc/apiClient.cjs.map +1 -1
  91. package/dist/rpc/apiClient.d.ts.map +1 -1
  92. package/dist/rpc/apiClient.js +18 -0
  93. package/dist/rpc/apiClient.js.map +1 -1
  94. package/dist/rpc/formMetadata.cjs +31 -0
  95. package/dist/rpc/formMetadata.cjs.map +1 -1
  96. package/dist/rpc/formMetadata.d.ts +3 -1
  97. package/dist/rpc/formMetadata.d.ts.map +1 -1
  98. package/dist/rpc/formMetadata.js +31 -0
  99. package/dist/rpc/formMetadata.js.map +1 -1
  100. package/dist/rpc/types.cjs.map +1 -1
  101. package/dist/rpc/types.d.ts +7 -0
  102. package/dist/rpc/types.d.ts.map +1 -1
  103. package/dist/rpc/types.js.map +1 -1
  104. package/dist/styles.css +1 -1
  105. package/package.json +1 -1
@@ -8,57 +8,73 @@ import { Combobox } from "./Combobox.js";
8
8
  import { DateTimePicker } from "./DateTimePicker.js";
9
9
  import { ArrayControl } from "./json-schema-form-array.js";
10
10
  import { renderObjectFields, renderFieldNodes } from "./json-schema-form-render.js";
11
- import { UiTrash } from "../icons/components/UiTrash.js";
11
+ import { controlMinHeightClass, fieldInnerGapClass, labelSizeClass, controlHeightClass, stackedRowGapClass, inputSizeClass } from "./json-schema-form-size.js";
12
12
  import { UiAdd } from "../icons/components/UiAdd.js";
13
- const inputClassName = "h-9 w-full min-w-0 rounded-md border border-input bg-background px-2 text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring";
13
+ import { UiTrash } from "../icons/components/UiTrash.js";
14
+ const INPUT_BASE = "w-full min-w-0 rounded-md border border-input bg-background text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring";
15
+ function inputClass(size) {
16
+ return cn(INPUT_BASE, inputSizeClass[size]);
17
+ }
14
18
  function FieldWrapper({
15
19
  label,
16
20
  value,
17
21
  helper,
18
22
  error,
19
- inline
23
+ layout,
24
+ size
20
25
  }) {
21
- if (inline) {
22
- return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[10rem_1fr] items-start gap-x-3 gap-y-0.5", children: [
23
- /* @__PURE__ */ jsx("div", { className: "flex min-h-9 items-center", children: label }),
24
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children: value }),
25
- helper && /* @__PURE__ */ jsx("p", { className: "col-start-2 text-xs text-muted-foreground", children: helper }),
26
- error && /* @__PURE__ */ jsx("p", { className: "col-start-2 text-xs text-destructive", children: error })
27
- ] });
26
+ if (layout.mode === "inline") {
27
+ const labelMaxWidth = layout.labelMaxWidth ?? "40ch";
28
+ const valueMaxWidth = layout.valueMaxWidth ?? "400px";
29
+ return /* @__PURE__ */ jsxs(
30
+ "div",
31
+ {
32
+ className: "grid items-start gap-x-3 gap-y-0.5",
33
+ style: { gridTemplateColumns: `minmax(0, ${labelMaxWidth}) minmax(0, ${valueMaxWidth})` },
34
+ children: [
35
+ /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 items-center", controlMinHeightClass[size]), children: label }),
36
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: value }),
37
+ helper && /* @__PURE__ */ jsx("p", { className: "col-start-2 text-xs text-muted-foreground", children: helper }),
38
+ error && /* @__PURE__ */ jsx("p", { className: "col-start-2 text-xs text-destructive", children: error })
39
+ ]
40
+ }
41
+ );
28
42
  }
29
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
43
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", fieldInnerGapClass[size]), children: [
30
44
  label,
31
45
  value,
32
46
  helper && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: helper }),
33
47
  error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error })
34
48
  ] });
35
49
  }
36
- function FieldLabel({ field, fieldId }) {
37
- return /* @__PURE__ */ jsxs("label", { htmlFor: fieldId, className: "flex items-center gap-2 text-sm font-medium", children: [
50
+ function FieldLabel({ field, fieldId, size }) {
51
+ return /* @__PURE__ */ jsxs("label", { htmlFor: fieldId, className: cn("flex items-center gap-2 font-medium", labelSizeClass[size]), children: [
38
52
  /* @__PURE__ */ jsx(LabelIcon, { icon: field.labelIcon, className: "text-[15px] text-muted-foreground" }),
39
53
  /* @__PURE__ */ jsx("span", { title: field.label !== field.key ? field.key : void 0, children: field.label }),
40
54
  field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" }),
41
55
  field.badge && /* @__PURE__ */ jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: field.badge })
42
56
  ] });
43
57
  }
44
- function fieldInputId(key) {
45
- return `jsf-${key.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
58
+ function fieldInputId(key, prefix) {
59
+ const safe = key.replace(/[^a-zA-Z0-9_-]/g, "_");
60
+ return prefix ? `jsf-${prefix}-${safe}` : `jsf-${safe}`;
46
61
  }
47
62
  function renderValueControl(field, ctx) {
48
- const fieldId = fieldInputId(field.key);
63
+ const fieldId = fieldInputId(field.key, ctx.idPrefix);
49
64
  if (field.readOnly && field.kind !== "array" && field.kind !== "object" && field.kind !== "string-map") {
50
- return /* @__PURE__ */ jsx(ReadOnlyValue, { field, fieldId });
65
+ return /* @__PURE__ */ jsx(ReadOnlyValue, { field, fieldId, size: ctx.size });
51
66
  }
52
67
  const readOnly = ctx.readOnly;
68
+ const size = ctx.size;
53
69
  switch (field.kind) {
54
70
  case "enum":
55
- return /* @__PURE__ */ jsx(EnumControl, { field, fieldId, readOnly });
71
+ return /* @__PURE__ */ jsx(EnumControl, { field, fieldId, readOnly, size });
56
72
  case "boolean":
57
- return /* @__PURE__ */ jsx(BooleanControl, { field, fieldId, readOnly });
73
+ return /* @__PURE__ */ jsx(BooleanControl, { field, fieldId, readOnly, size });
58
74
  case "number":
59
- return /* @__PURE__ */ jsx(NumberControl, { field, fieldId, readOnly });
75
+ return /* @__PURE__ */ jsx(NumberControl, { field, fieldId, readOnly, size });
60
76
  case "date":
61
- return /* @__PURE__ */ jsx(DateControl, { field, fieldId, readOnly });
77
+ return /* @__PURE__ */ jsx(DateControl, { field, fieldId, readOnly, size });
62
78
  case "array":
63
79
  return /* @__PURE__ */ jsx(ArrayControl, { field, fieldId, ctx });
64
80
  case "object":
@@ -66,10 +82,10 @@ function renderValueControl(field, ctx) {
66
82
  case "string-map":
67
83
  return /* @__PURE__ */ jsx(StringMapControl, { field, ctx });
68
84
  default:
69
- return /* @__PURE__ */ jsx(StringControl, { field, fieldId, readOnly });
85
+ return /* @__PURE__ */ jsx(StringControl, { field, fieldId, readOnly, size });
70
86
  }
71
87
  }
72
- function ReadOnlyValue({ field, fieldId }) {
88
+ function ReadOnlyValue({ field, fieldId, size }) {
73
89
  const text = toText(field.value);
74
90
  const display = field.kind === "date" && text ? formatDateTimeRelative(text) : text;
75
91
  return /* @__PURE__ */ jsx(
@@ -78,7 +94,7 @@ function ReadOnlyValue({ field, fieldId }) {
78
94
  id: fieldId,
79
95
  "data-jsf-input": true,
80
96
  "data-jsf-readonly": true,
81
- className: "flex h-9 items-center text-sm text-foreground",
97
+ className: cn("flex items-center text-foreground", controlHeightClass[size], labelSizeClass[size]),
82
98
  title: text || void 0,
83
99
  children: display || /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "—" })
84
100
  }
@@ -87,7 +103,8 @@ function ReadOnlyValue({ field, fieldId }) {
87
103
  function StringControl({
88
104
  field,
89
105
  fieldId,
90
- readOnly
106
+ readOnly,
107
+ size
91
108
  }) {
92
109
  return /* @__PURE__ */ jsx(
93
110
  "input",
@@ -95,7 +112,7 @@ function StringControl({
95
112
  id: fieldId,
96
113
  type: "text",
97
114
  "data-jsf-input": true,
98
- className: inputClassName,
115
+ className: inputClass(size),
99
116
  value: toText(field.value),
100
117
  disabled: readOnly,
101
118
  placeholder: defaultPlaceholder(field.schema),
@@ -106,7 +123,8 @@ function StringControl({
106
123
  function NumberControl({
107
124
  field,
108
125
  fieldId,
109
- readOnly
126
+ readOnly,
127
+ size
110
128
  }) {
111
129
  return /* @__PURE__ */ jsx(
112
130
  "input",
@@ -115,7 +133,7 @@ function NumberControl({
115
133
  type: "text",
116
134
  inputMode: "decimal",
117
135
  "data-jsf-input": true,
118
- className: inputClassName,
136
+ className: inputClass(size),
119
137
  value: toText(field.value),
120
138
  disabled: readOnly,
121
139
  placeholder: defaultPlaceholder(field.schema),
@@ -134,7 +152,8 @@ function NumberControl({
134
152
  function DateControl({
135
153
  field,
136
154
  fieldId,
137
- readOnly
155
+ readOnly,
156
+ size
138
157
  }) {
139
158
  const text = toText(field.value);
140
159
  if (readOnly) {
@@ -143,7 +162,7 @@ function DateControl({
143
162
  {
144
163
  id: fieldId,
145
164
  "data-jsf-input": true,
146
- className: "flex h-9 items-center text-sm text-foreground",
165
+ className: cn("flex items-center text-foreground", controlHeightClass[size], labelSizeClass[size]),
147
166
  title: text || void 0,
148
167
  children: text ? formatDateTimeRelative(text) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "—" })
149
168
  }
@@ -155,7 +174,7 @@ function DateControl({
155
174
  id: fieldId,
156
175
  "aria-label": field.label,
157
176
  "data-jsf-input": true,
158
- inputClassName: cn(inputClassName, "pr-8"),
177
+ inputClassName: cn(inputClass(size), "pr-8"),
159
178
  value: text,
160
179
  onChange: (next) => field.onChange(next),
161
180
  placeholder: defaultPlaceholder(field.schema)
@@ -165,10 +184,11 @@ function DateControl({
165
184
  function BooleanControl({
166
185
  field,
167
186
  fieldId,
168
- readOnly
187
+ readOnly,
188
+ size
169
189
  }) {
170
190
  if (typeof field.value === "boolean" || field.value === void 0 || field.value === null) {
171
- return /* @__PURE__ */ jsx("div", { className: "flex h-9 items-center", children: /* @__PURE__ */ jsx(
191
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center", controlHeightClass[size]), children: /* @__PURE__ */ jsx(
172
192
  "input",
173
193
  {
174
194
  id: fieldId,
@@ -180,17 +200,18 @@ function BooleanControl({
180
200
  }
181
201
  ) });
182
202
  }
183
- return /* @__PURE__ */ jsx(StringControl, { field, fieldId, readOnly });
203
+ return /* @__PURE__ */ jsx(StringControl, { field, fieldId, readOnly, size });
184
204
  }
185
205
  function EnumControl({
186
206
  field,
187
207
  fieldId,
188
- readOnly
208
+ readOnly,
209
+ size
189
210
  }) {
190
211
  const value = toText(field.value);
191
212
  const options = withSyntheticValue(field.options ?? [], value);
192
213
  if (field.display === "radio") {
193
- return /* @__PURE__ */ jsx(RadioGroupControl, { field, fieldId, readOnly, options, value });
214
+ return /* @__PURE__ */ jsx(RadioGroupControl, { field, fieldId, readOnly, options, value, size });
194
215
  }
195
216
  return /* @__PURE__ */ jsx(
196
217
  Combobox,
@@ -199,6 +220,7 @@ function EnumControl({
199
220
  options,
200
221
  value,
201
222
  disabled: readOnly,
223
+ size,
202
224
  allowCustomValue: field.allowCustomValue ?? false,
203
225
  onChange: (v) => field.onChange(v),
204
226
  ...defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {}
@@ -210,7 +232,8 @@ function RadioGroupControl({
210
232
  fieldId,
211
233
  readOnly,
212
234
  options,
213
- value
235
+ value,
236
+ size
214
237
  }) {
215
238
  return /* @__PURE__ */ jsx(
216
239
  "div",
@@ -226,7 +249,8 @@ function RadioGroupControl({
226
249
  "label",
227
250
  {
228
251
  className: cn(
229
- "inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1 text-sm",
252
+ "inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1",
253
+ labelSizeClass[size],
230
254
  checked ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground",
231
255
  readOnly && "cursor-not-allowed opacity-60"
232
256
  ),
@@ -259,7 +283,7 @@ function ObjectControl({ field, ctx }) {
259
283
  ...field.objectRequired ? { required: field.objectRequired } : {},
260
284
  ...Array.isArray(field.schema.allOf) ? { allOf: field.schema.allOf } : {}
261
285
  };
262
- return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: renderObjectFields(subSchema, obj, (next) => field.onChange(next), {
286
+ return /* @__PURE__ */ jsx("div", { className: cn("grid", stackedRowGapClass[ctx.size]), children: renderObjectFields(subSchema, obj, (next) => field.onChange(next), {
263
287
  ...ctx,
264
288
  readOnly: ctx.readOnly || field.readOnly === true,
265
289
  depth: ctx.depth + 1
@@ -272,6 +296,21 @@ function StringMapControl({ field, ctx }) {
272
296
  const knownKeys = Object.keys(known);
273
297
  const extraKeys = Object.keys(map).filter((k) => !(k in known));
274
298
  const childCtx = { ...ctx, readOnly, depth: ctx.depth + 1 };
299
+ function valueSchemaForKey(key) {
300
+ for (const { pattern, schema } of field.valuePatternSchemas ?? []) {
301
+ let re;
302
+ try {
303
+ re = new RegExp(pattern);
304
+ } catch {
305
+ re = void 0;
306
+ }
307
+ if (re == null ? void 0 : re.test(key)) return schema;
308
+ }
309
+ return field.valueSchema ?? { type: "string" };
310
+ }
311
+ function entryIsStacked(key) {
312
+ return valueSchemaForKey(key)["x-layout"] === "stack";
313
+ }
275
314
  function setEntry(key, next) {
276
315
  field.onChange({ ...map, [key]: next });
277
316
  }
@@ -303,35 +342,62 @@ function StringMapControl({ field, ctx }) {
303
342
  );
304
343
  return (nodes == null ? void 0 : nodes.value) ?? null;
305
344
  }
306
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-input p-2", children: [
345
+ function valueControlForKey(key) {
346
+ return valueControlFor(key, valueSchemaForKey(key));
347
+ }
348
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col rounded-md border border-input p-2", fieldInnerGapClass[childCtx.size]), children: [
307
349
  knownKeys.map((key) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[10rem_1fr] items-center gap-2", children: [
308
- /* @__PURE__ */ jsx("label", { htmlFor: fieldInputId(key), className: "truncate text-xs text-muted-foreground", title: key, children: key }),
350
+ /* @__PURE__ */ jsx("label", { htmlFor: fieldInputId(key, childCtx.idPrefix), className: "truncate text-xs text-muted-foreground", title: key, children: key }),
309
351
  /* @__PURE__ */ jsx("div", { className: "min-w-0", children: valueControlFor(key, known[key] ?? { type: "string" }) })
310
352
  ] }, `known-${key}`)),
311
- extraKeys.map((key) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[10rem_1fr_auto] items-center gap-2", children: [
312
- /* @__PURE__ */ jsx(
353
+ extraKeys.map((key) => {
354
+ const keyControl = field.keyOptions ? /* @__PURE__ */ jsx(
355
+ Combobox,
356
+ {
357
+ options: keyPickerOptions(field.keyOptions, extraKeys, key),
358
+ value: key,
359
+ disabled: readOnly,
360
+ size: childCtx.size,
361
+ allowCustomValue: false,
362
+ onChange: (next) => renameEntry(key, next),
363
+ placeholder: "Select…"
364
+ }
365
+ ) : /* @__PURE__ */ jsx(
313
366
  "input",
314
367
  {
315
368
  type: "text",
316
369
  "aria-label": "Field name",
317
- className: cn(inputClassName, "h-8 font-mono text-xs"),
370
+ className: cn(inputClass(childCtx.size), "font-mono"),
318
371
  value: key,
319
372
  disabled: readOnly,
320
373
  onChange: (e) => renameEntry(key, e.target.value)
321
374
  }
322
- ),
323
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children: valueControlFor(key, field.valueSchema ?? { type: "string" }) }),
324
- !readOnly && /* @__PURE__ */ jsx(
375
+ );
376
+ const removeButton = !readOnly ? /* @__PURE__ */ jsx(
325
377
  "button",
326
378
  {
327
379
  type: "button",
328
380
  "aria-label": `Remove ${key}`,
329
- className: "inline-flex h-8 w-8 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
381
+ className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
330
382
  onClick: () => removeEntry(key),
331
383
  children: /* @__PURE__ */ jsx(Icon, { icon: UiTrash, className: "text-sm" })
332
384
  }
333
- )
334
- ] }, `extra-${key}`)),
385
+ ) : null;
386
+ if (entryIsStacked(key)) {
387
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded-md border border-input p-2", children: [
388
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
389
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: keyControl }),
390
+ removeButton
391
+ ] }),
392
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: valueControlForKey(key) })
393
+ ] }, `extra-${key}`);
394
+ }
395
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[10rem_1fr_auto] items-center gap-2", children: [
396
+ keyControl,
397
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: valueControlForKey(key) }),
398
+ removeButton
399
+ ] }, `extra-${key}`);
400
+ }),
335
401
  !readOnly && field.allowExtraKeys !== false && /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addEntry, className: "gap-1.5", children: [
336
402
  /* @__PURE__ */ jsx(Icon, { icon: UiAdd, className: "text-sm" }),
337
403
  "Add field"
@@ -342,6 +408,10 @@ function withSyntheticValue(options, value) {
342
408
  if (!value || options.some((o) => o.value === value)) return options;
343
409
  return [{ value, label: value }, ...options];
344
410
  }
411
+ function keyPickerOptions(options, usedKeys, currentKey) {
412
+ const taken = new Set(usedKeys.filter((k) => k !== currentKey));
413
+ return options.filter((o) => !taken.has(o.value));
414
+ }
345
415
  function toText(value) {
346
416
  if (value === void 0 || value === null) return "";
347
417
  if (typeof value === "object") return "";
@@ -359,7 +429,7 @@ export {
359
429
  FieldWrapper,
360
430
  defaultPlaceholder,
361
431
  fieldInputId,
362
- inputClassName,
432
+ inputClass,
363
433
  renderValueControl
364
434
  };
365
435
  //# sourceMappingURL=json-schema-form-fields.js.map
@@ -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 { Icon, LabelIcon } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/Timestamp\";\nimport { UiAdd, UiTrash } from \"../icons\";\nimport { Button } from \"./button\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport { ArrayControl } from \"./json-schema-form-array\";\nimport { renderFieldNodes, renderObjectFields, type RenderContext } from \"./json-schema-form-render\";\nimport type {\n FieldControl,\n FieldOption,\n JsonSchemaObject,\n JsonSchemaProperty,\n} from \"./json-schema-form-types\";\n\nexport const inputClassName =\n \"h-9 w-full min-w-0 rounded-md border border-input bg-background px-2 text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring\";\n\n// FieldWrapper lays out a label + value (+ helper/error) either inline (2-col)\n// or stacked, mirroring the convention used elsewhere in the library.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n inline,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n inline?: boolean;\n}) {\n if (inline) {\n return (\n <div className=\"grid grid-cols-[10rem_1fr] items-start gap-x-3 gap-y-0.5\">\n <div className=\"flex min-h-9 items-center\">{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 className=\"space-y-1.5\">\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 }: { field: FieldControl; fieldId: string }) {\n return (\n <label htmlFor={fieldId} className=\"flex items-center gap-2 text-sm font-medium\">\n <LabelIcon icon={field.labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span title={field.label !== field.key ? field.key : undefined}>{field.label}</span>\n {field.required && <span className=\"text-destructive\">*</span>}\n {field.badge && (\n <span className=\"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\nexport function fieldInputId(key: string): string {\n return `jsf-${key.replace(/[^a-zA-Z0-9_-]/g, \"_\")}`;\n}\n\n// renderValueControl produces just the value node for a field, dispatching on\n// its resolved kind. The RenderContext carries readOnly + the pre/post stacks so\n// container controls (array/object/map) can recurse with full context.\nexport function renderValueControl(field: FieldControl, ctx: RenderContext): ReactNode {\n const fieldId = fieldInputId(field.key);\n // A field the schema marks `readOnly` is never editable: it shows its current\n // value as plain text, not a disabled input. (The form-level ctx.readOnly,\n // below, instead disables the real controls so the structure stays visible.)\n // Containers (array/object/map) still render structurally so nested read-only\n // values surface; their own children resolve their own readOnly.\n if (field.readOnly && field.kind !== \"array\" && field.kind !== \"object\" && field.kind !== \"string-map\") {\n return <ReadOnlyValue field={field} fieldId={fieldId} />;\n }\n const readOnly = ctx.readOnly;\n switch (field.kind) {\n case \"enum\":\n return <EnumControl field={field} fieldId={fieldId} readOnly={readOnly} />;\n case \"boolean\":\n return <BooleanControl field={field} fieldId={fieldId} readOnly={readOnly} />;\n case \"number\":\n return <NumberControl field={field} fieldId={fieldId} readOnly={readOnly} />;\n case \"date\":\n return <DateControl field={field} fieldId={fieldId} readOnly={readOnly} />;\n case \"array\":\n return <ArrayControl field={field} fieldId={fieldId} ctx={ctx} />;\n case \"object\":\n return <ObjectControl field={field} ctx={ctx} />;\n case \"string-map\":\n return <StringMapControl field={field} ctx={ctx} />;\n default:\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} />;\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.\nfunction ReadOnlyValue({ field, fieldId }: { field: FieldControl; fieldId: string }) {\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=\"flex h-9 items-center text-sm text-foreground\"\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\nfunction StringControl({\n field,\n fieldId,\n readOnly,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n}) {\n return (\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={inputClassName}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n );\n}\n\nfunction NumberControl({\n field,\n fieldId,\n readOnly,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\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 <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={inputClassName}\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 );\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.\nfunction DateControl({\n field,\n fieldId,\n readOnly,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className=\"flex h-9 items-center text-sm text-foreground\"\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(inputClassName, \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n />\n );\n}\n\nfunction BooleanControl({\n field,\n fieldId,\n readOnly,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\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=\"flex h-9 items-center\">\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} />;\n}\n\nfunction EnumControl({\n field,\n fieldId,\n readOnly,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} />;\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\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}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\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 text-sm\",\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// ObjectControl renders a nested structured object as a sub-form: its own\n// properties, required markers, if/then, soft errors — all via the shared\n// recursive renderer, so pre/post extensions apply at this depth too.\nfunction ObjectControl({ field, ctx }: { field: FieldControl; ctx: RenderContext }) {\n const obj = isPlainObject(field.value) ? (field.value as Record<string, unknown>) : {};\n const subSchema: JsonSchemaObject = {\n type: \"object\",\n properties: field.objectProperties ?? {},\n ...(field.objectRequired ? { required: field.objectRequired } : {}),\n ...(Array.isArray(field.schema.allOf) ? { allOf: field.schema.allOf } : {}),\n };\n // No border/box: nested objects render as flat headed sections (see\n // ObjectSection in renderFieldRow) rather than progressively indented\n // sub-forms, so deep schemas stay readable as a single column.\n // A read-only object marks its whole subtree non-editable: recurse with\n // form-level readOnly on so child inputs are disabled (and any child the\n // schema marks readOnly still renders as a value span).\n return (\n <div className=\"space-y-2\">\n {renderObjectFields(subSchema, obj, (next) => field.onChange(next), {\n ...ctx,\n readOnly: ctx.readOnly || field.readOnly === true,\n depth: ctx.depth + 1,\n })}\n </div>\n );\n}\n\n// StringMapControl edits an object as key/value rows. Known properties (from the\n// schema) render first with derived value controls; unknown keys render as\n// editable key/value pairs. \"Add field\" appears when extra keys are allowed.\n// Values recurse through the shared pipeline, so a value that is itself an object\n// or array renders structurally and pre/post extensions apply to it.\nfunction StringMapControl({ field, ctx }: { field: FieldControl; ctx: RenderContext }) {\n // A read-only map marks its whole subtree non-editable: no rename/remove/add\n // and value inputs disabled.\n const readOnly = ctx.readOnly || field.readOnly === true;\n const map = isPlainObject(field.value) ? (field.value as Record<string, unknown>) : {};\n const known = field.knownProperties ?? {};\n const knownKeys = Object.keys(known);\n const extraKeys = Object.keys(map).filter((k) => !(k in known));\n const childCtx: RenderContext = { ...ctx, readOnly, depth: ctx.depth + 1 };\n\n function setEntry(key: string, next: unknown) {\n field.onChange({ ...map, [key]: next });\n }\n function renameEntry(oldKey: string, newKey: string) {\n if (newKey === oldKey) return;\n const next: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(map)) next[k === oldKey ? newKey : k] = v;\n field.onChange(next);\n }\n function removeEntry(key: string) {\n const next = { ...map };\n delete next[key];\n field.onChange(next);\n }\n function addEntry() {\n if (\"\" in map) return;\n field.onChange({ ...map, \"\": \"\" });\n }\n\n function valueControlFor(key: string, valueSchema: JsonSchemaProperty): ReactNode {\n const nodes = renderFieldNodes(\n {\n key,\n prop: valueSchema,\n required: false,\n value: map[key] ?? \"\",\n onChange: (next) => setEntry(key, next),\n },\n childCtx,\n );\n return nodes?.value ?? null;\n }\n\n return (\n <div className=\"space-y-2 rounded-md border border-input p-2\">\n {knownKeys.map((key) => (\n <div key={`known-${key}`} className=\"grid grid-cols-[10rem_1fr] items-center gap-2\">\n <label htmlFor={fieldInputId(key)} className=\"truncate text-xs text-muted-foreground\" title={key}>\n {key}\n </label>\n <div className=\"min-w-0\">{valueControlFor(key, known[key] ?? { type: \"string\" })}</div>\n </div>\n ))}\n {extraKeys.map((key) => (\n <div key={`extra-${key}`} className=\"grid grid-cols-[10rem_1fr_auto] items-center gap-2\">\n <input\n type=\"text\"\n aria-label=\"Field name\"\n className={cn(inputClassName, \"h-8 font-mono text-xs\")}\n value={key}\n disabled={readOnly}\n onChange={(e) => renameEntry(key, e.target.value)}\n />\n <div className=\"min-w-0\">\n {valueControlFor(key, field.valueSchema ?? { type: \"string\" })}\n </div>\n {!readOnly && (\n <button\n type=\"button\"\n aria-label={`Remove ${key}`}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground\"\n onClick={() => removeEntry(key)}\n >\n <Icon icon={UiTrash} className=\"text-sm\" />\n </button>\n )}\n </div>\n ))}\n {!readOnly && field.allowExtraKeys !== false && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addEntry} className=\"gap-1.5\">\n <Icon icon={UiAdd} className=\"text-sm\" />\n Add field\n </Button>\n )}\n </div>\n );\n}\n\nfunction withSyntheticValue(\n options: { value: string; label: string }[],\n value: string,\n): { value: string; label: string }[] {\n if (!value || options.some((o) => o.value === value)) return options;\n return [{ value, label: value }, ...options];\n}\n\nfunction toText(value: unknown): string {\n if (value === undefined || value === null) return \"\";\n if (typeof value === \"object\") return \"\";\n return String(value);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function defaultPlaceholder(schema: JsonSchemaProperty): string {\n if (schema.default != null && typeof schema.default !== \"object\") return String(schema.default);\n return \"\";\n}\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 { Icon, LabelIcon } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/Timestamp\";\nimport { UiAdd, UiTrash } from \"../icons\";\nimport { Button } from \"./button\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport { ArrayControl } from \"./json-schema-form-array\";\nimport { renderFieldNodes, renderObjectFields, type RenderContext } from \"./json-schema-form-render\";\nimport type {\n FieldControl,\n FieldOption,\n FormLayout,\n JsonSchemaObject,\n JsonSchemaProperty,\n} from \"./json-schema-form-types\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inputSizeClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// Non-size styling shared by every text-like input box; the size token supplies\n// height, horizontal padding, and text size (see inputSizeClass).\nconst INPUT_BASE =\n \"w-full min-w-0 rounded-md border border-input bg-background text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring\";\n\nexport function inputClass(size: FormSize): string {\n return cn(INPUT_BASE, inputSizeClass[size]);\n}\n\n// FieldWrapper lays out a label + value (+ helper/error) either inline (2-col)\n// or stacked, mirroring the convention used elsewhere in the library. In inline\n// mode the label and value columns are capped (see FormLayout); the grid\n// template is set inline because Tailwind can't interpolate runtime widths.\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 const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"400px\";\n return (\n <div\n className=\"grid items-start gap-x-3 gap-y-0.5\"\n style={{ gridTemplateColumns: `minmax(0, ${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\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 className={cn(\"flex flex-col\", fieldInnerGapClass[size])}>\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 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span title={field.label !== field.key ? field.key : undefined}>{field.label}</span>\n {field.required && <span className=\"text-destructive\">*</span>}\n {field.badge && (\n <span className=\"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\nexport function fieldInputId(key: string, prefix?: string): string {\n const safe = key.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return prefix ? `jsf-${prefix}-${safe}` : `jsf-${safe}`;\n}\n\n// renderValueControl produces just the value node for a field, dispatching on\n// its resolved kind. The RenderContext carries readOnly + the pre/post stacks so\n// container controls (array/object/map) can recurse with full context.\nexport function renderValueControl(field: FieldControl, ctx: RenderContext): ReactNode {\n const fieldId = fieldInputId(field.key, ctx.idPrefix);\n // A field the schema marks `readOnly` is never editable: it shows its current\n // value as plain text, not a disabled input. (The form-level ctx.readOnly,\n // below, instead disables the real controls so the structure stays visible.)\n // Containers (array/object/map) still render structurally so nested read-only\n // values surface; their own children resolve their own readOnly.\n if (field.readOnly && field.kind !== \"array\" && field.kind !== \"object\" && field.kind !== \"string-map\") {\n return <ReadOnlyValue field={field} fieldId={fieldId} size={ctx.size} />;\n }\n const readOnly = ctx.readOnly;\n const size = ctx.size;\n switch (field.kind) {\n case \"enum\":\n return <EnumControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n case \"boolean\":\n return <BooleanControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n case \"number\":\n return <NumberControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n case \"date\":\n return <DateControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n case \"array\":\n return <ArrayControl field={field} fieldId={fieldId} ctx={ctx} />;\n case \"object\":\n return <ObjectControl field={field} ctx={ctx} />;\n case \"string-map\":\n return <StringMapControl field={field} ctx={ctx} />;\n default:\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\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.\nfunction 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\nfunction 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 <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={inputClass(size)}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n );\n}\n\nfunction 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 <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={inputClass(size)}\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 );\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.\nfunction 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), \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n />\n );\n}\n\nfunction 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\nfunction 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 {...(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// ObjectControl renders a nested structured object as a sub-form: its own\n// properties, required markers, if/then, soft errors — all via the shared\n// recursive renderer, so pre/post extensions apply at this depth too.\nfunction ObjectControl({ field, ctx }: { field: FieldControl; ctx: RenderContext }) {\n const obj = isPlainObject(field.value) ? (field.value as Record<string, unknown>) : {};\n const subSchema: JsonSchemaObject = {\n type: \"object\",\n properties: field.objectProperties ?? {},\n ...(field.objectRequired ? { required: field.objectRequired } : {}),\n ...(Array.isArray(field.schema.allOf) ? { allOf: field.schema.allOf } : {}),\n };\n // No border/box: nested objects render as flat headed sections (see\n // ObjectSection in renderFieldRow) rather than progressively indented\n // sub-forms, so deep schemas stay readable as a single column.\n // A read-only object marks its whole subtree non-editable: recurse with\n // form-level readOnly on so child inputs are disabled (and any child the\n // schema marks readOnly still renders as a value span).\n return (\n <div className={cn(\"grid\", stackedRowGapClass[ctx.size])}>\n {renderObjectFields(subSchema, obj, (next) => field.onChange(next), {\n ...ctx,\n readOnly: ctx.readOnly || field.readOnly === true,\n depth: ctx.depth + 1,\n })}\n </div>\n );\n}\n\n// StringMapControl edits an object as key/value rows. Known properties (from the\n// schema) render first with derived value controls; unknown keys render as\n// editable key/value pairs. \"Add field\" appears when extra keys are allowed.\n// Values recurse through the shared pipeline, so a value that is itself an object\n// or array renders structurally and pre/post extensions apply to it.\nfunction StringMapControl({ field, ctx }: { field: FieldControl; ctx: RenderContext }) {\n // A read-only map marks its whole subtree non-editable: no rename/remove/add\n // and value inputs disabled.\n const readOnly = ctx.readOnly || field.readOnly === true;\n const map = isPlainObject(field.value) ? (field.value as Record<string, unknown>) : {};\n const known = field.knownProperties ?? {};\n const knownKeys = Object.keys(known);\n const extraKeys = Object.keys(map).filter((k) => !(k in known));\n const childCtx: RenderContext = { ...ctx, readOnly, depth: ctx.depth + 1 };\n\n // valueSchemaForKey picks an entry's value schema: the first patternProperties\n // entry whose regex matches the key, else the `additionalProperties` schema,\n // else a bare string. Lets the value form vary by key (e.g. House vs Apartment).\n function valueSchemaForKey(key: string): JsonSchemaProperty {\n for (const { pattern, schema } of field.valuePatternSchemas ?? []) {\n let re: RegExp | undefined;\n try {\n re = new RegExp(pattern);\n } catch {\n re = undefined; // a malformed pattern simply never matches\n }\n if (re?.test(key)) return schema;\n }\n return field.valueSchema ?? { type: \"string\" };\n }\n\n // When the entry value opts into a stacked layout (`x-layout: \"stack\"`), the\n // key+value render as one full-width stacked unit instead of a key column\n // beside the value. Resolved per key, since pattern schemas may differ.\n function entryIsStacked(key: string): boolean {\n return valueSchemaForKey(key)[\"x-layout\"] === \"stack\";\n }\n\n function setEntry(key: string, next: unknown) {\n field.onChange({ ...map, [key]: next });\n }\n function renameEntry(oldKey: string, newKey: string) {\n if (newKey === oldKey) return;\n const next: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(map)) next[k === oldKey ? newKey : k] = v;\n field.onChange(next);\n }\n function removeEntry(key: string) {\n const next = { ...map };\n delete next[key];\n field.onChange(next);\n }\n function addEntry() {\n if (\"\" in map) return;\n field.onChange({ ...map, \"\": \"\" });\n }\n\n function valueControlFor(key: string, valueSchema: JsonSchemaProperty): ReactNode {\n const nodes = renderFieldNodes(\n {\n key,\n prop: valueSchema,\n required: false,\n value: map[key] ?? \"\",\n onChange: (next) => setEntry(key, next),\n },\n childCtx,\n );\n return nodes?.value ?? null;\n }\n\n // valueControlForKey resolves the per-key value schema (patternProperties /\n // additionalProperties) before rendering, so the form varies by key.\n function valueControlForKey(key: string): ReactNode {\n return valueControlFor(key, valueSchemaForKey(key));\n }\n\n return (\n <div className={cn(\"flex flex-col rounded-md border border-input p-2\", fieldInnerGapClass[childCtx.size])}>\n {knownKeys.map((key) => (\n <div key={`known-${key}`} className=\"grid grid-cols-[10rem_1fr] items-center gap-2\">\n <label htmlFor={fieldInputId(key, childCtx.idPrefix)} className=\"truncate text-xs text-muted-foreground\" title={key}>\n {key}\n </label>\n <div className=\"min-w-0\">{valueControlFor(key, known[key] ?? { type: \"string\" })}</div>\n </div>\n ))}\n {extraKeys.map((key) => {\n const keyControl = field.keyOptions ? (\n <Combobox\n options={keyPickerOptions(field.keyOptions, extraKeys, key)}\n value={key}\n disabled={readOnly}\n size={childCtx.size}\n allowCustomValue={false}\n onChange={(next) => renameEntry(key, next)}\n placeholder=\"Select…\"\n />\n ) : (\n <input\n type=\"text\"\n aria-label=\"Field name\"\n className={cn(inputClass(childCtx.size), \"font-mono\")}\n value={key}\n disabled={readOnly}\n onChange={(e) => renameEntry(key, e.target.value)}\n />\n );\n const removeButton = !readOnly ? (\n <button\n type=\"button\"\n aria-label={`Remove ${key}`}\n className=\"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground\"\n onClick={() => removeEntry(key)}\n >\n <Icon icon={UiTrash} className=\"text-sm\" />\n </button>\n ) : null;\n // When the value is stacked (`x-layout: \"stack\"` on the entry schema),\n // the key joins the stack: it sits full-width above its value as one\n // unit, rather than cramped in a fixed key column beside it.\n if (entryIsStacked(key)) {\n return (\n <div key={`extra-${key}`} className=\"space-y-1.5 rounded-md border border-input p-2\">\n <div className=\"flex items-center gap-2\">\n <div className=\"min-w-0 flex-1\">{keyControl}</div>\n {removeButton}\n </div>\n <div className=\"min-w-0\">{valueControlForKey(key)}</div>\n </div>\n );\n }\n return (\n <div key={`extra-${key}`} className=\"grid grid-cols-[10rem_1fr_auto] items-center gap-2\">\n {keyControl}\n <div className=\"min-w-0\">{valueControlForKey(key)}</div>\n {removeButton}\n </div>\n );\n })}\n {!readOnly && field.allowExtraKeys !== false && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addEntry} className=\"gap-1.5\">\n <Icon icon={UiAdd} className=\"text-sm\" />\n Add field\n </Button>\n )}\n </div>\n );\n}\n\nfunction withSyntheticValue(\n options: { value: string; label: string }[],\n value: string,\n): { value: string; label: string }[] {\n if (!value || options.some((o) => o.value === value)) return options;\n return [{ value, label: value }, ...options];\n}\n\n// keyPickerOptions narrows the map-key enum to keys not already used by another\n// entry, while always keeping this row's current key so it stays selectable.\nfunction keyPickerOptions(\n options: FieldOption[],\n usedKeys: string[],\n currentKey: string,\n): FieldOption[] {\n const taken = new Set(usedKeys.filter((k) => k !== currentKey));\n return options.filter((o) => !taken.has(o.value));\n}\n\nfunction toText(value: unknown): string {\n if (value === undefined || value === null) return \"\";\n if (typeof value === \"object\") return \"\";\n return String(value);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function defaultPlaceholder(schema: JsonSchemaProperty): string {\n if (schema.default != null && typeof schema.default !== \"object\") return String(schema.default);\n return \"\";\n}\n"],"names":[],"mappings":"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"}
@@ -2,9 +2,11 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  require("react");
5
+ const utils = require("../lib/utils.cjs");
5
6
  const Icon = require("../data/Icon.cjs");
6
7
  const jsonSchemaFormFields = require("./json-schema-form-fields.cjs");
7
8
  const jsonSchemaFormResolve = require("./json-schema-form-resolve.cjs");
9
+ const jsonSchemaFormSize = require("./json-schema-form-size.cjs");
8
10
  function buildField(args, ctx) {
9
11
  const base = jsonSchemaFormResolve.resolveControl(args);
10
12
  let field = base;
@@ -14,9 +16,11 @@ function buildField(args, ctx) {
14
16
  }
15
17
  if (!field) return null;
16
18
  if (ctx.hideReadOnlyFields && field.readOnly) return null;
17
- const fieldId = jsonSchemaFormFields.fieldInputId(field.key);
18
- let label = /* @__PURE__ */ jsxRuntime.jsx(jsonSchemaFormFields.FieldLabel, { field, fieldId });
19
- let value = jsonSchemaFormFields.renderValueControl(field, ctx);
19
+ const fieldId = jsonSchemaFormFields.fieldInputId(field.key, ctx.idPrefix);
20
+ let label = /* @__PURE__ */ jsxRuntime.jsx(jsonSchemaFormFields.FieldLabel, { field, fieldId, size: ctx.size });
21
+ const overrideMode = field.layout === "inline" ? "inline" : field.layout === "stack" ? "stacked" : void 0;
22
+ const valueCtx = overrideMode ? { ...ctx, layout: { ...ctx.layout, mode: overrideMode } } : ctx;
23
+ let value = jsonSchemaFormFields.renderValueControl(field, valueCtx);
20
24
  for (const ext of ctx.post) {
21
25
  const next = ext(field, { label, value });
22
26
  label = next.label;
@@ -33,12 +37,13 @@ function renderFieldRow(args, ctx, opts) {
33
37
  const built = buildField(args, ctx);
34
38
  if (!built) return null;
35
39
  const { field } = built;
36
- if (field.kind === "object") {
40
+ if (field.kind === "object" || field.layout === "table") {
37
41
  return /* @__PURE__ */ jsxRuntime.jsx(
38
42
  ObjectSection,
39
43
  {
40
44
  label: (opts == null ? void 0 : opts.labelOverride) ?? field.label,
41
45
  required: field.required,
46
+ size: ctx.size,
42
47
  ...field.badge ? { badge: field.badge } : {},
43
48
  ...field.helper ? { helper: field.helper } : {},
44
49
  ...field.labelIcon != null ? { labelIcon: field.labelIcon } : {},
@@ -46,7 +51,7 @@ function renderFieldRow(args, ctx, opts) {
46
51
  }
47
52
  );
48
53
  }
49
- const label = (opts == null ? void 0 : opts.labelOverride) ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
54
+ const label = (opts == null ? void 0 : opts.labelOverride) ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils.cn("flex items-center gap-2 font-medium", jsonSchemaFormSize.labelSizeClass[ctx.size]), children: [
50
55
  /* @__PURE__ */ jsxRuntime.jsx(Icon.LabelIcon, { icon: field.labelIcon, className: "text-[15px] text-muted-foreground" }),
51
56
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: opts.labelOverride }),
52
57
  field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive", children: "*" }),
@@ -56,7 +61,8 @@ function renderFieldRow(args, ctx, opts) {
56
61
  return /* @__PURE__ */ jsxRuntime.jsx(
57
62
  jsonSchemaFormFields.FieldWrapper,
58
63
  {
59
- inline: ctx.inline,
64
+ layout: ctx.layout,
65
+ size: ctx.size,
60
66
  label,
61
67
  value: built.value,
62
68
  ...field.helper ? { helper: field.helper } : {},
@@ -67,13 +73,14 @@ function renderFieldRow(args, ctx, opts) {
67
73
  function ObjectSection({
68
74
  label,
69
75
  required,
76
+ size,
70
77
  badge,
71
78
  helper,
72
79
  labelIcon,
73
80
  children
74
81
  }) {
75
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
76
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-b border-border pb-1 text-sm font-semibold", children: [
82
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex flex-col", jsonSchemaFormSize.fieldInnerGapClass[size]), children: [
83
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex items-center gap-2 border-b border-border pb-1 font-semibold", jsonSchemaFormSize.labelSizeClass[size]), children: [
77
84
  /* @__PURE__ */ jsxRuntime.jsx(Icon.LabelIcon, { icon: labelIcon, className: "text-[15px] text-muted-foreground" }),
78
85
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
79
86
  required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive", children: "*" }),