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