@shival99/z-ui 1.8.13 → 1.9.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 (89) hide show
  1. package/assets/css/tailwind.css +7 -11
  2. package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs +45 -71
  3. package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs.map +1 -1
  4. package/fesm2022/shival99-z-ui-components-z-button.mjs +21 -11
  5. package/fesm2022/shival99-z-ui-components-z-button.mjs.map +1 -1
  6. package/fesm2022/shival99-z-ui-components-z-calendar.mjs +61 -32
  7. package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
  8. package/fesm2022/shival99-z-ui-components-z-card.mjs +1 -1
  9. package/fesm2022/shival99-z-ui-components-z-card.mjs.map +1 -1
  10. package/fesm2022/shival99-z-ui-components-z-chat.mjs +429 -0
  11. package/fesm2022/shival99-z-ui-components-z-chat.mjs.map +1 -0
  12. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +1 -1
  13. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -1
  14. package/fesm2022/shival99-z-ui-components-z-code.mjs +51 -21
  15. package/fesm2022/shival99-z-ui-components-z-code.mjs.map +1 -1
  16. package/fesm2022/shival99-z-ui-components-z-drawer.mjs +8 -3
  17. package/fesm2022/shival99-z-ui-components-z-drawer.mjs.map +1 -1
  18. package/fesm2022/shival99-z-ui-components-z-editor.mjs +3 -3
  19. package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -1
  20. package/fesm2022/shival99-z-ui-components-z-empty.mjs.map +1 -1
  21. package/fesm2022/shival99-z-ui-components-z-filter.mjs +3 -3
  22. package/fesm2022/shival99-z-ui-components-z-filter.mjs.map +1 -1
  23. package/fesm2022/shival99-z-ui-components-z-gallery.mjs +169 -90
  24. package/fesm2022/shival99-z-ui-components-z-gallery.mjs.map +1 -1
  25. package/fesm2022/shival99-z-ui-components-z-icon.mjs +143 -360
  26. package/fesm2022/shival99-z-ui-components-z-icon.mjs.map +1 -1
  27. package/fesm2022/shival99-z-ui-components-z-input.mjs +131 -8
  28. package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -1
  29. package/fesm2022/shival99-z-ui-components-z-loading.mjs +2 -2
  30. package/fesm2022/shival99-z-ui-components-z-loading.mjs.map +1 -1
  31. package/fesm2022/shival99-z-ui-components-z-menu.mjs +2 -2
  32. package/fesm2022/shival99-z-ui-components-z-menu.mjs.map +1 -1
  33. package/fesm2022/shival99-z-ui-components-z-modal.mjs +8 -3
  34. package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -1
  35. package/fesm2022/shival99-z-ui-components-z-pagination.mjs +1 -1
  36. package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -1
  37. package/fesm2022/shival99-z-ui-components-z-popover.mjs +3 -3
  38. package/fesm2022/shival99-z-ui-components-z-popover.mjs.map +1 -1
  39. package/fesm2022/shival99-z-ui-components-z-select.mjs +31 -7
  40. package/fesm2022/shival99-z-ui-components-z-select.mjs.map +1 -1
  41. package/fesm2022/shival99-z-ui-components-z-skeleton.mjs +3 -3
  42. package/fesm2022/shival99-z-ui-components-z-skeleton.mjs.map +1 -1
  43. package/fesm2022/shival99-z-ui-components-z-steps.mjs +31 -29
  44. package/fesm2022/shival99-z-ui-components-z-steps.mjs.map +1 -1
  45. package/fesm2022/shival99-z-ui-components-z-switch.mjs +131 -15
  46. package/fesm2022/shival99-z-ui-components-z-switch.mjs.map +1 -1
  47. package/fesm2022/shival99-z-ui-components-z-table.mjs +57 -13
  48. package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
  49. package/fesm2022/shival99-z-ui-components-z-tabs.mjs +6 -6
  50. package/fesm2022/shival99-z-ui-components-z-tabs.mjs.map +1 -1
  51. package/fesm2022/shival99-z-ui-components-z-tags.mjs +1 -1
  52. package/fesm2022/shival99-z-ui-components-z-tags.mjs.map +1 -1
  53. package/fesm2022/shival99-z-ui-components-z-timeline.mjs +6 -6
  54. package/fesm2022/shival99-z-ui-components-z-timeline.mjs.map +1 -1
  55. package/fesm2022/shival99-z-ui-components-z-tooltip.mjs +4 -4
  56. package/fesm2022/shival99-z-ui-components-z-tooltip.mjs.map +1 -1
  57. package/fesm2022/shival99-z-ui-components-z-upload.mjs +5 -4
  58. package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -1
  59. package/fesm2022/shival99-z-ui-i18n.mjs +12 -0
  60. package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -1
  61. package/fesm2022/shival99-z-ui-pipes.mjs +18 -0
  62. package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -1
  63. package/fesm2022/shival99-z-ui-providers.mjs +30 -13
  64. package/fesm2022/shival99-z-ui-providers.mjs.map +1 -1
  65. package/fesm2022/shival99-z-ui-services.mjs +53 -29
  66. package/fesm2022/shival99-z-ui-services.mjs.map +1 -1
  67. package/fesm2022/z-ui.mjs +0 -4
  68. package/fesm2022/z-ui.mjs.map +1 -1
  69. package/package.json +6 -1
  70. package/types/shival99-z-ui-components-z-autocomplete.d.ts +25 -38
  71. package/types/shival99-z-ui-components-z-breadcrumb.d.ts +4 -4
  72. package/types/shival99-z-ui-components-z-button.d.ts +5 -4
  73. package/types/shival99-z-ui-components-z-chat.d.ts +148 -0
  74. package/types/shival99-z-ui-components-z-drawer.d.ts +3 -1
  75. package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +2 -2
  76. package/types/shival99-z-ui-components-z-empty.d.ts +3 -3
  77. package/types/shival99-z-ui-components-z-filter.d.ts +2 -2
  78. package/types/shival99-z-ui-components-z-gallery.d.ts +6 -3
  79. package/types/shival99-z-ui-components-z-icon.d.ts +18 -304
  80. package/types/shival99-z-ui-components-z-input.d.ts +42 -1
  81. package/types/shival99-z-ui-components-z-modal.d.ts +4 -2
  82. package/types/shival99-z-ui-components-z-select.d.ts +5 -2
  83. package/types/shival99-z-ui-components-z-steps.d.ts +4 -4
  84. package/types/shival99-z-ui-components-z-switch.d.ts +25 -4
  85. package/types/shival99-z-ui-components-z-table.d.ts +2 -0
  86. package/types/shival99-z-ui-pipes.d.ts +2 -0
  87. package/types/shival99-z-ui-providers.d.ts +10 -16
  88. package/types/shival99-z-ui-services.d.ts +6 -1
  89. package/types/z-ui.d.ts +1 -2
@@ -186,6 +186,37 @@ class ZGalleryItemComponent {
186
186
  formattedSize = computed(() => formatFileSize(this.file().size), ...(ngDevMode ? [{ debugName: "formattedSize" }] : []));
187
187
  category = computed(() => getFileCategory(this.file()), ...(ngDevMode ? [{ debugName: "category" }] : []));
188
188
  isImage = computed(() => this.category() === 'image', ...(ngDevMode ? [{ debugName: "isImage" }] : []));
189
+ fileExtension = computed(() => {
190
+ const fileName = this.file().name;
191
+ const parts = fileName.split('.');
192
+ if (parts.length < 2) {
193
+ return 'FILE';
194
+ }
195
+ return parts[parts.length - 1].toUpperCase();
196
+ }, ...(ngDevMode ? [{ debugName: "fileExtension" }] : []));
197
+ mediaHeightClass = computed(() => {
198
+ const size = this.zSize();
199
+ switch (size) {
200
+ case 'sm':
201
+ return 'h-36';
202
+ case 'lg':
203
+ return 'h-56';
204
+ default:
205
+ return 'h-44';
206
+ }
207
+ }, ...(ngDevMode ? [{ debugName: "mediaHeightClass" }] : []));
208
+ formattedDate = computed(() => {
209
+ const file = this.file();
210
+ const date = file.updatedAt || file.createdAt;
211
+ if (!date) {
212
+ return null;
213
+ }
214
+ return new Intl.DateTimeFormat(undefined, {
215
+ month: 'short',
216
+ day: '2-digit',
217
+ year: 'numeric',
218
+ }).format(date);
219
+ }, ...(ngDevMode ? [{ debugName: "formattedDate" }] : []));
189
220
  categoryStyle = computed(() => {
190
221
  const style = FILE_CATEGORY_STYLES[this.category()];
191
222
  return {
@@ -232,30 +263,25 @@ class ZGalleryItemComponent {
232
263
  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 }, zSelectable: { classPropertyName: "zSelectable", publicName: "zSelectable", isSignal: true, isRequired: false, transformFunction: null }, zSelected: { classPropertyName: "zSelected", publicName: "zSelected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnDownload: "zOnDownload", zOnPreview: "zOnPreview", zOnRemove: "zOnRemove", zOnSelect: "zOnSelect" }, host: { classAttribute: "z-gallery-item block" }, ngImport: i0, template: `
233
264
  @if (zMode() === 'grid') {
234
265
  <div
235
- class="group relative cursor-pointer rounded-xl border transition-all duration-200 hover:shadow-md"
236
- [class.border-border]="!zSelected()"
237
- [class.bg-card]="!zSelected()"
238
- [class.shadow-sm]="!zSelected()"
239
- [class.hover:border-primary/30]="!zSelected()"
240
- [class.border-primary/40]="zSelected()"
241
- [class.bg-primary/5]="zSelected()"
242
- [class.shadow-primary/15]="zSelected()"
243
- [class.shadow-md]="zSelected()"
266
+ class="group border-border bg-card hover:border-primary/30 relative cursor-pointer overflow-hidden rounded-2xl border shadow-sm transition-all duration-300 hover:shadow-lg"
267
+ [class.border-primary]="zSelected()"
268
+ [class.ring-1]="zSelected()"
269
+ [class.ring-primary/35]="zSelected()"
244
270
  (click)="onItemClick()"
245
271
  >
246
272
  @if (zSelectable()) {
247
273
  <div
248
- class="bg-primary shadow-card absolute -top-2 -right-2 z-10 flex size-6 items-center justify-center rounded-full transition-all duration-150"
274
+ class="bg-primary shadow-card absolute top-3 right-3 z-20 flex size-6 items-center justify-center rounded-full transition-all duration-200"
249
275
  [class.scale-100]="zSelected()"
250
276
  [class.opacity-100]="zSelected()"
251
- [class.scale-0]="!zSelected()"
277
+ [class.scale-90]="!zSelected()"
252
278
  [class.opacity-0]="!zSelected()"
253
279
  >
254
280
  <i z-icon zType="lucideCheck" zSize="14" class="text-primary-foreground"></i>
255
281
  </div>
256
282
  }
257
283
 
258
- <div class="relative aspect-square overflow-hidden rounded-t-xl">
284
+ <div class="relative overflow-hidden" [class]="mediaHeightClass()">
259
285
  @if (isImage()) {
260
286
  @if (isLoading()) {
261
287
  <z-skeleton class="absolute inset-0" />
@@ -263,108 +289,129 @@ class ZGalleryItemComponent {
263
289
  <img
264
290
  [src]="file().thumbnailUrl || file().url"
265
291
  [alt]="file().name"
266
- class="size-full object-cover transition-all duration-300"
292
+ class="size-full object-cover transition-all duration-500"
267
293
  [class.opacity-0]="isLoading()"
268
294
  [class.scale-100]="!isLoading()"
269
- [class.group-hover:scale-105]="!isLoading()"
295
+ [class.group-hover:scale-110]="!isLoading()"
270
296
  loading="lazy"
271
297
  (load)="onImageLoad()"
272
298
  (error)="onImageError()"
273
299
  />
274
300
  } @else {
275
- <div class="flex size-full flex-col items-center justify-center gap-2" [class]="categoryStyle().bg">
276
- <i z-icon [zType]="categoryStyle().icon" zSize="32" [class]="categoryStyle().text"></i>
277
- <span class="text-xs font-semibold" [class]="categoryStyle().text">
301
+ <div
302
+ class="flex size-full flex-col items-center justify-center gap-2 bg-gradient-to-br"
303
+ [class]="categoryStyle().bg"
304
+ >
305
+ <i z-icon [zType]="categoryStyle().icon" zSize="34" [class]="categoryStyle().text"></i>
306
+ <span class="text-xs font-semibold tracking-wide" [class]="categoryStyle().text">
278
307
  {{ categoryStyle().label }}
279
308
  </span>
280
309
  </div>
281
310
  }
282
311
 
312
+ <div class="absolute inset-x-0 top-0 z-10 flex items-start justify-between p-3">
313
+ <span
314
+ class="rounded-full border border-white/35 bg-black/45 px-2.5 py-1 text-[0.6875rem] font-semibold tracking-wide text-white backdrop-blur-md"
315
+ >
316
+ {{ fileExtension() }}
317
+ </span>
318
+ <span
319
+ class="rounded-full border border-white/25 bg-black/35 px-2 py-1 text-[0.625rem] font-medium text-white/95 backdrop-blur-md"
320
+ >
321
+ {{ formattedSize() }}
322
+ </span>
323
+ </div>
324
+
283
325
  <div
284
- 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"
326
+ class="absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black/70 via-black/30 to-transparent opacity-85 transition-opacity duration-300 group-hover:opacity-100"
327
+ ></div>
328
+
329
+ <div
330
+ class="absolute right-3 bottom-3 z-20 flex items-center gap-1.5 rounded-full border border-white/20 bg-black/40 p-1 opacity-0 backdrop-blur-md transition-all duration-200 group-hover:opacity-100"
285
331
  >
286
332
  @if (zShowPreview() && canPreview()) {
287
333
  <button
288
334
  z-button
289
335
  zType="secondary"
290
- zSize="sm"
336
+ zSize="xs"
291
337
  zShape="circle"
292
338
  class="shadow-lg"
293
339
  (click)="onPreviewClick($event)"
294
340
  z-tooltip
295
341
  [zContent]="'i18n_z_ui_gallery_preview' | translate"
296
342
  >
297
- <i z-icon zType="lucideEye" zSize="16"></i>
343
+ <i z-icon zType="lucideEye" zSize="14"></i>
298
344
  </button>
299
345
  }
300
346
  @if (zShowDownload() && (file().downloadUrl || file().url)) {
301
347
  <button
302
348
  z-button
303
349
  zType="secondary"
304
- zSize="sm"
350
+ zSize="xs"
305
351
  zShape="circle"
306
352
  class="shadow-lg"
307
353
  (click)="onDownload($event)"
308
354
  z-tooltip
309
355
  [zContent]="'i18n_z_ui_gallery_download' | translate"
310
356
  >
311
- <i z-icon zType="lucideDownload" zSize="16"></i>
357
+ <i z-icon zType="lucideDownload" zSize="14"></i>
312
358
  </button>
313
359
  }
314
360
  @if (zShowRemove()) {
315
361
  <button
316
362
  z-button
317
363
  zType="secondary"
318
- zSize="sm"
364
+ zSize="xs"
319
365
  zShape="circle"
320
366
  class="shadow-lg"
321
367
  (click)="onRemove($event)"
322
368
  z-tooltip
323
369
  [zContent]="'i18n_z_ui_gallery_remove' | translate"
324
370
  >
325
- <i z-icon zType="lucideTrash2" zSize="16"></i>
371
+ <i z-icon zType="lucideTrash2" zSize="14"></i>
326
372
  </button>
327
373
  }
328
374
  </div>
329
375
  </div>
330
376
 
331
- <div class="p-2.5">
332
- <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
377
+ <div class="p-3">
378
+ <p class="text-foreground truncate text-sm leading-none font-semibold" z-tooltip [zContent]="file().name">
333
379
  {{ file().name }}
334
380
  </p>
335
- <p class="text-muted-foreground mt-0.5 text-xs">
336
- {{ formattedSize() }}
337
- </p>
381
+ @if (formattedDate()) {
382
+ <p class="text-muted-foreground mt-1 text-xs">{{ formattedDate() }}</p>
383
+ }
338
384
  </div>
339
385
  </div>
340
386
  } @else {
341
387
  <div
342
- class="group flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 shadow-sm transition-all duration-150 hover:shadow-md"
343
- [class.border-border]="!zSelected()"
344
- [class.bg-card]="!zSelected()"
345
- [class.hover:border-primary/30]="!zSelected()"
346
- [class.border-primary/40]="zSelected()"
347
- [class.bg-primary/5]="zSelected()"
388
+ class="group border-border bg-card hover:border-primary/35 flex cursor-pointer items-center gap-3 rounded-xl border px-3 py-2.5 shadow-sm transition-all duration-200 hover:shadow-md"
389
+ [class.border-primary]="zSelected()"
390
+ [class.ring-1]="zSelected()"
391
+ [class.ring-primary/30]="zSelected()"
348
392
  (click)="onItemClick()"
349
393
  >
350
394
  @if (zSelectable()) {
351
395
  <div
352
- class="bg-primary -ml-1 flex size-5 shrink-0 items-center justify-center rounded-full transition-all duration-150"
396
+ class="bg-primary -ml-1 flex size-5 shrink-0 items-center justify-center rounded-full transition-all duration-200"
353
397
  [class.scale-100]="zSelected()"
354
398
  [class.opacity-100]="zSelected()"
355
- [class.scale-0]="!zSelected()"
399
+ [class.scale-90]="!zSelected()"
356
400
  [class.opacity-0]="!zSelected()"
357
401
  >
358
402
  <i z-icon zType="lucideCheck" zSize="12" class="text-primary-foreground"></i>
359
403
  </div>
360
404
  }
361
405
 
362
- <div class="flex size-10 shrink-0 items-center justify-center rounded-lg" [class]="categoryStyle().bg">
406
+ <div
407
+ class="flex size-11 shrink-0 items-center justify-center rounded-xl border border-white/40 shadow-sm"
408
+ [class]="categoryStyle().bg"
409
+ >
363
410
  @if (isImage() && (file().thumbnailUrl || file().url)) {
364
411
  <img
365
412
  [src]="file().thumbnailUrl || file().url"
366
413
  [alt]="file().name"
367
- class="size-full rounded-lg object-cover"
414
+ class="size-full rounded-xl object-cover"
368
415
  loading="lazy"
369
416
  />
370
417
  } @else {
@@ -376,16 +423,22 @@ class ZGalleryItemComponent {
376
423
  <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
377
424
  {{ file().name }}
378
425
  </p>
379
- <p class="text-muted-foreground text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
426
+ <p class="text-muted-foreground truncate text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
380
427
  </div>
381
428
 
382
- <div class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
429
+ <div class="ml-auto flex shrink-0 items-center gap-1.5">
430
+ <span
431
+ class="text-muted-foreground bg-muted/60 inline-flex min-w-14 items-center justify-center rounded-full border px-2.5 py-1 text-[0.6875rem] font-semibold tracking-wide"
432
+ >
433
+ {{ fileExtension() }}
434
+ </span>
383
435
  @if (zShowPreview() && canPreview()) {
384
436
  <button
385
437
  z-button
386
438
  zType="ghost"
387
439
  zSize="xs"
388
440
  zShape="circle"
441
+ class="text-muted-foreground hover:text-foreground"
389
442
  (click)="onPreviewClick($event)"
390
443
  z-tooltip
391
444
  [zContent]="'i18n_z_ui_gallery_preview' | translate"
@@ -399,6 +452,7 @@ class ZGalleryItemComponent {
399
452
  zType="ghost"
400
453
  zSize="xs"
401
454
  zShape="circle"
455
+ class="text-muted-foreground hover:text-foreground"
402
456
  (click)="onDownload($event)"
403
457
  z-tooltip
404
458
  [zContent]="'i18n_z_ui_gallery_download' | translate"
@@ -412,6 +466,7 @@ class ZGalleryItemComponent {
412
466
  zType="ghost"
413
467
  zSize="xs"
414
468
  zShape="circle"
469
+ class="text-muted-foreground hover:text-destructive"
415
470
  (click)="onRemove($event)"
416
471
  z-tooltip
417
472
  [zContent]="'i18n_z_ui_gallery_remove' | translate"
@@ -433,30 +488,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
433
488
  template: `
434
489
  @if (zMode() === 'grid') {
435
490
  <div
436
- class="group relative cursor-pointer rounded-xl border transition-all duration-200 hover:shadow-md"
437
- [class.border-border]="!zSelected()"
438
- [class.bg-card]="!zSelected()"
439
- [class.shadow-sm]="!zSelected()"
440
- [class.hover:border-primary/30]="!zSelected()"
441
- [class.border-primary/40]="zSelected()"
442
- [class.bg-primary/5]="zSelected()"
443
- [class.shadow-primary/15]="zSelected()"
444
- [class.shadow-md]="zSelected()"
491
+ class="group border-border bg-card hover:border-primary/30 relative cursor-pointer overflow-hidden rounded-2xl border shadow-sm transition-all duration-300 hover:shadow-lg"
492
+ [class.border-primary]="zSelected()"
493
+ [class.ring-1]="zSelected()"
494
+ [class.ring-primary/35]="zSelected()"
445
495
  (click)="onItemClick()"
446
496
  >
447
497
  @if (zSelectable()) {
448
498
  <div
449
- class="bg-primary shadow-card absolute -top-2 -right-2 z-10 flex size-6 items-center justify-center rounded-full transition-all duration-150"
499
+ class="bg-primary shadow-card absolute top-3 right-3 z-20 flex size-6 items-center justify-center rounded-full transition-all duration-200"
450
500
  [class.scale-100]="zSelected()"
451
501
  [class.opacity-100]="zSelected()"
452
- [class.scale-0]="!zSelected()"
502
+ [class.scale-90]="!zSelected()"
453
503
  [class.opacity-0]="!zSelected()"
454
504
  >
455
505
  <i z-icon zType="lucideCheck" zSize="14" class="text-primary-foreground"></i>
456
506
  </div>
457
507
  }
458
508
 
459
- <div class="relative aspect-square overflow-hidden rounded-t-xl">
509
+ <div class="relative overflow-hidden" [class]="mediaHeightClass()">
460
510
  @if (isImage()) {
461
511
  @if (isLoading()) {
462
512
  <z-skeleton class="absolute inset-0" />
@@ -464,108 +514,129 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
464
514
  <img
465
515
  [src]="file().thumbnailUrl || file().url"
466
516
  [alt]="file().name"
467
- class="size-full object-cover transition-all duration-300"
517
+ class="size-full object-cover transition-all duration-500"
468
518
  [class.opacity-0]="isLoading()"
469
519
  [class.scale-100]="!isLoading()"
470
- [class.group-hover:scale-105]="!isLoading()"
520
+ [class.group-hover:scale-110]="!isLoading()"
471
521
  loading="lazy"
472
522
  (load)="onImageLoad()"
473
523
  (error)="onImageError()"
474
524
  />
475
525
  } @else {
476
- <div class="flex size-full flex-col items-center justify-center gap-2" [class]="categoryStyle().bg">
477
- <i z-icon [zType]="categoryStyle().icon" zSize="32" [class]="categoryStyle().text"></i>
478
- <span class="text-xs font-semibold" [class]="categoryStyle().text">
526
+ <div
527
+ class="flex size-full flex-col items-center justify-center gap-2 bg-gradient-to-br"
528
+ [class]="categoryStyle().bg"
529
+ >
530
+ <i z-icon [zType]="categoryStyle().icon" zSize="34" [class]="categoryStyle().text"></i>
531
+ <span class="text-xs font-semibold tracking-wide" [class]="categoryStyle().text">
479
532
  {{ categoryStyle().label }}
480
533
  </span>
481
534
  </div>
482
535
  }
483
536
 
537
+ <div class="absolute inset-x-0 top-0 z-10 flex items-start justify-between p-3">
538
+ <span
539
+ class="rounded-full border border-white/35 bg-black/45 px-2.5 py-1 text-[0.6875rem] font-semibold tracking-wide text-white backdrop-blur-md"
540
+ >
541
+ {{ fileExtension() }}
542
+ </span>
543
+ <span
544
+ class="rounded-full border border-white/25 bg-black/35 px-2 py-1 text-[0.625rem] font-medium text-white/95 backdrop-blur-md"
545
+ >
546
+ {{ formattedSize() }}
547
+ </span>
548
+ </div>
549
+
484
550
  <div
485
- 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"
551
+ class="absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black/70 via-black/30 to-transparent opacity-85 transition-opacity duration-300 group-hover:opacity-100"
552
+ ></div>
553
+
554
+ <div
555
+ class="absolute right-3 bottom-3 z-20 flex items-center gap-1.5 rounded-full border border-white/20 bg-black/40 p-1 opacity-0 backdrop-blur-md transition-all duration-200 group-hover:opacity-100"
486
556
  >
487
557
  @if (zShowPreview() && canPreview()) {
488
558
  <button
489
559
  z-button
490
560
  zType="secondary"
491
- zSize="sm"
561
+ zSize="xs"
492
562
  zShape="circle"
493
563
  class="shadow-lg"
494
564
  (click)="onPreviewClick($event)"
495
565
  z-tooltip
496
566
  [zContent]="'i18n_z_ui_gallery_preview' | translate"
497
567
  >
498
- <i z-icon zType="lucideEye" zSize="16"></i>
568
+ <i z-icon zType="lucideEye" zSize="14"></i>
499
569
  </button>
500
570
  }
501
571
  @if (zShowDownload() && (file().downloadUrl || file().url)) {
502
572
  <button
503
573
  z-button
504
574
  zType="secondary"
505
- zSize="sm"
575
+ zSize="xs"
506
576
  zShape="circle"
507
577
  class="shadow-lg"
508
578
  (click)="onDownload($event)"
509
579
  z-tooltip
510
580
  [zContent]="'i18n_z_ui_gallery_download' | translate"
511
581
  >
512
- <i z-icon zType="lucideDownload" zSize="16"></i>
582
+ <i z-icon zType="lucideDownload" zSize="14"></i>
513
583
  </button>
514
584
  }
515
585
  @if (zShowRemove()) {
516
586
  <button
517
587
  z-button
518
588
  zType="secondary"
519
- zSize="sm"
589
+ zSize="xs"
520
590
  zShape="circle"
521
591
  class="shadow-lg"
522
592
  (click)="onRemove($event)"
523
593
  z-tooltip
524
594
  [zContent]="'i18n_z_ui_gallery_remove' | translate"
525
595
  >
526
- <i z-icon zType="lucideTrash2" zSize="16"></i>
596
+ <i z-icon zType="lucideTrash2" zSize="14"></i>
527
597
  </button>
528
598
  }
529
599
  </div>
530
600
  </div>
531
601
 
532
- <div class="p-2.5">
533
- <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
602
+ <div class="p-3">
603
+ <p class="text-foreground truncate text-sm leading-none font-semibold" z-tooltip [zContent]="file().name">
534
604
  {{ file().name }}
535
605
  </p>
536
- <p class="text-muted-foreground mt-0.5 text-xs">
537
- {{ formattedSize() }}
538
- </p>
606
+ @if (formattedDate()) {
607
+ <p class="text-muted-foreground mt-1 text-xs">{{ formattedDate() }}</p>
608
+ }
539
609
  </div>
540
610
  </div>
541
611
  } @else {
542
612
  <div
543
- class="group flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 shadow-sm transition-all duration-150 hover:shadow-md"
544
- [class.border-border]="!zSelected()"
545
- [class.bg-card]="!zSelected()"
546
- [class.hover:border-primary/30]="!zSelected()"
547
- [class.border-primary/40]="zSelected()"
548
- [class.bg-primary/5]="zSelected()"
613
+ class="group border-border bg-card hover:border-primary/35 flex cursor-pointer items-center gap-3 rounded-xl border px-3 py-2.5 shadow-sm transition-all duration-200 hover:shadow-md"
614
+ [class.border-primary]="zSelected()"
615
+ [class.ring-1]="zSelected()"
616
+ [class.ring-primary/30]="zSelected()"
549
617
  (click)="onItemClick()"
550
618
  >
551
619
  @if (zSelectable()) {
552
620
  <div
553
- class="bg-primary -ml-1 flex size-5 shrink-0 items-center justify-center rounded-full transition-all duration-150"
621
+ class="bg-primary -ml-1 flex size-5 shrink-0 items-center justify-center rounded-full transition-all duration-200"
554
622
  [class.scale-100]="zSelected()"
555
623
  [class.opacity-100]="zSelected()"
556
- [class.scale-0]="!zSelected()"
624
+ [class.scale-90]="!zSelected()"
557
625
  [class.opacity-0]="!zSelected()"
558
626
  >
559
627
  <i z-icon zType="lucideCheck" zSize="12" class="text-primary-foreground"></i>
560
628
  </div>
561
629
  }
562
630
 
563
- <div class="flex size-10 shrink-0 items-center justify-center rounded-lg" [class]="categoryStyle().bg">
631
+ <div
632
+ class="flex size-11 shrink-0 items-center justify-center rounded-xl border border-white/40 shadow-sm"
633
+ [class]="categoryStyle().bg"
634
+ >
564
635
  @if (isImage() && (file().thumbnailUrl || file().url)) {
565
636
  <img
566
637
  [src]="file().thumbnailUrl || file().url"
567
638
  [alt]="file().name"
568
- class="size-full rounded-lg object-cover"
639
+ class="size-full rounded-xl object-cover"
569
640
  loading="lazy"
570
641
  />
571
642
  } @else {
@@ -577,16 +648,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
577
648
  <p class="text-foreground truncate text-sm font-medium" z-tooltip [zContent]="file().name">
578
649
  {{ file().name }}
579
650
  </p>
580
- <p class="text-muted-foreground text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
651
+ <p class="text-muted-foreground truncate text-xs">{{ formattedSize() }} · {{ categoryStyle().label }}</p>
581
652
  </div>
582
653
 
583
- <div class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
654
+ <div class="ml-auto flex shrink-0 items-center gap-1.5">
655
+ <span
656
+ class="text-muted-foreground bg-muted/60 inline-flex min-w-14 items-center justify-center rounded-full border px-2.5 py-1 text-[0.6875rem] font-semibold tracking-wide"
657
+ >
658
+ {{ fileExtension() }}
659
+ </span>
584
660
  @if (zShowPreview() && canPreview()) {
585
661
  <button
586
662
  z-button
587
663
  zType="ghost"
588
664
  zSize="xs"
589
665
  zShape="circle"
666
+ class="text-muted-foreground hover:text-foreground"
590
667
  (click)="onPreviewClick($event)"
591
668
  z-tooltip
592
669
  [zContent]="'i18n_z_ui_gallery_preview' | translate"
@@ -600,6 +677,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
600
677
  zType="ghost"
601
678
  zSize="xs"
602
679
  zShape="circle"
680
+ class="text-muted-foreground hover:text-foreground"
603
681
  (click)="onDownload($event)"
604
682
  z-tooltip
605
683
  [zContent]="'i18n_z_ui_gallery_download' | translate"
@@ -613,6 +691,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
613
691
  zType="ghost"
614
692
  zSize="xs"
615
693
  zShape="circle"
694
+ class="text-muted-foreground hover:text-destructive"
616
695
  (click)="onRemove($event)"
617
696
  z-tooltip
618
697
  [zContent]="'i18n_z_ui_gallery_remove' | translate"
@@ -813,10 +892,10 @@ class ZGalleryPreviewComponent {
813
892
 
814
893
  <div class="flex h-full w-full flex-col items-center" (click)="$event.stopPropagation()">
815
894
  <div
816
- 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"
895
+ class="dark:!border-border absolute top-4 left-1/2 z-10 flex -translate-x-1/2 items-center gap-2 rounded-[0.375rem] border !border-transparent bg-black/60 px-4 py-2 text-sm text-white backdrop-blur-sm dark:border"
817
896
  >
818
897
  <div>
819
- <span class="max-w-[200px] truncate font-medium">{{ file().name }}</span>
898
+ <span class="max-w-[12.5rem] truncate font-medium">{{ file().name }}</span>
820
899
  <span class="text-white/70">{{ formattedSize() }}</span>
821
900
  </div>
822
901
 
@@ -1013,10 +1092,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
1013
1092
 
1014
1093
  <div class="flex h-full w-full flex-col items-center" (click)="$event.stopPropagation()">
1015
1094
  <div
1016
- 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"
1095
+ class="dark:!border-border absolute top-4 left-1/2 z-10 flex -translate-x-1/2 items-center gap-2 rounded-[0.375rem] border !border-transparent bg-black/60 px-4 py-2 text-sm text-white backdrop-blur-sm dark:border"
1017
1096
  >
1018
1097
  <div>
1019
- <span class="max-w-[200px] truncate font-medium">{{ file().name }}</span>
1098
+ <span class="max-w-[12.5rem] truncate font-medium">{{ file().name }}</span>
1020
1099
  <span class="text-white/70">{{ formattedSize() }}</span>
1021
1100
  </div>
1022
1101
 
@@ -1221,7 +1300,7 @@ const zGalleryFileIconVariants = cva('flex items-center justify-center rounded-m
1221
1300
  },
1222
1301
  },
1223
1302
  compoundVariants: [
1224
- { zMode: 'list', zSize: 'sm', class: 'size-8 text-[10px]' },
1303
+ { zMode: 'list', zSize: 'sm', class: 'size-8 text-[0.625rem]' },
1225
1304
  { zMode: 'list', zSize: 'default', class: 'size-10 text-xs' },
1226
1305
  { zMode: 'list', zSize: 'lg', class: 'size-12 text-sm' },
1227
1306
  ],
@@ -1459,7 +1538,7 @@ class ZGalleryComponent {
1459
1538
 
1460
1539
  @if (zSelectable() && filteredFiles().length > 0) {
1461
1540
  <div
1462
- class="bg-muted/30 shadow-card dark:!border-border mb-4 flex h-11 items-center justify-between gap-3 rounded-[6px] border border-transparent px-3"
1541
+ class="bg-muted/30 shadow-card dark:!border-border mb-4 flex h-11 items-center justify-between gap-3 rounded-[0.375rem] border border-transparent px-3"
1463
1542
  >
1464
1543
  <div class="flex items-center gap-3">
1465
1544
  <z-checkbox
@@ -1573,7 +1652,7 @@ class ZGalleryComponent {
1573
1652
  />
1574
1653
  }
1575
1654
  </div>
1576
- `, 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", "zSelectable", "zSelected"], outputs: ["zOnDownload", "zOnPreview", "zOnRemove", "zOnSelect"] }, { 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: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zOnBlur", "zOnFocus", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { 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", "zAlign", "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", "zOnBlur", "zOnFocus", "zOnKeydown", "zOnEnter", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1655
+ `, 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", "zSelectable", "zSelected"], outputs: ["zOnDownload", "zOnPreview", "zOnRemove", "zOnSelect"] }, { 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: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zOnBlur", "zOnFocus", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { 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", "zAlign", "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", "zColorConfig"], outputs: ["zOnSearch", "zOnChange", "zOnBlur", "zOnFocus", "zOnKeydown", "zOnEnter", "zOnColorCollapse", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1577
1656
  }
1578
1657
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZGalleryComponent, decorators: [{
1579
1658
  type: Component,
@@ -1629,7 +1708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
1629
1708
 
1630
1709
  @if (zSelectable() && filteredFiles().length > 0) {
1631
1710
  <div
1632
- class="bg-muted/30 shadow-card dark:!border-border mb-4 flex h-11 items-center justify-between gap-3 rounded-[6px] border border-transparent px-3"
1711
+ class="bg-muted/30 shadow-card dark:!border-border mb-4 flex h-11 items-center justify-between gap-3 rounded-[0.375rem] border border-transparent px-3"
1633
1712
  >
1634
1713
  <div class="flex items-center gap-3">
1635
1714
  <z-checkbox