@foliokit/cms-admin-ui 0.0.0 → 0.1.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/esm2022/index.js +14 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/cms-admin-ui/cms-admin-ui.js +3 -3
- package/esm2022/lib/page-editor/about-editor-form.component.js +84 -0
- package/esm2022/lib/page-editor/about-editor-form.component.js.map +1 -0
- package/esm2022/lib/page-editor/links-editor-form.component.js +474 -0
- package/esm2022/lib/page-editor/links-editor-form.component.js.map +1 -0
- package/esm2022/lib/page-editor/page-editor-hero-image.component.js +216 -0
- package/esm2022/lib/page-editor/page-editor-hero-image.component.js.map +1 -0
- package/esm2022/lib/page-editor/page-editor.store.js +198 -0
- package/esm2022/lib/page-editor/page-editor.store.js.map +1 -0
- package/esm2022/lib/post-editor/post-editor-cover-image.component.js +251 -0
- package/esm2022/lib/post-editor/post-editor-cover-image.component.js.map +1 -0
- package/esm2022/lib/post-editor/post-editor-embedded-media-item.component.js +99 -0
- package/esm2022/lib/post-editor/post-editor-embedded-media-item.component.js.map +1 -0
- package/esm2022/lib/post-editor/post-editor-embedded-media.component.js +173 -0
- package/esm2022/lib/post-editor/post-editor-embedded-media.component.js.map +1 -0
- package/esm2022/lib/post-editor/post-editor-media-tab.component.js +23 -0
- package/esm2022/lib/post-editor/post-editor-media-tab.component.js.map +1 -0
- package/esm2022/lib/post-editor/post-editor.store.js +189 -0
- package/esm2022/lib/post-editor/post-editor.store.js.map +1 -0
- package/esm2022/lib/posts-list/posts-board.component.js +66 -0
- package/esm2022/lib/posts-list/posts-board.component.js.map +1 -0
- package/esm2022/lib/posts-list/posts-draft-column.component.js +71 -0
- package/esm2022/lib/posts-list/posts-draft-column.component.js.map +1 -0
- package/esm2022/lib/posts-list/posts-list.component.js +79 -0
- package/esm2022/lib/posts-list/posts-list.component.js.map +1 -0
- package/esm2022/lib/posts-list/posts-list.store.js +43 -0
- package/esm2022/lib/posts-list/posts-list.store.js.map +1 -0
- package/esm2022/lib/posts-list/posts-published-column.component.js +129 -0
- package/esm2022/lib/posts-list/posts-published-column.component.js.map +1 -0
- package/esm2022/lib/posts-list/posts-queue-column.component.js +112 -0
- package/esm2022/lib/posts-list/posts-queue-column.component.js.map +1 -0
- package/index.d.ts +14 -0
- package/lib/page-editor/about-editor-form.component.d.ts +32 -0
- package/lib/page-editor/links-editor-form.component.d.ts +52 -0
- package/lib/page-editor/page-editor-hero-image.component.d.ts +51 -0
- package/lib/page-editor/page-editor.store.d.ts +37 -0
- package/lib/post-editor/post-editor-cover-image.component.d.ts +50 -0
- package/lib/post-editor/post-editor-embedded-media-item.component.d.ts +37 -0
- package/lib/post-editor/post-editor-embedded-media.component.d.ts +43 -0
- package/lib/post-editor/post-editor-media-tab.component.d.ts +5 -0
- package/lib/post-editor/post-editor.store.d.ts +37 -0
- package/lib/posts-list/posts-board.component.d.ts +24 -0
- package/lib/posts-list/posts-draft-column.component.d.ts +8 -0
- package/lib/posts-list/posts-list.component.d.ts +28 -0
- package/lib/posts-list/posts-list.store.d.ts +24 -0
- package/lib/posts-list/posts-published-column.component.d.ts +10 -0
- package/lib/posts-list/posts-queue-column.component.d.ts +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, PLATFORM_ID, ViewChild, inject, signal, } from '@angular/core';
|
|
2
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
3
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
4
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
5
|
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
6
|
+
import { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';
|
|
7
|
+
import { FIREBASE_STORAGE, PostService } from '@foliokit/cms-core';
|
|
8
|
+
import { PostEditorStore } from './post-editor.store';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
import * as i1 from "@angular/material/button";
|
|
11
|
+
import * as i2 from "@angular/material/icon";
|
|
12
|
+
import * as i3 from "@angular/material/progress-bar";
|
|
13
|
+
export class PostEditorCoverImageComponent {
|
|
14
|
+
fileInput;
|
|
15
|
+
store = inject(PostEditorStore);
|
|
16
|
+
storage = inject(FIREBASE_STORAGE);
|
|
17
|
+
postService = inject(PostService);
|
|
18
|
+
platformId = inject(PLATFORM_ID);
|
|
19
|
+
isBrowser = isPlatformBrowser(this.platformId);
|
|
20
|
+
uploading = signal(false, ...(ngDevMode ? [{ debugName: "uploading" }] : /* istanbul ignore next */ []));
|
|
21
|
+
uploadProgress = signal(0, ...(ngDevMode ? [{ debugName: "uploadProgress" }] : /* istanbul ignore next */ []));
|
|
22
|
+
uploadError = signal(null, ...(ngDevMode ? [{ debugName: "uploadError" }] : /* istanbul ignore next */ []));
|
|
23
|
+
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : /* istanbul ignore next */ []));
|
|
24
|
+
storagePath = signal(null, ...(ngDevMode ? [{ debugName: "storagePath" }] : /* istanbul ignore next */ []));
|
|
25
|
+
onDragOver(event) {
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
this.isDragOver.set(true);
|
|
28
|
+
}
|
|
29
|
+
onDragLeave() {
|
|
30
|
+
this.isDragOver.set(false);
|
|
31
|
+
}
|
|
32
|
+
onDrop(event) {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
this.isDragOver.set(false);
|
|
35
|
+
const files = event.dataTransfer?.files;
|
|
36
|
+
if (files?.length) {
|
|
37
|
+
this.upload(files[0]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
onFileSelected(files) {
|
|
41
|
+
if (!files?.length)
|
|
42
|
+
return;
|
|
43
|
+
this.upload(files[0]);
|
|
44
|
+
// Reset so the same file can be re-selected after a delete
|
|
45
|
+
if (this.fileInput?.nativeElement) {
|
|
46
|
+
this.fileInput.nativeElement.value = '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
onDeleteCover() {
|
|
50
|
+
if (!window.confirm('Remove cover image?'))
|
|
51
|
+
return;
|
|
52
|
+
const path = this.storagePath();
|
|
53
|
+
if (path) {
|
|
54
|
+
this.postService.deleteStorageFile(path).subscribe({
|
|
55
|
+
next: () => this.clearCover(),
|
|
56
|
+
error: () => this.clearCover(),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.clearCover();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
clearCover() {
|
|
64
|
+
this.store.updateField('thumbnailUrl', '');
|
|
65
|
+
this.store.updateField('thumbnailAlt', '');
|
|
66
|
+
this.storagePath.set(null);
|
|
67
|
+
this.uploadProgress.set(0);
|
|
68
|
+
}
|
|
69
|
+
upload(file) {
|
|
70
|
+
const previousPath = this.storagePath();
|
|
71
|
+
const postId = this.store.post()?.id || this.store.tempPostId();
|
|
72
|
+
const storagePath = `posts/${postId}/cover/${file.name}`;
|
|
73
|
+
if (previousPath) {
|
|
74
|
+
this.postService.deleteStorageFile(previousPath).subscribe();
|
|
75
|
+
}
|
|
76
|
+
const fileRef = ref(this.storage, storagePath);
|
|
77
|
+
this.uploading.set(true);
|
|
78
|
+
this.uploadProgress.set(0);
|
|
79
|
+
this.uploadError.set(null);
|
|
80
|
+
const task = uploadBytesResumable(fileRef, file);
|
|
81
|
+
task.on('state_changed', (snapshot) => {
|
|
82
|
+
this.uploadProgress.set(Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100));
|
|
83
|
+
}, (error) => {
|
|
84
|
+
this.uploading.set(false);
|
|
85
|
+
this.uploadError.set(error.message);
|
|
86
|
+
}, () => {
|
|
87
|
+
getDownloadURL(task.snapshot.ref).then((downloadUrl) => {
|
|
88
|
+
this.store.updateField('thumbnailUrl', downloadUrl);
|
|
89
|
+
this.store.updateField('thumbnailAlt', file.name);
|
|
90
|
+
this.storagePath.set(storagePath);
|
|
91
|
+
this.uploading.set(false);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorCoverImageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
96
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PostEditorCoverImageComponent, isStandalone: true, selector: "folio-post-editor-cover-image", viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: `
|
|
97
|
+
<div class="flex flex-col gap-2">
|
|
98
|
+
<span class="text-sm font-semibold">Cover Image</span>
|
|
99
|
+
|
|
100
|
+
@if (store.post()?.thumbnailUrl; as url) {
|
|
101
|
+
<!-- Filled state -->
|
|
102
|
+
<div class="image-wrapper rounded-lg overflow-hidden">
|
|
103
|
+
<div class="aspect-video w-full relative">
|
|
104
|
+
<img
|
|
105
|
+
[src]="url"
|
|
106
|
+
[alt]="store.post()?.thumbnailAlt ?? ''"
|
|
107
|
+
class="w-full h-full object-cover"
|
|
108
|
+
/>
|
|
109
|
+
<!-- Hover overlay -->
|
|
110
|
+
<div
|
|
111
|
+
class="hover-overlay absolute inset-0 flex items-center justify-center gap-3"
|
|
112
|
+
style="background: rgba(0,0,0,0.45)"
|
|
113
|
+
>
|
|
114
|
+
<button
|
|
115
|
+
mat-icon-button
|
|
116
|
+
style="color: white"
|
|
117
|
+
title="Replace image"
|
|
118
|
+
(click)="isBrowser && fileInput.click()"
|
|
119
|
+
>
|
|
120
|
+
<mat-icon>swap_horiz</mat-icon>
|
|
121
|
+
</button>
|
|
122
|
+
<button
|
|
123
|
+
mat-icon-button
|
|
124
|
+
style="color: white"
|
|
125
|
+
title="Delete image"
|
|
126
|
+
(click)="onDeleteCover()"
|
|
127
|
+
>
|
|
128
|
+
<mat-icon>delete</mat-icon>
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
} @else {
|
|
134
|
+
<!-- Empty state -->
|
|
135
|
+
<div
|
|
136
|
+
class="drop-zone w-full flex flex-col items-center justify-center gap-2 py-10 cursor-pointer select-none"
|
|
137
|
+
[class.drag-over]="isDragOver()"
|
|
138
|
+
(click)="isBrowser && fileInput.click()"
|
|
139
|
+
(dragover)="isBrowser && onDragOver($event)"
|
|
140
|
+
(dragleave)="isBrowser && onDragLeave()"
|
|
141
|
+
(drop)="isBrowser && onDrop($event)"
|
|
142
|
+
>
|
|
143
|
+
<mat-icon class="opacity-40" style="font-size: 2.5rem; width: 2.5rem; height: 2.5rem">
|
|
144
|
+
upload_file
|
|
145
|
+
</mat-icon>
|
|
146
|
+
<span class="text-sm opacity-50">Upload cover image</span>
|
|
147
|
+
</div>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
<!-- Hidden file input -->
|
|
151
|
+
<input
|
|
152
|
+
#fileInput
|
|
153
|
+
type="file"
|
|
154
|
+
accept="image/*"
|
|
155
|
+
class="hidden"
|
|
156
|
+
(change)="onFileSelected($any($event.target).files)"
|
|
157
|
+
/>
|
|
158
|
+
|
|
159
|
+
<!-- Progress bar -->
|
|
160
|
+
@if (uploading()) {
|
|
161
|
+
<mat-progress-bar mode="determinate" [value]="uploadProgress()" />
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
<!-- Error message -->
|
|
165
|
+
@if (uploadError()) {
|
|
166
|
+
<p class="text-sm text-red-500">{{ uploadError() }}</p>
|
|
167
|
+
}
|
|
168
|
+
</div>
|
|
169
|
+
`, isInline: true, styles: [":host{display:block}.drop-zone{border:2px dashed color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;transition:border-color .15s,background .15s}.drop-zone.drag-over{border-color:var(--mat-sys-primary);background:color-mix(in srgb,var(--mat-sys-primary) 8%,transparent)}.image-wrapper{position:relative}.image-wrapper:hover .hover-overlay{opacity:1}.hover-overlay{opacity:0;transition:opacity .15s}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i3.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
170
|
+
}
|
|
171
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorCoverImageComponent, decorators: [{
|
|
172
|
+
type: Component,
|
|
173
|
+
args: [{ selector: 'folio-post-editor-cover-image', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatButtonModule, MatIconModule, MatProgressBarModule], template: `
|
|
174
|
+
<div class="flex flex-col gap-2">
|
|
175
|
+
<span class="text-sm font-semibold">Cover Image</span>
|
|
176
|
+
|
|
177
|
+
@if (store.post()?.thumbnailUrl; as url) {
|
|
178
|
+
<!-- Filled state -->
|
|
179
|
+
<div class="image-wrapper rounded-lg overflow-hidden">
|
|
180
|
+
<div class="aspect-video w-full relative">
|
|
181
|
+
<img
|
|
182
|
+
[src]="url"
|
|
183
|
+
[alt]="store.post()?.thumbnailAlt ?? ''"
|
|
184
|
+
class="w-full h-full object-cover"
|
|
185
|
+
/>
|
|
186
|
+
<!-- Hover overlay -->
|
|
187
|
+
<div
|
|
188
|
+
class="hover-overlay absolute inset-0 flex items-center justify-center gap-3"
|
|
189
|
+
style="background: rgba(0,0,0,0.45)"
|
|
190
|
+
>
|
|
191
|
+
<button
|
|
192
|
+
mat-icon-button
|
|
193
|
+
style="color: white"
|
|
194
|
+
title="Replace image"
|
|
195
|
+
(click)="isBrowser && fileInput.click()"
|
|
196
|
+
>
|
|
197
|
+
<mat-icon>swap_horiz</mat-icon>
|
|
198
|
+
</button>
|
|
199
|
+
<button
|
|
200
|
+
mat-icon-button
|
|
201
|
+
style="color: white"
|
|
202
|
+
title="Delete image"
|
|
203
|
+
(click)="onDeleteCover()"
|
|
204
|
+
>
|
|
205
|
+
<mat-icon>delete</mat-icon>
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
} @else {
|
|
211
|
+
<!-- Empty state -->
|
|
212
|
+
<div
|
|
213
|
+
class="drop-zone w-full flex flex-col items-center justify-center gap-2 py-10 cursor-pointer select-none"
|
|
214
|
+
[class.drag-over]="isDragOver()"
|
|
215
|
+
(click)="isBrowser && fileInput.click()"
|
|
216
|
+
(dragover)="isBrowser && onDragOver($event)"
|
|
217
|
+
(dragleave)="isBrowser && onDragLeave()"
|
|
218
|
+
(drop)="isBrowser && onDrop($event)"
|
|
219
|
+
>
|
|
220
|
+
<mat-icon class="opacity-40" style="font-size: 2.5rem; width: 2.5rem; height: 2.5rem">
|
|
221
|
+
upload_file
|
|
222
|
+
</mat-icon>
|
|
223
|
+
<span class="text-sm opacity-50">Upload cover image</span>
|
|
224
|
+
</div>
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
<!-- Hidden file input -->
|
|
228
|
+
<input
|
|
229
|
+
#fileInput
|
|
230
|
+
type="file"
|
|
231
|
+
accept="image/*"
|
|
232
|
+
class="hidden"
|
|
233
|
+
(change)="onFileSelected($any($event.target).files)"
|
|
234
|
+
/>
|
|
235
|
+
|
|
236
|
+
<!-- Progress bar -->
|
|
237
|
+
@if (uploading()) {
|
|
238
|
+
<mat-progress-bar mode="determinate" [value]="uploadProgress()" />
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
<!-- Error message -->
|
|
242
|
+
@if (uploadError()) {
|
|
243
|
+
<p class="text-sm text-red-500">{{ uploadError() }}</p>
|
|
244
|
+
}
|
|
245
|
+
</div>
|
|
246
|
+
`, styles: [":host{display:block}.drop-zone{border:2px dashed color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;transition:border-color .15s,background .15s}.drop-zone.drag-over{border-color:var(--mat-sys-primary);background:color-mix(in srgb,var(--mat-sys-primary) 8%,transparent)}.image-wrapper{position:relative}.image-wrapper:hover .hover-overlay{opacity:1}.hover-overlay{opacity:0;transition:opacity .15s}\n"] }]
|
|
247
|
+
}], propDecorators: { fileInput: [{
|
|
248
|
+
type: ViewChild,
|
|
249
|
+
args: ['fileInput']
|
|
250
|
+
}] } });
|
|
251
|
+
//# sourceMappingURL=post-editor-cover-image.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-editor-cover-image.component.js","sourceRoot":"","sources":["../../../../../../libs/cms-admin-ui/src/lib/post-editor/post-editor-cover-image.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n PLATFORM_ID,\n ViewChild,\n inject,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';\nimport { FIREBASE_STORAGE, PostService } from '@foliokit/cms-core';\nimport { PostEditorStore } from './post-editor.store';\n\n@Component({\n selector: 'folio-post-editor-cover-image',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MatButtonModule, MatIconModule, MatProgressBarModule],\n styles: [\n `\n :host {\n display: block;\n }\n .drop-zone {\n border: 2px dashed color-mix(in srgb, currentColor 25%, transparent);\n border-radius: 8px;\n transition: border-color 0.15s, background 0.15s;\n }\n .drop-zone.drag-over {\n border-color: var(--mat-sys-primary);\n background: color-mix(in srgb, var(--mat-sys-primary) 8%, transparent);\n }\n .image-wrapper {\n position: relative;\n }\n .image-wrapper:hover .hover-overlay {\n opacity: 1;\n }\n .hover-overlay {\n opacity: 0;\n transition: opacity 0.15s;\n }\n `,\n ],\n template: `\n <div class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-semibold\">Cover Image</span>\n\n @if (store.post()?.thumbnailUrl; as url) {\n <!-- Filled state -->\n <div class=\"image-wrapper rounded-lg overflow-hidden\">\n <div class=\"aspect-video w-full relative\">\n <img\n [src]=\"url\"\n [alt]=\"store.post()?.thumbnailAlt ?? ''\"\n class=\"w-full h-full object-cover\"\n />\n <!-- Hover overlay -->\n <div\n class=\"hover-overlay absolute inset-0 flex items-center justify-center gap-3\"\n style=\"background: rgba(0,0,0,0.45)\"\n >\n <button\n mat-icon-button\n style=\"color: white\"\n title=\"Replace image\"\n (click)=\"isBrowser && fileInput.click()\"\n >\n <mat-icon>swap_horiz</mat-icon>\n </button>\n <button\n mat-icon-button\n style=\"color: white\"\n title=\"Delete image\"\n (click)=\"onDeleteCover()\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n </div>\n } @else {\n <!-- Empty state -->\n <div\n class=\"drop-zone w-full flex flex-col items-center justify-center gap-2 py-10 cursor-pointer select-none\"\n [class.drag-over]=\"isDragOver()\"\n (click)=\"isBrowser && fileInput.click()\"\n (dragover)=\"isBrowser && onDragOver($event)\"\n (dragleave)=\"isBrowser && onDragLeave()\"\n (drop)=\"isBrowser && onDrop($event)\"\n >\n <mat-icon class=\"opacity-40\" style=\"font-size: 2.5rem; width: 2.5rem; height: 2.5rem\">\n upload_file\n </mat-icon>\n <span class=\"text-sm opacity-50\">Upload cover image</span>\n </div>\n }\n\n <!-- Hidden file input -->\n <input\n #fileInput\n type=\"file\"\n accept=\"image/*\"\n class=\"hidden\"\n (change)=\"onFileSelected($any($event.target).files)\"\n />\n\n <!-- Progress bar -->\n @if (uploading()) {\n <mat-progress-bar mode=\"determinate\" [value]=\"uploadProgress()\" />\n }\n\n <!-- Error message -->\n @if (uploadError()) {\n <p class=\"text-sm text-red-500\">{{ uploadError() }}</p>\n }\n </div>\n `,\n})\nexport class PostEditorCoverImageComponent {\n @ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;\n\n readonly store = inject(PostEditorStore);\n private readonly storage = inject(FIREBASE_STORAGE);\n private readonly postService = inject(PostService);\n private readonly platformId = inject(PLATFORM_ID);\n\n readonly isBrowser = isPlatformBrowser(this.platformId);\n\n readonly uploading = signal(false);\n readonly uploadProgress = signal(0);\n readonly uploadError = signal<string | null>(null);\n readonly isDragOver = signal(false);\n readonly storagePath = signal<string | null>(null);\n\n onDragOver(event: DragEvent): void {\n event.preventDefault();\n this.isDragOver.set(true);\n }\n\n onDragLeave(): void {\n this.isDragOver.set(false);\n }\n\n onDrop(event: DragEvent): void {\n event.preventDefault();\n this.isDragOver.set(false);\n const files = event.dataTransfer?.files;\n if (files?.length) {\n this.upload(files[0]);\n }\n }\n\n onFileSelected(files: FileList | null): void {\n if (!files?.length) return;\n this.upload(files[0]);\n // Reset so the same file can be re-selected after a delete\n if (this.fileInput?.nativeElement) {\n this.fileInput.nativeElement.value = '';\n }\n }\n\n onDeleteCover(): void {\n if (!window.confirm('Remove cover image?')) return;\n const path = this.storagePath();\n if (path) {\n this.postService.deleteStorageFile(path).subscribe({\n next: () => this.clearCover(),\n error: () => this.clearCover(),\n });\n } else {\n this.clearCover();\n }\n }\n\n private clearCover(): void {\n this.store.updateField('thumbnailUrl', '');\n this.store.updateField('thumbnailAlt', '');\n this.storagePath.set(null);\n this.uploadProgress.set(0);\n }\n\n private upload(file: File): void {\n const previousPath = this.storagePath();\n const postId = this.store.post()?.id || this.store.tempPostId();\n const storagePath = `posts/${postId}/cover/${file.name}`;\n\n if (previousPath) {\n this.postService.deleteStorageFile(previousPath).subscribe();\n }\n\n const fileRef = ref(this.storage, storagePath);\n\n this.uploading.set(true);\n this.uploadProgress.set(0);\n this.uploadError.set(null);\n\n const task = uploadBytesResumable(fileRef, file);\n\n task.on(\n 'state_changed',\n (snapshot) => {\n this.uploadProgress.set(\n Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100),\n );\n },\n (error) => {\n this.uploading.set(false);\n this.uploadError.set(error.message);\n },\n () => {\n getDownloadURL(task.snapshot.ref).then((downloadUrl) => {\n this.store.updateField('thumbnailUrl', downloadUrl);\n this.store.updateField('thumbnailAlt', file.name);\n this.storagePath.set(storagePath);\n this.uploading.set(false);\n });\n },\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, inject, input, } from '@angular/core';
|
|
2
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
5
|
+
import { PostEditorStore } from './post-editor.store';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import * as i1 from "@angular/material/button";
|
|
8
|
+
import * as i2 from "@angular/material/icon";
|
|
9
|
+
import * as i3 from "@angular/material/tooltip";
|
|
10
|
+
export class PostEditorEmbeddedMediaItemComponent {
|
|
11
|
+
entry = input.required(...(ngDevMode ? [{ debugName: "entry" }] : /* istanbul ignore next */ []));
|
|
12
|
+
token = input.required(...(ngDevMode ? [{ debugName: "token" }] : /* istanbul ignore next */ []));
|
|
13
|
+
store = inject(PostEditorStore);
|
|
14
|
+
onInsert() {
|
|
15
|
+
this.store.insertMediaAtCursor(this.token());
|
|
16
|
+
}
|
|
17
|
+
onDelete() {
|
|
18
|
+
if (!window.confirm('Remove this image? It will be removed from Storage and any markdown references will break.'))
|
|
19
|
+
return;
|
|
20
|
+
this.store.removeEmbeddedMedia(this.token());
|
|
21
|
+
}
|
|
22
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorEmbeddedMediaItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
23
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: PostEditorEmbeddedMediaItemComponent, isStandalone: true, selector: "folio-post-editor-embedded-media-item", inputs: { entry: { classPropertyName: "entry", publicName: "entry", isSignal: true, isRequired: true, transformFunction: null }, token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
24
|
+
<div class="item-wrapper flex flex-col gap-1">
|
|
25
|
+
<div class="relative aspect-square rounded-md overflow-hidden bg-[var(--mat-sys-surface-container-high)]">
|
|
26
|
+
<img
|
|
27
|
+
[src]="entry().downloadUrl"
|
|
28
|
+
[alt]="entry().alt"
|
|
29
|
+
class="w-full h-full object-cover"
|
|
30
|
+
/>
|
|
31
|
+
<!-- Hover overlay -->
|
|
32
|
+
<div
|
|
33
|
+
class="hover-overlay absolute inset-0 flex items-center justify-center gap-2"
|
|
34
|
+
style="background: rgba(0,0,0,0.5)"
|
|
35
|
+
>
|
|
36
|
+
<button
|
|
37
|
+
mat-icon-button
|
|
38
|
+
style="color: white"
|
|
39
|
+
matTooltip="Insert at cursor"
|
|
40
|
+
(click)="onInsert()"
|
|
41
|
+
>
|
|
42
|
+
<mat-icon>add_photo_alternate</mat-icon>
|
|
43
|
+
</button>
|
|
44
|
+
<button
|
|
45
|
+
mat-icon-button
|
|
46
|
+
style="color: white"
|
|
47
|
+
matTooltip="Delete"
|
|
48
|
+
(click)="onDelete()"
|
|
49
|
+
>
|
|
50
|
+
<mat-icon>delete</mat-icon>
|
|
51
|
+
</button>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<span class="text-xs opacity-60 truncate" [title]="entry().alt">
|
|
55
|
+
{{ entry().alt }}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
`, isInline: true, styles: [":host{display:block}.item-wrapper:hover .hover-overlay{opacity:1}.hover-overlay{opacity:0;transition:opacity .15s}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
59
|
+
}
|
|
60
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorEmbeddedMediaItemComponent, decorators: [{
|
|
61
|
+
type: Component,
|
|
62
|
+
args: [{ selector: 'folio-post-editor-embedded-media-item', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatButtonModule, MatIconModule, MatTooltipModule], template: `
|
|
63
|
+
<div class="item-wrapper flex flex-col gap-1">
|
|
64
|
+
<div class="relative aspect-square rounded-md overflow-hidden bg-[var(--mat-sys-surface-container-high)]">
|
|
65
|
+
<img
|
|
66
|
+
[src]="entry().downloadUrl"
|
|
67
|
+
[alt]="entry().alt"
|
|
68
|
+
class="w-full h-full object-cover"
|
|
69
|
+
/>
|
|
70
|
+
<!-- Hover overlay -->
|
|
71
|
+
<div
|
|
72
|
+
class="hover-overlay absolute inset-0 flex items-center justify-center gap-2"
|
|
73
|
+
style="background: rgba(0,0,0,0.5)"
|
|
74
|
+
>
|
|
75
|
+
<button
|
|
76
|
+
mat-icon-button
|
|
77
|
+
style="color: white"
|
|
78
|
+
matTooltip="Insert at cursor"
|
|
79
|
+
(click)="onInsert()"
|
|
80
|
+
>
|
|
81
|
+
<mat-icon>add_photo_alternate</mat-icon>
|
|
82
|
+
</button>
|
|
83
|
+
<button
|
|
84
|
+
mat-icon-button
|
|
85
|
+
style="color: white"
|
|
86
|
+
matTooltip="Delete"
|
|
87
|
+
(click)="onDelete()"
|
|
88
|
+
>
|
|
89
|
+
<mat-icon>delete</mat-icon>
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
<span class="text-xs opacity-60 truncate" [title]="entry().alt">
|
|
94
|
+
{{ entry().alt }}
|
|
95
|
+
</span>
|
|
96
|
+
</div>
|
|
97
|
+
`, styles: [":host{display:block}.item-wrapper:hover .hover-overlay{opacity:1}.hover-overlay{opacity:0;transition:opacity .15s}\n"] }]
|
|
98
|
+
}], propDecorators: { entry: [{ type: i0.Input, args: [{ isSignal: true, alias: "entry", required: true }] }], token: [{ type: i0.Input, args: [{ isSignal: true, alias: "token", required: true }] }] } });
|
|
99
|
+
//# sourceMappingURL=post-editor-embedded-media-item.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-editor-embedded-media-item.component.js","sourceRoot":"","sources":["../../../../../../libs/cms-admin-ui/src/lib/post-editor/post-editor-embedded-media-item.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,MAAM,EACN,KAAK,GACN,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;;;;AA0DtD,MAAM,OAAO,oCAAoC;IACtC,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAsB,CAAC;IAC7C,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAU,CAAC;IAEjC,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAEzC,QAAQ;QACN,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,4FAA4F,CAAC;YAAE,OAAO;QAC1H,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;uGAbU,oCAAoC;2FAApC,oCAAoC,2VArCrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCT,6LAlDS,eAAe,qNAAE,aAAa,mLAAE,gBAAgB;;2FAoD/C,oCAAoC;kBAxDhD,SAAS;+BACE,uCAAuC,cACrC,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,eAAe,EAAE,aAAa,EAAE,gBAAgB,CAAC,YAejD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCT","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n inject,\n input,\n} from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatTooltipModule } from '@angular/material/tooltip';\nimport { EmbeddedMediaEntry } from '@foliokit/cms-core';\nimport { PostEditorStore } from './post-editor.store';\n\n@Component({\n selector: 'folio-post-editor-embedded-media-item',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MatButtonModule, MatIconModule, MatTooltipModule],\n styles: [\n `\n :host {\n display: block;\n }\n .item-wrapper:hover .hover-overlay {\n opacity: 1;\n }\n .hover-overlay {\n opacity: 0;\n transition: opacity 0.15s;\n }\n `,\n ],\n template: `\n <div class=\"item-wrapper flex flex-col gap-1\">\n <div class=\"relative aspect-square rounded-md overflow-hidden bg-[var(--mat-sys-surface-container-high)]\">\n <img\n [src]=\"entry().downloadUrl\"\n [alt]=\"entry().alt\"\n class=\"w-full h-full object-cover\"\n />\n <!-- Hover overlay -->\n <div\n class=\"hover-overlay absolute inset-0 flex items-center justify-center gap-2\"\n style=\"background: rgba(0,0,0,0.5)\"\n >\n <button\n mat-icon-button\n style=\"color: white\"\n matTooltip=\"Insert at cursor\"\n (click)=\"onInsert()\"\n >\n <mat-icon>add_photo_alternate</mat-icon>\n </button>\n <button\n mat-icon-button\n style=\"color: white\"\n matTooltip=\"Delete\"\n (click)=\"onDelete()\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n <span class=\"text-xs opacity-60 truncate\" [title]=\"entry().alt\">\n {{ entry().alt }}\n </span>\n </div>\n `,\n})\nexport class PostEditorEmbeddedMediaItemComponent {\n readonly entry = input.required<EmbeddedMediaEntry>();\n readonly token = input.required<string>();\n\n readonly store = inject(PostEditorStore);\n\n onInsert(): void {\n this.store.insertMediaAtCursor(this.token());\n }\n\n onDelete(): void {\n if (!window.confirm('Remove this image? It will be removed from Storage and any markdown references will break.')) return;\n this.store.removeEmbeddedMedia(this.token());\n }\n}\n"]}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, PLATFORM_ID, ViewChild, computed, inject, signal, } from '@angular/core';
|
|
2
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
3
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
4
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
5
|
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
6
|
+
import { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';
|
|
7
|
+
import { FIREBASE_STORAGE } from '@foliokit/cms-core';
|
|
8
|
+
import { PostEditorStore } from './post-editor.store';
|
|
9
|
+
import { PostEditorEmbeddedMediaItemComponent } from './post-editor-embedded-media-item.component';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
import * as i1 from "@angular/material/button";
|
|
12
|
+
import * as i2 from "@angular/material/icon";
|
|
13
|
+
import * as i3 from "@angular/material/progress-bar";
|
|
14
|
+
export class PostEditorEmbeddedMediaComponent {
|
|
15
|
+
fileInput;
|
|
16
|
+
store = inject(PostEditorStore);
|
|
17
|
+
storage = inject(FIREBASE_STORAGE);
|
|
18
|
+
platformId = inject(PLATFORM_ID);
|
|
19
|
+
isBrowser = isPlatformBrowser(this.platformId);
|
|
20
|
+
uploading = signal(false, ...(ngDevMode ? [{ debugName: "uploading" }] : /* istanbul ignore next */ []));
|
|
21
|
+
uploadError = signal(null, ...(ngDevMode ? [{ debugName: "uploadError" }] : /* istanbul ignore next */ []));
|
|
22
|
+
entries = computed(() => {
|
|
23
|
+
const media = this.store.post()?.embeddedMedia ?? {};
|
|
24
|
+
return Object.values(media);
|
|
25
|
+
}, ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
26
|
+
onFileSelected(files) {
|
|
27
|
+
if (!files?.length)
|
|
28
|
+
return;
|
|
29
|
+
this.upload(files[0]);
|
|
30
|
+
if (this.fileInput?.nativeElement) {
|
|
31
|
+
this.fileInput.nativeElement.value = '';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
upload(file) {
|
|
35
|
+
const postId = this.store.post()?.id || this.store.tempPostId();
|
|
36
|
+
const storagePath = `posts/${postId}/media/${file.name}`;
|
|
37
|
+
const fileRef = ref(this.storage, storagePath);
|
|
38
|
+
const token = crypto.randomUUID();
|
|
39
|
+
this.uploading.set(true);
|
|
40
|
+
this.uploadError.set(null);
|
|
41
|
+
const task = uploadBytesResumable(fileRef, file);
|
|
42
|
+
task.on('state_changed', null, (error) => {
|
|
43
|
+
this.uploading.set(false);
|
|
44
|
+
this.uploadError.set(error.message);
|
|
45
|
+
}, () => {
|
|
46
|
+
getDownloadURL(task.snapshot.ref).then((downloadUrl) => {
|
|
47
|
+
const entry = {
|
|
48
|
+
token,
|
|
49
|
+
storagePath,
|
|
50
|
+
downloadUrl,
|
|
51
|
+
alt: file.name,
|
|
52
|
+
mimeType: file.type,
|
|
53
|
+
};
|
|
54
|
+
const currentMedia = this.store.post()?.embeddedMedia ?? {};
|
|
55
|
+
this.store.updateField('embeddedMedia', {
|
|
56
|
+
...currentMedia,
|
|
57
|
+
[token]: entry,
|
|
58
|
+
});
|
|
59
|
+
this.uploading.set(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorEmbeddedMediaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
64
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PostEditorEmbeddedMediaComponent, isStandalone: true, selector: "folio-post-editor-embedded-media", viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: `
|
|
65
|
+
<div class="flex flex-col gap-3">
|
|
66
|
+
<!-- Header -->
|
|
67
|
+
<div class="flex items-center justify-between">
|
|
68
|
+
<span class="text-sm font-semibold">Embedded Media</span>
|
|
69
|
+
<button
|
|
70
|
+
mat-stroked-button
|
|
71
|
+
[disabled]="uploading()"
|
|
72
|
+
(click)="isBrowser && fileInput.click()"
|
|
73
|
+
>
|
|
74
|
+
<mat-icon>upload</mat-icon>
|
|
75
|
+
Upload Image
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Hidden file input -->
|
|
80
|
+
<input
|
|
81
|
+
#fileInput
|
|
82
|
+
type="file"
|
|
83
|
+
accept="image/*"
|
|
84
|
+
class="hidden"
|
|
85
|
+
(change)="onFileSelected($any($event.target).files)"
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
<!-- Upload progress -->
|
|
89
|
+
@if (uploading()) {
|
|
90
|
+
<mat-progress-bar mode="indeterminate" />
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
<!-- Upload error -->
|
|
94
|
+
@if (uploadError()) {
|
|
95
|
+
<p class="text-sm text-red-500">{{ uploadError() }}</p>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
<!-- Media grid -->
|
|
99
|
+
@if (entries().length > 0) {
|
|
100
|
+
<div class="grid grid-cols-2 gap-3">
|
|
101
|
+
@for (item of entries(); track item.token) {
|
|
102
|
+
<folio-post-editor-embedded-media-item
|
|
103
|
+
[entry]="item"
|
|
104
|
+
[token]="item.token"
|
|
105
|
+
/>
|
|
106
|
+
}
|
|
107
|
+
</div>
|
|
108
|
+
} @else {
|
|
109
|
+
<div class="flex items-center justify-center py-8">
|
|
110
|
+
<span class="text-sm opacity-40">No embedded media yet</span>
|
|
111
|
+
</div>
|
|
112
|
+
}
|
|
113
|
+
</div>
|
|
114
|
+
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i3.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: PostEditorEmbeddedMediaItemComponent, selector: "folio-post-editor-embedded-media-item", inputs: ["entry", "token"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
115
|
+
}
|
|
116
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorEmbeddedMediaComponent, decorators: [{
|
|
117
|
+
type: Component,
|
|
118
|
+
args: [{ selector: 'folio-post-editor-embedded-media', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatButtonModule, MatIconModule, MatProgressBarModule, PostEditorEmbeddedMediaItemComponent], template: `
|
|
119
|
+
<div class="flex flex-col gap-3">
|
|
120
|
+
<!-- Header -->
|
|
121
|
+
<div class="flex items-center justify-between">
|
|
122
|
+
<span class="text-sm font-semibold">Embedded Media</span>
|
|
123
|
+
<button
|
|
124
|
+
mat-stroked-button
|
|
125
|
+
[disabled]="uploading()"
|
|
126
|
+
(click)="isBrowser && fileInput.click()"
|
|
127
|
+
>
|
|
128
|
+
<mat-icon>upload</mat-icon>
|
|
129
|
+
Upload Image
|
|
130
|
+
</button>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<!-- Hidden file input -->
|
|
134
|
+
<input
|
|
135
|
+
#fileInput
|
|
136
|
+
type="file"
|
|
137
|
+
accept="image/*"
|
|
138
|
+
class="hidden"
|
|
139
|
+
(change)="onFileSelected($any($event.target).files)"
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<!-- Upload progress -->
|
|
143
|
+
@if (uploading()) {
|
|
144
|
+
<mat-progress-bar mode="indeterminate" />
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
<!-- Upload error -->
|
|
148
|
+
@if (uploadError()) {
|
|
149
|
+
<p class="text-sm text-red-500">{{ uploadError() }}</p>
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
<!-- Media grid -->
|
|
153
|
+
@if (entries().length > 0) {
|
|
154
|
+
<div class="grid grid-cols-2 gap-3">
|
|
155
|
+
@for (item of entries(); track item.token) {
|
|
156
|
+
<folio-post-editor-embedded-media-item
|
|
157
|
+
[entry]="item"
|
|
158
|
+
[token]="item.token"
|
|
159
|
+
/>
|
|
160
|
+
}
|
|
161
|
+
</div>
|
|
162
|
+
} @else {
|
|
163
|
+
<div class="flex items-center justify-center py-8">
|
|
164
|
+
<span class="text-sm opacity-40">No embedded media yet</span>
|
|
165
|
+
</div>
|
|
166
|
+
}
|
|
167
|
+
</div>
|
|
168
|
+
`, styles: [":host{display:block}\n"] }]
|
|
169
|
+
}], propDecorators: { fileInput: [{
|
|
170
|
+
type: ViewChild,
|
|
171
|
+
args: ['fileInput']
|
|
172
|
+
}] } });
|
|
173
|
+
//# sourceMappingURL=post-editor-embedded-media.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-editor-embedded-media.component.js","sourceRoot":"","sources":["../../../../../../libs/cms-admin-ui/src/lib/post-editor/post-editor-embedded-media.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n PLATFORM_ID,\n ViewChild,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';\nimport { EmbeddedMediaEntry, FIREBASE_STORAGE } from '@foliokit/cms-core';\nimport { PostEditorStore } from './post-editor.store';\nimport { PostEditorEmbeddedMediaItemComponent } from './post-editor-embedded-media-item.component';\n\n@Component({\n selector: 'folio-post-editor-embedded-media',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MatButtonModule, MatIconModule, MatProgressBarModule, PostEditorEmbeddedMediaItemComponent],\n styles: [\n `\n :host {\n display: block;\n }\n `,\n ],\n template: `\n <div class=\"flex flex-col gap-3\">\n <!-- Header -->\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm font-semibold\">Embedded Media</span>\n <button\n mat-stroked-button\n [disabled]=\"uploading()\"\n (click)=\"isBrowser && fileInput.click()\"\n >\n <mat-icon>upload</mat-icon>\n Upload Image\n </button>\n </div>\n\n <!-- Hidden file input -->\n <input\n #fileInput\n type=\"file\"\n accept=\"image/*\"\n class=\"hidden\"\n (change)=\"onFileSelected($any($event.target).files)\"\n />\n\n <!-- Upload progress -->\n @if (uploading()) {\n <mat-progress-bar mode=\"indeterminate\" />\n }\n\n <!-- Upload error -->\n @if (uploadError()) {\n <p class=\"text-sm text-red-500\">{{ uploadError() }}</p>\n }\n\n <!-- Media grid -->\n @if (entries().length > 0) {\n <div class=\"grid grid-cols-2 gap-3\">\n @for (item of entries(); track item.token) {\n <folio-post-editor-embedded-media-item\n [entry]=\"item\"\n [token]=\"item.token\"\n />\n }\n </div>\n } @else {\n <div class=\"flex items-center justify-center py-8\">\n <span class=\"text-sm opacity-40\">No embedded media yet</span>\n </div>\n }\n </div>\n `,\n})\nexport class PostEditorEmbeddedMediaComponent {\n @ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;\n\n readonly store = inject(PostEditorStore);\n private readonly storage = inject(FIREBASE_STORAGE);\n private readonly platformId = inject(PLATFORM_ID);\n\n readonly isBrowser = isPlatformBrowser(this.platformId);\n readonly uploading = signal(false);\n readonly uploadError = signal<string | null>(null);\n\n readonly entries = computed<EmbeddedMediaEntry[]>(() => {\n const media = this.store.post()?.embeddedMedia ?? {};\n return Object.values(media) as EmbeddedMediaEntry[];\n });\n\n onFileSelected(files: FileList | null): void {\n if (!files?.length) return;\n this.upload(files[0]);\n if (this.fileInput?.nativeElement) {\n this.fileInput.nativeElement.value = '';\n }\n }\n\n private upload(file: File): void {\n const postId = this.store.post()?.id || this.store.tempPostId();\n const storagePath = `posts/${postId}/media/${file.name}`;\n const fileRef = ref(this.storage, storagePath);\n const token = crypto.randomUUID();\n\n this.uploading.set(true);\n this.uploadError.set(null);\n\n const task = uploadBytesResumable(fileRef, file);\n\n task.on(\n 'state_changed',\n null,\n (error) => {\n this.uploading.set(false);\n this.uploadError.set(error.message);\n },\n () => {\n getDownloadURL(task.snapshot.ref).then((downloadUrl) => {\n const entry: EmbeddedMediaEntry = {\n token,\n storagePath,\n downloadUrl,\n alt: file.name,\n mimeType: file.type,\n };\n const currentMedia = this.store.post()?.embeddedMedia ?? {};\n this.store.updateField('embeddedMedia', {\n ...currentMedia,\n [token]: entry,\n });\n this.uploading.set(false);\n });\n },\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { PostEditorCoverImageComponent } from './post-editor-cover-image.component';
|
|
3
|
+
import { PostEditorEmbeddedMediaComponent } from './post-editor-embedded-media.component';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
export class PostEditorMediaTabComponent {
|
|
6
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorMediaTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.4", type: PostEditorMediaTabComponent, isStandalone: true, selector: "folio-post-editor-media-tab", ngImport: i0, template: `
|
|
8
|
+
<div class="flex flex-col gap-6 p-4">
|
|
9
|
+
<folio-post-editor-cover-image />
|
|
10
|
+
<folio-post-editor-embedded-media />
|
|
11
|
+
</div>
|
|
12
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow-y:auto}\n"], dependencies: [{ kind: "component", type: PostEditorCoverImageComponent, selector: "folio-post-editor-cover-image" }, { kind: "component", type: PostEditorEmbeddedMediaComponent, selector: "folio-post-editor-embedded-media" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13
|
+
}
|
|
14
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PostEditorMediaTabComponent, decorators: [{
|
|
15
|
+
type: Component,
|
|
16
|
+
args: [{ selector: 'folio-post-editor-media-tab', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PostEditorCoverImageComponent, PostEditorEmbeddedMediaComponent], template: `
|
|
17
|
+
<div class="flex flex-col gap-6 p-4">
|
|
18
|
+
<folio-post-editor-cover-image />
|
|
19
|
+
<folio-post-editor-embedded-media />
|
|
20
|
+
</div>
|
|
21
|
+
`, styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow-y:auto}\n"] }]
|
|
22
|
+
}] });
|
|
23
|
+
//# sourceMappingURL=post-editor-media-tab.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-editor-media-tab.component.js","sourceRoot":"","sources":["../../../../../../libs/cms-admin-ui/src/lib/post-editor/post-editor-media-tab.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,qCAAqC,CAAC;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;;AAyB1F,MAAM,OAAO,2BAA2B;uGAA3B,2BAA2B;2FAA3B,2BAA2B,uFAP5B;;;;;GAKT,yJAjBS,6BAA6B,0EAAE,gCAAgC;;2FAmB9D,2BAA2B;kBAvBvC,SAAS;+BACE,6BAA6B,cAC3B,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,6BAA6B,EAAE,gCAAgC,CAAC,YAYhE;;;;;GAKT","sourcesContent":["import { ChangeDetectionStrategy, Component } from '@angular/core';\nimport { PostEditorCoverImageComponent } from './post-editor-cover-image.component';\nimport { PostEditorEmbeddedMediaComponent } from './post-editor-embedded-media.component';\n\n@Component({\n selector: 'folio-post-editor-media-tab',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [PostEditorCoverImageComponent, PostEditorEmbeddedMediaComponent],\n styles: [\n `\n :host {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n `,\n ],\n template: `\n <div class=\"flex flex-col gap-6 p-4\">\n <folio-post-editor-cover-image />\n <folio-post-editor-embedded-media />\n </div>\n `,\n})\nexport class PostEditorMediaTabComponent {}\n"]}
|