@thebes/cadmea 1.0.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.
@@ -0,0 +1,730 @@
1
+ import { createComponent, escape, ssr, ssrAttribute } from "solid-js/web";
2
+ import { createMutation, createQuery, useQueryClient } from "@tanstack/solid-query";
3
+ import { For, Show, Suspense, createEffect, createSignal, lazy } from "solid-js";
4
+ import { Link, useBlocker } from "@tanstack/solid-router";
5
+ //#region src/CollectionEdit.tsx
6
+ 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
+ "<button type=\"button\" class=\"btn flex-1\"",
8
+ ">",
9
+ "</button>"
10
+ ], _tmpl$4$1 = [
11
+ "<button type=\"button\" class=\"btn btn-primary flex-1\"",
12
+ ">",
13
+ "</button>"
14
+ ], _tmpl$5$1 = [
15
+ "<button type=\"button\" class=\"btn btn-outline flex-1\"",
16
+ ">",
17
+ "</button>"
18
+ ], _tmpl$6$1 = [
19
+ "<form class=\"flex flex-col gap-4\">",
20
+ "",
21
+ "<div class=\"bg-base-100 sticky bottom-0 flex gap-2 border-t py-3\">",
22
+ "</div></form>"
23
+ ], _tmpl$7$1 = "<span class=\"text-error\"> *</span>", _tmpl$8$1 = [
24
+ "<div class=\"form-control\"><label class=\"label\"",
25
+ ">",
26
+ "",
27
+ "</label>",
28
+ "</div>"
29
+ ], _tmpl$9$1 = [
30
+ "<button type=\"submit\" class=\"btn btn-primary flex-1\"",
31
+ ">",
32
+ "</button>"
33
+ ], _tmpl$0$1 = [
34
+ "<input",
35
+ " class=\"input\" type=\"text\"",
36
+ "",
37
+ ">"
38
+ ], _tmpl$1$1 = [
39
+ "<select",
40
+ " class=\"select\"",
41
+ "",
42
+ ">",
43
+ "</select>"
44
+ ], _tmpl$10$1 = [
45
+ "<option",
46
+ ">",
47
+ "</option>"
48
+ ], _tmpl$11$1 = [
49
+ "<input",
50
+ " class=\"input\" type=\"number\"",
51
+ "",
52
+ ">"
53
+ ], _tmpl$12$1 = [
54
+ "<input",
55
+ " class=\"input\" type=\"text\" readonly",
56
+ ">"
57
+ ], _tmpl$13$1 = [
58
+ "<input",
59
+ " class=\"checkbox\" type=\"checkbox\"",
60
+ ">"
61
+ ], _tmpl$14$1 = ["<p class=\"text-sm opacity-70 break-all\">", "</p>"], _tmpl$15 = ["<p class=\"text-sm text-error\">", "</p>"], _tmpl$16 = [
62
+ "<div class=\"flex flex-col gap-2\">",
63
+ "<input",
64
+ " class=\"file-input\" type=\"file\"",
65
+ "",
66
+ ">",
67
+ "",
68
+ "</div>"
69
+ ], _tmpl$17 = [
70
+ "<select",
71
+ " class=\"select\"",
72
+ "",
73
+ "><option value>—</option>",
74
+ "</select>"
75
+ ], _tmpl$18 = [
76
+ "<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 })));
81
+ function editableFields(config) {
82
+ return Object.entries(config.fields).filter(([key]) => key !== "id");
83
+ }
84
+ 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
+ }));
95
+ }
96
+ const ctx = {
97
+ onUploadFile: props.onUploadFile,
98
+ relationshipOptions: props.relationshipOptions
99
+ };
100
+ const versioned = () => props.config.versions?.drafts && props.draftActions;
101
+ return ssr(_tmpl$6$1, escape(createComponent(Show, {
102
+ get when() {
103
+ return props.error;
104
+ },
105
+ get children() {
106
+ return ssr(_tmpl$$4, escape(props.error));
107
+ }
108
+ })), escape(createComponent(For, {
109
+ get each() {
110
+ return editableFields(props.config);
111
+ },
112
+ children: ([key, field]) => ssr(_tmpl$8$1, ssrAttribute("for", escape(key, true), false), escape(key), escape(createComponent(Show, {
113
+ get when() {
114
+ return field.required;
115
+ },
116
+ get children() {
117
+ return ssr(_tmpl$7$1);
118
+ }
119
+ })), escape(renderInput(key, field, values()[key], setField, ctx)))
120
+ })), escape(createComponent(Show, {
121
+ get when() {
122
+ return versioned();
123
+ },
124
+ get fallback() {
125
+ return createComponent(Show, {
126
+ get when() {
127
+ return props.capabilities?.canUpdate !== false;
128
+ },
129
+ get children() {
130
+ return ssr(_tmpl$9$1, ssrAttribute("disabled", props.saving, true), escape(createComponent(Show, {
131
+ get when() {
132
+ return props.saving;
133
+ },
134
+ get fallback() {
135
+ return props.submitLabel ?? "Save";
136
+ },
137
+ get children() {
138
+ return ssr(_tmpl$2$3);
139
+ }
140
+ })));
141
+ }
142
+ });
143
+ },
144
+ get children() {
145
+ return [
146
+ ssr(_tmpl$3$1, ssrAttribute("disabled", props.draftActions?.saving, true), escape(createComponent(Show, {
147
+ get when() {
148
+ return props.draftActions?.saving;
149
+ },
150
+ get fallback() {
151
+ return props.draftActions?.saveDraftLabel ?? "Save draft";
152
+ },
153
+ get children() {
154
+ return ssr(_tmpl$2$3);
155
+ }
156
+ }))),
157
+ ssr(_tmpl$4$1, ssrAttribute("disabled", !props.draftActions?.canPublish || props.draftActions?.publishing, true), escape(createComponent(Show, {
158
+ get when() {
159
+ return props.draftActions?.publishing;
160
+ },
161
+ get fallback() {
162
+ return props.draftActions?.publishLabel ?? "Publish";
163
+ },
164
+ get children() {
165
+ return ssr(_tmpl$2$3);
166
+ }
167
+ }))),
168
+ createComponent(Show, {
169
+ get when() {
170
+ return props.draftActions?.onPreview;
171
+ },
172
+ get children() {
173
+ return ssr(_tmpl$5$1, ssrAttribute("disabled", !props.draftActions?.canPreview || props.draftActions?.previewing, true), escape(createComponent(Show, {
174
+ get when() {
175
+ return props.draftActions?.previewing;
176
+ },
177
+ get fallback() {
178
+ return props.draftActions?.previewLabel ?? "Preview";
179
+ },
180
+ get children() {
181
+ return ssr(_tmpl$2$3);
182
+ }
183
+ })));
184
+ }
185
+ })
186
+ ];
187
+ }
188
+ })));
189
+ }
190
+ function renderInput(key, field, value, setField, ctx) {
191
+ switch (field.type) {
192
+ case "text": return ssr(_tmpl$0$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
193
+ 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, {
194
+ get each() {
195
+ return field.options;
196
+ },
197
+ children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option, true), false), escape(option))
198
+ })));
199
+ case "number": return ssr(_tmpl$11$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
200
+ case "date": return ssr(_tmpl$12$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(formatDateValue(value), true), false));
201
+ case "checkbox": return ssr(_tmpl$13$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("checked", value ?? false, true));
202
+ case "upload": return renderUploadInput(key, field, value, setField, ctx);
203
+ case "relationship": return renderRelationshipInput(key, field, value, setField, ctx);
204
+ case "array": return renderArrayInput(key, field, value, setField, ctx);
205
+ case "richText": return createComponent(Suspense, {
206
+ get fallback() {
207
+ return ssr(_tmpl$2$3);
208
+ },
209
+ get children() {
210
+ return createComponent(RichTextEditor, {
211
+ id: key,
212
+ content: value,
213
+ onChange: (doc) => setField(key, doc)
214
+ });
215
+ }
216
+ });
217
+ default: return null;
218
+ }
219
+ }
220
+ function renderUploadInput(key, field, value, setField, ctx) {
221
+ const [uploading, setUploading] = createSignal(false);
222
+ const [uploadError, setUploadError] = createSignal();
223
+ return ssr(_tmpl$16, escape(createComponent(Show, {
224
+ when: value,
225
+ get children() {
226
+ return ssr(_tmpl$14$1, escape(value));
227
+ }
228
+ })), ssrAttribute("id", escape(key, true), false), ssrAttribute("required", field.required && !value, true), ssrAttribute("disabled", uploading(), true), escape(createComponent(Show, {
229
+ get when() {
230
+ return uploading();
231
+ },
232
+ get children() {
233
+ return ssr(_tmpl$2$3);
234
+ }
235
+ })), escape(createComponent(Show, {
236
+ get when() {
237
+ return uploadError();
238
+ },
239
+ get children() {
240
+ return ssr(_tmpl$15, escape(uploadError()));
241
+ }
242
+ })));
243
+ }
244
+ function renderRelationshipInput(key, field, value, setField, ctx) {
245
+ if (field.hasMany) return null;
246
+ const options = ctx.relationshipOptions?.[field.relationTo] ?? [];
247
+ 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, {
248
+ each: options,
249
+ children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option.id, true), false), escape(option.label))
250
+ })));
251
+ }
252
+ function renderArrayInput(key, field, value, setField, ctx) {
253
+ const items = () => Array.isArray(value) ? value : [];
254
+ function updateItem(index, itemKey, itemValue) {
255
+ const next = items().slice();
256
+ next[index] = {
257
+ ...next[index],
258
+ [itemKey]: itemValue
259
+ };
260
+ setField(key, next);
261
+ }
262
+ function fieldsForItem(item) {
263
+ const base = Object.entries(field.fields);
264
+ const discriminator = field.discriminator;
265
+ if (!discriminator) return base;
266
+ const variantValue = item[discriminator.key];
267
+ const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
268
+ return variantFields ? [...base, ...Object.entries(variantFields)] : base;
269
+ }
270
+ return ssr(_tmpl$18, escape(createComponent(For, {
271
+ get each() {
272
+ return items();
273
+ },
274
+ children: (item, index) => ssr(_tmpl$19, escape(createComponent(For, {
275
+ get each() {
276
+ return fieldsForItem(item);
277
+ },
278
+ children: ([itemKey, itemField]) => {
279
+ const inputId = `${key}.${index()}.${itemKey}`;
280
+ return ssr(_tmpl$8$1, ssrAttribute("for", escape(inputId, true), false), escape(itemKey), escape(createComponent(Show, {
281
+ get when() {
282
+ return itemField.required;
283
+ },
284
+ get children() {
285
+ return ssr(_tmpl$7$1);
286
+ }
287
+ })), escape(renderInput(inputId, itemField, item[itemKey], (_, v) => updateItem(index(), itemKey, v), ctx)));
288
+ }
289
+ })))
290
+ })), escape(key));
291
+ }
292
+ function formatDateValue(value) {
293
+ if (!value) return "—";
294
+ const date = value instanceof Date ? value : new Date(value);
295
+ return Number.isNaN(date.getTime()) ? "—" : date.toLocaleString();
296
+ }
297
+ //#endregion
298
+ //#region src/tanstack-start/create.tsx
299
+ var _tmpl$$3 = [
300
+ "<div class=\"flex flex-col gap-4\"><h1 class=\"text-xl font-semibold\">",
301
+ "</h1>",
302
+ "</div>"
303
+ ];
304
+ /**
305
+ * Builds a create-page component for a collection. See
306
+ * `createCollectionListPage`'s doc comment for the same rationale on
307
+ * keeping navigation in the route file rather than this package.
308
+ */
309
+ function createCollectionCreatePage(options) {
310
+ return function CollectionCreatePage() {
311
+ const queryClient = useQueryClient();
312
+ const [error, setError] = createSignal();
313
+ const create = createMutation(() => ({
314
+ mutationFn: options.createFn,
315
+ onSuccess: (created) => {
316
+ queryClient.invalidateQueries({ queryKey: options.invalidateQueryKey });
317
+ options.onCreated?.(created);
318
+ },
319
+ onError: (e) => setError(e.message)
320
+ }));
321
+ return ssr(_tmpl$$3, escape(options.label ?? `New ${options.collection.slug}`), escape(createComponent(CollectionEdit, {
322
+ get config() {
323
+ return options.collection;
324
+ },
325
+ get submitLabel() {
326
+ return options.submitLabel ?? "Create";
327
+ },
328
+ get error() {
329
+ return error();
330
+ },
331
+ onSubmit: (values) => create.mutate(values),
332
+ get onUploadFile() {
333
+ return options.onUploadFile;
334
+ }
335
+ })));
336
+ };
337
+ }
338
+ //#endregion
339
+ //#region src/tanstack-start/edit.tsx
340
+ var _tmpl$$2 = ["<button type=\"button\" class=\"btn btn-error btn-outline btn-sm self-start\">", "</button>"], _tmpl$2$2 = [
341
+ "<div class=\"flex flex-col gap-4\"><h1 class=\"text-xl font-semibold\">",
342
+ "</h1>",
343
+ "",
344
+ "</div>"
345
+ ];
346
+ /**
347
+ * Builds an edit-page component for a collection — fetch, update, and
348
+ * delete, all wired together, plus a router-level unsaved-changes guard
349
+ * (`useBlocker`) driven by `CollectionEdit`'s `onDirtyChange`. See
350
+ * `createCollectionListPage`'s doc comment for the rationale on keeping
351
+ * navigation in the route file.
352
+ */
353
+ function createCollectionEditPage(options) {
354
+ return function CollectionEditPage() {
355
+ const queryClient = useQueryClient();
356
+ const [error, setError] = createSignal();
357
+ const [dirty, setDirty] = createSignal(false);
358
+ const [latestDraftId, setLatestDraftId] = createSignal();
359
+ useBlocker({
360
+ shouldBlockFn: () => dirty(),
361
+ enableBeforeUnload: () => dirty()
362
+ });
363
+ const row = createQuery(() => ({
364
+ queryKey: options.queryKey(),
365
+ queryFn: options.queryFn
366
+ }));
367
+ const update = createMutation(() => ({
368
+ mutationFn: options.updateFn,
369
+ onSuccess: () => {
370
+ setError(void 0);
371
+ queryClient.invalidateQueries({ queryKey: options.invalidateQueryKey });
372
+ },
373
+ onError: (e) => setError(e.message)
374
+ }));
375
+ createMutation(() => ({
376
+ mutationFn: options.deleteFn,
377
+ onSuccess: () => {
378
+ queryClient.invalidateQueries({ queryKey: options.invalidateQueryKey });
379
+ options.onDeleted?.();
380
+ },
381
+ onError: (e) => setError(e.message)
382
+ }));
383
+ const saveDraft = createMutation(() => ({
384
+ mutationFn: (values) => options.draftActions?.saveDraftFn(values) ?? Promise.reject(/* @__PURE__ */ new Error("No draftActions configured")),
385
+ onSuccess: (draft) => {
386
+ setError(void 0);
387
+ setLatestDraftId(draft.id);
388
+ },
389
+ onError: (e) => setError(e.message)
390
+ }));
391
+ const publish = createMutation(() => ({
392
+ mutationFn: () => {
393
+ const versionId = latestDraftId();
394
+ if (versionId === void 0 || !options.draftActions) return Promise.reject(/* @__PURE__ */ new Error("No draft saved yet"));
395
+ return options.draftActions.publishFn(versionId);
396
+ },
397
+ onSuccess: () => {
398
+ setError(void 0);
399
+ queryClient.invalidateQueries({ queryKey: options.invalidateQueryKey });
400
+ },
401
+ onError: (e) => setError(e.message)
402
+ }));
403
+ const preview = createMutation(() => ({
404
+ mutationFn: () => {
405
+ const versionId = latestDraftId();
406
+ if (versionId === void 0 || !options.draftActions?.previewFn) return Promise.reject(/* @__PURE__ */ new Error("No draft saved yet"));
407
+ return options.draftActions.previewFn(versionId);
408
+ },
409
+ onSuccess: ({ url }) => {
410
+ setError(void 0);
411
+ window.open(url, "_blank", "noopener,noreferrer");
412
+ },
413
+ onError: (e) => setError(e.message)
414
+ }));
415
+ return ssr(_tmpl$2$2, escape(options.label ?? `Edit ${options.collection.slug}`), escape(createComponent(Show, {
416
+ get when() {
417
+ return row.data;
418
+ },
419
+ get children() {
420
+ return createComponent(CollectionEdit, {
421
+ get config() {
422
+ return options.collection;
423
+ },
424
+ get initialValues() {
425
+ return row.data ?? void 0;
426
+ },
427
+ get submitLabel() {
428
+ return options.submitLabel ?? "Save changes";
429
+ },
430
+ get error() {
431
+ return error();
432
+ },
433
+ get saving() {
434
+ return update.isPending;
435
+ },
436
+ onSubmit: (values) => update.mutate(values),
437
+ get onUploadFile() {
438
+ return options.onUploadFile;
439
+ },
440
+ onDirtyChange: setDirty,
441
+ get capabilities() {
442
+ return options.capabilities?.();
443
+ },
444
+ get draftActions() {
445
+ return options.draftActions && {
446
+ onSaveDraft: (values) => saveDraft.mutate(values),
447
+ onPublish: () => publish.mutate(),
448
+ onPreview: options.draftActions.previewFn ? () => preview.mutate() : void 0,
449
+ saving: saveDraft.isPending,
450
+ publishing: publish.isPending,
451
+ previewing: preview.isPending,
452
+ canPublish: latestDraftId() !== void 0,
453
+ canPreview: latestDraftId() !== void 0,
454
+ saveDraftLabel: options.draftActions.saveDraftLabel,
455
+ publishLabel: options.draftActions.publishLabel,
456
+ previewLabel: options.draftActions.previewLabel
457
+ };
458
+ }
459
+ });
460
+ }
461
+ })), escape(createComponent(Show, {
462
+ get when() {
463
+ return options.capabilities?.()?.canDelete !== false;
464
+ },
465
+ get children() {
466
+ return ssr(_tmpl$$2, escape(options.deleteLabel ?? `Delete ${options.collection.slug}`));
467
+ }
468
+ })));
469
+ };
470
+ }
471
+ //#endregion
472
+ //#region src/CollectionList.tsx
473
+ var _tmpl$$1 = ["<button type=\"button\" class=\"btn btn-outline btn-sm\">", "</button>"], _tmpl$2$1 = [
474
+ "<div class=\"join\"><select aria-label=\"Sort by\" class=\"select select-sm join-item\"",
475
+ ">",
476
+ "</select><select aria-label=\"Sort direction\" class=\"select select-sm join-item\"",
477
+ "><option value=\"asc\">Ascending</option><option value=\"desc\">Descending</option></select></div>"
478
+ ], _tmpl$3 = "<th></th>", _tmpl$4 = [
479
+ "<table class=\"table hidden md:table\"><thead><tr>",
480
+ "",
481
+ "</tr></thead><tbody>",
482
+ "</tbody></table>"
483
+ ], _tmpl$5 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$6 = [
484
+ "<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\"",
485
+ ">Prev</button><span class=\"text-sm opacity-70\">Page ",
486
+ "</span><button type=\"button\" class=\"btn btn-sm\"",
487
+ ">Next</button></div>"
488
+ ], _tmpl$7 = [
489
+ "<div class=\"flex flex-col gap-3\"><div class=\"flex flex-wrap items-center justify-between gap-2\">",
490
+ "",
491
+ "</div>",
492
+ "",
493
+ "</div>"
494
+ ], _tmpl$8 = [
495
+ "<option",
496
+ ">",
497
+ "</option>"
498
+ ], _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 = [
499
+ "<tr",
500
+ ">",
501
+ "",
502
+ "</tr>"
503
+ ], _tmpl$11 = ["<td>", "</td>"], _tmpl$12 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$13 = [
504
+ "<div class=\"card bg-base-200 cursor-pointer p-3\" role=\"button\" tabindex=\"0\"><div class=\"flex items-start gap-3\">",
505
+ "<div class=\"flex flex-1 flex-col gap-1\">",
506
+ "</div></div></div>"
507
+ ], _tmpl$14 = [
508
+ "<div class=\"flex justify-between gap-2 text-sm\"><span class=\"opacity-60\">",
509
+ "</span><span class=\"text-right\">",
510
+ "</span></div>"
511
+ ];
512
+ function listableFields(config) {
513
+ const excluded = new Set([
514
+ "richText",
515
+ "array",
516
+ "relationship"
517
+ ]);
518
+ return Object.entries(config.fields).filter(([key, field]) => key !== "id" && !excluded.has(field.type));
519
+ }
520
+ function formatCellValue(value) {
521
+ if (value === null || value === void 0) return "—";
522
+ if (value instanceof Date) return value.toLocaleDateString();
523
+ return String(value);
524
+ }
525
+ function rowId(row) {
526
+ return typeof row.id === "number" ? row.id : void 0;
527
+ }
528
+ function CollectionList(props) {
529
+ const columns = () => listableFields(props.config);
530
+ const [selectMode, setSelectMode] = createSignal(false);
531
+ return ssr(_tmpl$7, escape(createComponent(Show, {
532
+ get when() {
533
+ return props.selectable;
534
+ },
535
+ get children() {
536
+ return ssr(_tmpl$$1, selectMode() ? "Done" : "Select");
537
+ }
538
+ })), escape(createComponent(Show, {
539
+ get when() {
540
+ return props.onSortChange;
541
+ },
542
+ get children() {
543
+ return ssr(_tmpl$2$1, ssrAttribute("value", escape(props.sortField ?? "", true), false), escape(createComponent(For, {
544
+ get each() {
545
+ return columns();
546
+ },
547
+ children: ([key]) => ssr(_tmpl$8, ssrAttribute("value", escape(key, true), false), escape(key))
548
+ })), ssrAttribute("value", escape(props.sortDirection ?? "asc", true), false));
549
+ }
550
+ })), escape(createComponent(Show, {
551
+ get when() {
552
+ return props.rows.length > 0;
553
+ },
554
+ get fallback() {
555
+ return ssr(_tmpl$9, escape(props.config.slug));
556
+ },
557
+ get children() {
558
+ return [ssr(_tmpl$4, escape(createComponent(Show, {
559
+ get when() {
560
+ return selectMode();
561
+ },
562
+ get children() {
563
+ return ssr(_tmpl$3);
564
+ }
565
+ })), escape(createComponent(For, {
566
+ get each() {
567
+ return columns();
568
+ },
569
+ children: ([key]) => ssr(_tmpl$0, escape(key))
570
+ })), escape(createComponent(For, {
571
+ get each() {
572
+ return props.rows;
573
+ },
574
+ children: (row) => ssr(_tmpl$10, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
575
+ get when() {
576
+ return selectMode();
577
+ },
578
+ get children() {
579
+ return ssr(_tmpl$1, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
580
+ }
581
+ })), escape(createComponent(For, {
582
+ get each() {
583
+ return columns();
584
+ },
585
+ children: ([key]) => ssr(_tmpl$11, escape(formatCellValue(row[key])))
586
+ })))
587
+ }))), ssr(_tmpl$5, escape(createComponent(For, {
588
+ get each() {
589
+ return props.rows;
590
+ },
591
+ children: (row) => ssr(_tmpl$13, escape(createComponent(Show, {
592
+ get when() {
593
+ return selectMode();
594
+ },
595
+ get children() {
596
+ return ssr(_tmpl$12, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
597
+ }
598
+ })), escape(createComponent(For, {
599
+ get each() {
600
+ return columns();
601
+ },
602
+ children: ([key]) => ssr(_tmpl$14, escape(key), escape(formatCellValue(row[key])))
603
+ })))
604
+ })))];
605
+ }
606
+ })), escape(createComponent(Show, {
607
+ get when() {
608
+ return props.page !== void 0 && props.pageSize !== void 0;
609
+ },
610
+ get children() {
611
+ 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));
612
+ }
613
+ })));
614
+ }
615
+ //#endregion
616
+ //#region src/tanstack-start/list.tsx
617
+ var _tmpl$ = [
618
+ "<div class=\"flex flex-col gap-4\"><div class=\"flex items-center justify-between\"><h1 class=\"text-xl font-semibold\">",
619
+ "</h1>",
620
+ "</div>",
621
+ "</div>"
622
+ ], _tmpl$2 = "<div class=\"loading loading-spinner\"></div>";
623
+ /**
624
+ * Builds a list-view page component for a collection — paginated/sortable
625
+ * query, loading state, and the generic table/card list, wired together.
626
+ * The returned component is meant to be passed directly as a route's
627
+ * `component`:
628
+ *
629
+ * ```tsx
630
+ * export const Route = createFileRoute('/admin/pages/')({
631
+ * component: createCollectionListPage({
632
+ * collection: pagesCollection,
633
+ * label: 'Pages',
634
+ * queryKey: ['pages'],
635
+ * queryFn: (params) => getPages({ data: params }),
636
+ * newHref: '/admin/pages/new',
637
+ * newLabel: 'New page',
638
+ * onRowClick: (row) => navigate({ to: '/admin/pages/$pageId', params: { pageId: String(row.id) } }),
639
+ * }),
640
+ * })
641
+ * ```
642
+ *
643
+ * Navigation stays in the route file (via `onRowClick`/`newHref` as plain
644
+ * strings) rather than this package calling `useNavigate()` itself —
645
+ * TanStack Router's route-typing is generated per-app, so a generic
646
+ * package can't produce a correctly-typed `navigate()` call for routes
647
+ * it doesn't know about.
648
+ */
649
+ function createCollectionListPage(options) {
650
+ return function CollectionListPage() {
651
+ const pageSize = options.pageSize ?? 20;
652
+ const [page, setPage] = createSignal(1);
653
+ const [sortField, setSortField] = createSignal(void 0);
654
+ const [sortDirection, setSortDirection] = createSignal("asc");
655
+ const result = createQuery(() => ({
656
+ queryKey: [
657
+ ...options.queryKey,
658
+ page(),
659
+ sortField(),
660
+ sortDirection()
661
+ ],
662
+ queryFn: () => options.queryFn({
663
+ page: page(),
664
+ pageSize,
665
+ sortField: sortField(),
666
+ sortDirection: sortDirection()
667
+ })
668
+ }));
669
+ function handleSortChange(field, direction) {
670
+ setSortField(field);
671
+ setSortDirection(direction);
672
+ setPage(1);
673
+ }
674
+ return ssr(_tmpl$, escape(options.label ?? options.collection.slug), escape(createComponent(Show, {
675
+ get when() {
676
+ return options.newHref && options.capabilities?.()?.canCreate !== false;
677
+ },
678
+ get children() {
679
+ return createComponent(Link, {
680
+ get to() {
681
+ return options.newHref;
682
+ },
683
+ "class": "btn btn-primary btn-sm",
684
+ get children() {
685
+ return options.newLabel ?? `New ${options.collection.slug}`;
686
+ }
687
+ });
688
+ }
689
+ })), escape(createComponent(Show, {
690
+ get when() {
691
+ return !result.isLoading;
692
+ },
693
+ get fallback() {
694
+ return ssr(_tmpl$2);
695
+ },
696
+ get children() {
697
+ return createComponent(CollectionList, {
698
+ get config() {
699
+ return options.collection;
700
+ },
701
+ get rows() {
702
+ return result.data?.rows ?? [];
703
+ },
704
+ get onRowClick() {
705
+ return options.onRowClick;
706
+ },
707
+ get page() {
708
+ return page();
709
+ },
710
+ pageSize,
711
+ get totalCount() {
712
+ return result.data?.total;
713
+ },
714
+ onPageChange: setPage,
715
+ get sortField() {
716
+ return sortField();
717
+ },
718
+ get sortDirection() {
719
+ return sortDirection();
720
+ },
721
+ onSortChange: handleSortChange
722
+ });
723
+ }
724
+ })));
725
+ };
726
+ }
727
+ //#endregion
728
+ export { createCollectionCreatePage, createCollectionEditPage, createCollectionListPage };
729
+
730
+ //# sourceMappingURL=server.js.map