@aphexcms/cms-core 9.2.0 → 9.2.2
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/dist/cli/generate-types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +21 -3
- package/dist/cli/generate-types.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +4 -0
- package/dist/components/AdminApp.svelte +27 -11
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/AdminLayout.svelte.d.ts +3 -3
- package/dist/components/admin/DocumentEditor.svelte +548 -21
- package/dist/components/admin/DocumentEditor.svelte.d.ts +6 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/SchemaField.svelte +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +2 -2
- package/dist/components/admin/fields/ImageField.svelte +85 -27
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/NumberField.svelte +61 -16
- package/dist/components/admin/fields/NumberField.svelte.d.ts +2 -2
- package/dist/components/admin/fields/NumberField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/StringField.svelte +26 -2
- package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/ImageBlockModal.svelte +133 -0
- package/dist/components/admin/fields/richtext/ImageBlockModal.svelte.d.ts +14 -0
- package/dist/components/admin/fields/richtext/ImageBlockModal.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte +2 -2
- package/dist/components/admin/fields/richtext/RichtextField.svelte +164 -13
- package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-serializer.js +6 -4
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/svelte-node-view.js +11 -0
- package/dist/lib/client/index.d.ts +1 -0
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +4 -0
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js +6 -4
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js +11 -0
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js.map +1 -1
- package/dist/lib/preview/assets.d.ts +19 -0
- package/dist/lib/preview/assets.d.ts.map +1 -0
- package/dist/lib/preview/assets.js +60 -0
- package/dist/lib/preview/assets.js.map +1 -0
- package/dist/lib/preview/perspective.d.ts +20 -0
- package/dist/lib/preview/perspective.d.ts.map +1 -0
- package/dist/lib/preview/perspective.js +21 -0
- package/dist/lib/preview/perspective.js.map +1 -0
- package/dist/lib/preview/stega.d.ts +25 -0
- package/dist/lib/preview/stega.d.ts.map +1 -0
- package/dist/lib/preview/stega.js +168 -0
- package/dist/lib/preview/stega.js.map +1 -0
- package/dist/lib/richtext-context.svelte.d.ts +18 -0
- package/dist/lib/richtext-context.svelte.d.ts.map +1 -0
- package/dist/lib/richtext-context.svelte.js +13 -0
- package/dist/lib/richtext-context.svelte.js.map +1 -0
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/services/asset-service.d.ts +12 -0
- package/dist/lib/services/asset-service.d.ts.map +1 -1
- package/dist/lib/services/asset-service.js +32 -0
- package/dist/lib/services/asset-service.js.map +1 -1
- package/dist/lib/types/asset.d.ts +15 -0
- package/dist/lib/types/asset.d.ts.map +1 -1
- package/dist/lib/types/config.d.ts +7 -0
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/schemas.d.ts +30 -1
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/types/schemas.js.map +1 -1
- package/dist/lib/utils/preview.d.ts +4 -0
- package/dist/lib/utils/preview.d.ts.map +1 -1
- package/dist/lib/utils/preview.js +7 -0
- package/dist/lib/utils/preview.js.map +1 -1
- package/dist/lib/vite/index.d.ts +12 -1
- package/dist/lib/vite/index.d.ts.map +1 -1
- package/dist/lib/vite/index.js +89 -1
- package/dist/lib/vite/index.js.map +1 -1
- package/dist/preview/assets.d.ts +19 -0
- package/dist/preview/assets.d.ts.map +1 -0
- package/dist/preview/assets.js +59 -0
- package/dist/preview/perspective.d.ts +20 -0
- package/dist/preview/perspective.d.ts.map +1 -0
- package/dist/preview/perspective.js +20 -0
- package/dist/preview/stega.d.ts +25 -0
- package/dist/preview/stega.d.ts.map +1 -0
- package/dist/preview/stega.js +167 -0
- package/dist/richtext-context.svelte.d.ts +18 -0
- package/dist/richtext-context.svelte.d.ts.map +1 -0
- package/dist/richtext-context.svelte.js +12 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/services/asset-service.d.ts +12 -0
- package/dist/services/asset-service.d.ts.map +1 -1
- package/dist/services/asset-service.js +32 -0
- package/dist/types/asset.d.ts +15 -0
- package/dist/types/asset.d.ts.map +1 -1
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +30 -1
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/preview.d.ts +4 -0
- package/dist/utils/preview.d.ts.map +1 -1
- package/dist/utils/preview.js +7 -0
- package/dist/vite/index.d.ts +12 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +89 -1
- package/package.json +3 -2
|
@@ -211,21 +211,25 @@
|
|
|
211
211
|
assetData?.originalFilename || assetData?.filename || value?.asset?._ref || 'Image'
|
|
212
212
|
);
|
|
213
213
|
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
214
|
+
// Two layers of alt text:
|
|
215
|
+
// • Default — stored on the ASSET, shared across every placement. Set it once and
|
|
216
|
+
// it applies everywhere the image is used.
|
|
217
|
+
// • Override — stored on THIS image value, for when one spot needs a different
|
|
218
|
+
// description. Render precedence is `value.alt || asset.alt`.
|
|
219
|
+
// The override is also what carries visual-editing stega.
|
|
220
|
+
let assetAltText = $state('');
|
|
221
|
+
let assetAltTimer: ReturnType<typeof setTimeout> | null = null;
|
|
222
|
+
|
|
223
|
+
// Sync the default from the loaded asset record.
|
|
219
224
|
$effect(() => {
|
|
220
|
-
|
|
225
|
+
assetAltText = assetData?.alt || '';
|
|
221
226
|
});
|
|
222
227
|
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
altSaveTimer = setTimeout(async () => {
|
|
228
|
+
function updateAssetAlt(newAlt: string) {
|
|
229
|
+
assetAltText = newAlt;
|
|
230
|
+
// Debounce the asset-update API call.
|
|
231
|
+
if (assetAltTimer) clearTimeout(assetAltTimer);
|
|
232
|
+
assetAltTimer = setTimeout(async () => {
|
|
229
233
|
const assetId = value?.asset?._ref;
|
|
230
234
|
if (!assetId) return;
|
|
231
235
|
try {
|
|
@@ -236,6 +240,17 @@
|
|
|
236
240
|
}, 800);
|
|
237
241
|
}
|
|
238
242
|
|
|
243
|
+
function updateOverrideAlt(newAlt: string) {
|
|
244
|
+
if (!value) return;
|
|
245
|
+
onUpdate({ ...value, alt: newAlt || undefined });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Whether to reveal the per-placement override input.
|
|
249
|
+
let showOverride = $state(false);
|
|
250
|
+
$effect(() => {
|
|
251
|
+
if (value?.alt) showOverride = true;
|
|
252
|
+
});
|
|
253
|
+
|
|
239
254
|
// Download image
|
|
240
255
|
function downloadImage() {
|
|
241
256
|
if (previewUrl) {
|
|
@@ -272,7 +287,7 @@
|
|
|
272
287
|
{:else if previewUrl}
|
|
273
288
|
<img
|
|
274
289
|
src={previewUrl}
|
|
275
|
-
alt={assetData?.alt || displayName}
|
|
290
|
+
alt={value?.alt || assetData?.alt || displayName}
|
|
276
291
|
class="h-full w-full object-cover"
|
|
277
292
|
loading="lazy"
|
|
278
293
|
/>
|
|
@@ -299,7 +314,7 @@
|
|
|
299
314
|
{:else if previewUrl}
|
|
300
315
|
<img
|
|
301
316
|
src={previewUrl}
|
|
302
|
-
alt={assetData?.alt || displayName}
|
|
317
|
+
alt={value?.alt || assetData?.alt || displayName}
|
|
303
318
|
class="h-full w-full object-cover"
|
|
304
319
|
loading="lazy"
|
|
305
320
|
/>
|
|
@@ -389,7 +404,7 @@
|
|
|
389
404
|
{:else if previewUrl}
|
|
390
405
|
<img
|
|
391
406
|
src={previewUrl}
|
|
392
|
-
alt={assetData?.alt || 'Uploaded image'}
|
|
407
|
+
alt={value?.alt || assetData?.alt || 'Uploaded image'}
|
|
393
408
|
class="h-full w-full object-contain"
|
|
394
409
|
loading="lazy"
|
|
395
410
|
/>
|
|
@@ -453,18 +468,61 @@
|
|
|
453
468
|
{/if}
|
|
454
469
|
</div>
|
|
455
470
|
|
|
456
|
-
<!-- Alt text
|
|
457
|
-
<div class="border-border space-y-
|
|
458
|
-
<
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
471
|
+
<!-- Alt text — asset-level default + optional per-placement override -->
|
|
472
|
+
<div class="border-border space-y-2 border-t p-3">
|
|
473
|
+
<div class="space-y-1.5">
|
|
474
|
+
<label class="text-sm font-medium" for="alt-{field.name}">Alt text</label>
|
|
475
|
+
<p class="text-muted-foreground text-xs">
|
|
476
|
+
Describes the image everywhere it's used (accessibility & SEO).
|
|
477
|
+
</p>
|
|
478
|
+
<Input
|
|
479
|
+
id="alt-{field.name}"
|
|
480
|
+
type="text"
|
|
481
|
+
placeholder="Describe this image..."
|
|
482
|
+
value={assetAltText}
|
|
483
|
+
oninput={(e) => updateAssetAlt(e.currentTarget.value)}
|
|
484
|
+
disabled={isReadOnly}
|
|
485
|
+
/>
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
{#if showOverride}
|
|
489
|
+
<div class="space-y-1.5">
|
|
490
|
+
<div class="flex items-center justify-between">
|
|
491
|
+
<label
|
|
492
|
+
class="text-muted-foreground text-xs font-medium"
|
|
493
|
+
for="alt-override-{field.name}">Override for this placement</label
|
|
494
|
+
>
|
|
495
|
+
{#if !isReadOnly}
|
|
496
|
+
<button
|
|
497
|
+
type="button"
|
|
498
|
+
class="text-muted-foreground hover:text-foreground text-xs underline-offset-2 hover:underline"
|
|
499
|
+
onclick={() => {
|
|
500
|
+
updateOverrideAlt('');
|
|
501
|
+
showOverride = false;
|
|
502
|
+
}}
|
|
503
|
+
>
|
|
504
|
+
Hide
|
|
505
|
+
</button>
|
|
506
|
+
{/if}
|
|
507
|
+
</div>
|
|
508
|
+
<Input
|
|
509
|
+
id="alt-override-{field.name}"
|
|
510
|
+
type="text"
|
|
511
|
+
placeholder={assetAltText || 'Describe this image...'}
|
|
512
|
+
value={value?.alt ?? ''}
|
|
513
|
+
oninput={(e) => updateOverrideAlt(e.currentTarget.value)}
|
|
514
|
+
disabled={isReadOnly}
|
|
515
|
+
/>
|
|
516
|
+
</div>
|
|
517
|
+
{:else if !isReadOnly}
|
|
518
|
+
<button
|
|
519
|
+
type="button"
|
|
520
|
+
class="text-muted-foreground hover:text-foreground text-xs underline-offset-2 hover:underline"
|
|
521
|
+
onclick={() => (showOverride = true)}
|
|
522
|
+
>
|
|
523
|
+
+ Override for this placement
|
|
524
|
+
</button>
|
|
525
|
+
{/if}
|
|
468
526
|
</div>
|
|
469
527
|
</div>
|
|
470
528
|
{:else}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAmB1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAmB1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAofF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Input } from '@aphexcms/ui/shadcn/input';
|
|
3
|
-
import
|
|
3
|
+
import { Slider } from '@aphexcms/ui/shadcn/slider';
|
|
4
|
+
import type { NumberField } from '../../../types/schemas';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
|
-
field:
|
|
7
|
+
field: NumberField;
|
|
7
8
|
value: number | null;
|
|
8
9
|
onUpdate: (value: number | null) => void;
|
|
9
10
|
validationClasses?: string;
|
|
@@ -22,6 +23,15 @@
|
|
|
22
23
|
readonly = false
|
|
23
24
|
}: Props = $props();
|
|
24
25
|
|
|
26
|
+
// --- Slider mode ---------------------------------------------------------------
|
|
27
|
+
const asSlider = $derived(field.options?.layout === 'slider');
|
|
28
|
+
const sliderMin = $derived(field.min ?? 0);
|
|
29
|
+
const sliderMax = $derived(field.max ?? 100);
|
|
30
|
+
const sliderStep = $derived(field.step ?? 1);
|
|
31
|
+
const unit = $derived(field.options?.unit ?? '');
|
|
32
|
+
// The slider always needs a concrete position; fall back to the min when unset.
|
|
33
|
+
const sliderValue = $derived(value ?? sliderMin);
|
|
34
|
+
|
|
25
35
|
// Convert value to string for input, handle null/undefined
|
|
26
36
|
let inputValue = $derived(value?.toString() || '');
|
|
27
37
|
|
|
@@ -33,8 +43,10 @@
|
|
|
33
43
|
if (newValue === '') {
|
|
34
44
|
onUpdate(null);
|
|
35
45
|
} else {
|
|
36
|
-
|
|
46
|
+
let numValue = parseFloat(newValue);
|
|
37
47
|
if (!isNaN(numValue)) {
|
|
48
|
+
// In slider mode the value is bounded, so clamp typed input to the range.
|
|
49
|
+
if (asSlider) numValue = Math.min(sliderMax, Math.max(sliderMin, numValue));
|
|
38
50
|
onUpdate(numValue);
|
|
39
51
|
}
|
|
40
52
|
}
|
|
@@ -67,16 +79,49 @@
|
|
|
67
79
|
}
|
|
68
80
|
</script>
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
{#if asSlider}
|
|
83
|
+
<div class="flex items-center gap-3" class:opacity-60={readonly}>
|
|
84
|
+
<Slider
|
|
85
|
+
type="single"
|
|
86
|
+
value={sliderValue}
|
|
87
|
+
onValueChange={(v) => onUpdate(v)}
|
|
88
|
+
min={sliderMin}
|
|
89
|
+
max={sliderMax}
|
|
90
|
+
step={sliderStep}
|
|
91
|
+
disabled={readonly}
|
|
92
|
+
class="flex-1"
|
|
93
|
+
/>
|
|
94
|
+
<div class="flex shrink-0 items-center gap-1.5">
|
|
95
|
+
<Input
|
|
96
|
+
type="number"
|
|
97
|
+
min={sliderMin}
|
|
98
|
+
max={sliderMax}
|
|
99
|
+
step={sliderStep}
|
|
100
|
+
value={inputValue}
|
|
101
|
+
oninput={handleInput}
|
|
102
|
+
onkeydown={handleKeydown}
|
|
103
|
+
onblur={onBlur}
|
|
104
|
+
onfocus={onFocus}
|
|
105
|
+
class="h-8 w-16 text-sm tabular-nums {validationClasses ?? ''}"
|
|
106
|
+
disabled={readonly}
|
|
107
|
+
/>
|
|
108
|
+
{#if unit}<span class="text-muted-foreground text-sm">{unit}</span>{/if}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
{:else}
|
|
112
|
+
<Input
|
|
113
|
+
id={field.name}
|
|
114
|
+
type="number"
|
|
115
|
+
step={field.step ?? 'any'}
|
|
116
|
+
min={field.min}
|
|
117
|
+
max={field.max}
|
|
118
|
+
placeholder={field.description || `Enter ${field.title?.toLowerCase() || 'number'}`}
|
|
119
|
+
value={inputValue}
|
|
120
|
+
oninput={handleInput}
|
|
121
|
+
onkeydown={handleKeydown}
|
|
122
|
+
onblur={onBlur}
|
|
123
|
+
onfocus={onFocus}
|
|
124
|
+
class={validationClasses}
|
|
125
|
+
disabled={readonly}
|
|
126
|
+
/>
|
|
127
|
+
{/if}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NumberField } from '../../../types/schemas.js';
|
|
2
2
|
interface Props {
|
|
3
|
-
field:
|
|
3
|
+
field: NumberField;
|
|
4
4
|
value: number | null;
|
|
5
5
|
onUpdate: (value: number | null) => void;
|
|
6
6
|
validationClasses?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/NumberField.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NumberField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/NumberField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGzD,UAAU,KAAK;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAuFF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Input } from '@aphexcms/ui/shadcn/input';
|
|
3
3
|
import * as Select from '@aphexcms/ui/shadcn/select';
|
|
4
4
|
import * as RadioGroup from '@aphexcms/ui/shadcn/radio-group';
|
|
5
|
+
import * as Tabs from '@aphexcms/ui/shadcn/tabs';
|
|
5
6
|
import { Label } from '@aphexcms/ui/shadcn/label';
|
|
6
7
|
import type { StringField, DependentList } from '../../../types/schemas';
|
|
7
8
|
import { cmsLogger } from '../../../utils/logger';
|
|
@@ -69,10 +70,12 @@
|
|
|
69
70
|
return '';
|
|
70
71
|
});
|
|
71
72
|
|
|
72
|
-
// Normalize list items to { title, value }
|
|
73
|
+
// Normalize list items to a uniform { title, value, icon } shape (icon may be undefined).
|
|
73
74
|
const listItems = $derived(
|
|
74
75
|
resolvedList().map((item) =>
|
|
75
|
-
typeof item === 'string'
|
|
76
|
+
typeof item === 'string'
|
|
77
|
+
? { title: item.toUpperCase(), value: item, icon: undefined }
|
|
78
|
+
: { title: item.title, value: item.value, icon: item.icon }
|
|
76
79
|
)
|
|
77
80
|
);
|
|
78
81
|
|
|
@@ -144,6 +147,27 @@
|
|
|
144
147
|
{/each}
|
|
145
148
|
</div>
|
|
146
149
|
</RadioGroup.Root>
|
|
150
|
+
{:else if layout === 'tabs'}
|
|
151
|
+
<!-- Segmented / tabs layout — icon-friendly (alignment-style pickers). -->
|
|
152
|
+
<Tabs.Root
|
|
153
|
+
value={value || ''}
|
|
154
|
+
onValueChange={(v) => v && handleSelectChange(v)}
|
|
155
|
+
class={validationClasses}
|
|
156
|
+
>
|
|
157
|
+
<Tabs.List>
|
|
158
|
+
{#each listItems as item (item.value)}
|
|
159
|
+
{@const Icon = item.icon}
|
|
160
|
+
<Tabs.Trigger value={item.value} disabled={readonly} title={item.title}>
|
|
161
|
+
{#if Icon}
|
|
162
|
+
<Icon class="size-4" />
|
|
163
|
+
<span class="sr-only">{item.title}</span>
|
|
164
|
+
{:else}
|
|
165
|
+
{item.title}
|
|
166
|
+
{/if}
|
|
167
|
+
</Tabs.Trigger>
|
|
168
|
+
{/each}
|
|
169
|
+
</Tabs.List>
|
|
170
|
+
</Tabs.Root>
|
|
147
171
|
{:else}
|
|
148
172
|
<!-- Dropdown/Select Layout -->
|
|
149
173
|
<Select.Root
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StringField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/StringField.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StringField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/StringField.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,wBAAwB,CAAC;AAIxE,UAAU,KAAK;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAmLF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '@aphexcms/ui/shadcn/button';
|
|
3
|
+
import { Input } from '@aphexcms/ui/shadcn/input';
|
|
4
|
+
import { Image as ImageIcon, Upload, Trash2, X } from '@lucide/svelte';
|
|
5
|
+
import { assets } from '../../../../api/assets';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
open: boolean;
|
|
9
|
+
assetRef?: string;
|
|
10
|
+
alt: string;
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
onAltChange: (alt: string) => void;
|
|
13
|
+
onReplace: () => void;
|
|
14
|
+
onRemove: () => void;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
open,
|
|
20
|
+
assetRef,
|
|
21
|
+
alt,
|
|
22
|
+
readonly = false,
|
|
23
|
+
onAltChange,
|
|
24
|
+
onReplace,
|
|
25
|
+
onRemove,
|
|
26
|
+
onClose
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const assetPromise = $derived(assetRef ? assets.getById(assetRef) : null);
|
|
30
|
+
|
|
31
|
+
function handleBackdropClick(e: MouseEvent) {
|
|
32
|
+
if (e.target === e.currentTarget) onClose();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
36
|
+
if (e.key === 'Escape') onClose();
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
{#if open}
|
|
41
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
42
|
+
<div
|
|
43
|
+
class="bg-background/80 absolute inset-0 z-50 flex items-center justify-center p-4 lg:p-6"
|
|
44
|
+
onclick={handleBackdropClick}
|
|
45
|
+
onkeydown={handleKeydown}
|
|
46
|
+
role="button"
|
|
47
|
+
tabindex="-1"
|
|
48
|
+
>
|
|
49
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
50
|
+
<div
|
|
51
|
+
class="border-border bg-background w-full max-w-lg overflow-hidden rounded-lg border shadow-lg"
|
|
52
|
+
onclick={(e) => e.stopPropagation()}
|
|
53
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
54
|
+
>
|
|
55
|
+
<div class="border-border flex items-center justify-between border-b px-4 py-2">
|
|
56
|
+
<span class="text-sm font-medium">Edit image</span>
|
|
57
|
+
<Button variant="ghost" size="icon" class="h-7 w-7" onclick={onClose}>
|
|
58
|
+
<X class="h-3.5 w-3.5" />
|
|
59
|
+
</Button>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="space-y-3 p-4">
|
|
63
|
+
<!-- Preview -->
|
|
64
|
+
<div
|
|
65
|
+
class="bg-muted flex items-center justify-center overflow-hidden rounded-md"
|
|
66
|
+
style="aspect-ratio: 16 / 9;"
|
|
67
|
+
>
|
|
68
|
+
{#if assetPromise}
|
|
69
|
+
{#await assetPromise}
|
|
70
|
+
<div
|
|
71
|
+
class="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent"
|
|
72
|
+
></div>
|
|
73
|
+
{:then result}
|
|
74
|
+
{#if result.success && result.data?.url}
|
|
75
|
+
<img src={result.data.url} {alt} class="h-full w-full object-contain" />
|
|
76
|
+
{:else}
|
|
77
|
+
<div class="text-muted-foreground flex flex-col items-center gap-1">
|
|
78
|
+
<ImageIcon class="h-8 w-8" />
|
|
79
|
+
<span class="text-xs">Image not found</span>
|
|
80
|
+
</div>
|
|
81
|
+
{/if}
|
|
82
|
+
{:catch}
|
|
83
|
+
<div class="text-muted-foreground flex flex-col items-center gap-1">
|
|
84
|
+
<ImageIcon class="h-8 w-8" />
|
|
85
|
+
<span class="text-xs">Failed to load</span>
|
|
86
|
+
</div>
|
|
87
|
+
{/await}
|
|
88
|
+
{:else}
|
|
89
|
+
<div class="text-muted-foreground flex flex-col items-center gap-1">
|
|
90
|
+
<ImageIcon class="h-8 w-8" />
|
|
91
|
+
<span class="text-xs">No image</span>
|
|
92
|
+
</div>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- Alt text (per-placement override of the asset's default alt) -->
|
|
97
|
+
<div class="space-y-1.5">
|
|
98
|
+
<label class="text-sm font-medium" for="image-block-alt">Alt text</label>
|
|
99
|
+
<p class="text-muted-foreground text-xs">
|
|
100
|
+
Overrides the image's default alt for this spot. Leave blank to use the default.
|
|
101
|
+
</p>
|
|
102
|
+
<Input
|
|
103
|
+
id="image-block-alt"
|
|
104
|
+
type="text"
|
|
105
|
+
placeholder="Describe this image..."
|
|
106
|
+
value={alt}
|
|
107
|
+
oninput={(e) => onAltChange(e.currentTarget.value)}
|
|
108
|
+
disabled={readonly}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<!-- Actions -->
|
|
113
|
+
{#if !readonly}
|
|
114
|
+
<div class="flex items-center justify-between gap-2 pt-1">
|
|
115
|
+
<Button variant="outline" size="sm" onclick={onReplace}>
|
|
116
|
+
<Upload class="mr-1.5 h-4 w-4" />
|
|
117
|
+
Replace
|
|
118
|
+
</Button>
|
|
119
|
+
<Button
|
|
120
|
+
variant="ghost"
|
|
121
|
+
size="sm"
|
|
122
|
+
class="text-destructive hover:text-destructive"
|
|
123
|
+
onclick={onRemove}
|
|
124
|
+
>
|
|
125
|
+
<Trash2 class="mr-1.5 h-4 w-4" />
|
|
126
|
+
Remove
|
|
127
|
+
</Button>
|
|
128
|
+
</div>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
{/if}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
open: boolean;
|
|
3
|
+
assetRef?: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
readonly?: boolean;
|
|
6
|
+
onAltChange: (alt: string) => void;
|
|
7
|
+
onReplace: () => void;
|
|
8
|
+
onRemove: () => void;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
}
|
|
11
|
+
declare const ImageBlockModal: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type ImageBlockModal = ReturnType<typeof ImageBlockModal>;
|
|
13
|
+
export default ImageBlockModal;
|
|
14
|
+
//# sourceMappingURL=ImageBlockModal.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageBlockModal.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/admin/fields/richtext/ImageBlockModal.svelte.ts"],"names":[],"mappings":"AASC,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAoGF,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
21
21
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
22
|
-
<div class="image-block-view group" class:selected onclick={onEdit}>
|
|
22
|
+
<div class="image-block-view group" class:selected data-pt-image-edit onclick={onEdit}>
|
|
23
23
|
{#if assetPromise}
|
|
24
24
|
{#await assetPromise}
|
|
25
25
|
<div class="image-block-placeholder">
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
</div>
|
|
30
30
|
{:then result}
|
|
31
31
|
{#if result.success && result.data?.url}
|
|
32
|
-
<img src={result.data.url} alt={
|
|
32
|
+
<img src={result.data.url} alt={(data?.alt as string) || ''} class="image-block-img" />
|
|
33
33
|
{:else}
|
|
34
34
|
<div class="image-block-placeholder">
|
|
35
35
|
<ImageIcon class="text-muted-foreground h-8 w-8" />
|