@shival99/z-ui 1.6.10 → 1.7.1

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.
Files changed (75) hide show
  1. package/assets/css/animations.css +6 -0
  2. package/assets/css/base.css +0 -6
  3. package/assets/css/themes/gray.css +8 -0
  4. package/assets/css/themes/green.css +8 -0
  5. package/assets/css/themes/hospital.css +8 -0
  6. package/assets/css/themes/neutral.css +8 -0
  7. package/assets/css/themes/orange.css +8 -0
  8. package/assets/css/themes/slate.css +8 -0
  9. package/assets/css/themes/stone.css +8 -0
  10. package/assets/css/themes/violet.css +8 -0
  11. package/assets/css/themes/zinc.css +8 -0
  12. package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs +19 -17
  13. package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs.map +1 -1
  14. package/fesm2022/shival99-z-ui-components-z-button-group.mjs +191 -0
  15. package/fesm2022/shival99-z-ui-components-z-button-group.mjs.map +1 -0
  16. package/fesm2022/shival99-z-ui-components-z-button.mjs +6 -1
  17. package/fesm2022/shival99-z-ui-components-z-button.mjs.map +1 -1
  18. package/fesm2022/shival99-z-ui-components-z-calendar.mjs +60 -52
  19. package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
  20. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +13 -5
  21. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -1
  22. package/fesm2022/shival99-z-ui-components-z-drawer.mjs +7 -4
  23. package/fesm2022/shival99-z-ui-components-z-drawer.mjs.map +1 -1
  24. package/fesm2022/shival99-z-ui-components-z-editor.mjs +28 -25
  25. package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -1
  26. package/fesm2022/shival99-z-ui-components-z-filter.mjs +1 -1
  27. package/fesm2022/shival99-z-ui-components-z-filter.mjs.map +1 -1
  28. package/fesm2022/shival99-z-ui-components-z-gallery.mjs +1573 -0
  29. package/fesm2022/shival99-z-ui-components-z-gallery.mjs.map +1 -0
  30. package/fesm2022/shival99-z-ui-components-z-icon.mjs +3 -1
  31. package/fesm2022/shival99-z-ui-components-z-icon.mjs.map +1 -1
  32. package/fesm2022/shival99-z-ui-components-z-input.mjs +114 -109
  33. package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -1
  34. package/fesm2022/shival99-z-ui-components-z-modal.mjs +10 -9
  35. package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -1
  36. package/fesm2022/shival99-z-ui-components-z-pagination.mjs +1 -1
  37. package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -1
  38. package/fesm2022/shival99-z-ui-components-z-radio.mjs +13 -5
  39. package/fesm2022/shival99-z-ui-components-z-radio.mjs.map +1 -1
  40. package/fesm2022/shival99-z-ui-components-z-select.mjs +37 -29
  41. package/fesm2022/shival99-z-ui-components-z-select.mjs.map +1 -1
  42. package/fesm2022/shival99-z-ui-components-z-switch.mjs +13 -5
  43. package/fesm2022/shival99-z-ui-components-z-switch.mjs.map +1 -1
  44. package/fesm2022/shival99-z-ui-components-z-table.mjs +1268 -1254
  45. package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
  46. package/fesm2022/shival99-z-ui-components-z-upload.mjs +21 -19
  47. package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -1
  48. package/fesm2022/shival99-z-ui-i18n.mjs +22 -0
  49. package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -1
  50. package/fesm2022/shival99-z-ui-pipes.mjs +32 -1
  51. package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -1
  52. package/fesm2022/shival99-z-ui-utils.mjs +9 -1
  53. package/fesm2022/shival99-z-ui-utils.mjs.map +1 -1
  54. package/package.json +9 -1
  55. package/types/shival99-z-ui-components-z-autocomplete.d.ts +11 -9
  56. package/types/shival99-z-ui-components-z-breadcrumb.d.ts +2 -2
  57. package/types/shival99-z-ui-components-z-button-group.d.ts +56 -0
  58. package/types/shival99-z-ui-components-z-button.d.ts +1 -1
  59. package/types/shival99-z-ui-components-z-calendar.d.ts +5 -4
  60. package/types/shival99-z-ui-components-z-checkbox.d.ts +5 -1
  61. package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +1 -1
  62. package/types/shival99-z-ui-components-z-editor.d.ts +8 -6
  63. package/types/shival99-z-ui-components-z-empty.d.ts +2 -2
  64. package/types/shival99-z-ui-components-z-filter.d.ts +1 -1
  65. package/types/shival99-z-ui-components-z-gallery.d.ts +192 -0
  66. package/types/shival99-z-ui-components-z-icon.d.ts +3 -1
  67. package/types/shival99-z-ui-components-z-input.d.ts +40 -27
  68. package/types/shival99-z-ui-components-z-modal.d.ts +1 -1
  69. package/types/shival99-z-ui-components-z-radio.d.ts +5 -1
  70. package/types/shival99-z-ui-components-z-select.d.ts +7 -6
  71. package/types/shival99-z-ui-components-z-switch.d.ts +5 -1
  72. package/types/shival99-z-ui-components-z-table.d.ts +25 -22
  73. package/types/shival99-z-ui-components-z-upload.d.ts +7 -3
  74. package/types/shival99-z-ui-pipes.d.ts +21 -2
  75. package/types/shival99-z-ui-utils.d.ts +39 -2
@@ -0,0 +1,1573 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, effect, model } from '@angular/core';
3
+ import * as i1 from '@angular/forms';
4
+ import { FormsModule } from '@angular/forms';
5
+ import { TranslatePipe } from '@ngx-translate/core';
6
+ import { ZButtonComponent } from '@shival99/z-ui/components/z-button';
7
+ import { ZButtonGroupComponent, ZButtonGroupItemDirective } from '@shival99/z-ui/components/z-button-group';
8
+ import { ZEmptyComponent } from '@shival99/z-ui/components/z-empty';
9
+ import { ZIconComponent } from '@shival99/z-ui/components/z-icon';
10
+ import { ZInputComponent } from '@shival99/z-ui/components/z-input';
11
+ import { zMergeClasses } from '@shival99/z-ui/utils';
12
+ import { ZSkeletonComponent } from '@shival99/z-ui/components/z-skeleton';
13
+ import { ZTooltipDirective } from '@shival99/z-ui/components/z-tooltip';
14
+ import { ZSafeResourceUrlPipe } from '@shival99/z-ui/pipes';
15
+ import { cva } from 'class-variance-authority';
16
+
17
+ const FILE_CATEGORY_STYLES = {
18
+ image: {
19
+ bg: 'bg-purple-100 dark:bg-purple-900/30',
20
+ text: 'text-purple-600 dark:text-purple-400',
21
+ label: 'IMG',
22
+ icon: 'lucideImage',
23
+ },
24
+ pdf: {
25
+ bg: 'bg-red-100 dark:bg-red-900/30',
26
+ text: 'text-red-600 dark:text-red-400',
27
+ label: 'PDF',
28
+ icon: 'lucideFileText',
29
+ },
30
+ word: {
31
+ bg: 'bg-blue-100 dark:bg-blue-900/30',
32
+ text: 'text-blue-600 dark:text-blue-400',
33
+ label: 'DOC',
34
+ icon: 'lucideFileText',
35
+ },
36
+ excel: {
37
+ bg: 'bg-green-100 dark:bg-green-900/30',
38
+ text: 'text-green-600 dark:text-green-400',
39
+ label: 'XLS',
40
+ icon: 'lucideFileSpreadsheet',
41
+ },
42
+ powerpoint: {
43
+ bg: 'bg-orange-100 dark:bg-orange-900/30',
44
+ text: 'text-orange-600 dark:text-orange-400',
45
+ label: 'PPT',
46
+ icon: 'lucideFileText',
47
+ },
48
+ video: {
49
+ bg: 'bg-pink-100 dark:bg-pink-900/30',
50
+ text: 'text-pink-600 dark:text-pink-400',
51
+ label: 'VID',
52
+ icon: 'lucideVideo',
53
+ },
54
+ audio: {
55
+ bg: 'bg-yellow-100 dark:bg-yellow-900/30',
56
+ text: 'text-yellow-700 dark:text-yellow-400',
57
+ label: 'AUD',
58
+ icon: 'lucideMusic',
59
+ },
60
+ archive: {
61
+ bg: 'bg-amber-100 dark:bg-amber-900/30',
62
+ text: 'text-amber-600 dark:text-amber-400',
63
+ label: 'ZIP',
64
+ icon: 'lucideFileArchive',
65
+ },
66
+ code: {
67
+ bg: 'bg-slate-100 dark:bg-slate-800',
68
+ text: 'text-slate-600 dark:text-slate-400',
69
+ label: 'CODE',
70
+ icon: 'lucideCode',
71
+ },
72
+ text: {
73
+ bg: 'bg-gray-100 dark:bg-gray-800',
74
+ text: 'text-gray-600 dark:text-gray-400',
75
+ label: 'TXT',
76
+ icon: 'lucideFileText',
77
+ },
78
+ unknown: {
79
+ bg: 'bg-gray-100 dark:bg-gray-800',
80
+ text: 'text-gray-500 dark:text-gray-400',
81
+ label: 'FILE',
82
+ icon: 'lucideFile',
83
+ },
84
+ };
85
+
86
+ const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'];
87
+ const VIDEO_EXTENSIONS = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'];
88
+ const AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a'];
89
+ const ARCHIVE_EXTENSIONS = ['zip', 'rar', 'tar', 'gz', '7z', 'bz2'];
90
+ const CODE_EXTENSIONS = [
91
+ 'js',
92
+ 'ts',
93
+ 'jsx',
94
+ 'tsx',
95
+ 'html',
96
+ 'css',
97
+ 'scss',
98
+ 'json',
99
+ 'xml',
100
+ 'py',
101
+ 'java',
102
+ 'c',
103
+ 'cpp',
104
+ 'go',
105
+ 'rs',
106
+ 'php',
107
+ 'rb',
108
+ 'swift',
109
+ 'kt',
110
+ ];
111
+ const TEXT_EXTENSIONS = ['txt', 'md', 'log'];
112
+ const WORD_EXTENSIONS = ['doc', 'docx', 'odt', 'rtf'];
113
+ const EXCEL_EXTENSIONS = ['xls', 'xlsx', 'csv', 'ods'];
114
+ const POWERPOINT_EXTENSIONS = ['ppt', 'pptx', 'odp'];
115
+ const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB'];
116
+ const FILE_SIZE_BASE = 1024;
117
+ const getFileCategory = (file) => {
118
+ const type = file.type?.toLowerCase() || '';
119
+ const name = file.name?.toLowerCase() || '';
120
+ const ext = name.split('.').pop() || '';
121
+ if (type.startsWith('image/') || IMAGE_EXTENSIONS.includes(ext)) {
122
+ return 'image';
123
+ }
124
+ if (type === 'application/pdf' || ext === 'pdf') {
125
+ return 'pdf';
126
+ }
127
+ if (type.includes('word') || type.includes('document') || WORD_EXTENSIONS.includes(ext)) {
128
+ return 'word';
129
+ }
130
+ if (type.includes('excel') || type.includes('spreadsheet') || EXCEL_EXTENSIONS.includes(ext)) {
131
+ return 'excel';
132
+ }
133
+ if (type.includes('powerpoint') || type.includes('presentation') || POWERPOINT_EXTENSIONS.includes(ext)) {
134
+ return 'powerpoint';
135
+ }
136
+ if (type.startsWith('video/') || VIDEO_EXTENSIONS.includes(ext)) {
137
+ return 'video';
138
+ }
139
+ if (type.startsWith('audio/') || AUDIO_EXTENSIONS.includes(ext)) {
140
+ return 'audio';
141
+ }
142
+ if (type.includes('zip') ||
143
+ type.includes('rar') ||
144
+ type.includes('tar') ||
145
+ type.includes('compressed') ||
146
+ ARCHIVE_EXTENSIONS.includes(ext)) {
147
+ return 'archive';
148
+ }
149
+ if (CODE_EXTENSIONS.includes(ext)) {
150
+ return 'code';
151
+ }
152
+ if (type.startsWith('text/') || TEXT_EXTENSIONS.includes(ext)) {
153
+ return 'text';
154
+ }
155
+ return 'unknown';
156
+ };
157
+ const formatFileSize = (bytes) => {
158
+ if (bytes === 0) {
159
+ return '0 B';
160
+ }
161
+ const i = Math.floor(Math.log(bytes) / Math.log(FILE_SIZE_BASE));
162
+ const size = bytes / Math.pow(FILE_SIZE_BASE, i);
163
+ return `${size.toFixed(i > 0 ? 1 : 0)} ${FILE_SIZE_UNITS[i]}`;
164
+ };
165
+ const isPreviewable = (file) => {
166
+ const category = getFileCategory(file);
167
+ return category === 'image' || category === 'pdf' || category === 'video';
168
+ };
169
+ const isImage = (file) => getFileCategory(file) === 'image';
170
+
171
+ class ZGalleryItemComponent {
172
+ file = input.required(...(ngDevMode ? [{ debugName: "file" }] : []));
173
+ zMode = input('grid', ...(ngDevMode ? [{ debugName: "zMode" }] : []));
174
+ zSize = input('default', ...(ngDevMode ? [{ debugName: "zSize" }] : []));
175
+ zShowDownload = input(true, ...(ngDevMode ? [{ debugName: "zShowDownload" }] : []));
176
+ zShowPreview = input(true, ...(ngDevMode ? [{ debugName: "zShowPreview" }] : []));
177
+ zShowRemove = input(false, ...(ngDevMode ? [{ debugName: "zShowRemove" }] : []));
178
+ zOnDownload = output();
179
+ zOnPreview = output();
180
+ zOnRemove = output();
181
+ isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
182
+ formattedSize = computed(() => formatFileSize(this.file().size), ...(ngDevMode ? [{ debugName: "formattedSize" }] : []));
183
+ category = computed(() => getFileCategory(this.file()), ...(ngDevMode ? [{ debugName: "category" }] : []));
184
+ isImage = computed(() => this.category() === 'image', ...(ngDevMode ? [{ debugName: "isImage" }] : []));
185
+ categoryStyle = computed(() => {
186
+ const style = FILE_CATEGORY_STYLES[this.category()];
187
+ return {
188
+ ...style,
189
+ icon: style.icon,
190
+ };
191
+ }, ...(ngDevMode ? [{ debugName: "categoryStyle" }] : []));
192
+ canPreview = computed(() => isPreviewable(this.file()), ...(ngDevMode ? [{ debugName: "canPreview" }] : []));
193
+ onImageLoad() {
194
+ this.isLoading.set(false);
195
+ }
196
+ onImageError() {
197
+ this.isLoading.set(false);
198
+ }
199
+ onDownload(event) {
200
+ event.stopPropagation();
201
+ this.zOnDownload.emit(this.file());
202
+ }
203
+ onPreviewClick(event) {
204
+ event.stopPropagation();
205
+ this.zOnPreview.emit(this.file());
206
+ }
207
+ onPreview() {
208
+ if (this.canPreview()) {
209
+ this.zOnPreview.emit(this.file());
210
+ }
211
+ }
212
+ onRemove(event) {
213
+ event.stopPropagation();
214
+ this.zOnRemove.emit(this.file());
215
+ }
216
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
217
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZGalleryItemComponent, isStandalone: true, selector: "z-gallery-item", inputs: { file: { classPropertyName: "file", publicName: "file", isSignal: true, isRequired: true, transformFunction: null }, zMode: { classPropertyName: "zMode", publicName: "zMode", isSignal: true, isRequired: false, transformFunction: null }, zSize: { classPropertyName: "zSize", publicName: "zSize", isSignal: true, isRequired: false, transformFunction: null }, zShowDownload: { classPropertyName: "zShowDownload", publicName: "zShowDownload", isSignal: true, isRequired: false, transformFunction: null }, zShowPreview: { classPropertyName: "zShowPreview", publicName: "zShowPreview", isSignal: true, isRequired: false, transformFunction: null }, zShowRemove: { classPropertyName: "zShowRemove", publicName: "zShowRemove", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnDownload: "zOnDownload", zOnPreview: "zOnPreview", zOnRemove: "zOnRemove" }, host: { classAttribute: "z-gallery-item block" }, ngImport: i0, template: `
218
+ @if (zMode() === 'grid') {
219
+ <div
220
+ class="group border-border bg-card hover:border-primary/30 relative cursor-pointer overflow-hidden rounded-xl border shadow-sm transition-all duration-200 hover:shadow-md"
221
+ (click)="onPreview()"
222
+ >
223
+ <div class="relative aspect-square overflow-hidden">
224
+ @if (isImage()) {
225
+ @if (isLoading()) {
226
+ <z-skeleton class="absolute inset-0" />
227
+ }
228
+ <img
229
+ [src]="file().thumbnailUrl || file().url"
230
+ [alt]="file().name"
231
+ class="size-full object-cover transition-all duration-300"
232
+ [class.opacity-0]="isLoading()"
233
+ [class.scale-100]="!isLoading()"
234
+ [class.group-hover:scale-105]="!isLoading()"
235
+ loading="lazy"
236
+ (load)="onImageLoad()"
237
+ (error)="onImageError()"
238
+ />
239
+ } @else {
240
+ <div class="flex size-full flex-col items-center justify-center gap-2" [class]="categoryStyle().bg">
241
+ <i z-icon [zType]="categoryStyle().icon" zSize="32" [class]="categoryStyle().text"></i>
242
+ <span class="text-xs font-semibold" [class]="categoryStyle().text">
243
+ {{ categoryStyle().label }}
244
+ </span>
245
+ </div>
246
+ }
247
+
248
+ <div
249
+ class="absolute inset-0 flex items-center justify-center gap-2 bg-black/40 opacity-0 backdrop-blur-[2px] transition-all duration-200 group-hover:opacity-100"
250
+ >
251
+ @if (zShowPreview() && canPreview()) {
252
+ <button
253
+ z-button
254
+ zType="secondary"
255
+ zSize="sm"
256
+ zShape="circle"
257
+ class="shadow-lg"
258
+ (click)="onPreviewClick($event)"
259
+ z-tooltip
260
+ [zContent]="'i18n_z_ui_gallery_preview' | translate"
261
+ >
262
+ <i z-icon zType="lucideEye" zSize="16"></i>
263
+ </button>
264
+ }
265
+ @if (zShowDownload() && (file().downloadUrl || file().url)) {
266
+ <button
267
+ z-button
268
+ zType="secondary"
269
+ zSize="sm"
270
+ zShape="circle"
271
+ class="shadow-lg"
272
+ (click)="onDownload($event)"
273
+ z-tooltip
274
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
275
+ >
276
+ <i z-icon zType="lucideDownload" zSize="16"></i>
277
+ </button>
278
+ }
279
+ @if (zShowRemove()) {
280
+ <button
281
+ z-button
282
+ zType="secondary"
283
+ zSize="sm"
284
+ zShape="circle"
285
+ class="shadow-lg"
286
+ (click)="onRemove($event)"
287
+ z-tooltip
288
+ [zContent]="'i18n_z_ui_gallery_remove' | translate"
289
+ >
290
+ <i z-icon zType="lucideTrash2" zSize="16"></i>
291
+ </button>
292
+ }
293
+ </div>
294
+ </div>
295
+
296
+ <div class="p-2.5">
297
+ <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
298
+ {{ file().name }}
299
+ </p>
300
+ <p class="text-muted-foreground mt-0.5 text-xs">
301
+ {{ formattedSize() }}
302
+ </p>
303
+ </div>
304
+ </div>
305
+ } @else {
306
+ <div
307
+ class="group border-border bg-card hover:border-primary/30 flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 shadow-sm transition-all duration-150 hover:shadow-md"
308
+ (click)="onPreview()"
309
+ >
310
+ <div class="flex size-10 shrink-0 items-center justify-center rounded-lg" [class]="categoryStyle().bg">
311
+ @if (isImage() && (file().thumbnailUrl || file().url)) {
312
+ <img
313
+ [src]="file().thumbnailUrl || file().url"
314
+ [alt]="file().name"
315
+ class="size-full rounded-lg object-cover"
316
+ loading="lazy"
317
+ />
318
+ } @else {
319
+ <i z-icon [zType]="categoryStyle().icon" zSize="18" [class]="categoryStyle().text"></i>
320
+ }
321
+ </div>
322
+
323
+ <div class="min-w-0 flex-1">
324
+ <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
325
+ {{ file().name }}
326
+ </p>
327
+ <p class="text-muted-foreground text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
328
+ </div>
329
+
330
+ <div class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
331
+ @if (zShowPreview() && canPreview()) {
332
+ <button
333
+ z-button
334
+ zType="ghost"
335
+ zSize="xs"
336
+ zShape="circle"
337
+ (click)="onPreviewClick($event)"
338
+ z-tooltip
339
+ [zContent]="'i18n_z_ui_gallery_preview' | translate"
340
+ >
341
+ <i z-icon zType="lucideEye" zSize="16"></i>
342
+ </button>
343
+ }
344
+ @if (zShowDownload() && (file().downloadUrl || file().url)) {
345
+ <button
346
+ z-button
347
+ zType="ghost"
348
+ zSize="xs"
349
+ zShape="circle"
350
+ (click)="onDownload($event)"
351
+ z-tooltip
352
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
353
+ >
354
+ <i z-icon zType="lucideDownload" zSize="16"></i>
355
+ </button>
356
+ }
357
+ @if (zShowRemove()) {
358
+ <button
359
+ z-button
360
+ zType="ghost"
361
+ zSize="xs"
362
+ zShape="circle"
363
+ (click)="onRemove($event)"
364
+ z-tooltip
365
+ [zContent]="'i18n_z_ui_gallery_remove' | translate"
366
+ >
367
+ <i z-icon zType="lucideTrash2" zSize="16"></i>
368
+ </button>
369
+ }
370
+ </div>
371
+ </div>
372
+ }
373
+ `, isInline: true, dependencies: [{ kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
374
+ }
375
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryItemComponent, decorators: [{
376
+ type: Component,
377
+ args: [{
378
+ selector: 'z-gallery-item',
379
+ imports: [ZButtonComponent, ZIconComponent, ZTooltipDirective, ZSkeletonComponent, TranslatePipe],
380
+ standalone: true,
381
+ template: `
382
+ @if (zMode() === 'grid') {
383
+ <div
384
+ class="group border-border bg-card hover:border-primary/30 relative cursor-pointer overflow-hidden rounded-xl border shadow-sm transition-all duration-200 hover:shadow-md"
385
+ (click)="onPreview()"
386
+ >
387
+ <div class="relative aspect-square overflow-hidden">
388
+ @if (isImage()) {
389
+ @if (isLoading()) {
390
+ <z-skeleton class="absolute inset-0" />
391
+ }
392
+ <img
393
+ [src]="file().thumbnailUrl || file().url"
394
+ [alt]="file().name"
395
+ class="size-full object-cover transition-all duration-300"
396
+ [class.opacity-0]="isLoading()"
397
+ [class.scale-100]="!isLoading()"
398
+ [class.group-hover:scale-105]="!isLoading()"
399
+ loading="lazy"
400
+ (load)="onImageLoad()"
401
+ (error)="onImageError()"
402
+ />
403
+ } @else {
404
+ <div class="flex size-full flex-col items-center justify-center gap-2" [class]="categoryStyle().bg">
405
+ <i z-icon [zType]="categoryStyle().icon" zSize="32" [class]="categoryStyle().text"></i>
406
+ <span class="text-xs font-semibold" [class]="categoryStyle().text">
407
+ {{ categoryStyle().label }}
408
+ </span>
409
+ </div>
410
+ }
411
+
412
+ <div
413
+ class="absolute inset-0 flex items-center justify-center gap-2 bg-black/40 opacity-0 backdrop-blur-[2px] transition-all duration-200 group-hover:opacity-100"
414
+ >
415
+ @if (zShowPreview() && canPreview()) {
416
+ <button
417
+ z-button
418
+ zType="secondary"
419
+ zSize="sm"
420
+ zShape="circle"
421
+ class="shadow-lg"
422
+ (click)="onPreviewClick($event)"
423
+ z-tooltip
424
+ [zContent]="'i18n_z_ui_gallery_preview' | translate"
425
+ >
426
+ <i z-icon zType="lucideEye" zSize="16"></i>
427
+ </button>
428
+ }
429
+ @if (zShowDownload() && (file().downloadUrl || file().url)) {
430
+ <button
431
+ z-button
432
+ zType="secondary"
433
+ zSize="sm"
434
+ zShape="circle"
435
+ class="shadow-lg"
436
+ (click)="onDownload($event)"
437
+ z-tooltip
438
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
439
+ >
440
+ <i z-icon zType="lucideDownload" zSize="16"></i>
441
+ </button>
442
+ }
443
+ @if (zShowRemove()) {
444
+ <button
445
+ z-button
446
+ zType="secondary"
447
+ zSize="sm"
448
+ zShape="circle"
449
+ class="shadow-lg"
450
+ (click)="onRemove($event)"
451
+ z-tooltip
452
+ [zContent]="'i18n_z_ui_gallery_remove' | translate"
453
+ >
454
+ <i z-icon zType="lucideTrash2" zSize="16"></i>
455
+ </button>
456
+ }
457
+ </div>
458
+ </div>
459
+
460
+ <div class="p-2.5">
461
+ <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
462
+ {{ file().name }}
463
+ </p>
464
+ <p class="text-muted-foreground mt-0.5 text-xs">
465
+ {{ formattedSize() }}
466
+ </p>
467
+ </div>
468
+ </div>
469
+ } @else {
470
+ <div
471
+ class="group border-border bg-card hover:border-primary/30 flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 shadow-sm transition-all duration-150 hover:shadow-md"
472
+ (click)="onPreview()"
473
+ >
474
+ <div class="flex size-10 shrink-0 items-center justify-center rounded-lg" [class]="categoryStyle().bg">
475
+ @if (isImage() && (file().thumbnailUrl || file().url)) {
476
+ <img
477
+ [src]="file().thumbnailUrl || file().url"
478
+ [alt]="file().name"
479
+ class="size-full rounded-lg object-cover"
480
+ loading="lazy"
481
+ />
482
+ } @else {
483
+ <i z-icon [zType]="categoryStyle().icon" zSize="18" [class]="categoryStyle().text"></i>
484
+ }
485
+ </div>
486
+
487
+ <div class="min-w-0 flex-1">
488
+ <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
489
+ {{ file().name }}
490
+ </p>
491
+ <p class="text-muted-foreground text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
492
+ </div>
493
+
494
+ <div class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
495
+ @if (zShowPreview() && canPreview()) {
496
+ <button
497
+ z-button
498
+ zType="ghost"
499
+ zSize="xs"
500
+ zShape="circle"
501
+ (click)="onPreviewClick($event)"
502
+ z-tooltip
503
+ [zContent]="'i18n_z_ui_gallery_preview' | translate"
504
+ >
505
+ <i z-icon zType="lucideEye" zSize="16"></i>
506
+ </button>
507
+ }
508
+ @if (zShowDownload() && (file().downloadUrl || file().url)) {
509
+ <button
510
+ z-button
511
+ zType="ghost"
512
+ zSize="xs"
513
+ zShape="circle"
514
+ (click)="onDownload($event)"
515
+ z-tooltip
516
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
517
+ >
518
+ <i z-icon zType="lucideDownload" zSize="16"></i>
519
+ </button>
520
+ }
521
+ @if (zShowRemove()) {
522
+ <button
523
+ z-button
524
+ zType="ghost"
525
+ zSize="xs"
526
+ zShape="circle"
527
+ (click)="onRemove($event)"
528
+ z-tooltip
529
+ [zContent]="'i18n_z_ui_gallery_remove' | translate"
530
+ >
531
+ <i z-icon zType="lucideTrash2" zSize="16"></i>
532
+ </button>
533
+ }
534
+ </div>
535
+ </div>
536
+ }
537
+ `,
538
+ changeDetection: ChangeDetectionStrategy.OnPush,
539
+ encapsulation: ViewEncapsulation.None,
540
+ host: {
541
+ class: 'z-gallery-item block',
542
+ },
543
+ }]
544
+ }], propDecorators: { file: [{ type: i0.Input, args: [{ isSignal: true, alias: "file", required: true }] }], zMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMode", required: false }] }], zSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSize", required: false }] }], zShowDownload: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowDownload", required: false }] }], zShowPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowPreview", required: false }] }], zShowRemove: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowRemove", required: false }] }], zOnDownload: [{ type: i0.Output, args: ["zOnDownload"] }], zOnPreview: [{ type: i0.Output, args: ["zOnPreview"] }], zOnRemove: [{ type: i0.Output, args: ["zOnRemove"] }] } });
545
+
546
+ class ZGalleryPreviewComponent {
547
+ file = input.required(...(ngDevMode ? [{ debugName: "file" }] : []));
548
+ files = input([], ...(ngDevMode ? [{ debugName: "files" }] : []));
549
+ zShowRemove = input(false, ...(ngDevMode ? [{ debugName: "zShowRemove" }] : []));
550
+ zOnClose = output();
551
+ zOnDownload = output();
552
+ zOnNavigate = output();
553
+ zOnRemove = output();
554
+ zoomed = signal(false, ...(ngDevMode ? [{ debugName: "zoomed" }] : []));
555
+ isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
556
+ rotation = signal(0, ...(ngDevMode ? [{ debugName: "rotation" }] : []));
557
+ _mediaLoaded = signal(false, ...(ngDevMode ? [{ debugName: "_mediaLoaded" }] : []));
558
+ _loadingStartTime = 0;
559
+ _minLoadingTime = 300;
560
+ constructor() {
561
+ effect(() => {
562
+ const currentFile = this.file();
563
+ if (currentFile) {
564
+ this.isLoading.set(true);
565
+ this._mediaLoaded.set(false);
566
+ this._loadingStartTime = Date.now();
567
+ this.zoomed.set(false);
568
+ this.rotation.set(0);
569
+ }
570
+ });
571
+ }
572
+ _completeLoading() {
573
+ const elapsed = Date.now() - this._loadingStartTime;
574
+ const remaining = this._minLoadingTime - elapsed;
575
+ if (remaining > 0) {
576
+ setTimeout(() => this.isLoading.set(false), remaining);
577
+ }
578
+ else {
579
+ this.isLoading.set(false);
580
+ }
581
+ }
582
+ currentIndex = computed(() => {
583
+ const allFiles = this.files();
584
+ const current = this.file();
585
+ return allFiles.findIndex(f => f.id === current.id);
586
+ }, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
587
+ hasPrev = computed(() => this.currentIndex() > 0, ...(ngDevMode ? [{ debugName: "hasPrev" }] : []));
588
+ hasNext = computed(() => this.currentIndex() < this.files().length - 1, ...(ngDevMode ? [{ debugName: "hasNext" }] : []));
589
+ isImageFile = computed(() => isImage(this.file()), ...(ngDevMode ? [{ debugName: "isImageFile" }] : []));
590
+ isPdf = computed(() => getFileCategory(this.file()) === 'pdf', ...(ngDevMode ? [{ debugName: "isPdf" }] : []));
591
+ isVideo = computed(() => getFileCategory(this.file()) === 'video', ...(ngDevMode ? [{ debugName: "isVideo" }] : []));
592
+ canPreview = computed(() => isPreviewable(this.file()), ...(ngDevMode ? [{ debugName: "canPreview" }] : []));
593
+ formattedSize = computed(() => formatFileSize(this.file().size), ...(ngDevMode ? [{ debugName: "formattedSize" }] : []));
594
+ imageTransform = computed(() => {
595
+ const rot = this.rotation();
596
+ const zoom = this.zoomed() ? 'scale(1.5)' : 'scale(1)';
597
+ return `rotate(${rot}deg) ${zoom}`;
598
+ }, ...(ngDevMode ? [{ debugName: "imageTransform" }] : []));
599
+ fileIcon = computed(() => {
600
+ const category = getFileCategory(this.file());
601
+ const iconMap = {
602
+ pdf: 'lucideFileText',
603
+ word: 'lucideFileText',
604
+ excel: 'lucideFileSpreadsheet',
605
+ video: 'lucideVideo',
606
+ audio: 'lucideMusic',
607
+ archive: 'lucideFileArchive',
608
+ code: 'lucideCode',
609
+ text: 'lucideFileText',
610
+ unknown: 'lucideFile',
611
+ };
612
+ return iconMap[category] || 'lucideFile';
613
+ }, ...(ngDevMode ? [{ debugName: "fileIcon" }] : []));
614
+ onKeyDown(event) {
615
+ switch (event.key) {
616
+ case 'Escape':
617
+ this.close();
618
+ break;
619
+ case 'ArrowLeft':
620
+ if (this.hasPrev()) {
621
+ this.navigate(-1);
622
+ }
623
+ break;
624
+ case 'ArrowRight':
625
+ if (this.hasNext()) {
626
+ this.navigate(1);
627
+ }
628
+ break;
629
+ case 'r':
630
+ case 'R':
631
+ if (this.isImageFile()) {
632
+ this.rotateRight();
633
+ }
634
+ break;
635
+ }
636
+ }
637
+ close() {
638
+ this.zOnClose.emit();
639
+ }
640
+ navigate(direction) {
641
+ const allFiles = this.files();
642
+ const newIndex = this.currentIndex() + direction;
643
+ if (newIndex >= 0 && newIndex < allFiles.length) {
644
+ this.zoomed.set(false);
645
+ this.rotation.set(0);
646
+ this.isLoading.set(true);
647
+ this._mediaLoaded.set(false);
648
+ this._loadingStartTime = Date.now();
649
+ this.zOnNavigate.emit(allFiles[newIndex]);
650
+ }
651
+ }
652
+ toggleZoom() {
653
+ if (this.isImageFile()) {
654
+ this.zoomed.update(z => !z);
655
+ }
656
+ }
657
+ rotateLeft() {
658
+ this.rotation.update(r => (r - 90 + 360) % 360);
659
+ }
660
+ rotateRight() {
661
+ this.rotation.update(r => (r + 90) % 360);
662
+ }
663
+ onImageLoad() {
664
+ this._completeLoading();
665
+ }
666
+ onVideoLoad() {
667
+ this._completeLoading();
668
+ }
669
+ onPdfLoad() {
670
+ this._completeLoading();
671
+ }
672
+ onMediaError() {
673
+ this._completeLoading();
674
+ }
675
+ download() {
676
+ this.zOnDownload.emit(this.file());
677
+ }
678
+ remove() {
679
+ this.zOnRemove.emit(this.file());
680
+ }
681
+ onBackdropClick(event) {
682
+ if (event.target.classList.contains('fixed')) {
683
+ this.close();
684
+ }
685
+ }
686
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
687
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZGalleryPreviewComponent, isStandalone: true, selector: "z-gallery-preview", inputs: { file: { classPropertyName: "file", publicName: "file", isSignal: true, isRequired: true, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, zShowRemove: { classPropertyName: "zShowRemove", publicName: "zShowRemove", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnClose: "zOnClose", zOnDownload: "zOnDownload", zOnNavigate: "zOnNavigate", zOnRemove: "zOnRemove" }, host: { listeners: { "document:keydown": "onKeyDown($event)" }, classAttribute: "z-gallery-preview" }, ngImport: i0, template: `
688
+ <div
689
+ class="animate-in fade-in fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm duration-200"
690
+ (click)="onBackdropClick($event)"
691
+ >
692
+ <button
693
+ z-button
694
+ zType="ghost"
695
+ zShape="circle"
696
+ class="absolute top-4 right-4 z-10 text-white hover:bg-white/20"
697
+ (click)="close()"
698
+ >
699
+ <i z-icon zType="lucideX" zSize="24"></i>
700
+ </button>
701
+
702
+ @if (hasPrev()) {
703
+ <button
704
+ z-button
705
+ zType="ghost"
706
+ zShape="circle"
707
+ class="absolute left-4 z-10 text-white hover:bg-white/20"
708
+ (click)="navigate(-1)"
709
+ >
710
+ <i z-icon zType="lucideChevronLeft" zSize="32"></i>
711
+ </button>
712
+ }
713
+
714
+ @if (hasNext()) {
715
+ <button
716
+ z-button
717
+ zType="ghost"
718
+ zShape="circle"
719
+ class="absolute right-4 z-10 text-white hover:bg-white/20"
720
+ (click)="navigate(1)"
721
+ >
722
+ <i z-icon zType="lucideChevronRight" zSize="32"></i>
723
+ </button>
724
+ }
725
+
726
+ <div class="flex h-full w-full flex-col items-center" (click)="$event.stopPropagation()">
727
+ <div
728
+ class="dark:border-border absolute top-4 left-1/2 z-10 flex -translate-x-1/2 items-center gap-2 rounded-[6px] border border-transparent bg-black/60 px-4 py-2 text-sm text-white backdrop-blur-sm dark:border"
729
+ >
730
+ <div>
731
+ <span class="max-w-[200px] truncate font-medium">{{ file().name }}</span>
732
+ <span class="text-white/70">{{ formattedSize() }}</span>
733
+ </div>
734
+
735
+ @if (files().length > 1) {
736
+ <span class="text-white/70">{{ currentIndex() + 1 }} / {{ files().length }}</span>
737
+ }
738
+
739
+ <div class="ml-1 flex items-center gap-0.5">
740
+ @if (isImageFile()) {
741
+ <button
742
+ z-button
743
+ zType="ghost"
744
+ zSize="xs"
745
+ zShape="circle"
746
+ class="text-white hover:bg-white/20"
747
+ z-tooltip
748
+ [zContent]="'i18n_z_ui_gallery_rotate_left' | translate"
749
+ (click)="rotateLeft()"
750
+ >
751
+ <i z-icon zType="lucideRefreshCcw" zSize="14"></i>
752
+ </button>
753
+ <button
754
+ z-button
755
+ zType="ghost"
756
+ zSize="xs"
757
+ zShape="circle"
758
+ class="text-white hover:bg-white/20"
759
+ z-tooltip
760
+ [zContent]="'i18n_z_ui_gallery_rotate_right' | translate"
761
+ (click)="rotateRight()"
762
+ >
763
+ <i z-icon zType="lucideRefreshCw" zSize="14"></i>
764
+ </button>
765
+ }
766
+
767
+ <button
768
+ z-button
769
+ zType="ghost"
770
+ zSize="xs"
771
+ zShape="circle"
772
+ class="text-white hover:bg-white/20"
773
+ z-tooltip
774
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
775
+ (click)="download()"
776
+ >
777
+ <i z-icon zType="lucideDownload" zSize="14"></i>
778
+ </button>
779
+
780
+ @if (zShowRemove()) {
781
+ <button
782
+ z-button
783
+ zType="ghost"
784
+ zSize="xs"
785
+ zShape="circle"
786
+ class="text-white hover:bg-white/20"
787
+ z-tooltip
788
+ [zContent]="'i18n_z_ui_gallery_delete' | translate"
789
+ (click)="remove()"
790
+ >
791
+ <i z-icon zType="lucideTrash2" zSize="14"></i>
792
+ </button>
793
+ }
794
+ </div>
795
+ </div>
796
+
797
+ <div class="relative flex flex-1 items-center justify-center pt-16">
798
+ @if (isImageFile()) {
799
+ <div class="relative flex h-[60vh] w-[70vw] items-center justify-center">
800
+ @if (isLoading()) {
801
+ <z-skeleton class="absolute inset-0 rounded-lg" />
802
+ }
803
+ <img
804
+ [src]="file().url || file().thumbnailUrl"
805
+ [alt]="file().name"
806
+ class="max-h-full max-w-full rounded-lg object-contain shadow-2xl transition-opacity duration-300"
807
+ [class.opacity-0]="isLoading()"
808
+ [class.opacity-100]="!isLoading()"
809
+ [class.cursor-zoom-in]="!zoomed()"
810
+ [class.cursor-zoom-out]="zoomed()"
811
+ [style.transform]="imageTransform()"
812
+ (load)="onImageLoad()"
813
+ (error)="onMediaError()"
814
+ (click)="toggleZoom()"
815
+ />
816
+ </div>
817
+ } @else if (isPdf()) {
818
+ <div class="relative h-[75vh] w-[85vw]">
819
+ @if (isLoading()) {
820
+ <z-skeleton class="absolute inset-0 rounded-lg" />
821
+ }
822
+ <iframe
823
+ [src]="file().url | zSafeResourceUrl"
824
+ class="absolute inset-0 size-full rounded-lg bg-white shadow-2xl transition-opacity duration-300"
825
+ [class.opacity-0]="isLoading()"
826
+ [class.opacity-100]="!isLoading()"
827
+ frameborder="0"
828
+ (load)="onPdfLoad()"
829
+ ></iframe>
830
+ </div>
831
+ } @else if (isVideo()) {
832
+ <div class="relative flex h-[60vh] w-[70vw] items-center justify-center">
833
+ @if (isLoading()) {
834
+ <z-skeleton class="absolute inset-0 rounded-lg" />
835
+ }
836
+ <video
837
+ [src]="file().url"
838
+ controls
839
+ autoplay
840
+ class="max-h-full max-w-full rounded-lg shadow-2xl transition-opacity duration-300"
841
+ [class.opacity-0]="isLoading()"
842
+ [class.opacity-100]="!isLoading()"
843
+ (loadeddata)="onVideoLoad()"
844
+ (error)="onMediaError()"
845
+ >
846
+ Your browser does not support the video tag.
847
+ </video>
848
+ </div>
849
+ } @else {
850
+ <div class="bg-card flex flex-col items-center justify-center rounded-lg p-12 shadow-2xl">
851
+ <div class="bg-muted mb-4 flex size-24 items-center justify-center rounded-xl">
852
+ <i z-icon [zType]="fileIcon()" zSize="40" class="text-muted-foreground"></i>
853
+ </div>
854
+ <span class="text-foreground mb-2 text-lg font-medium">{{ file().name }}</span>
855
+ <span class="text-muted-foreground mb-4 text-sm">{{ formattedSize() }}</span>
856
+ <span class="text-muted-foreground rounded-md bg-amber-100 px-3 py-1.5 text-xs dark:bg-amber-900/30">
857
+ <i
858
+ z-icon
859
+ zType="lucideInfo"
860
+ zSize="14"
861
+ class="mr-1 inline-block align-middle text-amber-600 dark:text-amber-400"
862
+ ></i>
863
+ <span class="align-middle text-amber-700 dark:text-amber-300">
864
+ {{ 'i18n_z_ui_gallery_preview_not_supported' | translate }}
865
+ </span>
866
+ </span>
867
+ </div>
868
+ }
869
+ </div>
870
+ </div>
871
+ </div>
872
+ `, isInline: true, dependencies: [{ kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "pipe", type: ZSafeResourceUrlPipe, name: "zSafeResourceUrl" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
873
+ }
874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryPreviewComponent, decorators: [{
875
+ type: Component,
876
+ args: [{
877
+ selector: 'z-gallery-preview',
878
+ imports: [
879
+ ZButtonComponent,
880
+ ZIconComponent,
881
+ ZSkeletonComponent,
882
+ ZTooltipDirective,
883
+ ZSafeResourceUrlPipe,
884
+ TranslatePipe,
885
+ ],
886
+ standalone: true,
887
+ template: `
888
+ <div
889
+ class="animate-in fade-in fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm duration-200"
890
+ (click)="onBackdropClick($event)"
891
+ >
892
+ <button
893
+ z-button
894
+ zType="ghost"
895
+ zShape="circle"
896
+ class="absolute top-4 right-4 z-10 text-white hover:bg-white/20"
897
+ (click)="close()"
898
+ >
899
+ <i z-icon zType="lucideX" zSize="24"></i>
900
+ </button>
901
+
902
+ @if (hasPrev()) {
903
+ <button
904
+ z-button
905
+ zType="ghost"
906
+ zShape="circle"
907
+ class="absolute left-4 z-10 text-white hover:bg-white/20"
908
+ (click)="navigate(-1)"
909
+ >
910
+ <i z-icon zType="lucideChevronLeft" zSize="32"></i>
911
+ </button>
912
+ }
913
+
914
+ @if (hasNext()) {
915
+ <button
916
+ z-button
917
+ zType="ghost"
918
+ zShape="circle"
919
+ class="absolute right-4 z-10 text-white hover:bg-white/20"
920
+ (click)="navigate(1)"
921
+ >
922
+ <i z-icon zType="lucideChevronRight" zSize="32"></i>
923
+ </button>
924
+ }
925
+
926
+ <div class="flex h-full w-full flex-col items-center" (click)="$event.stopPropagation()">
927
+ <div
928
+ class="dark:border-border absolute top-4 left-1/2 z-10 flex -translate-x-1/2 items-center gap-2 rounded-[6px] border border-transparent bg-black/60 px-4 py-2 text-sm text-white backdrop-blur-sm dark:border"
929
+ >
930
+ <div>
931
+ <span class="max-w-[200px] truncate font-medium">{{ file().name }}</span>
932
+ <span class="text-white/70">{{ formattedSize() }}</span>
933
+ </div>
934
+
935
+ @if (files().length > 1) {
936
+ <span class="text-white/70">{{ currentIndex() + 1 }} / {{ files().length }}</span>
937
+ }
938
+
939
+ <div class="ml-1 flex items-center gap-0.5">
940
+ @if (isImageFile()) {
941
+ <button
942
+ z-button
943
+ zType="ghost"
944
+ zSize="xs"
945
+ zShape="circle"
946
+ class="text-white hover:bg-white/20"
947
+ z-tooltip
948
+ [zContent]="'i18n_z_ui_gallery_rotate_left' | translate"
949
+ (click)="rotateLeft()"
950
+ >
951
+ <i z-icon zType="lucideRefreshCcw" zSize="14"></i>
952
+ </button>
953
+ <button
954
+ z-button
955
+ zType="ghost"
956
+ zSize="xs"
957
+ zShape="circle"
958
+ class="text-white hover:bg-white/20"
959
+ z-tooltip
960
+ [zContent]="'i18n_z_ui_gallery_rotate_right' | translate"
961
+ (click)="rotateRight()"
962
+ >
963
+ <i z-icon zType="lucideRefreshCw" zSize="14"></i>
964
+ </button>
965
+ }
966
+
967
+ <button
968
+ z-button
969
+ zType="ghost"
970
+ zSize="xs"
971
+ zShape="circle"
972
+ class="text-white hover:bg-white/20"
973
+ z-tooltip
974
+ [zContent]="'i18n_z_ui_gallery_download' | translate"
975
+ (click)="download()"
976
+ >
977
+ <i z-icon zType="lucideDownload" zSize="14"></i>
978
+ </button>
979
+
980
+ @if (zShowRemove()) {
981
+ <button
982
+ z-button
983
+ zType="ghost"
984
+ zSize="xs"
985
+ zShape="circle"
986
+ class="text-white hover:bg-white/20"
987
+ z-tooltip
988
+ [zContent]="'i18n_z_ui_gallery_delete' | translate"
989
+ (click)="remove()"
990
+ >
991
+ <i z-icon zType="lucideTrash2" zSize="14"></i>
992
+ </button>
993
+ }
994
+ </div>
995
+ </div>
996
+
997
+ <div class="relative flex flex-1 items-center justify-center pt-16">
998
+ @if (isImageFile()) {
999
+ <div class="relative flex h-[60vh] w-[70vw] items-center justify-center">
1000
+ @if (isLoading()) {
1001
+ <z-skeleton class="absolute inset-0 rounded-lg" />
1002
+ }
1003
+ <img
1004
+ [src]="file().url || file().thumbnailUrl"
1005
+ [alt]="file().name"
1006
+ class="max-h-full max-w-full rounded-lg object-contain shadow-2xl transition-opacity duration-300"
1007
+ [class.opacity-0]="isLoading()"
1008
+ [class.opacity-100]="!isLoading()"
1009
+ [class.cursor-zoom-in]="!zoomed()"
1010
+ [class.cursor-zoom-out]="zoomed()"
1011
+ [style.transform]="imageTransform()"
1012
+ (load)="onImageLoad()"
1013
+ (error)="onMediaError()"
1014
+ (click)="toggleZoom()"
1015
+ />
1016
+ </div>
1017
+ } @else if (isPdf()) {
1018
+ <div class="relative h-[75vh] w-[85vw]">
1019
+ @if (isLoading()) {
1020
+ <z-skeleton class="absolute inset-0 rounded-lg" />
1021
+ }
1022
+ <iframe
1023
+ [src]="file().url | zSafeResourceUrl"
1024
+ class="absolute inset-0 size-full rounded-lg bg-white shadow-2xl transition-opacity duration-300"
1025
+ [class.opacity-0]="isLoading()"
1026
+ [class.opacity-100]="!isLoading()"
1027
+ frameborder="0"
1028
+ (load)="onPdfLoad()"
1029
+ ></iframe>
1030
+ </div>
1031
+ } @else if (isVideo()) {
1032
+ <div class="relative flex h-[60vh] w-[70vw] items-center justify-center">
1033
+ @if (isLoading()) {
1034
+ <z-skeleton class="absolute inset-0 rounded-lg" />
1035
+ }
1036
+ <video
1037
+ [src]="file().url"
1038
+ controls
1039
+ autoplay
1040
+ class="max-h-full max-w-full rounded-lg shadow-2xl transition-opacity duration-300"
1041
+ [class.opacity-0]="isLoading()"
1042
+ [class.opacity-100]="!isLoading()"
1043
+ (loadeddata)="onVideoLoad()"
1044
+ (error)="onMediaError()"
1045
+ >
1046
+ Your browser does not support the video tag.
1047
+ </video>
1048
+ </div>
1049
+ } @else {
1050
+ <div class="bg-card flex flex-col items-center justify-center rounded-lg p-12 shadow-2xl">
1051
+ <div class="bg-muted mb-4 flex size-24 items-center justify-center rounded-xl">
1052
+ <i z-icon [zType]="fileIcon()" zSize="40" class="text-muted-foreground"></i>
1053
+ </div>
1054
+ <span class="text-foreground mb-2 text-lg font-medium">{{ file().name }}</span>
1055
+ <span class="text-muted-foreground mb-4 text-sm">{{ formattedSize() }}</span>
1056
+ <span class="text-muted-foreground rounded-md bg-amber-100 px-3 py-1.5 text-xs dark:bg-amber-900/30">
1057
+ <i
1058
+ z-icon
1059
+ zType="lucideInfo"
1060
+ zSize="14"
1061
+ class="mr-1 inline-block align-middle text-amber-600 dark:text-amber-400"
1062
+ ></i>
1063
+ <span class="align-middle text-amber-700 dark:text-amber-300">
1064
+ {{ 'i18n_z_ui_gallery_preview_not_supported' | translate }}
1065
+ </span>
1066
+ </span>
1067
+ </div>
1068
+ }
1069
+ </div>
1070
+ </div>
1071
+ </div>
1072
+ `,
1073
+ changeDetection: ChangeDetectionStrategy.OnPush,
1074
+ encapsulation: ViewEncapsulation.None,
1075
+ host: {
1076
+ class: 'z-gallery-preview',
1077
+ '(document:keydown)': 'onKeyDown($event)',
1078
+ },
1079
+ }]
1080
+ }], ctorParameters: () => [], propDecorators: { file: [{ type: i0.Input, args: [{ isSignal: true, alias: "file", required: true }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], zShowRemove: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowRemove", required: false }] }], zOnClose: [{ type: i0.Output, args: ["zOnClose"] }], zOnDownload: [{ type: i0.Output, args: ["zOnDownload"] }], zOnNavigate: [{ type: i0.Output, args: ["zOnNavigate"] }], zOnRemove: [{ type: i0.Output, args: ["zOnRemove"] }] } });
1081
+
1082
+ const zGalleryVariants = cva('z-gallery', {
1083
+ variants: {
1084
+ zSize: {
1085
+ sm: 'z-gallery--sm',
1086
+ default: 'z-gallery--default',
1087
+ lg: 'z-gallery--lg',
1088
+ },
1089
+ },
1090
+ defaultVariants: {
1091
+ zSize: 'default',
1092
+ },
1093
+ });
1094
+ // Grid item variants
1095
+ const zGalleryItemVariants = cva('group relative overflow-hidden rounded-lg border border-border bg-card transition-all duration-200 cursor-pointer', {
1096
+ variants: {
1097
+ zMode: {
1098
+ grid: 'flex flex-col hover:scale-[1.02] hover:shadow-md hover:border-primary/50',
1099
+ list: 'flex flex-row items-center gap-3 p-3 hover:bg-accent hover:border-primary/50',
1100
+ },
1101
+ zSize: {
1102
+ sm: '',
1103
+ default: '',
1104
+ lg: '',
1105
+ },
1106
+ },
1107
+ compoundVariants: [
1108
+ // Grid sizes
1109
+ { zMode: 'grid', zSize: 'sm', class: '' },
1110
+ { zMode: 'grid', zSize: 'default', class: '' },
1111
+ { zMode: 'grid', zSize: 'lg', class: '' },
1112
+ // List sizes
1113
+ { zMode: 'list', zSize: 'sm', class: 'p-2' },
1114
+ { zMode: 'list', zSize: 'default', class: 'p-3' },
1115
+ { zMode: 'list', zSize: 'lg', class: 'p-4' },
1116
+ ],
1117
+ defaultVariants: {
1118
+ zMode: 'grid',
1119
+ zSize: 'default',
1120
+ },
1121
+ });
1122
+ // File icon badge variants
1123
+ const zGalleryFileIconVariants = cva('flex items-center justify-center rounded-md font-bold uppercase select-none', {
1124
+ variants: {
1125
+ zSize: {
1126
+ sm: 'w-full h-16 text-xs',
1127
+ default: 'w-full h-24 text-sm',
1128
+ lg: 'w-full h-32 text-base',
1129
+ },
1130
+ zMode: {
1131
+ grid: '',
1132
+ list: '',
1133
+ },
1134
+ },
1135
+ compoundVariants: [
1136
+ { zMode: 'list', zSize: 'sm', class: 'size-8 text-[10px]' },
1137
+ { zMode: 'list', zSize: 'default', class: 'size-10 text-xs' },
1138
+ { zMode: 'list', zSize: 'lg', class: 'size-12 text-sm' },
1139
+ ],
1140
+ defaultVariants: {
1141
+ zSize: 'default',
1142
+ zMode: 'grid',
1143
+ },
1144
+ });
1145
+
1146
+ class ZGalleryComponent {
1147
+ zFiles = input([], ...(ngDevMode ? [{ debugName: "zFiles" }] : []));
1148
+ zMode = model('grid', ...(ngDevMode ? [{ debugName: "zMode" }] : []));
1149
+ zSize = input('default', ...(ngDevMode ? [{ debugName: "zSize" }] : []));
1150
+ zColumns = input(4, ...(ngDevMode ? [{ debugName: "zColumns" }] : []));
1151
+ zShowDownload = input(true, ...(ngDevMode ? [{ debugName: "zShowDownload" }] : []));
1152
+ zShowPreview = input(true, ...(ngDevMode ? [{ debugName: "zShowPreview" }] : []));
1153
+ zShowRemove = input(false, ...(ngDevMode ? [{ debugName: "zShowRemove" }] : []));
1154
+ zShowModeToggle = input(true, ...(ngDevMode ? [{ debugName: "zShowModeToggle" }] : []));
1155
+ zShowSearch = input(false, ...(ngDevMode ? [{ debugName: "zShowSearch" }] : []));
1156
+ zServerSearch = input(false, ...(ngDevMode ? [{ debugName: "zServerSearch" }] : []));
1157
+ zSearchPlaceholder = input('Search files...', ...(ngDevMode ? [{ debugName: "zSearchPlaceholder" }] : []));
1158
+ zToggleSize = input('xs', ...(ngDevMode ? [{ debugName: "zToggleSize" }] : []));
1159
+ zTitle = input('', ...(ngDevMode ? [{ debugName: "zTitle" }] : []));
1160
+ zEmptyText = input('No files', ...(ngDevMode ? [{ debugName: "zEmptyText" }] : []));
1161
+ zEmptyIcon = input('lucideImage', ...(ngDevMode ? [{ debugName: "zEmptyIcon" }] : []));
1162
+ zClass = input('', ...(ngDevMode ? [{ debugName: "zClass" }] : []));
1163
+ zOnDownload = output();
1164
+ zOnPreview = output();
1165
+ zOnRemove = output();
1166
+ zModeChange = output();
1167
+ zSearchChange = output();
1168
+ zControl = output();
1169
+ modeIndex = signal(0, ...(ngDevMode ? [{ debugName: "modeIndex" }] : []));
1170
+ previewFile = signal(null, ...(ngDevMode ? [{ debugName: "previewFile" }] : []));
1171
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
1172
+ currentMode = computed(() => {
1173
+ const index = this.modeIndex();
1174
+ return index === 1 ? 'list' : 'grid';
1175
+ }, ...(ngDevMode ? [{ debugName: "currentMode" }] : []));
1176
+ gridColumns = computed(() => {
1177
+ const cols = this.zColumns();
1178
+ return `repeat(${cols}, minmax(0, 1fr))`;
1179
+ }, ...(ngDevMode ? [{ debugName: "gridColumns" }] : []));
1180
+ containerClasses = computed(() => zMergeClasses(zGalleryVariants({ zSize: this.zSize() }), this.zClass()), ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
1181
+ showHeader = computed(() => this.zTitle() || this.zShowSearch() || (this.zShowModeToggle() && this.zFiles().length > 0), ...(ngDevMode ? [{ debugName: "showHeader" }] : []));
1182
+ toggleIconSize = computed(() => {
1183
+ const size = this.zToggleSize();
1184
+ switch (size) {
1185
+ case 'xs':
1186
+ return '14';
1187
+ case 'sm':
1188
+ return '16';
1189
+ case 'lg':
1190
+ return '20';
1191
+ default:
1192
+ return '18';
1193
+ }
1194
+ }, ...(ngDevMode ? [{ debugName: "toggleIconSize" }] : []));
1195
+ searchInputSize = computed(() => {
1196
+ const size = this.zToggleSize();
1197
+ if (size === 'xs') {
1198
+ return 'sm';
1199
+ }
1200
+ return size;
1201
+ }, ...(ngDevMode ? [{ debugName: "searchInputSize" }] : []));
1202
+ filteredFiles = computed(() => {
1203
+ const files = this.zFiles();
1204
+ const query = this.searchQuery().toLowerCase().trim();
1205
+ if (!query || this.zServerSearch()) {
1206
+ return files;
1207
+ }
1208
+ return files.filter(file => file.name.toLowerCase().includes(query));
1209
+ }, ...(ngDevMode ? [{ debugName: "filteredFiles" }] : []));
1210
+ ngAfterViewInit() {
1211
+ this.zControl.emit({
1212
+ setMode: mode => {
1213
+ this.modeIndex.set(mode === 'list' ? 1 : 0);
1214
+ this.zMode.set(mode);
1215
+ this.zModeChange.emit(mode);
1216
+ },
1217
+ getMode: () => this.currentMode(),
1218
+ getFiles: () => this.zFiles(),
1219
+ openPreview: file => this.previewFile.set(file),
1220
+ closePreview: () => this.previewFile.set(null),
1221
+ });
1222
+ }
1223
+ onSearchChange(query) {
1224
+ this.searchQuery.set(query);
1225
+ this.zSearchChange.emit(query);
1226
+ }
1227
+ onDownload(file) {
1228
+ this.zOnDownload.emit(file);
1229
+ const downloadLink = file.downloadUrl || file.url;
1230
+ if (downloadLink) {
1231
+ const link = document.createElement('a');
1232
+ link.href = downloadLink;
1233
+ link.download = file.name;
1234
+ link.target = '_blank';
1235
+ document.body.appendChild(link);
1236
+ link.click();
1237
+ document.body.removeChild(link);
1238
+ }
1239
+ }
1240
+ onPreview(file) {
1241
+ this.previewFile.set(file);
1242
+ this.zOnPreview.emit(file);
1243
+ }
1244
+ onRemove(file) {
1245
+ this.zOnRemove.emit(file);
1246
+ }
1247
+ closePreview() {
1248
+ this.previewFile.set(null);
1249
+ }
1250
+ onNavigatePreview(file) {
1251
+ this.previewFile.set(file);
1252
+ }
1253
+ onRemoveFromPreview(file) {
1254
+ this.zOnRemove.emit(file);
1255
+ const allFiles = this.filteredFiles();
1256
+ const currentIndex = allFiles.findIndex(f => f.id === file.id);
1257
+ if (allFiles.length <= 1) {
1258
+ this.closePreview();
1259
+ }
1260
+ if (currentIndex < allFiles.length - 1) {
1261
+ this.previewFile.set(allFiles[currentIndex + 1]);
1262
+ return;
1263
+ }
1264
+ this.previewFile.set(allFiles[currentIndex - 1]);
1265
+ }
1266
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1267
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZGalleryComponent, isStandalone: true, selector: "z-gallery", inputs: { zFiles: { classPropertyName: "zFiles", publicName: "zFiles", isSignal: true, isRequired: false, transformFunction: null }, zMode: { classPropertyName: "zMode", publicName: "zMode", isSignal: true, isRequired: false, transformFunction: null }, zSize: { classPropertyName: "zSize", publicName: "zSize", isSignal: true, isRequired: false, transformFunction: null }, zColumns: { classPropertyName: "zColumns", publicName: "zColumns", isSignal: true, isRequired: false, transformFunction: null }, zShowDownload: { classPropertyName: "zShowDownload", publicName: "zShowDownload", isSignal: true, isRequired: false, transformFunction: null }, zShowPreview: { classPropertyName: "zShowPreview", publicName: "zShowPreview", isSignal: true, isRequired: false, transformFunction: null }, zShowRemove: { classPropertyName: "zShowRemove", publicName: "zShowRemove", isSignal: true, isRequired: false, transformFunction: null }, zShowModeToggle: { classPropertyName: "zShowModeToggle", publicName: "zShowModeToggle", isSignal: true, isRequired: false, transformFunction: null }, zShowSearch: { classPropertyName: "zShowSearch", publicName: "zShowSearch", isSignal: true, isRequired: false, transformFunction: null }, zServerSearch: { classPropertyName: "zServerSearch", publicName: "zServerSearch", isSignal: true, isRequired: false, transformFunction: null }, zSearchPlaceholder: { classPropertyName: "zSearchPlaceholder", publicName: "zSearchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, zToggleSize: { classPropertyName: "zToggleSize", publicName: "zToggleSize", isSignal: true, isRequired: false, transformFunction: null }, zTitle: { classPropertyName: "zTitle", publicName: "zTitle", isSignal: true, isRequired: false, transformFunction: null }, zEmptyText: { classPropertyName: "zEmptyText", publicName: "zEmptyText", isSignal: true, isRequired: false, transformFunction: null }, zEmptyIcon: { classPropertyName: "zEmptyIcon", publicName: "zEmptyIcon", isSignal: true, isRequired: false, transformFunction: null }, zClass: { classPropertyName: "zClass", publicName: "zClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zMode: "zModeChange", zOnDownload: "zOnDownload", zOnPreview: "zOnPreview", zOnRemove: "zOnRemove", zModeChange: "zModeChange", zSearchChange: "zSearchChange", zControl: "zControl" }, host: { classAttribute: "z-gallery block" }, exportAs: ["zGallery"], ngImport: i0, template: `
1268
+ <div [class]="containerClasses()">
1269
+ @if (showHeader()) {
1270
+ <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
1271
+ <div class="flex items-center gap-3">
1272
+ @if (zTitle()) {
1273
+ <h3 class="text-sm font-medium">{{ zTitle() }}</h3>
1274
+ }
1275
+
1276
+ @if (zShowSearch()) {
1277
+ <z-input
1278
+ zSearch
1279
+ [zSize]="searchInputSize()"
1280
+ [zPlaceholder]="zSearchPlaceholder() || ('i18n_z_ui_gallery_search_placeholder' | translate)"
1281
+ [ngModel]="searchQuery()"
1282
+ (ngModelChange)="onSearchChange($event)"
1283
+ class="w-64"
1284
+ />
1285
+ }
1286
+ </div>
1287
+
1288
+ @if (zShowModeToggle() && zFiles().length > 0) {
1289
+ <z-button-group [(zActive)]="modeIndex">
1290
+ <button z-button zType="outline" [zSize]="zToggleSize()" z-button-group-item>
1291
+ <i z-icon zType="lucideLayoutGrid" [zSize]="toggleIconSize()"></i>
1292
+ </button>
1293
+ <button z-button zType="outline" [zSize]="zToggleSize()" z-button-group-item>
1294
+ <i z-icon zType="lucideList" [zSize]="toggleIconSize()"></i>
1295
+ </button>
1296
+ </z-button-group>
1297
+ }
1298
+ </div>
1299
+ }
1300
+
1301
+ @if (filteredFiles().length > 0) {
1302
+ @if (currentMode() === 'grid') {
1303
+ <div class="grid gap-4" [style.grid-template-columns]="gridColumns()">
1304
+ @for (file of filteredFiles(); track file.id) {
1305
+ <z-gallery-item
1306
+ [file]="file"
1307
+ zMode="grid"
1308
+ [zSize]="zSize()"
1309
+ [zShowDownload]="zShowDownload()"
1310
+ [zShowPreview]="zShowPreview()"
1311
+ [zShowRemove]="zShowRemove()"
1312
+ (zOnDownload)="onDownload($event)"
1313
+ (zOnPreview)="onPreview($event)"
1314
+ (zOnRemove)="onRemove($event)"
1315
+ />
1316
+ }
1317
+ </div>
1318
+ } @else {
1319
+ <div class="flex flex-col gap-2">
1320
+ @for (file of filteredFiles(); track file.id) {
1321
+ <z-gallery-item
1322
+ [file]="file"
1323
+ zMode="list"
1324
+ [zSize]="zSize()"
1325
+ [zShowDownload]="zShowDownload()"
1326
+ [zShowPreview]="zShowPreview()"
1327
+ [zShowRemove]="zShowRemove()"
1328
+ (zOnDownload)="onDownload($event)"
1329
+ (zOnPreview)="onPreview($event)"
1330
+ (zOnRemove)="onRemove($event)"
1331
+ />
1332
+ }
1333
+ </div>
1334
+ }
1335
+ } @else if (zFiles().length > 0 && searchQuery()) {
1336
+ <z-empty zIcon="lucideSearchX" [zMessage]="'i18n_z_ui_gallery_no_search_results' | translate" zSize="sm" />
1337
+ } @else {
1338
+ <z-empty
1339
+ [zIcon]="zEmptyIcon()"
1340
+ [zMessage]="zEmptyText() || ('i18n_z_ui_gallery_no_files' | translate)"
1341
+ zSize="sm"
1342
+ />
1343
+ }
1344
+
1345
+ @if (previewFile()) {
1346
+ <z-gallery-preview
1347
+ [file]="previewFile()!"
1348
+ [files]="filteredFiles()"
1349
+ [zShowRemove]="zShowRemove()"
1350
+ (zOnClose)="closePreview()"
1351
+ (zOnDownload)="onDownload($event)"
1352
+ (zOnNavigate)="onNavigatePreview($event)"
1353
+ (zOnRemove)="onRemoveFromPreview($event)"
1354
+ />
1355
+ }
1356
+ </div>
1357
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ZGalleryItemComponent, selector: "z-gallery-item", inputs: ["file", "zMode", "zSize", "zShowDownload", "zShowPreview", "zShowRemove"], outputs: ["zOnDownload", "zOnPreview", "zOnRemove"] }, { kind: "component", type: ZGalleryPreviewComponent, selector: "z-gallery-preview", inputs: ["file", "files", "zShowRemove"], outputs: ["zOnClose", "zOnDownload", "zOnNavigate", "zOnRemove"] }, { kind: "component", type: ZButtonGroupComponent, selector: "z-button-group", inputs: ["zClass", "zOrientation", "zActive"], outputs: ["zActiveChange"], exportAs: ["zButtonGroup"] }, { kind: "directive", type: ZButtonGroupItemDirective, selector: "[z-button-group-item]", exportAs: ["zButtonGroupItem"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "zType", "zIcon", "zIconSize", "zSize", "zMessage", "zDescription"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1358
+ }
1359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryComponent, decorators: [{
1360
+ type: Component,
1361
+ args: [{
1362
+ selector: 'z-gallery',
1363
+ imports: [
1364
+ FormsModule,
1365
+ TranslatePipe,
1366
+ ZGalleryItemComponent,
1367
+ ZGalleryPreviewComponent,
1368
+ ZButtonGroupComponent,
1369
+ ZButtonGroupItemDirective,
1370
+ ZButtonComponent,
1371
+ ZIconComponent,
1372
+ ZEmptyComponent,
1373
+ ZInputComponent,
1374
+ ],
1375
+ standalone: true,
1376
+ template: `
1377
+ <div [class]="containerClasses()">
1378
+ @if (showHeader()) {
1379
+ <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
1380
+ <div class="flex items-center gap-3">
1381
+ @if (zTitle()) {
1382
+ <h3 class="text-sm font-medium">{{ zTitle() }}</h3>
1383
+ }
1384
+
1385
+ @if (zShowSearch()) {
1386
+ <z-input
1387
+ zSearch
1388
+ [zSize]="searchInputSize()"
1389
+ [zPlaceholder]="zSearchPlaceholder() || ('i18n_z_ui_gallery_search_placeholder' | translate)"
1390
+ [ngModel]="searchQuery()"
1391
+ (ngModelChange)="onSearchChange($event)"
1392
+ class="w-64"
1393
+ />
1394
+ }
1395
+ </div>
1396
+
1397
+ @if (zShowModeToggle() && zFiles().length > 0) {
1398
+ <z-button-group [(zActive)]="modeIndex">
1399
+ <button z-button zType="outline" [zSize]="zToggleSize()" z-button-group-item>
1400
+ <i z-icon zType="lucideLayoutGrid" [zSize]="toggleIconSize()"></i>
1401
+ </button>
1402
+ <button z-button zType="outline" [zSize]="zToggleSize()" z-button-group-item>
1403
+ <i z-icon zType="lucideList" [zSize]="toggleIconSize()"></i>
1404
+ </button>
1405
+ </z-button-group>
1406
+ }
1407
+ </div>
1408
+ }
1409
+
1410
+ @if (filteredFiles().length > 0) {
1411
+ @if (currentMode() === 'grid') {
1412
+ <div class="grid gap-4" [style.grid-template-columns]="gridColumns()">
1413
+ @for (file of filteredFiles(); track file.id) {
1414
+ <z-gallery-item
1415
+ [file]="file"
1416
+ zMode="grid"
1417
+ [zSize]="zSize()"
1418
+ [zShowDownload]="zShowDownload()"
1419
+ [zShowPreview]="zShowPreview()"
1420
+ [zShowRemove]="zShowRemove()"
1421
+ (zOnDownload)="onDownload($event)"
1422
+ (zOnPreview)="onPreview($event)"
1423
+ (zOnRemove)="onRemove($event)"
1424
+ />
1425
+ }
1426
+ </div>
1427
+ } @else {
1428
+ <div class="flex flex-col gap-2">
1429
+ @for (file of filteredFiles(); track file.id) {
1430
+ <z-gallery-item
1431
+ [file]="file"
1432
+ zMode="list"
1433
+ [zSize]="zSize()"
1434
+ [zShowDownload]="zShowDownload()"
1435
+ [zShowPreview]="zShowPreview()"
1436
+ [zShowRemove]="zShowRemove()"
1437
+ (zOnDownload)="onDownload($event)"
1438
+ (zOnPreview)="onPreview($event)"
1439
+ (zOnRemove)="onRemove($event)"
1440
+ />
1441
+ }
1442
+ </div>
1443
+ }
1444
+ } @else if (zFiles().length > 0 && searchQuery()) {
1445
+ <z-empty zIcon="lucideSearchX" [zMessage]="'i18n_z_ui_gallery_no_search_results' | translate" zSize="sm" />
1446
+ } @else {
1447
+ <z-empty
1448
+ [zIcon]="zEmptyIcon()"
1449
+ [zMessage]="zEmptyText() || ('i18n_z_ui_gallery_no_files' | translate)"
1450
+ zSize="sm"
1451
+ />
1452
+ }
1453
+
1454
+ @if (previewFile()) {
1455
+ <z-gallery-preview
1456
+ [file]="previewFile()!"
1457
+ [files]="filteredFiles()"
1458
+ [zShowRemove]="zShowRemove()"
1459
+ (zOnClose)="closePreview()"
1460
+ (zOnDownload)="onDownload($event)"
1461
+ (zOnNavigate)="onNavigatePreview($event)"
1462
+ (zOnRemove)="onRemoveFromPreview($event)"
1463
+ />
1464
+ }
1465
+ </div>
1466
+ `,
1467
+ changeDetection: ChangeDetectionStrategy.OnPush,
1468
+ encapsulation: ViewEncapsulation.None,
1469
+ host: {
1470
+ class: 'z-gallery block',
1471
+ },
1472
+ exportAs: 'zGallery',
1473
+ }]
1474
+ }], propDecorators: { zFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "zFiles", required: false }] }], zMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMode", required: false }] }, { type: i0.Output, args: ["zModeChange"] }], zSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSize", required: false }] }], zColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "zColumns", required: false }] }], zShowDownload: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowDownload", required: false }] }], zShowPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowPreview", required: false }] }], zShowRemove: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowRemove", required: false }] }], zShowModeToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowModeToggle", required: false }] }], zShowSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowSearch", required: false }] }], zServerSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "zServerSearch", required: false }] }], zSearchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSearchPlaceholder", required: false }] }], zToggleSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zToggleSize", required: false }] }], zTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTitle", required: false }] }], zEmptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEmptyText", required: false }] }], zEmptyIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEmptyIcon", required: false }] }], zClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "zClass", required: false }] }], zOnDownload: [{ type: i0.Output, args: ["zOnDownload"] }], zOnPreview: [{ type: i0.Output, args: ["zOnPreview"] }], zOnRemove: [{ type: i0.Output, args: ["zOnRemove"] }], zModeChange: [{ type: i0.Output, args: ["zModeChange"] }], zSearchChange: [{ type: i0.Output, args: ["zSearchChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }] } });
1475
+
1476
+ class ZGalleryFileIconComponent {
1477
+ file = input.required(...(ngDevMode ? [{ debugName: "file" }] : []));
1478
+ zSize = input('default', ...(ngDevMode ? [{ debugName: "zSize" }] : []));
1479
+ zMode = input('grid', ...(ngDevMode ? [{ debugName: "zMode" }] : []));
1480
+ imageError = signal(false, ...(ngDevMode ? [{ debugName: "imageError" }] : []));
1481
+ isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1482
+ category = computed(() => getFileCategory(this.file()), ...(ngDevMode ? [{ debugName: "category" }] : []));
1483
+ style = computed(() => FILE_CATEGORY_STYLES[this.category()], ...(ngDevMode ? [{ debugName: "style" }] : []));
1484
+ showThumbnail = computed(() => {
1485
+ const file = this.file();
1486
+ const cat = this.category();
1487
+ return cat === 'image' && (file.thumbnailUrl || file.url) && !this.imageError();
1488
+ }, ...(ngDevMode ? [{ debugName: "showThumbnail" }] : []));
1489
+ containerClasses = computed(() => {
1490
+ const style = this.style();
1491
+ const showThumb = this.showThumbnail();
1492
+ return zMergeClasses(zGalleryFileIconVariants({
1493
+ zSize: this.zSize(),
1494
+ zMode: this.zMode(),
1495
+ }), showThumb ? 'bg-muted relative' : [style.bg, style.text]);
1496
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
1497
+ onImageLoad() {
1498
+ this.isLoading.set(false);
1499
+ }
1500
+ onImageError(event) {
1501
+ this.imageError.set(true);
1502
+ this.isLoading.set(false);
1503
+ const img = event.target;
1504
+ img.style.display = 'none';
1505
+ }
1506
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryFileIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1507
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZGalleryFileIconComponent, isStandalone: true, selector: "z-gallery-file-icon", inputs: { file: { classPropertyName: "file", publicName: "file", isSignal: true, isRequired: true, transformFunction: null }, zSize: { classPropertyName: "zSize", publicName: "zSize", isSignal: true, isRequired: false, transformFunction: null }, zMode: { classPropertyName: "zMode", publicName: "zMode", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "z-gallery-file-icon block" }, ngImport: i0, template: `
1508
+ <div [class]="containerClasses()" class="group/icon overflow-hidden">
1509
+ @if (showThumbnail()) {
1510
+ @if (isLoading()) {
1511
+ <z-skeleton class="absolute inset-0 size-full" />
1512
+ }
1513
+ <img
1514
+ [src]="file().thumbnailUrl || file().url"
1515
+ [alt]="file().name"
1516
+ class="size-full object-cover transition-transform duration-300 group-hover/icon:scale-105"
1517
+ loading="lazy"
1518
+ [class.opacity-0]="isLoading()"
1519
+ [class.opacity-100]="!isLoading()"
1520
+ (load)="onImageLoad()"
1521
+ (error)="onImageError($event)"
1522
+ />
1523
+ } @else {
1524
+ <div class="flex flex-col items-center justify-center gap-1">
1525
+ <span class="font-bold tracking-wide">{{ style().label }}</span>
1526
+ </div>
1527
+ }
1528
+ </div>
1529
+ `, isInline: true, dependencies: [{ kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1530
+ }
1531
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryFileIconComponent, decorators: [{
1532
+ type: Component,
1533
+ args: [{
1534
+ selector: 'z-gallery-file-icon',
1535
+ imports: [ZSkeletonComponent],
1536
+ standalone: true,
1537
+ template: `
1538
+ <div [class]="containerClasses()" class="group/icon overflow-hidden">
1539
+ @if (showThumbnail()) {
1540
+ @if (isLoading()) {
1541
+ <z-skeleton class="absolute inset-0 size-full" />
1542
+ }
1543
+ <img
1544
+ [src]="file().thumbnailUrl || file().url"
1545
+ [alt]="file().name"
1546
+ class="size-full object-cover transition-transform duration-300 group-hover/icon:scale-105"
1547
+ loading="lazy"
1548
+ [class.opacity-0]="isLoading()"
1549
+ [class.opacity-100]="!isLoading()"
1550
+ (load)="onImageLoad()"
1551
+ (error)="onImageError($event)"
1552
+ />
1553
+ } @else {
1554
+ <div class="flex flex-col items-center justify-center gap-1">
1555
+ <span class="font-bold tracking-wide">{{ style().label }}</span>
1556
+ </div>
1557
+ }
1558
+ </div>
1559
+ `,
1560
+ changeDetection: ChangeDetectionStrategy.OnPush,
1561
+ encapsulation: ViewEncapsulation.None,
1562
+ host: {
1563
+ class: 'z-gallery-file-icon block',
1564
+ },
1565
+ }]
1566
+ }], propDecorators: { file: [{ type: i0.Input, args: [{ isSignal: true, alias: "file", required: true }] }], zSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSize", required: false }] }], zMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMode", required: false }] }] } });
1567
+
1568
+ /**
1569
+ * Generated bundle index. Do not edit.
1570
+ */
1571
+
1572
+ export { FILE_CATEGORY_STYLES, ZGalleryComponent, ZGalleryFileIconComponent, ZGalleryItemComponent, ZGalleryPreviewComponent, formatFileSize, getFileCategory, isImage, isPreviewable, zGalleryFileIconVariants, zGalleryItemVariants, zGalleryVariants };
1573
+ //# sourceMappingURL=shival99-z-ui-components-z-gallery.mjs.map