@thebes/cadmea 1.1.0 → 1.2.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>"];
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
+ ];
80
124
  const RichTextEditor = lazy(() => import("../RichTextEditor-DcLqdFY7.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,118 @@ function CollectionEdit(props) {
188
288
  }
189
289
  })));
190
290
  }
191
- function renderInput(key, field, value, setField, ctx) {
192
- const Widget = ctx.fieldWidgets?.[key];
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)
224
385
  });
225
386
  }
226
387
  });
227
388
  default: return null;
228
389
  }
229
390
  }
230
- function renderUploadInput(key, field, value, setField, ctx) {
391
+ function UploadControl(props) {
231
392
  const [uploading, setUploading] = createSignal(false);
232
393
  const [uploadError, setUploadError] = createSignal();
233
- return ssr(_tmpl$16, escape(createComponent(Show, {
234
- when: value,
394
+ const value = () => props.fieldApi().state.value;
395
+ return ssr(_tmpl$19, escape(createComponent(Show, {
396
+ get when() {
397
+ return value();
398
+ },
235
399
  get children() {
236
- return ssr(_tmpl$14$1, escape(value));
400
+ return ssr(_tmpl$17, escape(value()));
237
401
  }
238
- })), ssrAttribute("id", escape(key, true), false), ssrAttribute("required", field.required && !value, true), ssrAttribute("disabled", uploading(), true), escape(createComponent(Show, {
402
+ })), 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
403
  get when() {
240
404
  return uploading();
241
405
  },
@@ -247,57 +411,175 @@ function renderUploadInput(key, field, value, setField, ctx) {
247
411
  return uploadError();
248
412
  },
249
413
  get children() {
250
- return ssr(_tmpl$15, escape(uploadError()));
414
+ return ssr(_tmpl$18, escape(uploadError()));
251
415
  }
252
416
  })));
253
417
  }
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))
418
+ function RelationshipField(props) {
419
+ const [query, setQuery] = createSignal("");
420
+ const [open, setOpen] = createSignal(false);
421
+ const [active, setActive] = createSignal(0);
422
+ const listId = `${props.name}-listbox`;
423
+ const isMulti = () => props.field.hasMany === true;
424
+ const value = () => props.fieldApi().state.value;
425
+ const selectedIds = () => {
426
+ const v = value();
427
+ if (isMulti()) return Array.isArray(v) ? v : [];
428
+ return v != null ? [v] : [];
429
+ };
430
+ const selectedOptions = () => props.options.filter((o) => selectedIds().includes(o.id));
431
+ const filtered = () => {
432
+ const q = query().toLowerCase();
433
+ return props.options.filter((o) => o.label.toLowerCase().includes(q) && (!isMulti() || !selectedIds().includes(o.id)));
434
+ };
435
+ const singleLabel = () => selectedOptions()[0]?.label ?? "";
436
+ return ssr(_tmpl$23, escape(createComponent(Show, {
437
+ get when() {
438
+ return isMulti() && selectedOptions().length > 0;
439
+ },
440
+ get children() {
441
+ return ssr(_tmpl$20, escape(createComponent(For, {
442
+ get each() {
443
+ return selectedOptions();
444
+ },
445
+ children: (option) => ssr(_tmpl$24, escape(option.label), `Remove ${escape(option.label, true)}`)
446
+ })));
447
+ }
448
+ })), 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, {
449
+ get when() {
450
+ return !isMulti() && value() != null && !props.field.required;
451
+ },
452
+ get children() {
453
+ return ssr(_tmpl$21);
454
+ }
455
+ })), escape(createComponent(Show, {
456
+ get when() {
457
+ return open() && filtered().length > 0;
458
+ },
459
+ get children() {
460
+ return ssr(_tmpl$22, ssrAttribute("id", escape(listId, true), false), escape(createComponent(For, {
461
+ get each() {
462
+ return filtered();
463
+ },
464
+ 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))
465
+ })));
466
+ }
260
467
  })));
261
468
  }
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);
469
+ function renderArray(form, ctx, name, field, label) {
470
+ return createComponent(form.Field, {
471
+ name,
472
+ mode: "array",
473
+ children: (fieldApi) => createComponent(BlockEditor, {
474
+ form,
475
+ ctx,
476
+ name,
477
+ field,
478
+ label,
479
+ fieldApi
480
+ })
481
+ });
482
+ }
483
+ function variantLabel(disc, variant) {
484
+ return disc.variantsAdmin?.[variant]?.label ?? humanize(variant);
485
+ }
486
+ function BlockEditor(props) {
487
+ const [collapsed, setCollapsed] = createSignal(/* @__PURE__ */ new Set());
488
+ const [menuOpen, setMenuOpen] = createSignal(false);
489
+ const disc = props.field.discriminator;
490
+ const variants = disc ? Object.keys(disc.variants) : [];
491
+ const items = () => Array.isArray(props.fieldApi().state.value) ? props.fieldApi().state.value : [];
492
+ function blockTitle(item) {
493
+ if (disc) {
494
+ const v = item[disc.key];
495
+ if (typeof v === "string") return variantLabel(disc, v);
496
+ }
497
+ return props.label;
271
498
  }
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;
499
+ function blockSummary(item) {
500
+ for (const [key, f] of fieldsForItem(props.field, item)) {
501
+ if (key === disc?.key) continue;
502
+ if ((f.type === "text" || f.type === "select") && item[key]) return String(item[key]);
503
+ }
504
+ return "";
279
505
  }
280
- return ssr(_tmpl$18, escape(createComponent(For, {
506
+ return ssr(_tmpl$28, escape(props.label), escape(createComponent(Show, {
507
+ get when() {
508
+ return props.field.required;
509
+ },
510
+ get children() {
511
+ return ssr(_tmpl$9$1);
512
+ }
513
+ })), escape(createComponent(Show, {
514
+ get when() {
515
+ return props.field.admin?.description;
516
+ },
517
+ get children() {
518
+ return ssr(_tmpl$0$1, escape(props.field.admin?.description));
519
+ }
520
+ })), escape(createComponent(Index, {
281
521
  get each() {
282
522
  return items();
283
523
  },
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));
524
+ children: (item, index) => {
525
+ const isCollapsed = () => collapsed().has(index);
526
+ return ssr(_tmpl$31, ssrAttribute("aria-expanded", !isCollapsed(), false), isCollapsed() ? "▸" : "▾", escape(blockTitle(item())), escape(createComponent(Show, {
527
+ get when() {
528
+ return isCollapsed() && blockSummary(item());
529
+ },
530
+ get children() {
531
+ return ssr(_tmpl$29, escape(blockSummary(item())));
532
+ }
533
+ })), ssrAttribute("disabled", index === 0, true), ssrAttribute("disabled", index === items().length - 1, true), escape(createComponent(Show, {
534
+ get when() {
535
+ return !isCollapsed();
536
+ },
537
+ get children() {
538
+ return ssr(_tmpl$30, escape(createComponent(For, {
539
+ get each() {
540
+ return fieldsForItem(props.field, item());
541
+ },
542
+ children: ([itemKey, itemField]) => renderField(props.form, props.ctx, `${props.name}[${index}].${itemKey}`, itemField, labelFor(itemKey, itemField))
543
+ })));
544
+ }
545
+ })));
546
+ }
547
+ })), escape(createComponent(Show, {
548
+ get when() {
549
+ return disc && variants.length > 0;
550
+ },
551
+ get fallback() {
552
+ return ssr(_tmpl$32, escape(props.label));
553
+ },
554
+ get children() {
555
+ return ssr(_tmpl$27, ssrAttribute("aria-expanded", escape(menuOpen(), true), false), escape(createComponent(Show, {
556
+ get when() {
557
+ return menuOpen();
558
+ },
559
+ get children() {
560
+ return ssr(_tmpl$26, escape(createComponent(For, {
561
+ each: variants,
562
+ children: (variant) => ssr(_tmpl$34, escape(createComponent(Show, {
563
+ get when() {
564
+ return disc?.variantsAdmin?.[variant]?.icon;
565
+ },
566
+ get children() {
567
+ return ssr(_tmpl$33, ssrAttribute("class", escape(disc?.variantsAdmin?.[variant]?.icon, true), false));
568
+ }
569
+ })), escape(variantLabel(disc, variant)))
570
+ })));
571
+ }
572
+ })));
573
+ }
574
+ })));
575
+ }
576
+ function fieldsForItem(field, item) {
577
+ const base = Object.entries(field.fields);
578
+ const discriminator = field.discriminator;
579
+ if (!discriminator) return base;
580
+ const variantValue = item[discriminator.key];
581
+ const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
582
+ return variantFields ? [...base, ...Object.entries(variantFields)] : base;
301
583
  }
302
584
  function formatDateValue(value) {
303
585
  if (!value) return "—";
@@ -491,36 +773,39 @@ var _tmpl$$1 = ["<button type=\"button\" class=\"btn btn-outline btn-sm\">", "</
491
773
  ">",
492
774
  "</select><select aria-label=\"Sort direction\" class=\"select select-sm join-item\"",
493
775
  "><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>",
776
+ ], _tmpl$3 = [
777
+ "<table class=\"table hidden md:table\"><thead>",
778
+ "</thead><tbody>",
498
779
  "</tbody></table>"
499
- ], _tmpl$5 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$6 = [
780
+ ], _tmpl$4 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$5 = [
500
781
  "<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
782
  ">Prev</button><span class=\"text-sm opacity-70\">Page ",
502
783
  "</span><button type=\"button\" class=\"btn btn-sm\"",
503
784
  ">Next</button></div>"
504
- ], _tmpl$7 = [
785
+ ], _tmpl$6 = [
505
786
  "<div class=\"flex flex-col gap-3\"><div class=\"flex flex-wrap items-center justify-between gap-2\">",
506
787
  "",
507
788
  "</div>",
508
789
  "",
509
790
  "</div>"
510
- ], _tmpl$8 = [
791
+ ], _tmpl$7 = [
511
792
  "<option",
512
793
  ">",
513
794
  "</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 = [
795
+ ], _tmpl$8 = ["<p class=\"text-sm opacity-70\">No ", " yet.</p>"], _tmpl$9 = "<th></th>", _tmpl$0 = [
796
+ "<tr>",
797
+ "",
798
+ "</tr>"
799
+ ], _tmpl$1 = ["<th>", "</th>"], _tmpl$10 = ["<td><input type=\"checkbox\" class=\"checkbox checkbox-sm\"", "></td>"], _tmpl$11 = [
515
800
  "<tr",
516
801
  ">",
517
802
  "",
518
803
  "</tr>"
519
- ], _tmpl$11 = ["<td>", "</td>"], _tmpl$12 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$13 = [
804
+ ], _tmpl$12 = ["<td>", "</td>"], _tmpl$13 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$14 = [
520
805
  "<div class=\"card bg-base-200 cursor-pointer p-3\" role=\"button\" tabindex=\"0\"><div class=\"flex items-start gap-3\">",
521
806
  "<div class=\"flex flex-1 flex-col gap-1\">",
522
807
  "</div></div></div>"
523
- ], _tmpl$14 = [
808
+ ], _tmpl$15 = [
524
809
  "<div class=\"flex justify-between gap-2 text-sm\"><span class=\"opacity-60\">",
525
810
  "</span><span class=\"text-right\">",
526
811
  "</span></div>"
@@ -542,9 +827,23 @@ function rowId(row) {
542
827
  return typeof row.id === "number" ? row.id : void 0;
543
828
  }
544
829
  function CollectionList(props) {
545
- const columns = () => listableFields(props.config);
830
+ const columns = () => listableFields(props.config).map(([key]) => ({
831
+ id: key,
832
+ accessorFn: (row) => row[key],
833
+ header: key,
834
+ cell: (info) => formatCellValue(info.getValue())
835
+ }));
836
+ const table = createSolidTable({
837
+ get data() {
838
+ return props.rows;
839
+ },
840
+ get columns() {
841
+ return columns();
842
+ },
843
+ getCoreRowModel: getCoreRowModel()
844
+ });
546
845
  const [selectMode, setSelectMode] = createSignal(false);
547
- return ssr(_tmpl$7, escape(createComponent(Show, {
846
+ return ssr(_tmpl$6, escape(createComponent(Show, {
548
847
  get when() {
549
848
  return props.selectable;
550
849
  },
@@ -558,9 +857,9 @@ function CollectionList(props) {
558
857
  get children() {
559
858
  return ssr(_tmpl$2$1, ssrAttribute("value", escape(props.sortField ?? "", true), false), escape(createComponent(For, {
560
859
  get each() {
561
- return columns();
860
+ return table.getAllColumns();
562
861
  },
563
- children: ([key]) => ssr(_tmpl$8, ssrAttribute("value", escape(key, true), false), escape(key))
862
+ children: (column) => ssr(_tmpl$7, ssrAttribute("value", escape(column.id, true), false), escape(column.id))
564
863
  })), ssrAttribute("value", escape(props.sortDirection ?? "asc", true), false));
565
864
  }
566
865
  })), escape(createComponent(Show, {
@@ -568,54 +867,59 @@ function CollectionList(props) {
568
867
  return props.rows.length > 0;
569
868
  },
570
869
  get fallback() {
571
- return ssr(_tmpl$9, escape(props.config.slug));
870
+ return ssr(_tmpl$8, escape(props.config.slug));
572
871
  },
573
872
  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, {
873
+ return [ssr(_tmpl$3, escape(createComponent(For, {
582
874
  get each() {
583
- return columns();
875
+ return table.getHeaderGroups();
584
876
  },
585
- children: ([key]) => ssr(_tmpl$0, escape(key))
877
+ children: (headerGroup) => ssr(_tmpl$0, escape(createComponent(Show, {
878
+ get when() {
879
+ return selectMode();
880
+ },
881
+ get children() {
882
+ return ssr(_tmpl$9);
883
+ }
884
+ })), escape(createComponent(For, {
885
+ get each() {
886
+ return headerGroup.headers;
887
+ },
888
+ children: (header) => ssr(_tmpl$1, escape(flexRender(header.column.columnDef.header, header.getContext())))
889
+ })))
586
890
  })), escape(createComponent(For, {
587
891
  get each() {
588
- return props.rows;
892
+ return table.getRowModel().rows;
589
893
  },
590
- children: (row) => ssr(_tmpl$10, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
894
+ children: (row) => ssr(_tmpl$11, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
591
895
  get when() {
592
896
  return selectMode();
593
897
  },
594
898
  get children() {
595
- return ssr(_tmpl$1, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
899
+ return ssr(_tmpl$10, ssrAttribute("checked", rowId(row.original) !== void 0 && (props.selectedIds?.has(rowId(row.original)) ?? false), true));
596
900
  }
597
901
  })), escape(createComponent(For, {
598
902
  get each() {
599
- return columns();
903
+ return row.getVisibleCells();
600
904
  },
601
- children: ([key]) => ssr(_tmpl$11, escape(formatCellValue(row[key])))
905
+ children: (cell) => ssr(_tmpl$12, escape(flexRender(cell.column.columnDef.cell, cell.getContext())))
602
906
  })))
603
- }))), ssr(_tmpl$5, escape(createComponent(For, {
907
+ }))), ssr(_tmpl$4, escape(createComponent(For, {
604
908
  get each() {
605
- return props.rows;
909
+ return table.getRowModel().rows;
606
910
  },
607
- children: (row) => ssr(_tmpl$13, escape(createComponent(Show, {
911
+ children: (row) => ssr(_tmpl$14, escape(createComponent(Show, {
608
912
  get when() {
609
913
  return selectMode();
610
914
  },
611
915
  get children() {
612
- return ssr(_tmpl$12, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
916
+ return ssr(_tmpl$13, ssrAttribute("checked", rowId(row.original) !== void 0 && (props.selectedIds?.has(rowId(row.original)) ?? false), true));
613
917
  }
614
918
  })), escape(createComponent(For, {
615
919
  get each() {
616
- return columns();
920
+ return row.getVisibleCells();
617
921
  },
618
- children: ([key]) => ssr(_tmpl$14, escape(key), escape(formatCellValue(row[key])))
922
+ children: (cell) => ssr(_tmpl$15, escape(cell.column.id), escape(flexRender(cell.column.columnDef.cell, cell.getContext())))
619
923
  })))
620
924
  })))];
621
925
  }
@@ -624,7 +928,7 @@ function CollectionList(props) {
624
928
  return props.page !== void 0 && props.pageSize !== void 0;
625
929
  },
626
930
  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));
931
+ 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
932
  }
629
933
  })));
630
934
  }