@aphexcms/cms-core 9.0.0 → 9.2.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/dist/cli/generate-types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +197 -8
- package/dist/cli/generate-types.js.map +1 -1
- package/dist/components/AdminApp.svelte +46 -137
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +237 -206
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentVersionPanel.svelte +3 -2
- package/dist/components/admin/DocumentVersionPanel.svelte.d.ts.map +1 -1
- package/dist/components/admin/MediaBrowser.svelte +22 -4
- package/dist/components/admin/MediaBrowser.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +62 -75
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/SchemaField.svelte +2 -2
- package/dist/components/admin/fields/ArrayField.svelte +9 -1
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +37 -8
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte +116 -0
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts +12 -0
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte +111 -0
- package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte.d.ts +12 -0
- package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte +67 -0
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts +12 -0
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/RichtextField.svelte +1314 -0
- package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts +15 -0
- package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/custom-annotation-mark.d.ts +7 -0
- package/dist/components/admin/fields/richtext/custom-annotation-mark.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/custom-annotation-mark.js +33 -0
- package/dist/components/admin/fields/richtext/portable-text-inline-object.d.ts +11 -0
- package/dist/components/admin/fields/richtext/portable-text-inline-object.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/portable-text-inline-object.js +31 -0
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts +11 -0
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/portable-text-object-node.js +31 -0
- package/dist/components/admin/fields/richtext/portable-text-serializer.d.ts +38 -0
- package/dist/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/portable-text-serializer.js +318 -0
- package/dist/components/admin/fields/richtext/svelte-inline-node-view.d.ts +8 -0
- package/dist/components/admin/fields/richtext/svelte-inline-node-view.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/svelte-inline-node-view.js +67 -0
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts +8 -0
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/svelte-node-view.js +63 -0
- package/dist/db/interfaces/document.d.ts +8 -2
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/graphql/resolvers.d.ts.map +1 -1
- package/dist/graphql/resolvers.js +13 -11
- package/dist/graphql/schema.d.ts.map +1 -1
- package/dist/graphql/schema.js +7 -3
- package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.d.ts +7 -0
- package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.js +34 -0
- package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.js.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.d.ts +11 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.js +32 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.js.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts +11 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js +32 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.d.ts +38 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js +319 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js.map +1 -0
- package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.d.ts +8 -0
- package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.js +68 -0
- package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.js.map +1 -0
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts +8 -0
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -0
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js +64 -0
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js.map +1 -0
- package/dist/lib/db/interfaces/document.d.ts +8 -2
- package/dist/lib/db/interfaces/document.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.js +13 -11
- package/dist/lib/graphql/resolvers.js.map +1 -1
- package/dist/lib/graphql/schema.d.ts.map +1 -1
- package/dist/lib/graphql/schema.js +7 -3
- package/dist/lib/graphql/schema.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +7 -0
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +5 -1
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/server/api/routes/assets-by-id.d.ts.map +1 -1
- package/dist/lib/server/api/routes/assets-by-id.js +10 -2
- package/dist/lib/server/api/routes/assets-by-id.js.map +1 -1
- package/dist/lib/server/api/routes/assets-references.d.ts.map +1 -1
- package/dist/lib/server/api/routes/assets-references.js +6 -4
- package/dist/lib/server/api/routes/assets-references.js.map +1 -1
- package/dist/lib/types/schemas.d.ts +27 -0
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/types/schemas.js +3 -1
- package/dist/lib/types/schemas.js.map +1 -1
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +7 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +5 -1
- package/dist/server/api/routes/assets-by-id.d.ts.map +1 -1
- package/dist/server/api/routes/assets-by-id.js +10 -2
- package/dist/server/api/routes/assets-references.d.ts.map +1 -1
- package/dist/server/api/routes/assets-references.js +6 -4
- package/dist/types/schemas.d.ts +27 -0
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +3 -1
- package/package.json +6 -1
|
@@ -53,10 +53,6 @@
|
|
|
53
53
|
const backdropZ = $derived(50 + Math.max(myDepth - 1, 0) * 10);
|
|
54
54
|
const panelZ = $derived(backdropZ + 1);
|
|
55
55
|
|
|
56
|
-
// Handle backdrop click — only close if this is the topmost modal, so a
|
|
57
|
-
// click that visually lands on a parent's backdrop (because the inner
|
|
58
|
-
// backdrop doesn't extend that far) doesn't accidentally close the
|
|
59
|
-
// parent.
|
|
60
56
|
function isTopmost(): boolean {
|
|
61
57
|
return myDepth === modalStack;
|
|
62
58
|
}
|
|
@@ -75,89 +71,80 @@
|
|
|
75
71
|
</script>
|
|
76
72
|
|
|
77
73
|
{#if open}
|
|
78
|
-
<!-- NOTE: scoped to the editor area via `absolute inset-0` (DocumentEditor
|
|
79
|
-
is the nearest positioned ancestor). The trade-off: a nested modal's
|
|
80
|
-
backdrop only covers its parent panel, so clicks in the truly
|
|
81
|
-
"outside" region land on the grandparent backdrop. The modalStack
|
|
82
|
-
check above neutralises that case (only the topmost modal reacts).
|
|
83
|
-
We may later move this to a viewport-level portal so nested
|
|
84
|
-
backdrops cover the full screen, but for now keeping it inside the
|
|
85
|
-
editor is the design we want. -->
|
|
86
74
|
<div
|
|
87
|
-
class="bg-background/80 absolute inset-0"
|
|
75
|
+
class="bg-background/80 absolute inset-0 flex items-center justify-center p-4 lg:p-6"
|
|
88
76
|
style="z-index: {backdropZ}"
|
|
89
77
|
onclick={handleBackdropClick}
|
|
90
78
|
onkeydown={handleKeydown}
|
|
91
79
|
role="button"
|
|
92
80
|
tabindex="-1"
|
|
93
|
-
></div>
|
|
94
|
-
|
|
95
|
-
<!-- Panel: centered dialog. max-h is relative to the editor container
|
|
96
|
-
so tall schemas scroll inside the panel instead of being clipped;
|
|
97
|
-
min-h-0 on the body (below) is what actually lets the inner
|
|
98
|
-
overflow-auto take effect inside this flex column. -->
|
|
99
|
-
<div
|
|
100
|
-
class="border-border bg-background absolute top-1/2 left-1/2 flex max-h-[calc(100%-4rem)] w-[calc(100%-2rem)] max-w-3xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border shadow-lg"
|
|
101
|
-
style="z-index: {panelZ}"
|
|
102
81
|
>
|
|
82
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
103
83
|
<div
|
|
104
|
-
class="border-border bg-background
|
|
84
|
+
class="border-border bg-background max-h-full w-full max-w-3xl overflow-auto rounded-lg border shadow-lg"
|
|
85
|
+
style="z-index: {panelZ}"
|
|
86
|
+
onclick={(e) => e.stopPropagation()}
|
|
87
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
105
88
|
>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<span
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<span
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<span
|
|
128
|
-
|
|
129
|
-
|
|
89
|
+
<div
|
|
90
|
+
class="border-border bg-background sticky top-0 z-10 flex items-center justify-between gap-3 border-b px-4 py-2"
|
|
91
|
+
>
|
|
92
|
+
<span class="text-sm font-medium">Edit {schema.title}</span>
|
|
93
|
+
<div class="flex items-center gap-2">
|
|
94
|
+
{#if saveState}
|
|
95
|
+
{#if saveState.saving}
|
|
96
|
+
<span
|
|
97
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
98
|
+
>
|
|
99
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
|
|
100
|
+
Saving
|
|
101
|
+
</span>
|
|
102
|
+
{:else if saveState.hasUnsavedChanges}
|
|
103
|
+
<span
|
|
104
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
105
|
+
>
|
|
106
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
107
|
+
Unsaved
|
|
108
|
+
</span>
|
|
109
|
+
{:else if saveState.savedAgoText}
|
|
110
|
+
<span
|
|
111
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
112
|
+
>
|
|
113
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
114
|
+
Auto-saved
|
|
115
|
+
</span>
|
|
116
|
+
{/if}
|
|
130
117
|
{/if}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</
|
|
141
|
-
</
|
|
118
|
+
<Button variant="ghost" size="icon" class="h-7 w-7" onclick={onClose}>
|
|
119
|
+
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
120
|
+
<path
|
|
121
|
+
stroke-linecap="round"
|
|
122
|
+
stroke-linejoin="round"
|
|
123
|
+
stroke-width="2"
|
|
124
|
+
d="M6 18L18 6M6 6l12 12"
|
|
125
|
+
/>
|
|
126
|
+
</svg>
|
|
127
|
+
</Button>
|
|
128
|
+
</div>
|
|
142
129
|
</div>
|
|
143
|
-
</div>
|
|
144
130
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
131
|
+
<div class="bg-background space-y-3 p-4">
|
|
132
|
+
{#if schema.fields}
|
|
133
|
+
{#each schema.fields as field, index (index)}
|
|
134
|
+
<SchemaField
|
|
135
|
+
{field}
|
|
136
|
+
value={(value ?? {})[field.name]}
|
|
137
|
+
documentData={value ?? {}}
|
|
138
|
+
onUpdate={(newValue) => {
|
|
139
|
+
onUpdate({ ...value, [field.name]: newValue });
|
|
140
|
+
}}
|
|
141
|
+
{onOpenReference}
|
|
142
|
+
{readonly}
|
|
143
|
+
{organizationId}
|
|
144
|
+
/>
|
|
145
|
+
{/each}
|
|
146
|
+
{/if}
|
|
147
|
+
</div>
|
|
161
148
|
</div>
|
|
162
149
|
</div>
|
|
163
150
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKxD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKxD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAwGF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
}: Props = $props();
|
|
62
62
|
|
|
63
63
|
// Build full field path
|
|
64
|
-
const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
64
|
+
const fieldPath = $derived(parentPath ? `${parentPath}.${field.name}` : field.name);
|
|
65
65
|
|
|
66
66
|
// Validation state for the wrapper (displays errors and status)
|
|
67
67
|
let validationErrors = $state<ValidationError[]>([]);
|
|
@@ -239,7 +239,7 @@
|
|
|
239
239
|
{/each}
|
|
240
240
|
</div>
|
|
241
241
|
|
|
242
|
-
<!-- Array Field -->
|
|
242
|
+
<!-- Array Field (includes block content editor when of contains {type: 'block'}) -->
|
|
243
243
|
{:else if field.type === 'array' && field.of}
|
|
244
244
|
<ArrayField {field} {value} {onUpdate} {onOpenReference} {readonly} {organizationId} />
|
|
245
245
|
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
import { documents } from '../../../api/documents';
|
|
34
34
|
import { SvelteMap } from 'svelte/reactivity';
|
|
35
35
|
import { getDocumentVersion } from '../../../document-refresh.svelte';
|
|
36
|
+
import RichtextField from './richtext/RichtextField.svelte';
|
|
36
37
|
|
|
37
38
|
interface Props {
|
|
38
39
|
field: ArrayFieldType;
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
return getSchemaByName(schemas, typeName);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
const isBlockArray = $derived(field.of.some((ref) => ref.type === 'block'));
|
|
76
78
|
const isGridLayout = $derived(field.options?.layout === 'grid');
|
|
77
79
|
const isReferenceArray = $derived(
|
|
78
80
|
field.of && field.of.length > 0 && field.of[0]?.type === 'reference'
|
|
@@ -494,7 +496,9 @@
|
|
|
494
496
|
}
|
|
495
497
|
</script>
|
|
496
498
|
|
|
497
|
-
{#if
|
|
499
|
+
{#if isBlockArray}
|
|
500
|
+
<RichtextField {field} {value} {onUpdate} {readonly} {onOpenReference} {organizationId} />
|
|
501
|
+
{:else if isReferenceArray && referenceFieldShape}
|
|
498
502
|
<!-- Reference array — each row reuses ReferenceField. Drag handle on the
|
|
499
503
|
left, ReferenceField fills the row, an X button on empty rows lets the
|
|
500
504
|
user discard a never-picked row (populated rows use ReferenceField's
|
|
@@ -599,6 +603,8 @@
|
|
|
599
603
|
class="border-border/50 bg-background group relative aspect-square overflow-hidden rounded-md border"
|
|
600
604
|
class:opacity-50={sortable.isDragging}
|
|
601
605
|
>
|
|
606
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
607
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
602
608
|
<div
|
|
603
609
|
class="h-full w-full cursor-pointer"
|
|
604
610
|
onclick={() => handleOpenImageModal(index)}
|
|
@@ -695,6 +701,8 @@
|
|
|
695
701
|
</button>
|
|
696
702
|
{/if}
|
|
697
703
|
|
|
704
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
705
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
698
706
|
<div
|
|
699
707
|
class="min-w-0 flex-1 cursor-pointer text-left"
|
|
700
708
|
onclick={() => handleOpenImageModal(index)}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AA+BtF,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;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA65BF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -42,14 +42,16 @@
|
|
|
42
42
|
const refId = $derived(value?._ref ?? null);
|
|
43
43
|
|
|
44
44
|
// Cast to reference field type
|
|
45
|
-
const referenceField = field as ReferenceFieldType;
|
|
46
|
-
const targetType = referenceField.to?.[0]?.type;
|
|
45
|
+
const referenceField = $derived(field as ReferenceFieldType);
|
|
46
|
+
const targetType = $derived(referenceField.to?.[0]?.type);
|
|
47
47
|
|
|
48
48
|
const schemas = getSchemaContext();
|
|
49
49
|
const targetSchema = $derived(targetType ? getSchemaByName(schemas, targetType) : null);
|
|
50
50
|
const TargetIcon = $derived(targetSchema?.icon ?? null);
|
|
51
51
|
|
|
52
52
|
// State
|
|
53
|
+
let inputWrapperEl = $state<HTMLElement>();
|
|
54
|
+
let dropdownPos = $state({ top: 0, left: 0, width: 0 });
|
|
53
55
|
let open = $state(false);
|
|
54
56
|
let searchResults = $state<any[]>([]);
|
|
55
57
|
let selectedDocument = $state<any>(null);
|
|
@@ -145,6 +147,18 @@
|
|
|
145
147
|
});
|
|
146
148
|
});
|
|
147
149
|
|
|
150
|
+
function updateDropdownPos() {
|
|
151
|
+
if (!inputWrapperEl) return;
|
|
152
|
+
const rect = inputWrapperEl.getBoundingClientRect();
|
|
153
|
+
dropdownPos = { top: rect.bottom + 4, left: rect.left, width: rect.width };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
$effect(() => {
|
|
157
|
+
if (open && inputWrapperEl) {
|
|
158
|
+
updateDropdownPos();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
148
162
|
function closeAndFocusTrigger() {
|
|
149
163
|
open = false;
|
|
150
164
|
}
|
|
@@ -291,22 +305,33 @@
|
|
|
291
305
|
{:else}
|
|
292
306
|
<!-- Inline empty state: search input + chevron + Create button. The input
|
|
293
307
|
is the primary focus target — the results dropdown is a sibling div
|
|
294
|
-
positioned
|
|
295
|
-
closing it. No popover wrapping the input, so no
|
|
308
|
+
positioned with fixed so it escapes overflow containers (e.g. modals),
|
|
309
|
+
with click-outside closing it. No popover wrapping the input, so no
|
|
310
|
+
double-click. -->
|
|
296
311
|
<div class="flex items-center gap-1">
|
|
297
|
-
<div
|
|
312
|
+
<div
|
|
313
|
+
bind:this={inputWrapperEl}
|
|
314
|
+
class="relative min-w-0 flex-1"
|
|
315
|
+
use:clickOutside={() => (open = false)}
|
|
316
|
+
>
|
|
298
317
|
<div
|
|
299
318
|
class="border-input bg-background focus-within:ring-ring flex h-9 items-center rounded-md border focus-within:ring-1"
|
|
300
319
|
>
|
|
301
320
|
<Input
|
|
302
321
|
bind:value={query}
|
|
303
322
|
placeholder="Type to search..."
|
|
304
|
-
onfocus={() =>
|
|
323
|
+
onfocus={() => {
|
|
324
|
+
updateDropdownPos();
|
|
325
|
+
open = true;
|
|
326
|
+
}}
|
|
305
327
|
class="h-8 border-0 bg-transparent shadow-none focus-visible:ring-0"
|
|
306
328
|
/>
|
|
307
329
|
<button
|
|
308
330
|
type="button"
|
|
309
|
-
onclick={() =>
|
|
331
|
+
onclick={() => {
|
|
332
|
+
updateDropdownPos();
|
|
333
|
+
open = !open;
|
|
334
|
+
}}
|
|
310
335
|
class="text-muted-foreground hover:text-foreground flex h-8 w-8 shrink-0 items-center justify-center"
|
|
311
336
|
aria-label="Toggle results"
|
|
312
337
|
>
|
|
@@ -315,7 +340,11 @@
|
|
|
315
340
|
</div>
|
|
316
341
|
{#if open}
|
|
317
342
|
<div
|
|
318
|
-
class="bg-popover text-popover-foreground
|
|
343
|
+
class="bg-popover text-popover-foreground fixed z-[9999] rounded-md border p-0 shadow-md"
|
|
344
|
+
style="top: {dropdownPos.top}px; left: {dropdownPos.left}px; width: {Math.min(
|
|
345
|
+
400,
|
|
346
|
+
dropdownPos.width
|
|
347
|
+
)}px;"
|
|
319
348
|
>
|
|
320
349
|
<Command.Root shouldFilter={false}>
|
|
321
350
|
<Command.List class="max-h-[300px] overflow-y-auto">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAUzF,KAAK,cAAc,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAUzF,KAAK,cAAc,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AA4VF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Trash2, Image as ImageIcon } from '@lucide/svelte';
|
|
3
|
+
import { assets } from '../../../../api/assets';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
type: string;
|
|
7
|
+
nodeKey: string;
|
|
8
|
+
data: Record<string, unknown>;
|
|
9
|
+
selected?: boolean;
|
|
10
|
+
onEdit: () => void;
|
|
11
|
+
onDelete: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { data, selected = false, onEdit, onDelete }: Props = $props();
|
|
15
|
+
|
|
16
|
+
const assetRef = $derived((data?.asset as any)?._ref as string | undefined);
|
|
17
|
+
const assetPromise = $derived(assetRef ? assets.getById(assetRef) : null);
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
21
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
22
|
+
<div class="image-block-view group" class:selected onclick={onEdit}>
|
|
23
|
+
{#if assetPromise}
|
|
24
|
+
{#await assetPromise}
|
|
25
|
+
<div class="image-block-placeholder">
|
|
26
|
+
<div
|
|
27
|
+
class="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent"
|
|
28
|
+
></div>
|
|
29
|
+
</div>
|
|
30
|
+
{:then result}
|
|
31
|
+
{#if result.success && result.data?.url}
|
|
32
|
+
<img src={result.data.url} alt={result.data.alt || ''} class="image-block-img" />
|
|
33
|
+
{:else}
|
|
34
|
+
<div class="image-block-placeholder">
|
|
35
|
+
<ImageIcon class="text-muted-foreground h-8 w-8" />
|
|
36
|
+
<span class="text-muted-foreground text-xs">Image not found</span>
|
|
37
|
+
</div>
|
|
38
|
+
{/if}
|
|
39
|
+
{:catch}
|
|
40
|
+
<div class="image-block-placeholder">
|
|
41
|
+
<ImageIcon class="text-muted-foreground h-8 w-8" />
|
|
42
|
+
<span class="text-muted-foreground text-xs">Failed to load</span>
|
|
43
|
+
</div>
|
|
44
|
+
{/await}
|
|
45
|
+
{:else}
|
|
46
|
+
<div class="image-block-placeholder">
|
|
47
|
+
<ImageIcon class="text-muted-foreground h-8 w-8" />
|
|
48
|
+
<span class="text-muted-foreground text-xs">No image</span>
|
|
49
|
+
</div>
|
|
50
|
+
{/if}
|
|
51
|
+
|
|
52
|
+
<div class="image-block-actions">
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
class="text-muted-foreground hover:text-destructive rounded p-1 transition-colors"
|
|
56
|
+
onclick={(e) => {
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
onDelete();
|
|
59
|
+
}}
|
|
60
|
+
title="Remove"
|
|
61
|
+
>
|
|
62
|
+
<Trash2 class="h-3.5 w-3.5" />
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<style>
|
|
68
|
+
.image-block-view {
|
|
69
|
+
position: relative;
|
|
70
|
+
width: 100%;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
border: 1px solid var(--border);
|
|
75
|
+
border-radius: var(--radius);
|
|
76
|
+
background: var(--muted);
|
|
77
|
+
margin: 0.5em 0;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
transition: border-color 0.15s;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.image-block-view.selected {
|
|
84
|
+
border-color: var(--primary);
|
|
85
|
+
box-shadow: 0 0 0 1px var(--primary);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.image-block-img {
|
|
89
|
+
display: block;
|
|
90
|
+
max-width: 100%;
|
|
91
|
+
max-height: 400px;
|
|
92
|
+
object-fit: contain;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.image-block-placeholder {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
gap: 0.25rem;
|
|
101
|
+
width: 100%;
|
|
102
|
+
height: 120px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.image-block-actions {
|
|
106
|
+
position: absolute;
|
|
107
|
+
top: 0.5rem;
|
|
108
|
+
right: 0.5rem;
|
|
109
|
+
opacity: 0;
|
|
110
|
+
transition: opacity 0.15s;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.image-block-view:hover .image-block-actions {
|
|
114
|
+
opacity: 1;
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
type: string;
|
|
3
|
+
nodeKey: string;
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
selected?: boolean;
|
|
6
|
+
onEdit: () => void;
|
|
7
|
+
onDelete: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const PortableTextImageView: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type PortableTextImageView = ReturnType<typeof PortableTextImageView>;
|
|
11
|
+
export default PortableTextImageView;
|
|
12
|
+
//# sourceMappingURL=PortableTextImageView.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableTextImageView.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/admin/fields/richtext/PortableTextImageView.svelte.ts"],"names":[],"mappings":"AAOC,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAwDF,QAAA,MAAM,qBAAqB,2CAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
type: string;
|
|
4
|
+
nodeKey: string;
|
|
5
|
+
data: Record<string, unknown>;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
onEdit: () => void;
|
|
8
|
+
onDelete: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { type, data, selected, onEdit, onDelete }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const label = $derived(
|
|
14
|
+
(data?.title as string) || (data?.text as string) || (data?.name as string) || type
|
|
15
|
+
);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
19
|
+
<span
|
|
20
|
+
class="inline-object-chip"
|
|
21
|
+
class:selected
|
|
22
|
+
onclick={onEdit}
|
|
23
|
+
title="Edit {type}"
|
|
24
|
+
role="button"
|
|
25
|
+
tabindex="0"
|
|
26
|
+
onkeydown={(e) => {
|
|
27
|
+
if (e.key === 'Enter') onEdit();
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
<span class="inline-object-type">{type.slice(0, 2).toUpperCase()}</span>
|
|
31
|
+
<span class="inline-object-label">{label}</span>
|
|
32
|
+
<button
|
|
33
|
+
class="inline-object-delete"
|
|
34
|
+
onclick={(e) => {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
onDelete();
|
|
37
|
+
}}
|
|
38
|
+
title="Remove">×</button
|
|
39
|
+
>
|
|
40
|
+
</span>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.inline-object-chip {
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: 2px;
|
|
47
|
+
padding: 1px 6px 1px 2px;
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
border: 1px solid var(--border);
|
|
50
|
+
background: var(--muted);
|
|
51
|
+
font-size: 0.75rem;
|
|
52
|
+
line-height: 1.4;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
vertical-align: baseline;
|
|
55
|
+
transition: border-color 0.15s;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.inline-object-chip:hover,
|
|
59
|
+
.inline-object-chip.selected {
|
|
60
|
+
border-color: var(--primary);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.inline-object-type {
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
width: 16px;
|
|
68
|
+
height: 16px;
|
|
69
|
+
border-radius: 2px;
|
|
70
|
+
background: var(--muted-foreground);
|
|
71
|
+
color: var(--background);
|
|
72
|
+
font-size: 8px;
|
|
73
|
+
font-weight: 600;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.inline-object-label {
|
|
78
|
+
max-width: 120px;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
text-overflow: ellipsis;
|
|
81
|
+
white-space: nowrap;
|
|
82
|
+
color: var(--foreground);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.inline-object-delete {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
width: 14px;
|
|
90
|
+
height: 14px;
|
|
91
|
+
border: none;
|
|
92
|
+
background: none;
|
|
93
|
+
color: var(--muted-foreground);
|
|
94
|
+
font-size: 12px;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
padding: 0;
|
|
97
|
+
border-radius: 2px;
|
|
98
|
+
opacity: 0;
|
|
99
|
+
transition:
|
|
100
|
+
opacity 0.15s,
|
|
101
|
+
color 0.15s;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.inline-object-chip:hover .inline-object-delete {
|
|
105
|
+
opacity: 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.inline-object-delete:hover {
|
|
109
|
+
color: var(--destructive);
|
|
110
|
+
}
|
|
111
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
type: string;
|
|
3
|
+
nodeKey: string;
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
selected: boolean;
|
|
6
|
+
onEdit: () => void;
|
|
7
|
+
onDelete: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const PortableTextInlineView: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type PortableTextInlineView = ReturnType<typeof PortableTextInlineView>;
|
|
11
|
+
export default PortableTextInlineView;
|
|
12
|
+
//# sourceMappingURL=PortableTextInlineView.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableTextInlineView.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/admin/fields/richtext/PortableTextInlineView.svelte.ts"],"names":[],"mappings":"AAGC,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AA2BF,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Pencil, Trash2 } from '@lucide/svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
type: string;
|
|
6
|
+
nodeKey: string;
|
|
7
|
+
data: Record<string, unknown>;
|
|
8
|
+
selected?: boolean;
|
|
9
|
+
onEdit: () => void;
|
|
10
|
+
onDelete: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { type, data, selected = false, onEdit, onDelete }: Props = $props();
|
|
14
|
+
|
|
15
|
+
const title = $derived((data.title as string) || (data.name as string) || type);
|
|
16
|
+
|
|
17
|
+
const subtitle = $derived.by(() => {
|
|
18
|
+
const preview: string[] = [];
|
|
19
|
+
for (const [key, val] of Object.entries(data)) {
|
|
20
|
+
if (key.startsWith('_') || key === 'title' || key === 'name') continue;
|
|
21
|
+
if (typeof val === 'string' && val.length > 0) {
|
|
22
|
+
preview.push(val.length > 40 ? val.slice(0, 40) + '...' : val);
|
|
23
|
+
if (preview.length >= 2) break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return preview.join(' - ');
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div
|
|
31
|
+
class="border-rule bg-muted/20 hover:bg-muted/40 group my-2 flex items-center gap-2 rounded-md border px-3 py-2 transition-colors"
|
|
32
|
+
class:ring-primary={selected}
|
|
33
|
+
class:ring-2={selected}
|
|
34
|
+
>
|
|
35
|
+
<button type="button" class="flex min-w-0 flex-1 items-center gap-3 text-left" onclick={onEdit}>
|
|
36
|
+
<div
|
|
37
|
+
class="bg-muted text-muted-foreground flex h-8 w-8 shrink-0 items-center justify-center rounded text-xs font-medium uppercase"
|
|
38
|
+
>
|
|
39
|
+
{type.slice(0, 2)}
|
|
40
|
+
</div>
|
|
41
|
+
<div class="min-w-0 flex-1">
|
|
42
|
+
<div class="text-foreground text-sm font-medium capitalize">{title}</div>
|
|
43
|
+
{#if subtitle}
|
|
44
|
+
<div class="text-muted-foreground truncate text-xs">{subtitle}</div>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
class="text-muted-foreground hover:text-foreground rounded p-1 transition-colors"
|
|
53
|
+
onclick={onEdit}
|
|
54
|
+
title="Edit"
|
|
55
|
+
>
|
|
56
|
+
<Pencil class="h-3.5 w-3.5" />
|
|
57
|
+
</button>
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
class="text-muted-foreground hover:text-destructive rounded p-1 transition-colors"
|
|
61
|
+
onclick={onDelete}
|
|
62
|
+
title="Remove"
|
|
63
|
+
>
|
|
64
|
+
<Trash2 class="h-3.5 w-3.5" />
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|