@aphexcms/cms-core 0.1.15 → 0.1.17
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/dist/cli/generate-types.js +3 -1
- package/dist/cli/index.js +0 -0
- package/dist/components/admin/fields/ArrayField.svelte +340 -104
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ImageField.svelte +208 -110
- package/dist/components/admin/fields/ImageField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/SlugField.svelte +22 -12
- package/dist/components/admin/fields/SlugField.svelte.d.ts +2 -2
- package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
- package/dist/db/interfaces/asset.d.ts.map +1 -1
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +1 -3
- package/dist/lib/field-validation/utils.js +1 -3
- package/dist/local-api/collection-api.d.ts +2 -2
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/types/filters.d.ts.map +1 -1
- package/package.json +101 -103
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Benjamin Sinidol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -132,7 +132,9 @@ export function generateTypes(schemas) {
|
|
|
132
132
|
const objectSchemas = schemas.filter((s) => s.type === 'object');
|
|
133
133
|
// Generate interfaces for all schemas
|
|
134
134
|
const objectInterfaces = objectSchemas.map((s) => generateInterface(s, schemaMap)).join('\n\n');
|
|
135
|
-
const documentInterfaces = documentSchemas
|
|
135
|
+
const documentInterfaces = documentSchemas
|
|
136
|
+
.map((s) => generateInterface(s, schemaMap))
|
|
137
|
+
.join('\n\n');
|
|
136
138
|
// Generate Collections interface augmentation
|
|
137
139
|
const collectionsAugmentation = generateCollectionsAugmentation(documentSchemas);
|
|
138
140
|
// Build the complete file
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Button } from '@aphexcms/ui/shadcn/button';
|
|
3
|
+
import { Input } from '@aphexcms/ui/shadcn/input';
|
|
4
|
+
import { Textarea } from '@aphexcms/ui/shadcn/textarea';
|
|
5
|
+
import { Checkbox } from '@aphexcms/ui/shadcn/checkbox';
|
|
3
6
|
import * as DropdownMenu from '@aphexcms/ui/shadcn/dropdown-menu';
|
|
7
|
+
import * as Card from '@aphexcms/ui/shadcn/card';
|
|
4
8
|
import type { ArrayField as ArrayFieldType, SchemaType } from '../../../types/schemas';
|
|
5
9
|
import { getArrayTypes, getSchemaByName } from '../../../schema-utils/utils';
|
|
6
10
|
import { getSchemaContext } from '../../../schema-context.svelte';
|
|
7
11
|
import ObjectModal from '../ObjectModal.svelte';
|
|
12
|
+
import ImageField from './ImageField.svelte';
|
|
8
13
|
|
|
9
14
|
interface Props {
|
|
10
15
|
field: ArrayFieldType;
|
|
@@ -19,19 +24,72 @@
|
|
|
19
24
|
// Get schemas from context
|
|
20
25
|
const schemas = getSchemaContext();
|
|
21
26
|
|
|
22
|
-
//
|
|
27
|
+
// Determine if this is a primitive array or object array
|
|
28
|
+
// If the type is not found in schemas, it's a primitive type
|
|
29
|
+
const isPrimitiveArray = $derived(
|
|
30
|
+
field.of && field.of.length > 0 && field.of[0]?.type && !getSchemaByName(schemas, field.of[0].type)
|
|
31
|
+
);
|
|
32
|
+
const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
|
|
33
|
+
|
|
34
|
+
// Get available types for this array field (for object arrays)
|
|
23
35
|
const availableTypes = $derived(getArrayTypes(schemas, field));
|
|
24
36
|
|
|
25
|
-
// Modal state
|
|
37
|
+
// Modal state (for object arrays)
|
|
26
38
|
let modalOpen = $state(false);
|
|
27
39
|
let editingIndex = $state<number | null>(null);
|
|
28
40
|
let editingType = $state<string | null>(null);
|
|
29
41
|
let editingSchema = $state<SchemaType | null>(null);
|
|
30
42
|
let editingValue = $state<Record<string, any>>({});
|
|
31
43
|
|
|
44
|
+
// Image modal state
|
|
45
|
+
let imageModalOpen = $state(false);
|
|
46
|
+
let imageModalValue = $state<any>(null);
|
|
47
|
+
|
|
32
48
|
// Ensure value is always an array
|
|
33
49
|
const arrayValue = $derived(Array.isArray(value) ? value : []);
|
|
34
50
|
|
|
51
|
+
// Primitive array functions
|
|
52
|
+
function handleAddPrimitive() {
|
|
53
|
+
if (readonly) return;
|
|
54
|
+
|
|
55
|
+
// For images, open the modal
|
|
56
|
+
if (primitiveType === 'image') {
|
|
57
|
+
imageModalValue = null;
|
|
58
|
+
imageModalOpen = true;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// For other primitives, add a new empty item
|
|
63
|
+
const newArray = [...arrayValue];
|
|
64
|
+
const defaultValue = primitiveType === 'boolean' ? false : primitiveType === 'number' ? 0 : '';
|
|
65
|
+
newArray.push(defaultValue);
|
|
66
|
+
onUpdate(newArray);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleUpdatePrimitive(index: number, newValue: any) {
|
|
70
|
+
if (readonly) return;
|
|
71
|
+
const newArray = [...arrayValue];
|
|
72
|
+
newArray[index] = newValue;
|
|
73
|
+
onUpdate(newArray);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function handleImageModalClose() {
|
|
77
|
+
imageModalOpen = false;
|
|
78
|
+
imageModalValue = null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleImageUpload(newValue: any) {
|
|
82
|
+
if (newValue) {
|
|
83
|
+
// Auto-add the image to the array
|
|
84
|
+
const newArray = [...arrayValue, newValue];
|
|
85
|
+
onUpdate(newArray);
|
|
86
|
+
// Close the modal
|
|
87
|
+
imageModalOpen = false;
|
|
88
|
+
imageModalValue = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Object array functions
|
|
35
93
|
function handleTypeSelected(selectedType: string) {
|
|
36
94
|
if (readonly || !selectedType) return;
|
|
37
95
|
|
|
@@ -135,120 +193,249 @@
|
|
|
135
193
|
<div class="border-border space-y-4 rounded-md border p-4">
|
|
136
194
|
<h4 class="text-sm font-medium">{field.title}</h4>
|
|
137
195
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
196
|
+
{#if isPrimitiveArray}
|
|
197
|
+
<!-- Primitive array UI -->
|
|
198
|
+
{#if arrayValue.length === 0}
|
|
199
|
+
<!-- Empty state -->
|
|
200
|
+
<div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
|
|
201
|
+
<p class="text-muted-foreground text-sm">No items</p>
|
|
202
|
+
</div>
|
|
203
|
+
{:else}
|
|
204
|
+
<div class="space-y-2">
|
|
205
|
+
{#each arrayValue as item, index (index)}
|
|
206
|
+
{#if primitiveType === 'image'}
|
|
207
|
+
<!-- Image item in compact mode -->
|
|
208
|
+
<ImageField
|
|
209
|
+
field={{
|
|
210
|
+
...field.of?.[0],
|
|
211
|
+
name: `image-${index}`,
|
|
212
|
+
type: 'image',
|
|
213
|
+
title: `Image ${index + 1}`
|
|
214
|
+
}}
|
|
215
|
+
value={item}
|
|
216
|
+
onUpdate={(newValue) => {
|
|
217
|
+
const newArray = [...arrayValue];
|
|
218
|
+
if (newValue === null) {
|
|
219
|
+
// Remove the image if null
|
|
220
|
+
newArray.splice(index, 1);
|
|
221
|
+
} else {
|
|
222
|
+
newArray[index] = newValue;
|
|
223
|
+
}
|
|
224
|
+
onUpdate(newArray);
|
|
225
|
+
}}
|
|
226
|
+
{readonly}
|
|
227
|
+
compact={true}
|
|
228
|
+
/>
|
|
229
|
+
{:else}
|
|
230
|
+
<!-- Always-editable primitive with options menu -->
|
|
231
|
+
<div class="border-border/50 flex items-center gap-2 rounded border p-2">
|
|
145
232
|
<span class="text-muted-foreground text-xs">#{index + 1}</span>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
233
|
+
{#if primitiveType === 'boolean'}
|
|
234
|
+
<div class="flex flex-1 items-center gap-2">
|
|
235
|
+
<Checkbox
|
|
236
|
+
checked={item}
|
|
237
|
+
onCheckedChange={(checked) => handleUpdatePrimitive(index, checked)}
|
|
238
|
+
disabled={readonly}
|
|
239
|
+
/>
|
|
240
|
+
<span class="text-sm">{item ? 'True' : 'False'}</span>
|
|
241
|
+
</div>
|
|
242
|
+
{:else if primitiveType === 'text'}
|
|
243
|
+
<Textarea
|
|
244
|
+
value={item}
|
|
245
|
+
oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
|
|
246
|
+
readonly={readonly}
|
|
247
|
+
class="flex-1"
|
|
248
|
+
rows={3}
|
|
249
|
+
placeholder="Enter text..."
|
|
250
|
+
/>
|
|
251
|
+
{:else if primitiveType === 'number'}
|
|
252
|
+
<Input
|
|
253
|
+
type="number"
|
|
254
|
+
value={item}
|
|
255
|
+
oninput={(e) => handleUpdatePrimitive(index, parseFloat(e.currentTarget.value) || 0)}
|
|
256
|
+
readonly={readonly}
|
|
257
|
+
class="flex-1"
|
|
258
|
+
placeholder="Enter number..."
|
|
259
|
+
/>
|
|
260
|
+
{:else}
|
|
261
|
+
<Input
|
|
262
|
+
value={item}
|
|
263
|
+
oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
|
|
264
|
+
readonly={readonly}
|
|
265
|
+
class="flex-1"
|
|
266
|
+
placeholder="Enter value..."
|
|
267
|
+
/>
|
|
268
|
+
{/if}
|
|
269
|
+
{#if !readonly}
|
|
270
|
+
<DropdownMenu.Root>
|
|
271
|
+
<DropdownMenu.Trigger>
|
|
272
|
+
<Button variant="ghost" size="sm" class="h-8 w-8 p-0">
|
|
273
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
274
|
+
<path
|
|
275
|
+
stroke-linecap="round"
|
|
276
|
+
stroke-linejoin="round"
|
|
277
|
+
stroke-width="2"
|
|
278
|
+
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
|
|
279
|
+
/>
|
|
280
|
+
</svg>
|
|
281
|
+
</Button>
|
|
282
|
+
</DropdownMenu.Trigger>
|
|
283
|
+
<DropdownMenu.Content align="end">
|
|
284
|
+
<DropdownMenu.Item onclick={() => handleRemoveItem(index)}>
|
|
285
|
+
<svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
286
|
+
<path
|
|
287
|
+
stroke-linecap="round"
|
|
288
|
+
stroke-linejoin="round"
|
|
289
|
+
stroke-width="2"
|
|
290
|
+
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
291
|
+
/>
|
|
292
|
+
</svg>
|
|
293
|
+
Remove
|
|
294
|
+
</DropdownMenu.Item>
|
|
295
|
+
</DropdownMenu.Content>
|
|
296
|
+
</DropdownMenu.Root>
|
|
149
297
|
{/if}
|
|
150
298
|
</div>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
299
|
+
{/if}
|
|
300
|
+
{/each}
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
|
|
304
|
+
<!-- Add primitive item section -->
|
|
305
|
+
{#if !readonly}
|
|
306
|
+
<Button variant="outline" class="w-full" onclick={handleAddPrimitive}>
|
|
307
|
+
<svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
308
|
+
<path
|
|
309
|
+
stroke-linecap="round"
|
|
310
|
+
stroke-linejoin="round"
|
|
311
|
+
stroke-width="2"
|
|
312
|
+
d="M12 4v16m8-8H4"
|
|
313
|
+
/>
|
|
314
|
+
</svg>
|
|
315
|
+
Add Item
|
|
316
|
+
</Button>
|
|
317
|
+
{/if}
|
|
318
|
+
{:else}
|
|
319
|
+
<!-- Object array UI (existing code) -->
|
|
320
|
+
{#if arrayValue.length === 0}
|
|
321
|
+
<!-- Empty state -->
|
|
322
|
+
<div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
|
|
323
|
+
<p class="text-muted-foreground text-sm">No items</p>
|
|
324
|
+
</div>
|
|
325
|
+
{:else}
|
|
326
|
+
<div class="space-y-2">
|
|
327
|
+
{#each arrayValue as item, index (index)}
|
|
328
|
+
<div class="border-border/50 space-y-2 rounded border p-3">
|
|
329
|
+
<div class="flex items-center justify-between">
|
|
330
|
+
<div class="flex items-center gap-2">
|
|
331
|
+
<span class="text-muted-foreground text-xs">#{index + 1}</span>
|
|
332
|
+
<h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
|
|
333
|
+
{#if item._type}
|
|
334
|
+
<span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
|
|
185
335
|
{/if}
|
|
186
|
-
</
|
|
187
|
-
|
|
188
|
-
{#if !readonly}
|
|
336
|
+
</div>
|
|
337
|
+
<div class="flex items-center gap-2">
|
|
189
338
|
<Button
|
|
190
339
|
variant="ghost"
|
|
191
340
|
size="sm"
|
|
192
|
-
onclick={() =>
|
|
193
|
-
|
|
194
|
-
|
|
341
|
+
onclick={() => {
|
|
342
|
+
handleEditItem(index);
|
|
343
|
+
}}
|
|
344
|
+
class="h-8 w-8 p-0"
|
|
345
|
+
title={readonly ? 'View item' : 'Edit item'}
|
|
195
346
|
>
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
347
|
+
{#if readonly}
|
|
348
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
349
|
+
<path
|
|
350
|
+
stroke-linecap="round"
|
|
351
|
+
stroke-linejoin="round"
|
|
352
|
+
stroke-width="2"
|
|
353
|
+
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
354
|
+
/>
|
|
355
|
+
<path
|
|
356
|
+
stroke-linecap="round"
|
|
357
|
+
stroke-linejoin="round"
|
|
358
|
+
stroke-width="2"
|
|
359
|
+
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
360
|
+
/>
|
|
361
|
+
</svg>
|
|
362
|
+
{:else}
|
|
363
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
364
|
+
<path
|
|
365
|
+
stroke-linecap="round"
|
|
366
|
+
stroke-linejoin="round"
|
|
367
|
+
stroke-width="2"
|
|
368
|
+
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
369
|
+
/>
|
|
370
|
+
</svg>
|
|
371
|
+
{/if}
|
|
204
372
|
</Button>
|
|
205
|
-
|
|
373
|
+
|
|
374
|
+
{#if !readonly}
|
|
375
|
+
<Button
|
|
376
|
+
variant="ghost"
|
|
377
|
+
size="sm"
|
|
378
|
+
onclick={() => handleRemoveItem(index)}
|
|
379
|
+
class="text-destructive hover:text-destructive h-8 w-8 p-0"
|
|
380
|
+
title="Remove item"
|
|
381
|
+
>
|
|
382
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
383
|
+
<path
|
|
384
|
+
stroke-linecap="round"
|
|
385
|
+
stroke-linejoin="round"
|
|
386
|
+
stroke-width="2"
|
|
387
|
+
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
388
|
+
/>
|
|
389
|
+
</svg>
|
|
390
|
+
</Button>
|
|
391
|
+
{/if}
|
|
392
|
+
</div>
|
|
206
393
|
</div>
|
|
207
|
-
</div>
|
|
208
394
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
395
|
+
<!-- Show a preview of the item content -->
|
|
396
|
+
<div class="text-muted-foreground pl-6 text-xs">
|
|
397
|
+
{#if item.title || item.heading}
|
|
398
|
+
{item.title || item.heading}
|
|
399
|
+
{:else if item.description}
|
|
400
|
+
{item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
|
|
401
|
+
{:else}
|
|
402
|
+
{readonly ? 'Click view to see details' : 'Click edit to configure this item'}
|
|
403
|
+
{/if}
|
|
404
|
+
</div>
|
|
218
405
|
</div>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</DropdownMenu.
|
|
250
|
-
</
|
|
251
|
-
|
|
406
|
+
{/each}
|
|
407
|
+
</div>
|
|
408
|
+
{/if}
|
|
409
|
+
|
|
410
|
+
<!-- Add Item section (hidden for read-only) -->
|
|
411
|
+
{#if !readonly}
|
|
412
|
+
<div class="border-border border-t pt-2">
|
|
413
|
+
<DropdownMenu.Root>
|
|
414
|
+
<DropdownMenu.Trigger>
|
|
415
|
+
{#snippet child({ props })}
|
|
416
|
+
<Button {...props} variant="outline" class="w-full cursor-pointer">
|
|
417
|
+
<svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
418
|
+
<path
|
|
419
|
+
stroke-linecap="round"
|
|
420
|
+
stroke-linejoin="round"
|
|
421
|
+
stroke-width="2"
|
|
422
|
+
d="M12 4v16m8-8H4"
|
|
423
|
+
/>
|
|
424
|
+
</svg>
|
|
425
|
+
Add Item
|
|
426
|
+
</Button>
|
|
427
|
+
{/snippet}
|
|
428
|
+
</DropdownMenu.Trigger>
|
|
429
|
+
<DropdownMenu.Content class="w-56">
|
|
430
|
+
{#each availableTypes as type, index (index)}
|
|
431
|
+
<DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
|
|
432
|
+
{type.title}
|
|
433
|
+
</DropdownMenu.Item>
|
|
434
|
+
{/each}
|
|
435
|
+
</DropdownMenu.Content>
|
|
436
|
+
</DropdownMenu.Root>
|
|
437
|
+
</div>
|
|
438
|
+
{/if}
|
|
252
439
|
{/if}
|
|
253
440
|
</div>
|
|
254
441
|
|
|
@@ -264,3 +451,52 @@
|
|
|
264
451
|
{readonly}
|
|
265
452
|
/>
|
|
266
453
|
{/if}
|
|
454
|
+
|
|
455
|
+
<!-- Image upload modal -->
|
|
456
|
+
{#if imageModalOpen}
|
|
457
|
+
<div
|
|
458
|
+
class="bg-background/80 fixed bottom-0 left-0 right-0 top-12 z-[100] flex items-center justify-center p-6 backdrop-blur-xs sm:absolute sm:top-0 sm:p-4"
|
|
459
|
+
onclick={(e) => {
|
|
460
|
+
if (e.target === e.currentTarget) handleImageModalClose();
|
|
461
|
+
}}
|
|
462
|
+
onkeydown={(e) => {
|
|
463
|
+
if (e.key === 'Escape') handleImageModalClose();
|
|
464
|
+
}}
|
|
465
|
+
role="button"
|
|
466
|
+
tabindex="-1"
|
|
467
|
+
>
|
|
468
|
+
<Card.Root class="flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden shadow-lg">
|
|
469
|
+
<Card.Header class="border-b">
|
|
470
|
+
<div class="flex items-center justify-between">
|
|
471
|
+
<div>
|
|
472
|
+
<Card.Title>{field.title} - Add Image</Card.Title>
|
|
473
|
+
<Card.Description>Upload a new image to add to the array</Card.Description>
|
|
474
|
+
</div>
|
|
475
|
+
<Button variant="ghost" size="icon" onclick={handleImageModalClose}>
|
|
476
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
477
|
+
<path
|
|
478
|
+
stroke-linecap="round"
|
|
479
|
+
stroke-linejoin="round"
|
|
480
|
+
stroke-width="2"
|
|
481
|
+
d="M6 18L18 6M6 6l12 12"
|
|
482
|
+
/>
|
|
483
|
+
</svg>
|
|
484
|
+
</Button>
|
|
485
|
+
</div>
|
|
486
|
+
</Card.Header>
|
|
487
|
+
|
|
488
|
+
<Card.Content class="flex-1 overflow-auto">
|
|
489
|
+
<ImageField
|
|
490
|
+
field={{
|
|
491
|
+
...field.of?.[0],
|
|
492
|
+
name: 'image',
|
|
493
|
+
type: 'image',
|
|
494
|
+
title: 'Image'
|
|
495
|
+
}}
|
|
496
|
+
value={imageModalValue}
|
|
497
|
+
onUpdate={handleImageUpload}
|
|
498
|
+
/>
|
|
499
|
+
</Card.Content>
|
|
500
|
+
</Card.Root>
|
|
501
|
+
</div>
|
|
502
|
+
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAOtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0YF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|