@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.
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/dist/RichTextEditor-BPilh7Pw.js +36 -0
- package/dist/RichTextEditor-BPilh7Pw.js.map +1 -0
- package/dist/RichTextEditor-DcLqdFY7.js +15 -0
- package/dist/RichTextEditor-DcLqdFY7.js.map +1 -0
- package/dist/index/index.d.ts +147 -0
- package/dist/index/index.js +740 -0
- package/dist/index/index.js.map +1 -0
- package/dist/index/server.js +508 -0
- package/dist/index/server.js.map +1 -0
- package/dist/tanstack-start/index.d.ts +180 -0
- package/dist/tanstack-start/index.js +897 -0
- package/dist/tanstack-start/index.js.map +1 -0
- package/dist/tanstack-start/server.js +730 -0
- package/dist/tanstack-start/server.js.map +1 -0
- package/package.json +138 -0
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
import { className, createComponent, delegateEvents, effect, insert, memo, setAttribute, template, use } from "solid-js/web";
|
|
2
|
+
import { For, Show, Suspense, createEffect, createSignal, lazy, onCleanup } from "solid-js";
|
|
3
|
+
//#region src/CollectionEdit.tsx
|
|
4
|
+
var _tmpl$$2 = /*#__PURE__*/ template(`<p class="text-sm text-error"role=alert>`), _tmpl$2$2 = /*#__PURE__*/ template(`<span class="loading loading-spinner loading-sm">`), _tmpl$3$2 = /*#__PURE__*/ template(`<button type=button class="btn flex-1">`), _tmpl$4$2 = /*#__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`);
|
|
5
|
+
const RichTextEditor = lazy(() => import("../RichTextEditor-BPilh7Pw.js").then((mod) => ({ default: mod.RichTextEditor })));
|
|
6
|
+
function editableFields(config) {
|
|
7
|
+
return Object.entries(config.fields).filter(([key]) => key !== "id");
|
|
8
|
+
}
|
|
9
|
+
function CollectionEdit(props) {
|
|
10
|
+
const initialSnapshot = JSON.stringify(props.initialValues ?? {});
|
|
11
|
+
const [values, setValues] = createSignal(props.initialValues ?? {});
|
|
12
|
+
createEffect(() => {
|
|
13
|
+
props.onDirtyChange?.(JSON.stringify(values()) !== initialSnapshot);
|
|
14
|
+
});
|
|
15
|
+
function setField(key, value) {
|
|
16
|
+
setValues((prev) => ({
|
|
17
|
+
...prev,
|
|
18
|
+
[key]: value
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
function editablePayload() {
|
|
22
|
+
return Object.fromEntries(Object.entries(values()).filter(([key]) => props.config.fields[key]?.type !== "date"));
|
|
23
|
+
}
|
|
24
|
+
function handleSubmit(event) {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
props.onSubmit(editablePayload());
|
|
27
|
+
}
|
|
28
|
+
const ctx = {
|
|
29
|
+
onUploadFile: props.onUploadFile,
|
|
30
|
+
relationshipOptions: props.relationshipOptions
|
|
31
|
+
};
|
|
32
|
+
const versioned = () => props.config.versions?.drafts && props.draftActions;
|
|
33
|
+
return (() => {
|
|
34
|
+
var _el$ = _tmpl$6$1(), _el$3 = _el$.firstChild;
|
|
35
|
+
_el$.addEventListener("submit", handleSubmit);
|
|
36
|
+
insert(_el$, createComponent(Show, {
|
|
37
|
+
get when() {
|
|
38
|
+
return props.error;
|
|
39
|
+
},
|
|
40
|
+
get children() {
|
|
41
|
+
var _el$2 = _tmpl$$2();
|
|
42
|
+
insert(_el$2, () => props.error);
|
|
43
|
+
return _el$2;
|
|
44
|
+
}
|
|
45
|
+
}), _el$3);
|
|
46
|
+
insert(_el$, createComponent(For, {
|
|
47
|
+
get each() {
|
|
48
|
+
return editableFields(props.config);
|
|
49
|
+
},
|
|
50
|
+
children: ([key, field]) => (() => {
|
|
51
|
+
var _el$0 = _tmpl$8$1(), _el$1 = _el$0.firstChild;
|
|
52
|
+
setAttribute(_el$1, "for", key);
|
|
53
|
+
insert(_el$1, key, null);
|
|
54
|
+
insert(_el$1, createComponent(Show, {
|
|
55
|
+
get when() {
|
|
56
|
+
return field.required;
|
|
57
|
+
},
|
|
58
|
+
get children() {
|
|
59
|
+
return _tmpl$7$1();
|
|
60
|
+
}
|
|
61
|
+
}), null);
|
|
62
|
+
insert(_el$0, () => renderInput(key, field, values()[key], setField, ctx), null);
|
|
63
|
+
return _el$0;
|
|
64
|
+
})()
|
|
65
|
+
}), _el$3);
|
|
66
|
+
insert(_el$3, createComponent(Show, {
|
|
67
|
+
get when() {
|
|
68
|
+
return versioned();
|
|
69
|
+
},
|
|
70
|
+
get fallback() {
|
|
71
|
+
return createComponent(Show, {
|
|
72
|
+
get when() {
|
|
73
|
+
return props.capabilities?.canUpdate !== false;
|
|
74
|
+
},
|
|
75
|
+
get children() {
|
|
76
|
+
var _el$11 = _tmpl$9$1();
|
|
77
|
+
insert(_el$11, createComponent(Show, {
|
|
78
|
+
get when() {
|
|
79
|
+
return props.saving;
|
|
80
|
+
},
|
|
81
|
+
get fallback() {
|
|
82
|
+
return props.submitLabel ?? "Save";
|
|
83
|
+
},
|
|
84
|
+
get children() {
|
|
85
|
+
return _tmpl$2$2();
|
|
86
|
+
}
|
|
87
|
+
}));
|
|
88
|
+
effect(() => _el$11.disabled = props.saving);
|
|
89
|
+
return _el$11;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
get children() {
|
|
94
|
+
return [
|
|
95
|
+
(() => {
|
|
96
|
+
var _el$4 = _tmpl$3$2();
|
|
97
|
+
_el$4.$$click = () => void props.draftActions?.onSaveDraft(editablePayload());
|
|
98
|
+
insert(_el$4, createComponent(Show, {
|
|
99
|
+
get when() {
|
|
100
|
+
return props.draftActions?.saving;
|
|
101
|
+
},
|
|
102
|
+
get fallback() {
|
|
103
|
+
return props.draftActions?.saveDraftLabel ?? "Save draft";
|
|
104
|
+
},
|
|
105
|
+
get children() {
|
|
106
|
+
return _tmpl$2$2();
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
effect(() => _el$4.disabled = props.draftActions?.saving);
|
|
110
|
+
return _el$4;
|
|
111
|
+
})(),
|
|
112
|
+
(() => {
|
|
113
|
+
var _el$6 = _tmpl$4$2();
|
|
114
|
+
_el$6.$$click = () => void props.draftActions?.onPublish?.();
|
|
115
|
+
insert(_el$6, createComponent(Show, {
|
|
116
|
+
get when() {
|
|
117
|
+
return props.draftActions?.publishing;
|
|
118
|
+
},
|
|
119
|
+
get fallback() {
|
|
120
|
+
return props.draftActions?.publishLabel ?? "Publish";
|
|
121
|
+
},
|
|
122
|
+
get children() {
|
|
123
|
+
return _tmpl$2$2();
|
|
124
|
+
}
|
|
125
|
+
}));
|
|
126
|
+
effect(() => _el$6.disabled = !props.draftActions?.canPublish || props.draftActions?.publishing);
|
|
127
|
+
return _el$6;
|
|
128
|
+
})(),
|
|
129
|
+
createComponent(Show, {
|
|
130
|
+
get when() {
|
|
131
|
+
return props.draftActions?.onPreview;
|
|
132
|
+
},
|
|
133
|
+
get children() {
|
|
134
|
+
var _el$8 = _tmpl$5$1();
|
|
135
|
+
_el$8.$$click = () => void props.draftActions?.onPreview?.();
|
|
136
|
+
insert(_el$8, createComponent(Show, {
|
|
137
|
+
get when() {
|
|
138
|
+
return props.draftActions?.previewing;
|
|
139
|
+
},
|
|
140
|
+
get fallback() {
|
|
141
|
+
return props.draftActions?.previewLabel ?? "Preview";
|
|
142
|
+
},
|
|
143
|
+
get children() {
|
|
144
|
+
return _tmpl$2$2();
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
effect(() => _el$8.disabled = !props.draftActions?.canPreview || props.draftActions?.previewing);
|
|
148
|
+
return _el$8;
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
return _el$;
|
|
155
|
+
})();
|
|
156
|
+
}
|
|
157
|
+
function renderInput(key, field, value, setField, ctx) {
|
|
158
|
+
switch (field.type) {
|
|
159
|
+
case "text": return (() => {
|
|
160
|
+
var _el$13 = _tmpl$0$1();
|
|
161
|
+
_el$13.$$input = (e) => setField(key, e.currentTarget.value);
|
|
162
|
+
setAttribute(_el$13, "id", key);
|
|
163
|
+
_el$13.value = value ?? "";
|
|
164
|
+
effect(() => _el$13.required = field.required);
|
|
165
|
+
return _el$13;
|
|
166
|
+
})();
|
|
167
|
+
case "select": return (() => {
|
|
168
|
+
var _el$14 = _tmpl$1$1();
|
|
169
|
+
_el$14.addEventListener("change", (e) => setField(key, e.currentTarget.value));
|
|
170
|
+
setAttribute(_el$14, "id", key);
|
|
171
|
+
_el$14.value = value ?? "";
|
|
172
|
+
insert(_el$14, createComponent(For, {
|
|
173
|
+
get each() {
|
|
174
|
+
return field.options;
|
|
175
|
+
},
|
|
176
|
+
children: (option) => (() => {
|
|
177
|
+
var _el$15 = _tmpl$10$1();
|
|
178
|
+
_el$15.value = option;
|
|
179
|
+
insert(_el$15, option);
|
|
180
|
+
return _el$15;
|
|
181
|
+
})()
|
|
182
|
+
}));
|
|
183
|
+
effect(() => _el$14.required = field.required);
|
|
184
|
+
return _el$14;
|
|
185
|
+
})();
|
|
186
|
+
case "number": return (() => {
|
|
187
|
+
var _el$16 = _tmpl$11$1();
|
|
188
|
+
_el$16.$$input = (e) => setField(key, e.currentTarget.valueAsNumber);
|
|
189
|
+
setAttribute(_el$16, "id", key);
|
|
190
|
+
_el$16.value = value ?? "";
|
|
191
|
+
effect(() => _el$16.required = field.required);
|
|
192
|
+
return _el$16;
|
|
193
|
+
})();
|
|
194
|
+
case "date": return (() => {
|
|
195
|
+
var _el$17 = _tmpl$12$1();
|
|
196
|
+
setAttribute(_el$17, "id", key);
|
|
197
|
+
effect(() => _el$17.value = formatDateValue(value));
|
|
198
|
+
return _el$17;
|
|
199
|
+
})();
|
|
200
|
+
case "checkbox": return (() => {
|
|
201
|
+
var _el$18 = _tmpl$13$1();
|
|
202
|
+
_el$18.addEventListener("change", (e) => setField(key, e.currentTarget.checked));
|
|
203
|
+
setAttribute(_el$18, "id", key);
|
|
204
|
+
_el$18.checked = value ?? false;
|
|
205
|
+
return _el$18;
|
|
206
|
+
})();
|
|
207
|
+
case "upload": return renderUploadInput(key, field, value, setField, ctx);
|
|
208
|
+
case "relationship": return renderRelationshipInput(key, field, value, setField, ctx);
|
|
209
|
+
case "array": return renderArrayInput(key, field, value, setField, ctx);
|
|
210
|
+
case "richText": return createComponent(Suspense, {
|
|
211
|
+
get fallback() {
|
|
212
|
+
return _tmpl$2$2();
|
|
213
|
+
},
|
|
214
|
+
get children() {
|
|
215
|
+
return createComponent(RichTextEditor, {
|
|
216
|
+
id: key,
|
|
217
|
+
content: value,
|
|
218
|
+
onChange: (doc) => setField(key, doc)
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
default: return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function renderUploadInput(key, field, value, setField, ctx) {
|
|
226
|
+
const [uploading, setUploading] = createSignal(false);
|
|
227
|
+
const [uploadError, setUploadError] = createSignal();
|
|
228
|
+
async function handleFileChange(e) {
|
|
229
|
+
const file = e.currentTarget.files?.[0];
|
|
230
|
+
if (!file) return;
|
|
231
|
+
if (!ctx.onUploadFile) {
|
|
232
|
+
setUploadError("No upload handler configured for this form.");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
setUploading(true);
|
|
236
|
+
setUploadError(void 0);
|
|
237
|
+
try {
|
|
238
|
+
const { url } = await ctx.onUploadFile(file);
|
|
239
|
+
setField(key, url);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
setUploadError(err instanceof Error ? err.message : "Upload failed");
|
|
242
|
+
} finally {
|
|
243
|
+
setUploading(false);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return (() => {
|
|
247
|
+
var _el$20 = _tmpl$16(), _el$22 = _el$20.firstChild;
|
|
248
|
+
insert(_el$20, createComponent(Show, {
|
|
249
|
+
when: value,
|
|
250
|
+
get children() {
|
|
251
|
+
var _el$21 = _tmpl$14();
|
|
252
|
+
insert(_el$21, value);
|
|
253
|
+
return _el$21;
|
|
254
|
+
}
|
|
255
|
+
}), _el$22);
|
|
256
|
+
_el$22.addEventListener("change", handleFileChange);
|
|
257
|
+
setAttribute(_el$22, "id", key);
|
|
258
|
+
insert(_el$20, createComponent(Show, {
|
|
259
|
+
get when() {
|
|
260
|
+
return uploading();
|
|
261
|
+
},
|
|
262
|
+
get children() {
|
|
263
|
+
return _tmpl$2$2();
|
|
264
|
+
}
|
|
265
|
+
}), null);
|
|
266
|
+
insert(_el$20, createComponent(Show, {
|
|
267
|
+
get when() {
|
|
268
|
+
return uploadError();
|
|
269
|
+
},
|
|
270
|
+
get children() {
|
|
271
|
+
var _el$24 = _tmpl$15();
|
|
272
|
+
insert(_el$24, uploadError);
|
|
273
|
+
return _el$24;
|
|
274
|
+
}
|
|
275
|
+
}), null);
|
|
276
|
+
effect((_p$) => {
|
|
277
|
+
var _v$ = field.required && !value, _v$2 = uploading();
|
|
278
|
+
_v$ !== _p$.e && (_el$22.required = _p$.e = _v$);
|
|
279
|
+
_v$2 !== _p$.t && (_el$22.disabled = _p$.t = _v$2);
|
|
280
|
+
return _p$;
|
|
281
|
+
}, {
|
|
282
|
+
e: void 0,
|
|
283
|
+
t: void 0
|
|
284
|
+
});
|
|
285
|
+
return _el$20;
|
|
286
|
+
})();
|
|
287
|
+
}
|
|
288
|
+
function renderRelationshipInput(key, field, value, setField, ctx) {
|
|
289
|
+
if (field.hasMany) return null;
|
|
290
|
+
const options = ctx.relationshipOptions?.[field.relationTo] ?? [];
|
|
291
|
+
return (() => {
|
|
292
|
+
var _el$25 = _tmpl$17();
|
|
293
|
+
_el$25.firstChild;
|
|
294
|
+
_el$25.addEventListener("change", (e) => setField(key, e.currentTarget.value === "" ? null : Number(e.currentTarget.value)));
|
|
295
|
+
setAttribute(_el$25, "id", key);
|
|
296
|
+
insert(_el$25, createComponent(For, {
|
|
297
|
+
each: options,
|
|
298
|
+
children: (option) => (() => {
|
|
299
|
+
var _el$27 = _tmpl$10$1();
|
|
300
|
+
insert(_el$27, () => option.label);
|
|
301
|
+
effect(() => _el$27.value = option.id);
|
|
302
|
+
return _el$27;
|
|
303
|
+
})()
|
|
304
|
+
}), null);
|
|
305
|
+
effect(() => _el$25.required = field.required);
|
|
306
|
+
effect(() => _el$25.value = value != null ? String(value) : "");
|
|
307
|
+
return _el$25;
|
|
308
|
+
})();
|
|
309
|
+
}
|
|
310
|
+
function renderArrayInput(key, field, value, setField, ctx) {
|
|
311
|
+
const items = () => Array.isArray(value) ? value : [];
|
|
312
|
+
function updateItem(index, itemKey, itemValue) {
|
|
313
|
+
const next = items().slice();
|
|
314
|
+
next[index] = {
|
|
315
|
+
...next[index],
|
|
316
|
+
[itemKey]: itemValue
|
|
317
|
+
};
|
|
318
|
+
setField(key, next);
|
|
319
|
+
}
|
|
320
|
+
function addItem() {
|
|
321
|
+
setField(key, [...items(), {}]);
|
|
322
|
+
}
|
|
323
|
+
function removeItem(index) {
|
|
324
|
+
setField(key, items().filter((_, i) => i !== index));
|
|
325
|
+
}
|
|
326
|
+
function fieldsForItem(item) {
|
|
327
|
+
const base = Object.entries(field.fields);
|
|
328
|
+
const discriminator = field.discriminator;
|
|
329
|
+
if (!discriminator) return base;
|
|
330
|
+
const variantValue = item[discriminator.key];
|
|
331
|
+
const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
|
|
332
|
+
return variantFields ? [...base, ...Object.entries(variantFields)] : base;
|
|
333
|
+
}
|
|
334
|
+
return (() => {
|
|
335
|
+
var _el$28 = _tmpl$18(), _el$29 = _el$28.firstChild;
|
|
336
|
+
_el$29.firstChild;
|
|
337
|
+
insert(_el$28, createComponent(For, {
|
|
338
|
+
get each() {
|
|
339
|
+
return items();
|
|
340
|
+
},
|
|
341
|
+
children: (item, index) => (() => {
|
|
342
|
+
var _el$31 = _tmpl$19(), _el$32 = _el$31.firstChild;
|
|
343
|
+
insert(_el$31, createComponent(For, {
|
|
344
|
+
get each() {
|
|
345
|
+
return fieldsForItem(item);
|
|
346
|
+
},
|
|
347
|
+
children: ([itemKey, itemField]) => {
|
|
348
|
+
const inputId = `${key}.${index()}.${itemKey}`;
|
|
349
|
+
return (() => {
|
|
350
|
+
var _el$33 = _tmpl$8$1(), _el$34 = _el$33.firstChild;
|
|
351
|
+
setAttribute(_el$34, "for", inputId);
|
|
352
|
+
insert(_el$34, itemKey, null);
|
|
353
|
+
insert(_el$34, createComponent(Show, {
|
|
354
|
+
get when() {
|
|
355
|
+
return itemField.required;
|
|
356
|
+
},
|
|
357
|
+
get children() {
|
|
358
|
+
return _tmpl$7$1();
|
|
359
|
+
}
|
|
360
|
+
}), null);
|
|
361
|
+
insert(_el$33, () => renderInput(inputId, itemField, item[itemKey], (_, v) => updateItem(index(), itemKey, v), ctx), null);
|
|
362
|
+
return _el$33;
|
|
363
|
+
})();
|
|
364
|
+
}
|
|
365
|
+
}), _el$32);
|
|
366
|
+
_el$32.$$click = () => removeItem(index());
|
|
367
|
+
return _el$31;
|
|
368
|
+
})()
|
|
369
|
+
}), _el$29);
|
|
370
|
+
_el$29.$$click = addItem;
|
|
371
|
+
insert(_el$29, key, null);
|
|
372
|
+
return _el$28;
|
|
373
|
+
})();
|
|
374
|
+
}
|
|
375
|
+
function formatDateValue(value) {
|
|
376
|
+
if (!value) return "—";
|
|
377
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
378
|
+
return Number.isNaN(date.getTime()) ? "—" : date.toLocaleString();
|
|
379
|
+
}
|
|
380
|
+
delegateEvents(["click", "input"]);
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region src/CollectionList.tsx
|
|
383
|
+
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$1 = /*#__PURE__*/ template(`<th>`), _tmpl$4$1 = /*#__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>`);
|
|
384
|
+
function listableFields(config) {
|
|
385
|
+
const excluded = new Set([
|
|
386
|
+
"richText",
|
|
387
|
+
"array",
|
|
388
|
+
"relationship"
|
|
389
|
+
]);
|
|
390
|
+
return Object.entries(config.fields).filter(([key, field]) => key !== "id" && !excluded.has(field.type));
|
|
391
|
+
}
|
|
392
|
+
function formatCellValue(value) {
|
|
393
|
+
if (value === null || value === void 0) return "—";
|
|
394
|
+
if (value instanceof Date) return value.toLocaleDateString();
|
|
395
|
+
return String(value);
|
|
396
|
+
}
|
|
397
|
+
function rowId(row) {
|
|
398
|
+
return typeof row.id === "number" ? row.id : void 0;
|
|
399
|
+
}
|
|
400
|
+
function CollectionList(props) {
|
|
401
|
+
const columns = () => listableFields(props.config);
|
|
402
|
+
const [selectMode, setSelectMode] = createSignal(false);
|
|
403
|
+
function toggleSelected(id) {
|
|
404
|
+
const next = new Set(props.selectedIds ?? []);
|
|
405
|
+
if (next.has(id)) next.delete(id);
|
|
406
|
+
else next.add(id);
|
|
407
|
+
props.onSelectionChange?.(next);
|
|
408
|
+
}
|
|
409
|
+
function handleRowActivate(row) {
|
|
410
|
+
if (selectMode()) {
|
|
411
|
+
const id = rowId(row);
|
|
412
|
+
if (id !== void 0) toggleSelected(id);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
props.onRowClick?.(row);
|
|
416
|
+
}
|
|
417
|
+
return (() => {
|
|
418
|
+
var _el$ = _tmpl$7(), _el$2 = _el$.firstChild;
|
|
419
|
+
insert(_el$2, createComponent(Show, {
|
|
420
|
+
get when() {
|
|
421
|
+
return props.selectable;
|
|
422
|
+
},
|
|
423
|
+
get children() {
|
|
424
|
+
var _el$3 = _tmpl$$1();
|
|
425
|
+
_el$3.$$click = () => setSelectMode((v) => !v);
|
|
426
|
+
insert(_el$3, () => selectMode() ? "Done" : "Select");
|
|
427
|
+
return _el$3;
|
|
428
|
+
}
|
|
429
|
+
}), null);
|
|
430
|
+
insert(_el$2, createComponent(Show, {
|
|
431
|
+
get when() {
|
|
432
|
+
return props.onSortChange;
|
|
433
|
+
},
|
|
434
|
+
get children() {
|
|
435
|
+
var _el$4 = _tmpl$2$1(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling;
|
|
436
|
+
_el$5.addEventListener("change", (e) => props.onSortChange?.(e.currentTarget.value, props.sortDirection ?? "asc"));
|
|
437
|
+
insert(_el$5, createComponent(For, {
|
|
438
|
+
get each() {
|
|
439
|
+
return columns();
|
|
440
|
+
},
|
|
441
|
+
children: ([key]) => (() => {
|
|
442
|
+
var _el$16 = _tmpl$8();
|
|
443
|
+
_el$16.value = key;
|
|
444
|
+
insert(_el$16, key);
|
|
445
|
+
return _el$16;
|
|
446
|
+
})()
|
|
447
|
+
}));
|
|
448
|
+
_el$6.addEventListener("change", (e) => props.onSortChange?.(props.sortField ?? columns()[0]?.[0] ?? "", e.currentTarget.value));
|
|
449
|
+
effect(() => _el$5.value = props.sortField ?? "");
|
|
450
|
+
effect(() => _el$6.value = props.sortDirection ?? "asc");
|
|
451
|
+
return _el$4;
|
|
452
|
+
}
|
|
453
|
+
}), null);
|
|
454
|
+
insert(_el$, createComponent(Show, {
|
|
455
|
+
get when() {
|
|
456
|
+
return props.rows.length > 0;
|
|
457
|
+
},
|
|
458
|
+
get fallback() {
|
|
459
|
+
return (() => {
|
|
460
|
+
var _el$17 = _tmpl$9(), _el$20 = _el$17.firstChild.nextSibling;
|
|
461
|
+
_el$20.nextSibling;
|
|
462
|
+
insert(_el$17, () => props.config.slug, _el$20);
|
|
463
|
+
return _el$17;
|
|
464
|
+
})();
|
|
465
|
+
},
|
|
466
|
+
get children() {
|
|
467
|
+
return [(() => {
|
|
468
|
+
var _el$7 = _tmpl$4$1(), _el$8 = _el$7.firstChild, _el$9 = _el$8.firstChild, _el$1 = _el$8.nextSibling;
|
|
469
|
+
insert(_el$9, createComponent(Show, {
|
|
470
|
+
get when() {
|
|
471
|
+
return selectMode();
|
|
472
|
+
},
|
|
473
|
+
get children() {
|
|
474
|
+
return _tmpl$3$1();
|
|
475
|
+
}
|
|
476
|
+
}), null);
|
|
477
|
+
insert(_el$9, createComponent(For, {
|
|
478
|
+
get each() {
|
|
479
|
+
return columns();
|
|
480
|
+
},
|
|
481
|
+
children: ([key]) => (() => {
|
|
482
|
+
var _el$21 = _tmpl$3$1();
|
|
483
|
+
insert(_el$21, key);
|
|
484
|
+
return _el$21;
|
|
485
|
+
})()
|
|
486
|
+
}), null);
|
|
487
|
+
insert(_el$1, createComponent(For, {
|
|
488
|
+
get each() {
|
|
489
|
+
return props.rows;
|
|
490
|
+
},
|
|
491
|
+
children: (row) => (() => {
|
|
492
|
+
var _el$22 = _tmpl$1();
|
|
493
|
+
_el$22.$$click = () => handleRowActivate(row);
|
|
494
|
+
insert(_el$22, createComponent(Show, {
|
|
495
|
+
get when() {
|
|
496
|
+
return selectMode();
|
|
497
|
+
},
|
|
498
|
+
get children() {
|
|
499
|
+
var _el$23 = _tmpl$0(), _el$24 = _el$23.firstChild;
|
|
500
|
+
_el$24.addEventListener("change", () => {
|
|
501
|
+
const id = rowId(row);
|
|
502
|
+
if (id !== void 0) toggleSelected(id);
|
|
503
|
+
});
|
|
504
|
+
_el$24.$$click = (e) => e.stopPropagation();
|
|
505
|
+
effect(() => _el$24.checked = rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false));
|
|
506
|
+
return _el$23;
|
|
507
|
+
}
|
|
508
|
+
}), null);
|
|
509
|
+
insert(_el$22, createComponent(For, {
|
|
510
|
+
get each() {
|
|
511
|
+
return columns();
|
|
512
|
+
},
|
|
513
|
+
children: ([key]) => (() => {
|
|
514
|
+
var _el$25 = _tmpl$10();
|
|
515
|
+
insert(_el$25, () => formatCellValue(row[key]));
|
|
516
|
+
return _el$25;
|
|
517
|
+
})()
|
|
518
|
+
}), null);
|
|
519
|
+
effect(() => className(_el$22, props.onRowClick || selectMode() ? "cursor-pointer hover" : void 0));
|
|
520
|
+
return _el$22;
|
|
521
|
+
})()
|
|
522
|
+
}));
|
|
523
|
+
return _el$7;
|
|
524
|
+
})(), (() => {
|
|
525
|
+
var _el$10 = _tmpl$5();
|
|
526
|
+
insert(_el$10, createComponent(For, {
|
|
527
|
+
get each() {
|
|
528
|
+
return props.rows;
|
|
529
|
+
},
|
|
530
|
+
children: (row) => (() => {
|
|
531
|
+
var _el$26 = _tmpl$12(), _el$27 = _el$26.firstChild, _el$29 = _el$27.firstChild;
|
|
532
|
+
_el$26.$$keydown = (e) => {
|
|
533
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
534
|
+
e.preventDefault();
|
|
535
|
+
handleRowActivate(row);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
_el$26.$$click = () => handleRowActivate(row);
|
|
539
|
+
insert(_el$27, createComponent(Show, {
|
|
540
|
+
get when() {
|
|
541
|
+
return selectMode();
|
|
542
|
+
},
|
|
543
|
+
get children() {
|
|
544
|
+
var _el$28 = _tmpl$11();
|
|
545
|
+
_el$28.addEventListener("change", () => {
|
|
546
|
+
const id = rowId(row);
|
|
547
|
+
if (id !== void 0) toggleSelected(id);
|
|
548
|
+
});
|
|
549
|
+
_el$28.$$click = (e) => e.stopPropagation();
|
|
550
|
+
effect(() => _el$28.checked = rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false));
|
|
551
|
+
return _el$28;
|
|
552
|
+
}
|
|
553
|
+
}), _el$29);
|
|
554
|
+
insert(_el$29, createComponent(For, {
|
|
555
|
+
get each() {
|
|
556
|
+
return columns();
|
|
557
|
+
},
|
|
558
|
+
children: ([key]) => (() => {
|
|
559
|
+
var _el$30 = _tmpl$13(), _el$31 = _el$30.firstChild, _el$32 = _el$31.nextSibling;
|
|
560
|
+
insert(_el$31, key);
|
|
561
|
+
insert(_el$32, () => formatCellValue(row[key]));
|
|
562
|
+
return _el$30;
|
|
563
|
+
})()
|
|
564
|
+
}));
|
|
565
|
+
return _el$26;
|
|
566
|
+
})()
|
|
567
|
+
}));
|
|
568
|
+
return _el$10;
|
|
569
|
+
})()];
|
|
570
|
+
}
|
|
571
|
+
}), null);
|
|
572
|
+
insert(_el$, createComponent(Show, {
|
|
573
|
+
get when() {
|
|
574
|
+
return memo(() => props.page !== void 0)() && props.pageSize !== void 0;
|
|
575
|
+
},
|
|
576
|
+
get children() {
|
|
577
|
+
var _el$11 = _tmpl$6(), _el$12 = _el$11.firstChild, _el$13 = _el$12.nextSibling;
|
|
578
|
+
_el$13.firstChild;
|
|
579
|
+
var _el$15 = _el$13.nextSibling;
|
|
580
|
+
_el$12.$$click = () => props.onPageChange?.((props.page ?? 1) - 1);
|
|
581
|
+
insert(_el$13, () => props.page, null);
|
|
582
|
+
_el$15.$$click = () => props.onPageChange?.((props.page ?? 1) + 1);
|
|
583
|
+
effect((_p$) => {
|
|
584
|
+
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);
|
|
585
|
+
_v$ !== _p$.e && (_el$12.disabled = _p$.e = _v$);
|
|
586
|
+
_v$2 !== _p$.t && (_el$15.disabled = _p$.t = _v$2);
|
|
587
|
+
return _p$;
|
|
588
|
+
}, {
|
|
589
|
+
e: void 0,
|
|
590
|
+
t: void 0
|
|
591
|
+
});
|
|
592
|
+
return _el$11;
|
|
593
|
+
}
|
|
594
|
+
}), null);
|
|
595
|
+
return _el$;
|
|
596
|
+
})();
|
|
597
|
+
}
|
|
598
|
+
delegateEvents(["click", "keydown"]);
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/SearchPalette.tsx
|
|
601
|
+
var _tmpl$ = /*#__PURE__*/ template(`<ul class="max-h-80 overflow-y-auto py-2">`), _tmpl$2 = /*#__PURE__*/ template(`<div aria-hidden=true class="fixed inset-0 z-50 flex items-start justify-center bg-[var(--color-backdrop)] px-4 pt-[15vh]"><div role=dialog aria-modal=true aria-label=Search class="w-full max-w-lg overflow-hidden rounded-2xl border border-[var(--line)] bg-[var(--surface-strong)] shadow-2xl"><div class="flex items-center gap-2 border-b border-[var(--line)] px-4 py-3"><i class="ph ph-magnifying-glass text-lg text-[var(--sea-ink-soft)]"aria-hidden=true></i><input type=text placeholder=Search… aria-label=Search class="flex-1 bg-transparent text-base text-[var(--sea-ink)] outline-none placeholder:text-[var(--sea-ink-soft)]"><kbd class="rounded border border-[var(--chip-line)] bg-[var(--chip-bg)] px-1.5 py-0.5 text-xs text-[var(--sea-ink-soft)]">Esc`), _tmpl$3 = /*#__PURE__*/ template(`<p class="px-4 py-6 text-center text-sm text-[var(--sea-ink-soft)]">`), _tmpl$4 = /*#__PURE__*/ template(`<li><button type=button class="flex w-full items-center justify-between gap-3 px-4 py-2 text-left text-sm text-[var(--sea-ink)] hover:bg-[var(--link-bg-hover)]"><span class=truncate></span><span class="shrink-0 text-xs text-[var(--sea-ink-soft)]">`);
|
|
602
|
+
const DEBOUNCE_MS = 200;
|
|
603
|
+
function capitalize(value) {
|
|
604
|
+
return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Self-contained Cmd+K (Ctrl+K on non-Mac) search palette — issue #29.
|
|
608
|
+
* Owns its own open/closed state and keyboard listener; the host app only
|
|
609
|
+
* supplies `onSearch` (wired to a server function that fans out across
|
|
610
|
+
* every collection with `search` configured) and `onSelect` (navigation).
|
|
611
|
+
* Mirrors PanelNav's focus-trap-on-open / restore-focus-on-close pattern
|
|
612
|
+
* rather than introducing a second one.
|
|
613
|
+
*/
|
|
614
|
+
function SearchPalette(props) {
|
|
615
|
+
const [open, setOpen] = createSignal(false);
|
|
616
|
+
const [query, setQuery] = createSignal("");
|
|
617
|
+
const [results, setResults] = createSignal([]);
|
|
618
|
+
const [activeIndex, setActiveIndex] = createSignal(0);
|
|
619
|
+
let inputRef;
|
|
620
|
+
let triggeredBy = null;
|
|
621
|
+
let debounceTimer;
|
|
622
|
+
let latestQueryToken = 0;
|
|
623
|
+
function close() {
|
|
624
|
+
setOpen(false);
|
|
625
|
+
setQuery("");
|
|
626
|
+
setResults([]);
|
|
627
|
+
triggeredBy?.focus();
|
|
628
|
+
}
|
|
629
|
+
function runSearch(value) {
|
|
630
|
+
const token = ++latestQueryToken;
|
|
631
|
+
if (value.trim().length < 2) {
|
|
632
|
+
setResults([]);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
props.onSearch(value).then((found) => {
|
|
636
|
+
if (token !== latestQueryToken) return;
|
|
637
|
+
setResults(found);
|
|
638
|
+
setActiveIndex(0);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function onInput(value) {
|
|
642
|
+
setQuery(value);
|
|
643
|
+
clearTimeout(debounceTimer);
|
|
644
|
+
debounceTimer = setTimeout(() => runSearch(value), DEBOUNCE_MS);
|
|
645
|
+
}
|
|
646
|
+
function selectResult(result) {
|
|
647
|
+
if (!result) return;
|
|
648
|
+
props.onSelect(result);
|
|
649
|
+
close();
|
|
650
|
+
}
|
|
651
|
+
createEffect(() => {
|
|
652
|
+
function onGlobalKeyDown(event) {
|
|
653
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
654
|
+
event.preventDefault();
|
|
655
|
+
triggeredBy = document.activeElement;
|
|
656
|
+
setOpen(true);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
document.addEventListener("keydown", onGlobalKeyDown);
|
|
660
|
+
onCleanup(() => document.removeEventListener("keydown", onGlobalKeyDown));
|
|
661
|
+
});
|
|
662
|
+
createEffect(() => {
|
|
663
|
+
if (open()) inputRef?.focus();
|
|
664
|
+
});
|
|
665
|
+
function onDialogKeyDown(event) {
|
|
666
|
+
if (event.key === "Escape") {
|
|
667
|
+
close();
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (event.key === "ArrowDown") {
|
|
671
|
+
event.preventDefault();
|
|
672
|
+
setActiveIndex((i) => Math.min(i + 1, results().length - 1));
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (event.key === "ArrowUp") {
|
|
676
|
+
event.preventDefault();
|
|
677
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (event.key === "Enter") {
|
|
681
|
+
event.preventDefault();
|
|
682
|
+
selectResult(results()[activeIndex()]);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return createComponent(Show, {
|
|
686
|
+
get when() {
|
|
687
|
+
return open();
|
|
688
|
+
},
|
|
689
|
+
get children() {
|
|
690
|
+
var _el$ = _tmpl$2(), _el$2 = _el$.firstChild, _el$5 = _el$2.firstChild.firstChild.nextSibling;
|
|
691
|
+
_el$.$$click = close;
|
|
692
|
+
_el$2.$$keydown = onDialogKeyDown;
|
|
693
|
+
_el$2.$$click = (event) => event.stopPropagation();
|
|
694
|
+
_el$5.$$input = (event) => onInput(event.currentTarget.value);
|
|
695
|
+
var _ref$ = inputRef;
|
|
696
|
+
typeof _ref$ === "function" ? use(_ref$, _el$5) : inputRef = _el$5;
|
|
697
|
+
insert(_el$2, createComponent(Show, {
|
|
698
|
+
get when() {
|
|
699
|
+
return results().length > 0;
|
|
700
|
+
},
|
|
701
|
+
get fallback() {
|
|
702
|
+
return (() => {
|
|
703
|
+
var _el$7 = _tmpl$3();
|
|
704
|
+
insert(_el$7, () => query().trim().length < 2 ? "Keep typing to search…" : "No results");
|
|
705
|
+
return _el$7;
|
|
706
|
+
})();
|
|
707
|
+
},
|
|
708
|
+
get children() {
|
|
709
|
+
var _el$6 = _tmpl$();
|
|
710
|
+
insert(_el$6, createComponent(For, {
|
|
711
|
+
get each() {
|
|
712
|
+
return results();
|
|
713
|
+
},
|
|
714
|
+
children: (result, index) => (() => {
|
|
715
|
+
var _el$8 = _tmpl$4(), _el$9 = _el$8.firstChild, _el$0 = _el$9.firstChild, _el$1 = _el$0.nextSibling;
|
|
716
|
+
_el$9.addEventListener("mouseenter", () => setActiveIndex(index()));
|
|
717
|
+
_el$9.$$click = () => selectResult(result);
|
|
718
|
+
insert(_el$0, () => result.label);
|
|
719
|
+
insert(_el$1, () => capitalize(result.collection));
|
|
720
|
+
effect(() => _el$9.classList.toggle("bg-[var(--link-bg-hover)]", !!(activeIndex() === index())));
|
|
721
|
+
return _el$8;
|
|
722
|
+
})()
|
|
723
|
+
}));
|
|
724
|
+
return _el$6;
|
|
725
|
+
}
|
|
726
|
+
}), null);
|
|
727
|
+
effect(() => _el$5.value = query());
|
|
728
|
+
return _el$;
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
delegateEvents([
|
|
733
|
+
"click",
|
|
734
|
+
"keydown",
|
|
735
|
+
"input"
|
|
736
|
+
]);
|
|
737
|
+
//#endregion
|
|
738
|
+
export { CollectionEdit, CollectionList, SearchPalette };
|
|
739
|
+
|
|
740
|
+
//# sourceMappingURL=index.js.map
|