@thebes/cadmea 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,10 @@
1
1
  import { createComponent, escape, ssr, ssrAttribute } from "solid-js/web";
2
2
  import { createMutation, createQuery, useQueryClient } from "@tanstack/solid-query";
3
- import { For, Show, Suspense, createEffect, createSignal, lazy } from "solid-js";
3
+ import { For, Index, Show, Suspense, createEffect, createSignal, lazy } from "solid-js";
4
+ import { createForm } from "@tanstack/solid-form";
5
+ import { validateDocument } from "@thebes/cadmus/cms";
4
6
  import { Link, useBlocker } from "@tanstack/solid-router";
7
+ import { createSolidTable, flexRender, getCoreRowModel } from "@tanstack/solid-table";
5
8
  //#region src/CollectionEdit.tsx
6
9
  var _tmpl$$4 = ["<p class=\"text-sm text-error\" role=\"alert\">", "</p>"], _tmpl$2$3 = "<span class=\"loading loading-spinner loading-sm\"></span>", _tmpl$3$1 = [
7
10
  "<button type=\"button\" class=\"btn flex-1\"",
@@ -20,45 +23,50 @@ var _tmpl$$4 = ["<p class=\"text-sm text-error\" role=\"alert\">", "</p>"], _tmp
20
23
  "",
21
24
  "<div class=\"bg-base-100 sticky bottom-0 flex gap-2 border-t py-3\">",
22
25
  "</div></form>"
23
- ], _tmpl$7$1 = "<span class=\"text-error\"> *</span>", _tmpl$8$1 = [
24
- "<div class=\"form-control\"><label class=\"label\"",
26
+ ], _tmpl$7$1 = [
27
+ "<fieldset class=\"border-base-300 rounded-box border p-4\"><legend class=\"px-2 text-sm font-semibold\">",
28
+ "</legend>",
29
+ "</fieldset>"
30
+ ], _tmpl$8$1 = ["<div class=\"grid grid-cols-1 gap-4 md:grid-cols-2\">", "</div>"], _tmpl$9$1 = "<span class=\"text-error\"> *</span>", _tmpl$0$1 = ["<p class=\"text-base-content/60 mb-1 text-xs\">", "</p>"], _tmpl$1$1 = ["<p class=\"text-error mt-1 text-sm\" role=\"alert\">", "</p>"], _tmpl$10$1 = [
31
+ "<div class=\"",
32
+ "\"><label class=\"label\"",
25
33
  ">",
26
34
  "",
27
35
  "</label>",
36
+ "",
37
+ "",
28
38
  "</div>"
29
- ], _tmpl$9$1 = [
30
- "<button type=\"submit\" class=\"btn btn-primary flex-1\"",
31
- ">",
32
- "</button>"
33
- ], _tmpl$0$1 = [
39
+ ], _tmpl$11$1 = [
34
40
  "<input",
35
41
  " class=\"input\" type=\"text\"",
36
42
  "",
37
43
  ">"
38
- ], _tmpl$1$1 = [
44
+ ], _tmpl$12$1 = [
39
45
  "<select",
40
46
  " class=\"select\"",
41
47
  "",
48
+ "",
42
49
  ">",
43
50
  "</select>"
44
- ], _tmpl$10$1 = [
51
+ ], _tmpl$13$1 = [
45
52
  "<option",
46
53
  ">",
47
54
  "</option>"
48
- ], _tmpl$11$1 = [
55
+ ], _tmpl$14$1 = [
49
56
  "<input",
50
57
  " class=\"input\" type=\"number\"",
51
58
  "",
52
59
  ">"
53
- ], _tmpl$12$1 = [
60
+ ], _tmpl$15$1 = [
54
61
  "<input",
55
62
  " class=\"input\" type=\"text\" readonly",
56
63
  ">"
57
- ], _tmpl$13$1 = [
64
+ ], _tmpl$16 = [
58
65
  "<input",
59
66
  " class=\"checkbox\" type=\"checkbox\"",
67
+ "",
60
68
  ">"
61
- ], _tmpl$14$1 = ["<p class=\"text-sm opacity-70 break-all\">", "</p>"], _tmpl$15 = ["<p class=\"text-sm text-error\">", "</p>"], _tmpl$16 = [
69
+ ], _tmpl$17 = ["<p class=\"text-sm opacity-70 break-all\">", "</p>"], _tmpl$18 = ["<p class=\"text-sm text-error\">", "</p>"], _tmpl$19 = [
62
70
  "<div class=\"flex flex-col gap-2\">",
63
71
  "<input",
64
72
  " class=\"file-input\" type=\"file\"",
@@ -66,38 +74,115 @@ var _tmpl$$4 = ["<p class=\"text-sm text-error\" role=\"alert\">", "</p>"], _tmp
66
74
  ">",
67
75
  "",
68
76
  "</div>"
69
- ], _tmpl$17 = [
70
- "<select",
71
- " class=\"select\"",
77
+ ], _tmpl$20 = ["<div class=\"mb-1 flex flex-wrap gap-1\">", "</div>"], _tmpl$21 = "<button type=\"button\" aria-label=\"Clear\" class=\"absolute top-2 right-2 cursor-pointer opacity-60\">×</button>", _tmpl$22 = [
78
+ "<div",
79
+ " role=\"listbox\" class=\"bg-base-100 border-base-300 rounded-box absolute z-10 mt-1 flex max-h-56 w-full flex-col overflow-auto border p-1 shadow\">",
80
+ "</div>"
81
+ ], _tmpl$23 = [
82
+ "<div class=\"relative\">",
83
+ "<input",
84
+ " type=\"text\" role=\"combobox\"",
85
+ " autocomplete=\"off\" class=\"input\"",
72
86
  "",
73
- "><option value>—</option>",
74
- "</select>"
75
- ], _tmpl$18 = [
87
+ ">",
88
+ "",
89
+ "</div>"
90
+ ], _tmpl$24 = [
91
+ "<span class=\"badge badge-primary gap-1\">",
92
+ "<button type=\"button\" aria-label=\"",
93
+ "\" class=\"cursor-pointer\">×</button></span>"
94
+ ], _tmpl$25 = [
95
+ "<button type=\"button\" role=\"option\"",
96
+ " class=\"",
97
+ "\">",
98
+ "</button>"
99
+ ], _tmpl$26 = ["<div role=\"menu\" class=\"bg-base-100 border-base-300 rounded-box absolute z-10 mt-1 flex flex-col border p-1 shadow\">", "</div>"], _tmpl$27 = [
100
+ "<div class=\"relative self-start\"><button type=\"button\" class=\"btn btn-outline btn-sm\" aria-haspopup=\"menu\"",
101
+ ">Add block</button>",
102
+ "</div>"
103
+ ], _tmpl$28 = [
104
+ "<div class=\"form-control md:col-span-2\"><div class=\"label font-medium\">",
105
+ "",
106
+ "</div>",
76
107
  "<div class=\"flex flex-col gap-3\">",
77
- "<button type=\"button\" class=\"btn btn-outline btn-sm self-start\">Add ",
78
- "</button></div>"
79
- ], _tmpl$19 = ["<div class=\"card bg-base-200 flex flex-col gap-2 p-3\">", "<button type=\"button\" class=\"btn btn-error btn-outline btn-sm self-start\">Remove</button></div>"];
80
- const RichTextEditor = lazy(() => import("../RichTextEditor-DcLqdFY7.js").then((mod) => ({ default: mod.RichTextEditor })));
108
+ "",
109
+ "</div></div>"
110
+ ], _tmpl$29 = ["<span class=\"text-base-content/60 truncate text-sm\">", "</span>"], _tmpl$30 = ["<div class=\"flex flex-col gap-2\">", "</div>"], _tmpl$31 = [
111
+ "<div class=\"card bg-base-200 flex flex-col gap-2 p-3\"><div class=\"flex items-center gap-2\"><button type=\"button\" class=\"btn btn-ghost btn-sm gap-2\"",
112
+ "><span aria-hidden=\"true\">",
113
+ "</span><span class=\"font-semibold\">",
114
+ "</span></button>",
115
+ "<div class=\"ml-auto flex gap-1\"><button type=\"button\" class=\"btn btn-ghost btn-xs\" aria-label=\"Move up\"",
116
+ ">↑</button><button type=\"button\" class=\"btn btn-ghost btn-xs\" aria-label=\"Move down\"",
117
+ ">↓</button><button type=\"button\" class=\"btn btn-ghost btn-xs\" aria-label=\"Duplicate\">⧉</button><button type=\"button\" class=\"btn btn-ghost btn-xs text-error\" aria-label=\"Remove\">Remove</button></div></div>",
118
+ "</div>"
119
+ ], _tmpl$32 = ["<button type=\"button\" class=\"btn btn-outline btn-sm self-start\">Add ", "</button>"], _tmpl$33 = ["<i", " aria-hidden=\"true\"></i>"], _tmpl$34 = [
120
+ "<button type=\"button\" role=\"menuitem\" class=\"flex items-center gap-2 rounded px-3 py-2 text-left\">",
121
+ "",
122
+ "</button>"
123
+ ];
124
+ const RichTextEditor = lazy(() => import("../RichTextEditor-CPQTvhQD.js").then((mod) => ({ default: mod.RichTextEditor })));
81
125
  function editableFields(config) {
82
126
  return Object.entries(config.fields).filter(([key]) => key !== "id");
83
127
  }
128
+ function humanize(key) {
129
+ const spaced = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
130
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
131
+ }
132
+ function labelFor(key, field) {
133
+ return field.admin?.label ?? humanize(key);
134
+ }
135
+ function groupFields(entries) {
136
+ const groups = [];
137
+ const byName = /* @__PURE__ */ new Map();
138
+ for (const entry of entries) {
139
+ const name = entry[1].admin?.group;
140
+ let group = byName.get(name);
141
+ if (!group) {
142
+ group = {
143
+ name,
144
+ fields: []
145
+ };
146
+ byName.set(name, group);
147
+ groups.push(group);
148
+ }
149
+ group.fields.push(entry);
150
+ }
151
+ return groups;
152
+ }
84
153
  function CollectionEdit(props) {
85
- const initialSnapshot = JSON.stringify(props.initialValues ?? {});
86
- const [values, setValues] = createSignal(props.initialValues ?? {});
87
- createEffect(() => {
88
- props.onDirtyChange?.(JSON.stringify(values()) !== initialSnapshot);
89
- });
90
- function setField(key, value) {
91
- setValues((prev) => ({
92
- ...prev,
93
- [key]: value
94
- }));
154
+ const operation = props.initialValues?.id != null ? "update" : "create";
155
+ async function validateForm(value) {
156
+ const violations = await validateDocument(props.config, value, { operation });
157
+ const fields = {};
158
+ for (const v of violations) if (v.severity === "error" && !(v.path in fields)) fields[v.path] = v.message;
159
+ return Object.keys(fields).length > 0 ? { fields } : void 0;
160
+ }
161
+ const form = createForm(() => ({
162
+ defaultValues: props.initialValues ?? {},
163
+ validators: { onSubmitAsync: ({ value }) => validateForm(value) },
164
+ onSubmit: async ({ value }) => {
165
+ await props.onSubmit(editablePayload(value));
166
+ }
167
+ }));
168
+ const isDefaultValue = form.useStore((s) => s.isDefaultValue);
169
+ createEffect(() => props.onDirtyChange?.(!isDefaultValue()));
170
+ const formValues = form.useStore((s) => s.values);
171
+ function editablePayload(value) {
172
+ return Object.fromEntries(Object.entries(value).filter(([key]) => props.config.fields[key]?.type !== "date"));
95
173
  }
96
174
  const ctx = {
97
- onUploadFile: props.onUploadFile,
98
- relationshipOptions: props.relationshipOptions,
99
- fieldWidgets: props.fieldWidgets
175
+ get onUploadFile() {
176
+ return props.onUploadFile;
177
+ },
178
+ get relationshipOptions() {
179
+ return props.relationshipOptions;
180
+ },
181
+ get fieldWidgets() {
182
+ return props.fieldWidgets;
183
+ }
100
184
  };
185
+ const fieldGroups = groupFields(editableFields(props.config));
101
186
  const versioned = () => props.config.versions?.drafts && props.draftActions;
102
187
  return ssr(_tmpl$6$1, escape(createComponent(Show, {
103
188
  get when() {
@@ -107,17 +192,32 @@ function CollectionEdit(props) {
107
192
  return ssr(_tmpl$$4, escape(props.error));
108
193
  }
109
194
  })), escape(createComponent(For, {
110
- get each() {
111
- return editableFields(props.config);
112
- },
113
- children: ([key, field]) => ssr(_tmpl$8$1, ssrAttribute("for", escape(key, true), false), escape(key), escape(createComponent(Show, {
195
+ each: fieldGroups,
196
+ children: (group) => createComponent(Show, {
114
197
  get when() {
115
- return field.required;
198
+ return group.name;
199
+ },
200
+ get fallback() {
201
+ return createComponent(FieldsGrid, {
202
+ form,
203
+ ctx,
204
+ get fields() {
205
+ return group.fields;
206
+ },
207
+ values: formValues
208
+ });
116
209
  },
117
210
  get children() {
118
- return ssr(_tmpl$7$1);
211
+ return ssr(_tmpl$7$1, escape(group.name), escape(createComponent(FieldsGrid, {
212
+ form,
213
+ ctx,
214
+ get fields() {
215
+ return group.fields;
216
+ },
217
+ values: formValues
218
+ })));
119
219
  }
120
- })), escape(renderInput(key, field, values()[key], setField, ctx)))
220
+ })
121
221
  })), escape(createComponent(Show, {
122
222
  get when() {
123
223
  return versioned();
@@ -128,7 +228,7 @@ function CollectionEdit(props) {
128
228
  return props.capabilities?.canUpdate !== false;
129
229
  },
130
230
  get children() {
131
- return ssr(_tmpl$9$1, ssrAttribute("disabled", props.saving, true), escape(createComponent(Show, {
231
+ return ssr(_tmpl$4$1, ssrAttribute("disabled", props.saving, true), escape(createComponent(Show, {
132
232
  get when() {
133
233
  return props.saving;
134
234
  },
@@ -188,54 +288,121 @@ function CollectionEdit(props) {
188
288
  }
189
289
  })));
190
290
  }
191
- function renderInput(key, field, value, setField, ctx) {
192
- const Widget = ctx.fieldWidgets?.[key] ?? ctx.fieldWidgets?.[key.slice(key.lastIndexOf(".") + 1)];
291
+ function FieldsGrid(props) {
292
+ return ssr(_tmpl$8$1, escape(createComponent(For, {
293
+ get each() {
294
+ return props.fields;
295
+ },
296
+ children: ([key, field]) => createComponent(Show, {
297
+ get when() {
298
+ return !field.admin?.condition || field.admin.condition(props.values());
299
+ },
300
+ get children() {
301
+ return renderField(props.form, props.ctx, key, field, labelFor(key, field));
302
+ }
303
+ })
304
+ })));
305
+ }
306
+ function renderField(form, ctx, name, field, label) {
307
+ if (field.type === "array") return renderArray(form, ctx, name, field, label);
308
+ const spanClass = field.admin?.width === "half" ? "md:col-span-1" : "md:col-span-2";
309
+ return createComponent(form.Field, {
310
+ name,
311
+ children: (fieldApi) => ssr(_tmpl$10$1, `form-control ${escape(spanClass, true)}`, ssrAttribute("for", escape(name, true), false), escape(label), escape(createComponent(Show, {
312
+ get when() {
313
+ return field.required;
314
+ },
315
+ get children() {
316
+ return ssr(_tmpl$9$1);
317
+ }
318
+ })), escape(createComponent(Show, {
319
+ get when() {
320
+ return field.admin?.description;
321
+ },
322
+ get children() {
323
+ return ssr(_tmpl$0$1, escape(field.admin?.description));
324
+ }
325
+ })), escape(renderControl(ctx, name, field, fieldApi)), escape(createComponent(Show, {
326
+ get when() {
327
+ return (fieldApi().state.meta.errors?.length ?? 0) > 0;
328
+ },
329
+ get children() {
330
+ return ssr(_tmpl$1$1, escape(fieldApi().state.meta.errors.filter(Boolean).join(", ")));
331
+ }
332
+ })))
333
+ });
334
+ }
335
+ function renderControl(ctx, name, field, fieldApi) {
336
+ const Widget = ctx.fieldWidgets?.[name] ?? ctx.fieldWidgets?.[name.slice(name.lastIndexOf(".") + 1)];
193
337
  if (Widget) return createComponent(Widget, {
194
- fieldKey: key,
195
- value,
196
- setValue: (v) => setField(key, v),
338
+ fieldKey: name,
339
+ get value() {
340
+ return fieldApi().state.value;
341
+ },
342
+ setValue: (v) => fieldApi().handleChange(v),
197
343
  get onUploadFile() {
198
344
  return ctx.onUploadFile;
199
345
  }
200
346
  });
347
+ const readOnly = field.admin?.readOnly;
348
+ const change = (v) => fieldApi().handleChange(v);
201
349
  switch (field.type) {
202
- case "text": return ssr(_tmpl$0$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
203
- case "select": return ssr(_tmpl$1$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true), escape(createComponent(For, {
350
+ case "text": return ssr(_tmpl$11$1, ssrAttribute("id", escape(name, true), false), ssrAttribute("placeholder", escape(field.admin?.placeholder, true), false) + ssrAttribute("readonly", escape(readOnly, true), false) + ssrAttribute("value", escape(fieldApi().state.value ?? "", true), false), ssrAttribute("required", field.required, true));
351
+ case "select": return ssr(_tmpl$12$1, ssrAttribute("id", escape(name, true), false), ssrAttribute("value", escape(fieldApi().state.value ?? "", true), false), ssrAttribute("required", field.required, true), ssrAttribute("disabled", readOnly, true), escape(createComponent(For, {
204
352
  get each() {
205
353
  return field.options;
206
354
  },
207
- children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option, true), false), escape(option))
355
+ children: (option) => ssr(_tmpl$13$1, ssrAttribute("value", escape(option, true), false), escape(option))
208
356
  })));
209
- case "number": return ssr(_tmpl$11$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
210
- case "date": return ssr(_tmpl$12$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(formatDateValue(value), true), false));
211
- case "checkbox": return ssr(_tmpl$13$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("checked", value ?? false, true));
212
- case "upload": return renderUploadInput(key, field, value, setField, ctx);
213
- case "relationship": return renderRelationshipInput(key, field, value, setField, ctx);
214
- case "array": return renderArrayInput(key, field, value, setField, ctx);
357
+ case "number": return ssr(_tmpl$14$1, ssrAttribute("id", escape(name, true), false), ssrAttribute("placeholder", escape(field.admin?.placeholder, true), false) + ssrAttribute("readonly", escape(readOnly, true), false) + ssrAttribute("value", escape(fieldApi().state.value ?? "", true), false), ssrAttribute("required", field.required, true));
358
+ case "date": return ssr(_tmpl$15$1, ssrAttribute("id", escape(name, true), false), ssrAttribute("value", escape(formatDateValue(fieldApi().state.value), true), false));
359
+ case "checkbox": return ssr(_tmpl$16, ssrAttribute("id", escape(name, true), false), ssrAttribute("disabled", readOnly, true), ssrAttribute("checked", fieldApi().state.value ?? false, true));
360
+ case "upload": return createComponent(UploadControl, {
361
+ name,
362
+ field,
363
+ fieldApi,
364
+ ctx
365
+ });
366
+ case "relationship": return createComponent(RelationshipField, {
367
+ name,
368
+ field,
369
+ fieldApi,
370
+ get options() {
371
+ return ctx.relationshipOptions?.[field.relationTo] ?? [];
372
+ }
373
+ });
215
374
  case "richText": return createComponent(Suspense, {
216
375
  get fallback() {
217
376
  return ssr(_tmpl$2$3);
218
377
  },
219
378
  get children() {
220
379
  return createComponent(RichTextEditor, {
221
- id: key,
222
- content: value,
223
- onChange: (doc) => setField(key, doc)
380
+ id: name,
381
+ get content() {
382
+ return fieldApi().state.value;
383
+ },
384
+ onChange: (doc) => change(doc),
385
+ get onUploadFile() {
386
+ return ctx.onUploadFile;
387
+ }
224
388
  });
225
389
  }
226
390
  });
227
391
  default: return null;
228
392
  }
229
393
  }
230
- function renderUploadInput(key, field, value, setField, ctx) {
394
+ function UploadControl(props) {
231
395
  const [uploading, setUploading] = createSignal(false);
232
396
  const [uploadError, setUploadError] = createSignal();
233
- return ssr(_tmpl$16, escape(createComponent(Show, {
234
- when: value,
397
+ const value = () => props.fieldApi().state.value;
398
+ return ssr(_tmpl$19, escape(createComponent(Show, {
399
+ get when() {
400
+ return value();
401
+ },
235
402
  get children() {
236
- return ssr(_tmpl$14$1, escape(value));
403
+ return ssr(_tmpl$17, escape(value()));
237
404
  }
238
- })), ssrAttribute("id", escape(key, true), false), ssrAttribute("required", field.required && !value, true), ssrAttribute("disabled", uploading(), true), escape(createComponent(Show, {
405
+ })), ssrAttribute("id", escape(props.name, true), false), ssrAttribute("required", props.field.required && !value(), true), ssrAttribute("disabled", uploading() || props.field.admin?.readOnly, true), escape(createComponent(Show, {
239
406
  get when() {
240
407
  return uploading();
241
408
  },
@@ -247,57 +414,175 @@ function renderUploadInput(key, field, value, setField, ctx) {
247
414
  return uploadError();
248
415
  },
249
416
  get children() {
250
- return ssr(_tmpl$15, escape(uploadError()));
417
+ return ssr(_tmpl$18, escape(uploadError()));
251
418
  }
252
419
  })));
253
420
  }
254
- function renderRelationshipInput(key, field, value, setField, ctx) {
255
- if (field.hasMany) return null;
256
- const options = ctx.relationshipOptions?.[field.relationTo] ?? [];
257
- return ssr(_tmpl$17, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", value != null ? escape(String(value), true) : "", false), ssrAttribute("required", field.required, true), escape(createComponent(For, {
258
- each: options,
259
- children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option.id, true), false), escape(option.label))
421
+ function RelationshipField(props) {
422
+ const [query, setQuery] = createSignal("");
423
+ const [open, setOpen] = createSignal(false);
424
+ const [active, setActive] = createSignal(0);
425
+ const listId = `${props.name}-listbox`;
426
+ const isMulti = () => props.field.hasMany === true;
427
+ const value = () => props.fieldApi().state.value;
428
+ const selectedIds = () => {
429
+ const v = value();
430
+ if (isMulti()) return Array.isArray(v) ? v : [];
431
+ return v != null ? [v] : [];
432
+ };
433
+ const selectedOptions = () => props.options.filter((o) => selectedIds().includes(o.id));
434
+ const filtered = () => {
435
+ const q = query().toLowerCase();
436
+ return props.options.filter((o) => o.label.toLowerCase().includes(q) && (!isMulti() || !selectedIds().includes(o.id)));
437
+ };
438
+ const singleLabel = () => selectedOptions()[0]?.label ?? "";
439
+ return ssr(_tmpl$23, escape(createComponent(Show, {
440
+ get when() {
441
+ return isMulti() && selectedOptions().length > 0;
442
+ },
443
+ get children() {
444
+ return ssr(_tmpl$20, escape(createComponent(For, {
445
+ get each() {
446
+ return selectedOptions();
447
+ },
448
+ children: (option) => ssr(_tmpl$24, escape(option.label), `Remove ${escape(option.label, true)}`)
449
+ })));
450
+ }
451
+ })), ssrAttribute("id", escape(props.name, true), false), ssrAttribute("aria-expanded", escape(open(), true), false) + ssrAttribute("aria-controls", escape(listId, true), false), ssrAttribute("required", props.field.required && selectedIds().length === 0, true), ssrAttribute("disabled", props.field.admin?.readOnly, true) + ssrAttribute("placeholder", escape(props.field.admin?.placeholder ?? "Search…", true), false) + ssrAttribute("value", open() || isMulti() ? escape(query(), true) : escape(singleLabel(), true), false), escape(createComponent(Show, {
452
+ get when() {
453
+ return !isMulti() && value() != null && !props.field.required;
454
+ },
455
+ get children() {
456
+ return ssr(_tmpl$21);
457
+ }
458
+ })), escape(createComponent(Show, {
459
+ get when() {
460
+ return open() && filtered().length > 0;
461
+ },
462
+ get children() {
463
+ return ssr(_tmpl$22, ssrAttribute("id", escape(listId, true), false), escape(createComponent(For, {
464
+ get each() {
465
+ return filtered();
466
+ },
467
+ children: (option, i) => ssr(_tmpl$25, ssrAttribute("aria-selected", escape(selectedIds().includes(option.id), true), false), `rounded px-3 py-2 text-left ${i() === active() ? "bg-base-200" : ""}`, escape(option.label))
468
+ })));
469
+ }
260
470
  })));
261
471
  }
262
- function renderArrayInput(key, field, value, setField, ctx) {
263
- const items = () => Array.isArray(value) ? value : [];
264
- function updateItem(index, itemKey, itemValue) {
265
- const next = items().slice();
266
- next[index] = {
267
- ...next[index],
268
- [itemKey]: itemValue
269
- };
270
- setField(key, next);
472
+ function renderArray(form, ctx, name, field, label) {
473
+ return createComponent(form.Field, {
474
+ name,
475
+ mode: "array",
476
+ children: (fieldApi) => createComponent(BlockEditor, {
477
+ form,
478
+ ctx,
479
+ name,
480
+ field,
481
+ label,
482
+ fieldApi
483
+ })
484
+ });
485
+ }
486
+ function variantLabel(disc, variant) {
487
+ return disc.variantsAdmin?.[variant]?.label ?? humanize(variant);
488
+ }
489
+ function BlockEditor(props) {
490
+ const [collapsed, setCollapsed] = createSignal(/* @__PURE__ */ new Set());
491
+ const [menuOpen, setMenuOpen] = createSignal(false);
492
+ const disc = props.field.discriminator;
493
+ const variants = disc ? Object.keys(disc.variants) : [];
494
+ const items = () => Array.isArray(props.fieldApi().state.value) ? props.fieldApi().state.value : [];
495
+ function blockTitle(item) {
496
+ if (disc) {
497
+ const v = item[disc.key];
498
+ if (typeof v === "string") return variantLabel(disc, v);
499
+ }
500
+ return props.label;
271
501
  }
272
- function fieldsForItem(item) {
273
- const base = Object.entries(field.fields);
274
- const discriminator = field.discriminator;
275
- if (!discriminator) return base;
276
- const variantValue = item[discriminator.key];
277
- const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
278
- return variantFields ? [...base, ...Object.entries(variantFields)] : base;
502
+ function blockSummary(item) {
503
+ for (const [key, f] of fieldsForItem(props.field, item)) {
504
+ if (key === disc?.key) continue;
505
+ if ((f.type === "text" || f.type === "select") && item[key]) return String(item[key]);
506
+ }
507
+ return "";
279
508
  }
280
- return ssr(_tmpl$18, escape(createComponent(For, {
509
+ return ssr(_tmpl$28, escape(props.label), escape(createComponent(Show, {
510
+ get when() {
511
+ return props.field.required;
512
+ },
513
+ get children() {
514
+ return ssr(_tmpl$9$1);
515
+ }
516
+ })), escape(createComponent(Show, {
517
+ get when() {
518
+ return props.field.admin?.description;
519
+ },
520
+ get children() {
521
+ return ssr(_tmpl$0$1, escape(props.field.admin?.description));
522
+ }
523
+ })), escape(createComponent(Index, {
281
524
  get each() {
282
525
  return items();
283
526
  },
284
- children: (item, index) => ssr(_tmpl$19, escape(createComponent(For, {
285
- get each() {
286
- return fieldsForItem(item);
287
- },
288
- children: ([itemKey, itemField]) => {
289
- const inputId = `${key}.${index()}.${itemKey}`;
290
- return ssr(_tmpl$8$1, ssrAttribute("for", escape(inputId, true), false), escape(itemKey), escape(createComponent(Show, {
291
- get when() {
292
- return itemField.required;
293
- },
294
- get children() {
295
- return ssr(_tmpl$7$1);
296
- }
297
- })), escape(renderInput(inputId, itemField, item[itemKey], (_, v) => updateItem(index(), itemKey, v), ctx)));
298
- }
299
- })))
300
- })), escape(key));
527
+ children: (item, index) => {
528
+ const isCollapsed = () => collapsed().has(index);
529
+ return ssr(_tmpl$31, ssrAttribute("aria-expanded", !isCollapsed(), false), isCollapsed() ? "▸" : "▾", escape(blockTitle(item())), escape(createComponent(Show, {
530
+ get when() {
531
+ return isCollapsed() && blockSummary(item());
532
+ },
533
+ get children() {
534
+ return ssr(_tmpl$29, escape(blockSummary(item())));
535
+ }
536
+ })), ssrAttribute("disabled", index === 0, true), ssrAttribute("disabled", index === items().length - 1, true), escape(createComponent(Show, {
537
+ get when() {
538
+ return !isCollapsed();
539
+ },
540
+ get children() {
541
+ return ssr(_tmpl$30, escape(createComponent(For, {
542
+ get each() {
543
+ return fieldsForItem(props.field, item());
544
+ },
545
+ children: ([itemKey, itemField]) => renderField(props.form, props.ctx, `${props.name}[${index}].${itemKey}`, itemField, labelFor(itemKey, itemField))
546
+ })));
547
+ }
548
+ })));
549
+ }
550
+ })), escape(createComponent(Show, {
551
+ get when() {
552
+ return disc && variants.length > 0;
553
+ },
554
+ get fallback() {
555
+ return ssr(_tmpl$32, escape(props.label));
556
+ },
557
+ get children() {
558
+ return ssr(_tmpl$27, ssrAttribute("aria-expanded", escape(menuOpen(), true), false), escape(createComponent(Show, {
559
+ get when() {
560
+ return menuOpen();
561
+ },
562
+ get children() {
563
+ return ssr(_tmpl$26, escape(createComponent(For, {
564
+ each: variants,
565
+ children: (variant) => ssr(_tmpl$34, escape(createComponent(Show, {
566
+ get when() {
567
+ return disc?.variantsAdmin?.[variant]?.icon;
568
+ },
569
+ get children() {
570
+ return ssr(_tmpl$33, ssrAttribute("class", escape(disc?.variantsAdmin?.[variant]?.icon, true), false));
571
+ }
572
+ })), escape(variantLabel(disc, variant)))
573
+ })));
574
+ }
575
+ })));
576
+ }
577
+ })));
578
+ }
579
+ function fieldsForItem(field, item) {
580
+ const base = Object.entries(field.fields);
581
+ const discriminator = field.discriminator;
582
+ if (!discriminator) return base;
583
+ const variantValue = item[discriminator.key];
584
+ const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
585
+ return variantFields ? [...base, ...Object.entries(variantFields)] : base;
301
586
  }
302
587
  function formatDateValue(value) {
303
588
  if (!value) return "—";
@@ -491,36 +776,39 @@ var _tmpl$$1 = ["<button type=\"button\" class=\"btn btn-outline btn-sm\">", "</
491
776
  ">",
492
777
  "</select><select aria-label=\"Sort direction\" class=\"select select-sm join-item\"",
493
778
  "><option value=\"asc\">Ascending</option><option value=\"desc\">Descending</option></select></div>"
494
- ], _tmpl$3 = "<th></th>", _tmpl$4 = [
495
- "<table class=\"table hidden md:table\"><thead><tr>",
496
- "",
497
- "</tr></thead><tbody>",
779
+ ], _tmpl$3 = [
780
+ "<table class=\"table hidden md:table\"><thead>",
781
+ "</thead><tbody>",
498
782
  "</tbody></table>"
499
- ], _tmpl$5 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$6 = [
783
+ ], _tmpl$4 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$5 = [
500
784
  "<div class=\"bg-base-100 sticky bottom-0 flex items-center justify-between gap-2 border-t py-2\"><button type=\"button\" class=\"btn btn-sm\"",
501
785
  ">Prev</button><span class=\"text-sm opacity-70\">Page ",
502
786
  "</span><button type=\"button\" class=\"btn btn-sm\"",
503
787
  ">Next</button></div>"
504
- ], _tmpl$7 = [
788
+ ], _tmpl$6 = [
505
789
  "<div class=\"flex flex-col gap-3\"><div class=\"flex flex-wrap items-center justify-between gap-2\">",
506
790
  "",
507
791
  "</div>",
508
792
  "",
509
793
  "</div>"
510
- ], _tmpl$8 = [
794
+ ], _tmpl$7 = [
511
795
  "<option",
512
796
  ">",
513
797
  "</option>"
514
- ], _tmpl$9 = ["<p class=\"text-sm opacity-70\">No ", " yet.</p>"], _tmpl$0 = ["<th>", "</th>"], _tmpl$1 = ["<td><input type=\"checkbox\" class=\"checkbox checkbox-sm\"", "></td>"], _tmpl$10 = [
798
+ ], _tmpl$8 = ["<p class=\"text-sm opacity-70\">No ", " yet.</p>"], _tmpl$9 = "<th></th>", _tmpl$0 = [
799
+ "<tr>",
800
+ "",
801
+ "</tr>"
802
+ ], _tmpl$1 = ["<th>", "</th>"], _tmpl$10 = ["<td><input type=\"checkbox\" class=\"checkbox checkbox-sm\"", "></td>"], _tmpl$11 = [
515
803
  "<tr",
516
804
  ">",
517
805
  "",
518
806
  "</tr>"
519
- ], _tmpl$11 = ["<td>", "</td>"], _tmpl$12 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$13 = [
807
+ ], _tmpl$12 = ["<td>", "</td>"], _tmpl$13 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$14 = [
520
808
  "<div class=\"card bg-base-200 cursor-pointer p-3\" role=\"button\" tabindex=\"0\"><div class=\"flex items-start gap-3\">",
521
809
  "<div class=\"flex flex-1 flex-col gap-1\">",
522
810
  "</div></div></div>"
523
- ], _tmpl$14 = [
811
+ ], _tmpl$15 = [
524
812
  "<div class=\"flex justify-between gap-2 text-sm\"><span class=\"opacity-60\">",
525
813
  "</span><span class=\"text-right\">",
526
814
  "</span></div>"
@@ -542,9 +830,23 @@ function rowId(row) {
542
830
  return typeof row.id === "number" ? row.id : void 0;
543
831
  }
544
832
  function CollectionList(props) {
545
- const columns = () => listableFields(props.config);
833
+ const columns = () => listableFields(props.config).map(([key]) => ({
834
+ id: key,
835
+ accessorFn: (row) => row[key],
836
+ header: key,
837
+ cell: (info) => formatCellValue(info.getValue())
838
+ }));
839
+ const table = createSolidTable({
840
+ get data() {
841
+ return props.rows;
842
+ },
843
+ get columns() {
844
+ return columns();
845
+ },
846
+ getCoreRowModel: getCoreRowModel()
847
+ });
546
848
  const [selectMode, setSelectMode] = createSignal(false);
547
- return ssr(_tmpl$7, escape(createComponent(Show, {
849
+ return ssr(_tmpl$6, escape(createComponent(Show, {
548
850
  get when() {
549
851
  return props.selectable;
550
852
  },
@@ -558,9 +860,9 @@ function CollectionList(props) {
558
860
  get children() {
559
861
  return ssr(_tmpl$2$1, ssrAttribute("value", escape(props.sortField ?? "", true), false), escape(createComponent(For, {
560
862
  get each() {
561
- return columns();
863
+ return table.getAllColumns();
562
864
  },
563
- children: ([key]) => ssr(_tmpl$8, ssrAttribute("value", escape(key, true), false), escape(key))
865
+ children: (column) => ssr(_tmpl$7, ssrAttribute("value", escape(column.id, true), false), escape(column.id))
564
866
  })), ssrAttribute("value", escape(props.sortDirection ?? "asc", true), false));
565
867
  }
566
868
  })), escape(createComponent(Show, {
@@ -568,54 +870,59 @@ function CollectionList(props) {
568
870
  return props.rows.length > 0;
569
871
  },
570
872
  get fallback() {
571
- return ssr(_tmpl$9, escape(props.config.slug));
873
+ return ssr(_tmpl$8, escape(props.config.slug));
572
874
  },
573
875
  get children() {
574
- return [ssr(_tmpl$4, escape(createComponent(Show, {
575
- get when() {
576
- return selectMode();
577
- },
578
- get children() {
579
- return ssr(_tmpl$3);
580
- }
581
- })), escape(createComponent(For, {
876
+ return [ssr(_tmpl$3, escape(createComponent(For, {
582
877
  get each() {
583
- return columns();
878
+ return table.getHeaderGroups();
584
879
  },
585
- children: ([key]) => ssr(_tmpl$0, escape(key))
880
+ children: (headerGroup) => ssr(_tmpl$0, escape(createComponent(Show, {
881
+ get when() {
882
+ return selectMode();
883
+ },
884
+ get children() {
885
+ return ssr(_tmpl$9);
886
+ }
887
+ })), escape(createComponent(For, {
888
+ get each() {
889
+ return headerGroup.headers;
890
+ },
891
+ children: (header) => ssr(_tmpl$1, escape(flexRender(header.column.columnDef.header, header.getContext())))
892
+ })))
586
893
  })), escape(createComponent(For, {
587
894
  get each() {
588
- return props.rows;
895
+ return table.getRowModel().rows;
589
896
  },
590
- children: (row) => ssr(_tmpl$10, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
897
+ children: (row) => ssr(_tmpl$11, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
591
898
  get when() {
592
899
  return selectMode();
593
900
  },
594
901
  get children() {
595
- return ssr(_tmpl$1, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
902
+ return ssr(_tmpl$10, ssrAttribute("checked", rowId(row.original) !== void 0 && (props.selectedIds?.has(rowId(row.original)) ?? false), true));
596
903
  }
597
904
  })), escape(createComponent(For, {
598
905
  get each() {
599
- return columns();
906
+ return row.getVisibleCells();
600
907
  },
601
- children: ([key]) => ssr(_tmpl$11, escape(formatCellValue(row[key])))
908
+ children: (cell) => ssr(_tmpl$12, escape(flexRender(cell.column.columnDef.cell, cell.getContext())))
602
909
  })))
603
- }))), ssr(_tmpl$5, escape(createComponent(For, {
910
+ }))), ssr(_tmpl$4, escape(createComponent(For, {
604
911
  get each() {
605
- return props.rows;
912
+ return table.getRowModel().rows;
606
913
  },
607
- children: (row) => ssr(_tmpl$13, escape(createComponent(Show, {
914
+ children: (row) => ssr(_tmpl$14, escape(createComponent(Show, {
608
915
  get when() {
609
916
  return selectMode();
610
917
  },
611
918
  get children() {
612
- return ssr(_tmpl$12, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
919
+ return ssr(_tmpl$13, ssrAttribute("checked", rowId(row.original) !== void 0 && (props.selectedIds?.has(rowId(row.original)) ?? false), true));
613
920
  }
614
921
  })), escape(createComponent(For, {
615
922
  get each() {
616
- return columns();
923
+ return row.getVisibleCells();
617
924
  },
618
- children: ([key]) => ssr(_tmpl$14, escape(key), escape(formatCellValue(row[key])))
925
+ children: (cell) => ssr(_tmpl$15, escape(cell.column.id), escape(flexRender(cell.column.columnDef.cell, cell.getContext())))
619
926
  })))
620
927
  })))];
621
928
  }
@@ -624,7 +931,7 @@ function CollectionList(props) {
624
931
  return props.page !== void 0 && props.pageSize !== void 0;
625
932
  },
626
933
  get children() {
627
- return ssr(_tmpl$6, ssrAttribute("disabled", (props.page ?? 1) <= 1, true), escape(props.page), ssrAttribute("disabled", props.totalCount !== void 0 ? (props.page ?? 1) * (props.pageSize ?? 0) >= props.totalCount : props.rows.length < (props.pageSize ?? 0), true));
934
+ return ssr(_tmpl$5, ssrAttribute("disabled", (props.page ?? 1) <= 1, true), escape(props.page), ssrAttribute("disabled", props.totalCount !== void 0 ? (props.page ?? 1) * (props.pageSize ?? 0) >= props.totalCount : props.rows.length < (props.pageSize ?? 0), true));
628
935
  }
629
936
  })));
630
937
  }