@m1z23r/ngx-ui 1.1.56 → 1.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/m1z23r-ngx-ui.mjs +633 -2
- package/fesm2022/m1z23r-ngx-ui.mjs.map +1 -1
- package/package.json +3 -2
- package/styles/_variables.scss +30 -0
- package/types/m1z23r-ngx-ui.d.ts +227 -4
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, PLATFORM_ID, signal, Injectable, computed, InjectionToken, ApplicationRef, EnvironmentInjector, createComponent, Injector, input,
|
|
2
|
+
import { inject, PLATFORM_ID, signal, Injectable, computed, InjectionToken, ApplicationRef, EnvironmentInjector, createComponent, Injector, input, model, ChangeDetectionStrategy, Component, viewChild, afterNextRender, HostListener, output, ElementRef, effect, Directive, Pipe, contentChildren, TemplateRef, contentChild, ViewChild, afterRenderEffect, DestroyRef } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser, DOCUMENT, NgTemplateOutlet, NgComponentOutlet, DecimalPipe } from '@angular/common';
|
|
4
4
|
import * as i1 from '@angular/forms';
|
|
5
5
|
import { FormsModule } from '@angular/forms';
|
|
6
|
+
import { RouterLink } from '@angular/router';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Collection of built-in validators for form inputs
|
|
@@ -426,6 +427,207 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
426
427
|
args: [{ providedIn: 'root' }]
|
|
427
428
|
}] });
|
|
428
429
|
|
|
430
|
+
class CarouselComponent {
|
|
431
|
+
images = input.required(...(ngDevMode ? [{ debugName: "images" }] : []));
|
|
432
|
+
startIndex = input(0, ...(ngDevMode ? [{ debugName: "startIndex" }] : []));
|
|
433
|
+
loop = input(true, ...(ngDevMode ? [{ debugName: "loop" }] : []));
|
|
434
|
+
showCounter = input(true, ...(ngDevMode ? [{ debugName: "showCounter" }] : []));
|
|
435
|
+
ariaLabel = input('Image carousel', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
436
|
+
index = model(...(ngDevMode ? [undefined, { debugName: "index" }] : []));
|
|
437
|
+
/** Horizontal drag offset in px while a swipe is in progress */
|
|
438
|
+
dragOffset = signal(0, ...(ngDevMode ? [{ debugName: "dragOffset" }] : []));
|
|
439
|
+
dragging = signal(false, ...(ngDevMode ? [{ debugName: "dragging" }] : []));
|
|
440
|
+
pointerId = null;
|
|
441
|
+
dragStartX = 0;
|
|
442
|
+
dragStartY = 0;
|
|
443
|
+
dragAxis = null;
|
|
444
|
+
currentIndex = computed(() => {
|
|
445
|
+
const count = this.images().length;
|
|
446
|
+
if (count === 0)
|
|
447
|
+
return 0;
|
|
448
|
+
const index = this.index() ?? this.startIndex();
|
|
449
|
+
return Math.max(0, Math.min(count - 1, index));
|
|
450
|
+
}, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
|
|
451
|
+
hasPrev = computed(() => {
|
|
452
|
+
return this.loop() ? this.images().length > 1 : this.currentIndex() > 0;
|
|
453
|
+
}, ...(ngDevMode ? [{ debugName: "hasPrev" }] : []));
|
|
454
|
+
hasNext = computed(() => {
|
|
455
|
+
return this.loop()
|
|
456
|
+
? this.images().length > 1
|
|
457
|
+
: this.currentIndex() < this.images().length - 1;
|
|
458
|
+
}, ...(ngDevMode ? [{ debugName: "hasNext" }] : []));
|
|
459
|
+
prev() {
|
|
460
|
+
if (!this.hasPrev())
|
|
461
|
+
return;
|
|
462
|
+
const count = this.images().length;
|
|
463
|
+
this.index.set((this.currentIndex() - 1 + count) % count);
|
|
464
|
+
}
|
|
465
|
+
next() {
|
|
466
|
+
if (!this.hasNext())
|
|
467
|
+
return;
|
|
468
|
+
const count = this.images().length;
|
|
469
|
+
this.index.set((this.currentIndex() + 1) % count);
|
|
470
|
+
}
|
|
471
|
+
handleKeyDown(event) {
|
|
472
|
+
switch (event.key) {
|
|
473
|
+
case 'ArrowLeft':
|
|
474
|
+
event.preventDefault();
|
|
475
|
+
this.prev();
|
|
476
|
+
break;
|
|
477
|
+
case 'ArrowRight':
|
|
478
|
+
event.preventDefault();
|
|
479
|
+
this.next();
|
|
480
|
+
break;
|
|
481
|
+
case 'Home':
|
|
482
|
+
event.preventDefault();
|
|
483
|
+
this.index.set(0);
|
|
484
|
+
break;
|
|
485
|
+
case 'End':
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
this.index.set(this.images().length - 1);
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
onPointerDown(event) {
|
|
492
|
+
if (this.images().length < 2)
|
|
493
|
+
return;
|
|
494
|
+
this.pointerId = event.pointerId;
|
|
495
|
+
this.dragStartX = event.clientX;
|
|
496
|
+
this.dragStartY = event.clientY;
|
|
497
|
+
this.dragAxis = null;
|
|
498
|
+
this.dragging.set(true);
|
|
499
|
+
}
|
|
500
|
+
onPointerMove(event) {
|
|
501
|
+
if (this.pointerId !== event.pointerId)
|
|
502
|
+
return;
|
|
503
|
+
const deltaX = event.clientX - this.dragStartX;
|
|
504
|
+
const deltaY = event.clientY - this.dragStartY;
|
|
505
|
+
// Lock the gesture axis once movement is unambiguous, so vertical
|
|
506
|
+
// page scrolling on touch devices is not hijacked
|
|
507
|
+
if (this.dragAxis === null && (Math.abs(deltaX) > 8 || Math.abs(deltaY) > 8)) {
|
|
508
|
+
this.dragAxis = Math.abs(deltaX) > Math.abs(deltaY) ? 'x' : 'y';
|
|
509
|
+
if (this.dragAxis === 'x') {
|
|
510
|
+
event.target.setPointerCapture?.(event.pointerId);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (this.dragAxis === 'x') {
|
|
514
|
+
this.dragOffset.set(deltaX);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
onPointerUp(event) {
|
|
518
|
+
if (this.pointerId !== event.pointerId)
|
|
519
|
+
return;
|
|
520
|
+
const offset = this.dragOffset();
|
|
521
|
+
const threshold = 48;
|
|
522
|
+
if (this.dragAxis === 'x') {
|
|
523
|
+
if (offset <= -threshold) {
|
|
524
|
+
this.next();
|
|
525
|
+
}
|
|
526
|
+
else if (offset >= threshold) {
|
|
527
|
+
this.prev();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.pointerId = null;
|
|
531
|
+
this.dragAxis = null;
|
|
532
|
+
this.dragOffset.set(0);
|
|
533
|
+
this.dragging.set(false);
|
|
534
|
+
}
|
|
535
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
536
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: CarouselComponent, isStandalone: true, selector: "ui-carousel", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: true, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, showCounter: { classPropertyName: "showCounter", publicName: "showCounter", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { index: "indexChange" }, ngImport: i0, template: "<div\n class=\"ui-carousel\"\n role=\"group\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <div\n class=\"ui-carousel__viewport\"\n tabindex=\"0\"\n (keydown)=\"handleKeyDown($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n >\n <div\n class=\"ui-carousel__track\"\n [class.ui-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"'translateX(calc(-' + currentIndex() * 100 + '% + ' + dragOffset() + 'px))'\"\n >\n @for (image of images(); track image.src; let i = $index) {\n <div\n class=\"ui-carousel__slide\"\n role=\"group\"\n aria-roledescription=\"slide\"\n [attr.aria-label]=\"(i + 1) + ' of ' + images().length\"\n [attr.aria-hidden]=\"i !== currentIndex()\"\n >\n <img\n class=\"ui-carousel__image\"\n [src]=\"image.src\"\n [alt]=\"image.alt || ''\"\n draggable=\"false\"\n [attr.loading]=\"i === currentIndex() ? null : 'lazy'\"\n />\n </div>\n }\n </div>\n </div>\n\n @if (hasPrev()) {\n <button\n type=\"button\"\n class=\"ui-carousel__arrow ui-carousel__arrow--prev\"\n aria-label=\"Previous image\"\n (click)=\"prev()\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"/>\n </svg>\n </button>\n }\n @if (hasNext()) {\n <button\n type=\"button\"\n class=\"ui-carousel__arrow ui-carousel__arrow--next\"\n aria-label=\"Next image\"\n (click)=\"next()\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n </button>\n }\n\n @if (showCounter() && images().length > 0) {\n <span class=\"ui-carousel__counter\" aria-live=\"polite\">\n {{ currentIndex() + 1 }}/{{ images().length }}\n </span>\n }\n</div>\n", styles: [":host{display:block}.ui-carousel{position:relative;overflow:hidden;height:100%;border-radius:var(--ui-radius-lg);background-color:var(--ui-bg-tertiary)}.ui-carousel__viewport{overflow:hidden;touch-action:pan-y;height:100%}.ui-carousel__viewport:focus-visible{outline:2px solid var(--ui-primary);outline-offset:-2px}.ui-carousel__track{display:flex;height:100%;transition:transform var(--ui-transition-normal)}.ui-carousel__track--dragging{transition:none}.ui-carousel__slide{flex:0 0 100%;min-width:0;display:flex;align-items:center;justify-content:center}.ui-carousel__image{max-width:100%;max-height:100%;width:100%;height:100%;object-fit:cover;-webkit-user-select:none;user-select:none}.ui-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:none;border-radius:50%;background-color:var(--ui-carousel-control-bg);color:var(--ui-carousel-control-text);cursor:pointer;opacity:0;transition:opacity var(--ui-transition-fast),background-color var(--ui-transition-fast)}.ui-carousel__arrow--prev{left:var(--ui-spacing-sm)}.ui-carousel__arrow--next{right:var(--ui-spacing-sm)}.ui-carousel:hover .ui-carousel__arrow,.ui-carousel__arrow:focus-visible{opacity:1}@media(hover:none){.ui-carousel__arrow{opacity:1}}.ui-carousel__arrow:hover{background-color:color-mix(in srgb,var(--ui-carousel-control-bg) 80%,#000000)}.ui-carousel__arrow:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-carousel__counter{position:absolute;right:var(--ui-spacing-sm);bottom:var(--ui-spacing-sm);padding:2px var(--ui-spacing-sm);border-radius:var(--ui-radius-sm);background-color:var(--ui-carousel-counter-bg);color:var(--ui-carousel-control-text);font-size:var(--ui-font-xs);font-weight:500;pointer-events:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
537
|
+
}
|
|
538
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CarouselComponent, decorators: [{
|
|
539
|
+
type: Component,
|
|
540
|
+
args: [{ selector: 'ui-carousel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ui-carousel\"\n role=\"group\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <div\n class=\"ui-carousel__viewport\"\n tabindex=\"0\"\n (keydown)=\"handleKeyDown($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n >\n <div\n class=\"ui-carousel__track\"\n [class.ui-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"'translateX(calc(-' + currentIndex() * 100 + '% + ' + dragOffset() + 'px))'\"\n >\n @for (image of images(); track image.src; let i = $index) {\n <div\n class=\"ui-carousel__slide\"\n role=\"group\"\n aria-roledescription=\"slide\"\n [attr.aria-label]=\"(i + 1) + ' of ' + images().length\"\n [attr.aria-hidden]=\"i !== currentIndex()\"\n >\n <img\n class=\"ui-carousel__image\"\n [src]=\"image.src\"\n [alt]=\"image.alt || ''\"\n draggable=\"false\"\n [attr.loading]=\"i === currentIndex() ? null : 'lazy'\"\n />\n </div>\n }\n </div>\n </div>\n\n @if (hasPrev()) {\n <button\n type=\"button\"\n class=\"ui-carousel__arrow ui-carousel__arrow--prev\"\n aria-label=\"Previous image\"\n (click)=\"prev()\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"/>\n </svg>\n </button>\n }\n @if (hasNext()) {\n <button\n type=\"button\"\n class=\"ui-carousel__arrow ui-carousel__arrow--next\"\n aria-label=\"Next image\"\n (click)=\"next()\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n </button>\n }\n\n @if (showCounter() && images().length > 0) {\n <span class=\"ui-carousel__counter\" aria-live=\"polite\">\n {{ currentIndex() + 1 }}/{{ images().length }}\n </span>\n }\n</div>\n", styles: [":host{display:block}.ui-carousel{position:relative;overflow:hidden;height:100%;border-radius:var(--ui-radius-lg);background-color:var(--ui-bg-tertiary)}.ui-carousel__viewport{overflow:hidden;touch-action:pan-y;height:100%}.ui-carousel__viewport:focus-visible{outline:2px solid var(--ui-primary);outline-offset:-2px}.ui-carousel__track{display:flex;height:100%;transition:transform var(--ui-transition-normal)}.ui-carousel__track--dragging{transition:none}.ui-carousel__slide{flex:0 0 100%;min-width:0;display:flex;align-items:center;justify-content:center}.ui-carousel__image{max-width:100%;max-height:100%;width:100%;height:100%;object-fit:cover;-webkit-user-select:none;user-select:none}.ui-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:none;border-radius:50%;background-color:var(--ui-carousel-control-bg);color:var(--ui-carousel-control-text);cursor:pointer;opacity:0;transition:opacity var(--ui-transition-fast),background-color var(--ui-transition-fast)}.ui-carousel__arrow--prev{left:var(--ui-spacing-sm)}.ui-carousel__arrow--next{right:var(--ui-spacing-sm)}.ui-carousel:hover .ui-carousel__arrow,.ui-carousel__arrow:focus-visible{opacity:1}@media(hover:none){.ui-carousel__arrow{opacity:1}}.ui-carousel__arrow:hover{background-color:color-mix(in srgb,var(--ui-carousel-control-bg) 80%,#000000)}.ui-carousel__arrow:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-carousel__counter{position:absolute;right:var(--ui-spacing-sm);bottom:var(--ui-spacing-sm);padding:2px var(--ui-spacing-sm);border-radius:var(--ui-radius-sm);background-color:var(--ui-carousel-counter-bg);color:var(--ui-carousel-control-text);font-size:var(--ui-font-xs);font-weight:500;pointer-events:none}\n"] }]
|
|
541
|
+
}], propDecorators: { images: [{ type: i0.Input, args: [{ isSignal: true, alias: "images", required: true }] }], startIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "startIndex", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], showCounter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCounter", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }, { type: i0.Output, args: ["indexChange"] }] } });
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Fullscreen image lightbox. Opened via LightboxService — not meant to be
|
|
545
|
+
* embedded directly in templates.
|
|
546
|
+
*/
|
|
547
|
+
class LightboxComponent {
|
|
548
|
+
config = inject(DIALOG_DATA);
|
|
549
|
+
dialogRef = inject(DIALOG_REF);
|
|
550
|
+
carousel = viewChild.required(CarouselComponent);
|
|
551
|
+
viewport = viewChild.required('container');
|
|
552
|
+
/** Tracks if mousedown started on the backdrop (to prevent close on drag-out) */
|
|
553
|
+
mouseDownOnBackdrop = signal(false, ...(ngDevMode ? [{ debugName: "mouseDownOnBackdrop" }] : []));
|
|
554
|
+
constructor() {
|
|
555
|
+
afterNextRender(() => {
|
|
556
|
+
this.viewport().nativeElement.focus();
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
onKeyDown(event) {
|
|
560
|
+
switch (event.key) {
|
|
561
|
+
case 'Escape':
|
|
562
|
+
event.preventDefault();
|
|
563
|
+
this.close();
|
|
564
|
+
break;
|
|
565
|
+
case 'ArrowLeft':
|
|
566
|
+
event.preventDefault();
|
|
567
|
+
this.carousel().prev();
|
|
568
|
+
break;
|
|
569
|
+
case 'ArrowRight':
|
|
570
|
+
event.preventDefault();
|
|
571
|
+
this.carousel().next();
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
onBackdropMouseDown(event) {
|
|
576
|
+
this.mouseDownOnBackdrop.set(event.target === event.currentTarget);
|
|
577
|
+
}
|
|
578
|
+
onBackdropClick(event) {
|
|
579
|
+
if (this.mouseDownOnBackdrop() && event.target === event.currentTarget) {
|
|
580
|
+
this.close();
|
|
581
|
+
}
|
|
582
|
+
this.mouseDownOnBackdrop.set(false);
|
|
583
|
+
}
|
|
584
|
+
close() {
|
|
585
|
+
this.dialogRef.close();
|
|
586
|
+
}
|
|
587
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LightboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
588
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.1", type: LightboxComponent, isStandalone: true, selector: "ui-lightbox", host: { listeners: { "document:keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "carousel", first: true, predicate: CarouselComponent, descendants: true, isSignal: true }, { propertyName: "viewport", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"ui-lightbox__backdrop\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n>\n <div\n #container\n class=\"ui-lightbox__container\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Image gallery\"\n tabindex=\"-1\"\n >\n <button\n type=\"button\"\n class=\"ui-lightbox__close\"\n aria-label=\"Close\"\n (click)=\"close()\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n\n <ui-carousel\n class=\"ui-lightbox__carousel\"\n [images]=\"config.images\"\n [startIndex]=\"config.startIndex ?? 0\"\n [showCounter]=\"true\"\n />\n </div>\n</div>\n", styles: [".ui-lightbox__backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background-color:var(--ui-lightbox-backdrop);animation:fadeIn var(--ui-transition-fast)}.ui-lightbox__container{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:var(--ui-spacing-xl);outline:none;pointer-events:none}.ui-lightbox__carousel{width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:auto}.ui-lightbox__carousel ::ng-deep .ui-carousel{height:100%;background:transparent;border-radius:0}.ui-lightbox__carousel ::ng-deep .ui-carousel__image{object-fit:contain}.ui-lightbox__carousel ::ng-deep .ui-carousel__counter{right:50%;transform:translate(50%);font-size:var(--ui-font-sm)}.ui-lightbox__close{position:absolute;top:var(--ui-spacing-md);right:var(--ui-spacing-md);z-index:1;display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border:none;border-radius:50%;background-color:var(--ui-lightbox-control-bg);color:var(--ui-lightbox-control-text);cursor:pointer;pointer-events:auto;transition:background-color var(--ui-transition-fast)}.ui-lightbox__close:hover{background-color:color-mix(in srgb,var(--ui-lightbox-control-bg) 80%,#000000)}.ui-lightbox__close:focus-visible{outline:2px solid var(--ui-lightbox-control-text);outline-offset:2px}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "component", type: CarouselComponent, selector: "ui-carousel", inputs: ["images", "startIndex", "loop", "showCounter", "ariaLabel", "index"], outputs: ["indexChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
589
|
+
}
|
|
590
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LightboxComponent, decorators: [{
|
|
591
|
+
type: Component,
|
|
592
|
+
args: [{ selector: 'ui-lightbox', standalone: true, imports: [CarouselComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ui-lightbox__backdrop\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n>\n <div\n #container\n class=\"ui-lightbox__container\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Image gallery\"\n tabindex=\"-1\"\n >\n <button\n type=\"button\"\n class=\"ui-lightbox__close\"\n aria-label=\"Close\"\n (click)=\"close()\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n\n <ui-carousel\n class=\"ui-lightbox__carousel\"\n [images]=\"config.images\"\n [startIndex]=\"config.startIndex ?? 0\"\n [showCounter]=\"true\"\n />\n </div>\n</div>\n", styles: [".ui-lightbox__backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background-color:var(--ui-lightbox-backdrop);animation:fadeIn var(--ui-transition-fast)}.ui-lightbox__container{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:var(--ui-spacing-xl);outline:none;pointer-events:none}.ui-lightbox__carousel{width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:auto}.ui-lightbox__carousel ::ng-deep .ui-carousel{height:100%;background:transparent;border-radius:0}.ui-lightbox__carousel ::ng-deep .ui-carousel__image{object-fit:contain}.ui-lightbox__carousel ::ng-deep .ui-carousel__counter{right:50%;transform:translate(50%);font-size:var(--ui-font-sm)}.ui-lightbox__close{position:absolute;top:var(--ui-spacing-md);right:var(--ui-spacing-md);z-index:1;display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border:none;border-radius:50%;background-color:var(--ui-lightbox-control-bg);color:var(--ui-lightbox-control-text);cursor:pointer;pointer-events:auto;transition:background-color var(--ui-transition-fast)}.ui-lightbox__close:hover{background-color:color-mix(in srgb,var(--ui-lightbox-control-bg) 80%,#000000)}.ui-lightbox__close:focus-visible{outline:2px solid var(--ui-lightbox-control-text);outline-offset:2px}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
|
|
593
|
+
}], ctorParameters: () => [], propDecorators: { carousel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CarouselComponent), { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: ['container', { isSignal: true }] }], onKeyDown: [{
|
|
594
|
+
type: HostListener,
|
|
595
|
+
args: ['document:keydown', ['$event']]
|
|
596
|
+
}] } });
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Service for opening a fullscreen image lightbox.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```typescript
|
|
603
|
+
* const ref = this.lightboxService.open({
|
|
604
|
+
* images: [{ src: '/photos/1.jpg', alt: 'Living room' }],
|
|
605
|
+
* startIndex: 0,
|
|
606
|
+
* });
|
|
607
|
+
*
|
|
608
|
+
* await ref.afterClosed();
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
class LightboxService {
|
|
612
|
+
dialogService = inject(DialogService);
|
|
613
|
+
/**
|
|
614
|
+
* Opens the lightbox with the given images.
|
|
615
|
+
*
|
|
616
|
+
* @returns A DialogRef that resolves when the lightbox is closed
|
|
617
|
+
*/
|
|
618
|
+
open(config) {
|
|
619
|
+
return this.dialogService.open(LightboxComponent, {
|
|
620
|
+
data: config,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LightboxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
624
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LightboxService, providedIn: 'root' });
|
|
625
|
+
}
|
|
626
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LightboxService, decorators: [{
|
|
627
|
+
type: Injectable,
|
|
628
|
+
args: [{ providedIn: 'root' }]
|
|
629
|
+
}] });
|
|
630
|
+
|
|
429
631
|
class ToastComponent {
|
|
430
632
|
data = input.required(...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
431
633
|
dismissed = output();
|
|
@@ -6391,6 +6593,435 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
6391
6593
|
args: ['document:click', ['$event']]
|
|
6392
6594
|
}] } });
|
|
6393
6595
|
|
|
6596
|
+
class SegmentedComponent {
|
|
6597
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
6598
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
6599
|
+
fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
|
|
6600
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
6601
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
6602
|
+
value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
|
|
6603
|
+
track = viewChild.required('track');
|
|
6604
|
+
segmentedClasses = computed(() => {
|
|
6605
|
+
const classes = [`ui-segmented--${this.size()}`];
|
|
6606
|
+
if (this.fullWidth()) {
|
|
6607
|
+
classes.push('ui-segmented--full-width');
|
|
6608
|
+
}
|
|
6609
|
+
if (this.disabled()) {
|
|
6610
|
+
classes.push('ui-segmented--disabled');
|
|
6611
|
+
}
|
|
6612
|
+
return classes.join(' ');
|
|
6613
|
+
}, ...(ngDevMode ? [{ debugName: "segmentedClasses" }] : []));
|
|
6614
|
+
selectedIndex = computed(() => {
|
|
6615
|
+
return this.options().findIndex(option => option.value === this.value());
|
|
6616
|
+
}, ...(ngDevMode ? [{ debugName: "selectedIndex" }] : []));
|
|
6617
|
+
isSegmentDisabled(option) {
|
|
6618
|
+
return this.disabled() || !!option.disabled;
|
|
6619
|
+
}
|
|
6620
|
+
isTabbable(index) {
|
|
6621
|
+
const selected = this.selectedIndex();
|
|
6622
|
+
if (selected >= 0)
|
|
6623
|
+
return index === selected;
|
|
6624
|
+
// No selection yet: first enabled segment holds the tab stop
|
|
6625
|
+
const firstEnabled = this.options().findIndex(option => !this.isSegmentDisabled(option));
|
|
6626
|
+
return index === firstEnabled;
|
|
6627
|
+
}
|
|
6628
|
+
selectSegment(option) {
|
|
6629
|
+
if (this.isSegmentDisabled(option))
|
|
6630
|
+
return;
|
|
6631
|
+
this.value.set(option.value);
|
|
6632
|
+
}
|
|
6633
|
+
handleKeyDown(event, currentIndex) {
|
|
6634
|
+
const enabledSegments = this.options()
|
|
6635
|
+
.map((option, index) => ({ option, index }))
|
|
6636
|
+
.filter(({ option }) => !this.isSegmentDisabled(option));
|
|
6637
|
+
if (enabledSegments.length === 0)
|
|
6638
|
+
return;
|
|
6639
|
+
const currentEnabledIndex = enabledSegments.findIndex(({ index }) => index === currentIndex);
|
|
6640
|
+
let nextEnabledIndex = -1;
|
|
6641
|
+
switch (event.key) {
|
|
6642
|
+
case 'ArrowRight':
|
|
6643
|
+
case 'ArrowDown':
|
|
6644
|
+
event.preventDefault();
|
|
6645
|
+
nextEnabledIndex = (currentEnabledIndex + 1) % enabledSegments.length;
|
|
6646
|
+
break;
|
|
6647
|
+
case 'ArrowLeft':
|
|
6648
|
+
case 'ArrowUp':
|
|
6649
|
+
event.preventDefault();
|
|
6650
|
+
nextEnabledIndex = (currentEnabledIndex - 1 + enabledSegments.length) % enabledSegments.length;
|
|
6651
|
+
break;
|
|
6652
|
+
case 'Home':
|
|
6653
|
+
event.preventDefault();
|
|
6654
|
+
nextEnabledIndex = 0;
|
|
6655
|
+
break;
|
|
6656
|
+
case 'End':
|
|
6657
|
+
event.preventDefault();
|
|
6658
|
+
nextEnabledIndex = enabledSegments.length - 1;
|
|
6659
|
+
break;
|
|
6660
|
+
default:
|
|
6661
|
+
return;
|
|
6662
|
+
}
|
|
6663
|
+
if (nextEnabledIndex >= 0) {
|
|
6664
|
+
const { option, index } = enabledSegments[nextEnabledIndex];
|
|
6665
|
+
this.selectSegment(option);
|
|
6666
|
+
this.focusSegment(index);
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
focusSegment(index) {
|
|
6670
|
+
const buttons = this.track().nativeElement.querySelectorAll('.ui-segmented__segment');
|
|
6671
|
+
buttons[index]?.focus();
|
|
6672
|
+
}
|
|
6673
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SegmentedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6674
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: SegmentedComponent, isStandalone: true, selector: "ui-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #track\n class=\"ui-segmented\"\n [class]=\"segmentedClasses()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n type=\"button\"\n class=\"ui-segmented__segment\"\n role=\"radio\"\n [class.ui-segmented__segment--active]=\"i === selectedIndex()\"\n [class.ui-segmented__segment--disabled]=\"isSegmentDisabled(option)\"\n [attr.aria-checked]=\"i === selectedIndex()\"\n [attr.tabindex]=\"isTabbable(i) ? 0 : -1\"\n [disabled]=\"isSegmentDisabled(option)\"\n (click)=\"selectSegment(option)\"\n (keydown)=\"handleKeyDown($event, i)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.ui-segmented{display:inline-flex;padding:3px;gap:2px;background-color:var(--ui-segmented-track-bg);border-radius:var(--ui-radius-md)}.ui-segmented__segment{flex:0 1 auto;border:none;background:transparent;color:var(--ui-text-muted);font-weight:500;white-space:nowrap;cursor:pointer;border-radius:var(--ui-radius-sm);transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-segmented__segment:hover:not(:disabled):not(.ui-segmented__segment--active){color:var(--ui-text)}.ui-segmented__segment:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-segmented__segment--active{background-color:var(--ui-segmented-active-bg);color:var(--ui-segmented-active-text);box-shadow:var(--ui-shadow-sm)}.ui-segmented__segment--disabled{opacity:.5;cursor:not-allowed}.ui-segmented--disabled{opacity:.7}:host:has(.ui-segmented--full-width){display:block}.ui-segmented--full-width{display:flex;width:100%}.ui-segmented--full-width .ui-segmented__segment{flex:1 1 0}.ui-segmented--sm .ui-segmented__segment{padding:.25rem .75rem;font-size:var(--ui-font-sm)}.ui-segmented--md .ui-segmented__segment{padding:.375rem 1rem;font-size:var(--ui-font-sm)}.ui-segmented--lg .ui-segmented__segment{padding:.5rem 1.25rem;font-size:var(--ui-font-md)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6675
|
+
}
|
|
6676
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SegmentedComponent, decorators: [{
|
|
6677
|
+
type: Component,
|
|
6678
|
+
args: [{ selector: 'ui-segmented', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #track\n class=\"ui-segmented\"\n [class]=\"segmentedClasses()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n type=\"button\"\n class=\"ui-segmented__segment\"\n role=\"radio\"\n [class.ui-segmented__segment--active]=\"i === selectedIndex()\"\n [class.ui-segmented__segment--disabled]=\"isSegmentDisabled(option)\"\n [attr.aria-checked]=\"i === selectedIndex()\"\n [attr.tabindex]=\"isTabbable(i) ? 0 : -1\"\n [disabled]=\"isSegmentDisabled(option)\"\n (click)=\"selectSegment(option)\"\n (keydown)=\"handleKeyDown($event, i)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.ui-segmented{display:inline-flex;padding:3px;gap:2px;background-color:var(--ui-segmented-track-bg);border-radius:var(--ui-radius-md)}.ui-segmented__segment{flex:0 1 auto;border:none;background:transparent;color:var(--ui-text-muted);font-weight:500;white-space:nowrap;cursor:pointer;border-radius:var(--ui-radius-sm);transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-segmented__segment:hover:not(:disabled):not(.ui-segmented__segment--active){color:var(--ui-text)}.ui-segmented__segment:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-segmented__segment--active{background-color:var(--ui-segmented-active-bg);color:var(--ui-segmented-active-text);box-shadow:var(--ui-shadow-sm)}.ui-segmented__segment--disabled{opacity:.5;cursor:not-allowed}.ui-segmented--disabled{opacity:.7}:host:has(.ui-segmented--full-width){display:block}.ui-segmented--full-width{display:flex;width:100%}.ui-segmented--full-width .ui-segmented__segment{flex:1 1 0}.ui-segmented--sm .ui-segmented__segment{padding:.25rem .75rem;font-size:var(--ui-font-sm)}.ui-segmented--md .ui-segmented__segment{padding:.375rem 1rem;font-size:var(--ui-font-sm)}.ui-segmented--lg .ui-segmented__segment{padding:.5rem 1.25rem;font-size:var(--ui-font-md)}\n"] }]
|
|
6679
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
|
|
6680
|
+
|
|
6681
|
+
class StepperComponent {
|
|
6682
|
+
steps = input.required(...(ngDevMode ? [{ debugName: "steps" }] : []));
|
|
6683
|
+
linear = input(true, ...(ngDevMode ? [{ debugName: "linear" }] : []));
|
|
6684
|
+
ariaLabel = input('Progress', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
6685
|
+
activeIndex = model(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
|
|
6686
|
+
stepClick = output();
|
|
6687
|
+
stepStates = computed(() => {
|
|
6688
|
+
const active = this.activeIndex();
|
|
6689
|
+
return this.steps().map((step, index) => ({
|
|
6690
|
+
step,
|
|
6691
|
+
index,
|
|
6692
|
+
completed: index < active,
|
|
6693
|
+
active: index === active,
|
|
6694
|
+
clickable: this.linear() ? index < active : index !== active,
|
|
6695
|
+
}));
|
|
6696
|
+
}, ...(ngDevMode ? [{ debugName: "stepStates" }] : []));
|
|
6697
|
+
selectStep(index, clickable) {
|
|
6698
|
+
if (!clickable)
|
|
6699
|
+
return;
|
|
6700
|
+
this.activeIndex.set(index);
|
|
6701
|
+
this.stepClick.emit(index);
|
|
6702
|
+
}
|
|
6703
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: StepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6704
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: StepperComponent, isStandalone: true, selector: "ui-stepper", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: true, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeIndex: "activeIndexChange", stepClick: "stepClick" }, ngImport: i0, template: "<ol class=\"ui-stepper\" [attr.aria-label]=\"ariaLabel()\">\n @for (state of stepStates(); track state.step.key) {\n <li\n class=\"ui-stepper__step\"\n [class.ui-stepper__step--completed]=\"state.completed\"\n [class.ui-stepper__step--active]=\"state.active\"\n [attr.aria-current]=\"state.active ? 'step' : null\"\n >\n @if (!$first) {\n <span class=\"ui-stepper__connector\" aria-hidden=\"true\"></span>\n }\n @if (state.clickable) {\n <button\n type=\"button\"\n class=\"ui-stepper__button\"\n (click)=\"selectStep(state.index, true)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"stepContent\"\n [ngTemplateOutletContext]=\"{ $implicit: state }\"\n />\n </button>\n } @else {\n <span class=\"ui-stepper__button ui-stepper__button--static\">\n <ng-container\n [ngTemplateOutlet]=\"stepContent\"\n [ngTemplateOutletContext]=\"{ $implicit: state }\"\n />\n </span>\n }\n </li>\n }\n</ol>\n\n<ng-template #stepContent let-state>\n <span class=\"ui-stepper__circle\" aria-hidden=\"true\">\n @if (state.completed) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\">\n <path d=\"M20 6L9 17l-5-5\"/>\n </svg>\n } @else {\n {{ state.index + 1 }}\n }\n </span>\n <span class=\"ui-stepper__label\">{{ state.step.label }}</span>\n</ng-template>\n", styles: [":host{display:block}.ui-stepper{display:flex;margin:0;padding:0;list-style:none}.ui-stepper__step{display:flex;align-items:center;flex:0 1 auto;min-width:0}.ui-stepper__step:not(:first-child){flex:1 1 0}.ui-stepper__connector{flex:1 1 0;height:2px;min-width:var(--ui-spacing-md);margin:0 var(--ui-spacing-sm);background-color:var(--ui-stepper-connector);transition:background-color var(--ui-transition-normal)}.ui-stepper__step--completed .ui-stepper__connector,.ui-stepper__step--active .ui-stepper__connector{background-color:var(--ui-stepper-completed)}.ui-stepper__button{display:inline-flex;align-items:center;gap:var(--ui-spacing-sm);padding:var(--ui-spacing-xs);border:none;background:transparent;font:inherit;color:var(--ui-text-muted);border-radius:var(--ui-radius-md);white-space:nowrap;min-width:0}button.ui-stepper__button{cursor:pointer;transition:color var(--ui-transition-fast)}button.ui-stepper__button:hover{color:var(--ui-text)}button.ui-stepper__button:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-stepper__circle{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ui-stepper-circle-size);height:var(--ui-stepper-circle-size);border:2px solid var(--ui-stepper-connector);border-radius:50%;font-size:var(--ui-font-sm);font-weight:600;color:var(--ui-text-muted);background-color:var(--ui-bg);transition:all var(--ui-transition-normal)}.ui-stepper__label{font-size:var(--ui-font-sm);font-weight:500;overflow:hidden;text-overflow:ellipsis}.ui-stepper__step--completed .ui-stepper__circle{border-color:var(--ui-stepper-completed);background-color:var(--ui-stepper-completed);color:var(--ui-stepper-completed-text)}.ui-stepper__step--completed .ui-stepper__label{color:var(--ui-text)}.ui-stepper__step--active .ui-stepper__circle{border-color:var(--ui-primary);color:var(--ui-primary)}.ui-stepper__step--active .ui-stepper__label{color:var(--ui-text);font-weight:600}@media(max-width:768px){.ui-stepper__step:not(.ui-stepper__step--active) .ui-stepper__label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6705
|
+
}
|
|
6706
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: StepperComponent, decorators: [{
|
|
6707
|
+
type: Component,
|
|
6708
|
+
args: [{ selector: 'ui-stepper', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ol class=\"ui-stepper\" [attr.aria-label]=\"ariaLabel()\">\n @for (state of stepStates(); track state.step.key) {\n <li\n class=\"ui-stepper__step\"\n [class.ui-stepper__step--completed]=\"state.completed\"\n [class.ui-stepper__step--active]=\"state.active\"\n [attr.aria-current]=\"state.active ? 'step' : null\"\n >\n @if (!$first) {\n <span class=\"ui-stepper__connector\" aria-hidden=\"true\"></span>\n }\n @if (state.clickable) {\n <button\n type=\"button\"\n class=\"ui-stepper__button\"\n (click)=\"selectStep(state.index, true)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"stepContent\"\n [ngTemplateOutletContext]=\"{ $implicit: state }\"\n />\n </button>\n } @else {\n <span class=\"ui-stepper__button ui-stepper__button--static\">\n <ng-container\n [ngTemplateOutlet]=\"stepContent\"\n [ngTemplateOutletContext]=\"{ $implicit: state }\"\n />\n </span>\n }\n </li>\n }\n</ol>\n\n<ng-template #stepContent let-state>\n <span class=\"ui-stepper__circle\" aria-hidden=\"true\">\n @if (state.completed) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\">\n <path d=\"M20 6L9 17l-5-5\"/>\n </svg>\n } @else {\n {{ state.index + 1 }}\n }\n </span>\n <span class=\"ui-stepper__label\">{{ state.step.label }}</span>\n</ng-template>\n", styles: [":host{display:block}.ui-stepper{display:flex;margin:0;padding:0;list-style:none}.ui-stepper__step{display:flex;align-items:center;flex:0 1 auto;min-width:0}.ui-stepper__step:not(:first-child){flex:1 1 0}.ui-stepper__connector{flex:1 1 0;height:2px;min-width:var(--ui-spacing-md);margin:0 var(--ui-spacing-sm);background-color:var(--ui-stepper-connector);transition:background-color var(--ui-transition-normal)}.ui-stepper__step--completed .ui-stepper__connector,.ui-stepper__step--active .ui-stepper__connector{background-color:var(--ui-stepper-completed)}.ui-stepper__button{display:inline-flex;align-items:center;gap:var(--ui-spacing-sm);padding:var(--ui-spacing-xs);border:none;background:transparent;font:inherit;color:var(--ui-text-muted);border-radius:var(--ui-radius-md);white-space:nowrap;min-width:0}button.ui-stepper__button{cursor:pointer;transition:color var(--ui-transition-fast)}button.ui-stepper__button:hover{color:var(--ui-text)}button.ui-stepper__button:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-stepper__circle{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ui-stepper-circle-size);height:var(--ui-stepper-circle-size);border:2px solid var(--ui-stepper-connector);border-radius:50%;font-size:var(--ui-font-sm);font-weight:600;color:var(--ui-text-muted);background-color:var(--ui-bg);transition:all var(--ui-transition-normal)}.ui-stepper__label{font-size:var(--ui-font-sm);font-weight:500;overflow:hidden;text-overflow:ellipsis}.ui-stepper__step--completed .ui-stepper__circle{border-color:var(--ui-stepper-completed);background-color:var(--ui-stepper-completed);color:var(--ui-stepper-completed-text)}.ui-stepper__step--completed .ui-stepper__label{color:var(--ui-text)}.ui-stepper__step--active .ui-stepper__circle{border-color:var(--ui-primary);color:var(--ui-primary)}.ui-stepper__step--active .ui-stepper__label{color:var(--ui-text);font-weight:600}@media(max-width:768px){.ui-stepper__step:not(.ui-stepper__step--active) .ui-stepper__label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}}\n"] }]
|
|
6709
|
+
}], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }, { type: i0.Output, args: ["activeIndexChange"] }], stepClick: [{ type: i0.Output, args: ["stepClick"] }] } });
|
|
6710
|
+
|
|
6711
|
+
class BreadcrumbSeparatorDirective {
|
|
6712
|
+
templateRef = inject((TemplateRef));
|
|
6713
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BreadcrumbSeparatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
6714
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: BreadcrumbSeparatorDirective, isStandalone: true, selector: "[uiBreadcrumbSeparator]", ngImport: i0 });
|
|
6715
|
+
}
|
|
6716
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BreadcrumbSeparatorDirective, decorators: [{
|
|
6717
|
+
type: Directive,
|
|
6718
|
+
args: [{
|
|
6719
|
+
selector: '[uiBreadcrumbSeparator]',
|
|
6720
|
+
standalone: true,
|
|
6721
|
+
}]
|
|
6722
|
+
}] });
|
|
6723
|
+
|
|
6724
|
+
class BreadcrumbComponent {
|
|
6725
|
+
items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
6726
|
+
separator = input('/', ...(ngDevMode ? [{ debugName: "separator" }] : []));
|
|
6727
|
+
separatorTemplate = contentChild(BreadcrumbSeparatorDirective, ...(ngDevMode ? [{ debugName: "separatorTemplate" }] : []));
|
|
6728
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6729
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: BreadcrumbComponent, isStandalone: true, selector: "ui-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "separatorTemplate", first: true, predicate: BreadcrumbSeparatorDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<nav class=\"ui-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"ui-breadcrumb__list\">\n @for (item of items(); track $index) {\n <li class=\"ui-breadcrumb__item\">\n @if (!$first) {\n <span class=\"ui-breadcrumb__separator\" aria-hidden=\"true\">\n @if (separatorTemplate(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl.templateRef\" />\n } @else {\n {{ separator() }}\n }\n </span>\n }\n @if (item.url && !$last) {\n <a class=\"ui-breadcrumb__link\" [routerLink]=\"item.url\">{{ item.label }}</a>\n } @else {\n <span\n class=\"ui-breadcrumb__current\"\n [attr.aria-current]=\"$last ? 'page' : null\"\n >{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [":host{display:block}.ui-breadcrumb__list{display:flex;align-items:center;flex-wrap:wrap;margin:0;padding:0;list-style:none}.ui-breadcrumb__item{display:flex;align-items:center;min-width:0;font-size:var(--ui-font-sm)}.ui-breadcrumb__separator{display:inline-flex;align-items:center;margin:0 var(--ui-spacing-sm);color:var(--ui-text-muted);-webkit-user-select:none;user-select:none}.ui-breadcrumb__link,.ui-breadcrumb__current{max-width:16rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-breadcrumb__link{color:var(--ui-text-muted);text-decoration:none;border-radius:var(--ui-radius-sm);transition:color var(--ui-transition-fast)}.ui-breadcrumb__link:hover{color:var(--ui-text);text-decoration:underline}.ui-breadcrumb__link:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-breadcrumb__current{color:var(--ui-text);font-weight:500}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6730
|
+
}
|
|
6731
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BreadcrumbComponent, decorators: [{
|
|
6732
|
+
type: Component,
|
|
6733
|
+
args: [{ selector: 'ui-breadcrumb', standalone: true, imports: [NgTemplateOutlet, RouterLink], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"ui-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"ui-breadcrumb__list\">\n @for (item of items(); track $index) {\n <li class=\"ui-breadcrumb__item\">\n @if (!$first) {\n <span class=\"ui-breadcrumb__separator\" aria-hidden=\"true\">\n @if (separatorTemplate(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl.templateRef\" />\n } @else {\n {{ separator() }}\n }\n </span>\n }\n @if (item.url && !$last) {\n <a class=\"ui-breadcrumb__link\" [routerLink]=\"item.url\">{{ item.label }}</a>\n } @else {\n <span\n class=\"ui-breadcrumb__current\"\n [attr.aria-current]=\"$last ? 'page' : null\"\n >{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [":host{display:block}.ui-breadcrumb__list{display:flex;align-items:center;flex-wrap:wrap;margin:0;padding:0;list-style:none}.ui-breadcrumb__item{display:flex;align-items:center;min-width:0;font-size:var(--ui-font-sm)}.ui-breadcrumb__separator{display:inline-flex;align-items:center;margin:0 var(--ui-spacing-sm);color:var(--ui-text-muted);-webkit-user-select:none;user-select:none}.ui-breadcrumb__link,.ui-breadcrumb__current{max-width:16rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-breadcrumb__link{color:var(--ui-text-muted);text-decoration:none;border-radius:var(--ui-radius-sm);transition:color var(--ui-transition-fast)}.ui-breadcrumb__link:hover{color:var(--ui-text);text-decoration:underline}.ui-breadcrumb__link:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-breadcrumb__current{color:var(--ui-text);font-weight:500}\n"] }]
|
|
6734
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], separatorTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => BreadcrumbSeparatorDirective), { isSignal: true }] }] } });
|
|
6735
|
+
|
|
6736
|
+
class SkeletonComponent {
|
|
6737
|
+
variant = input('line', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
6738
|
+
width = input(...(ngDevMode ? [undefined, { debugName: "width" }] : []));
|
|
6739
|
+
height = input(...(ngDevMode ? [undefined, { debugName: "height" }] : []));
|
|
6740
|
+
count = input(1, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
6741
|
+
items = computed(() => {
|
|
6742
|
+
return Array.from({ length: Math.max(1, this.count()) }, (_, i) => i);
|
|
6743
|
+
}, ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
6744
|
+
itemWidth(index) {
|
|
6745
|
+
// Last line of a multi-line paragraph placeholder is shortened
|
|
6746
|
+
if (this.variant() === 'line' && this.count() > 1 && index === this.count() - 1) {
|
|
6747
|
+
return '60%';
|
|
6748
|
+
}
|
|
6749
|
+
return this.width() ?? null;
|
|
6750
|
+
}
|
|
6751
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6752
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: SkeletonComponent, isStandalone: true, selector: "ui-skeleton", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"ui-skeleton\" aria-hidden=\"true\">\n @for (item of items(); track item) {\n <span\n class=\"ui-skeleton__item\"\n [class]=\"'ui-skeleton__item--' + variant()\"\n [style.width]=\"itemWidth(item)\"\n [style.height]=\"height() ?? null\"\n ></span>\n }\n</div>\n", styles: [":host{display:block}.ui-skeleton{display:flex;flex-direction:column;gap:var(--ui-spacing-sm)}.ui-skeleton__item{display:block;background:linear-gradient(90deg,var(--ui-skeleton-base) 25%,var(--ui-skeleton-highlight) 37%,var(--ui-skeleton-base) 63%);background-size:400% 100%;animation:ui-skeleton-shimmer 1.4s ease infinite}.ui-skeleton__item--line{width:100%;height:.875rem;border-radius:var(--ui-radius-sm)}.ui-skeleton__item--box{width:100%;height:6rem;border-radius:var(--ui-radius-md)}.ui-skeleton__item--circle{width:3rem;height:3rem;border-radius:50%}@keyframes ui-skeleton-shimmer{0%{background-position:100% 50%}to{background-position:0 50%}}@media(prefers-reduced-motion:reduce){.ui-skeleton__item{background:var(--ui-skeleton-base);animation:ui-skeleton-pulse 2s ease-in-out infinite}@keyframes ui-skeleton-pulse{0%,to{opacity:1}50%{opacity:.6}}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6753
|
+
}
|
|
6754
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SkeletonComponent, decorators: [{
|
|
6755
|
+
type: Component,
|
|
6756
|
+
args: [{ selector: 'ui-skeleton', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ui-skeleton\" aria-hidden=\"true\">\n @for (item of items(); track item) {\n <span\n class=\"ui-skeleton__item\"\n [class]=\"'ui-skeleton__item--' + variant()\"\n [style.width]=\"itemWidth(item)\"\n [style.height]=\"height() ?? null\"\n ></span>\n }\n</div>\n", styles: [":host{display:block}.ui-skeleton{display:flex;flex-direction:column;gap:var(--ui-spacing-sm)}.ui-skeleton__item{display:block;background:linear-gradient(90deg,var(--ui-skeleton-base) 25%,var(--ui-skeleton-highlight) 37%,var(--ui-skeleton-base) 63%);background-size:400% 100%;animation:ui-skeleton-shimmer 1.4s ease infinite}.ui-skeleton__item--line{width:100%;height:.875rem;border-radius:var(--ui-radius-sm)}.ui-skeleton__item--box{width:100%;height:6rem;border-radius:var(--ui-radius-md)}.ui-skeleton__item--circle{width:3rem;height:3rem;border-radius:50%}@keyframes ui-skeleton-shimmer{0%{background-position:100% 50%}to{background-position:0 50%}}@media(prefers-reduced-motion:reduce){.ui-skeleton__item{background:var(--ui-skeleton-base);animation:ui-skeleton-pulse 2s ease-in-out infinite}@keyframes ui-skeleton-pulse{0%,to{opacity:1}50%{opacity:.6}}}\n"] }]
|
|
6757
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }] } });
|
|
6758
|
+
|
|
6759
|
+
class ImageUploaderComponent {
|
|
6760
|
+
maxCount = input(30, ...(ngDevMode ? [{ debugName: "maxCount" }] : []));
|
|
6761
|
+
accept = input('image/*', ...(ngDevMode ? [{ debugName: "accept" }] : []));
|
|
6762
|
+
maxFileSizeMb = input(...(ngDevMode ? [undefined, { debugName: "maxFileSizeMb" }] : []));
|
|
6763
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
6764
|
+
coverLabel = input('Cover', ...(ngDevMode ? [{ debugName: "coverLabel" }] : []));
|
|
6765
|
+
addLabel = input('Add images', ...(ngDevMode ? [{ debugName: "addLabel" }] : []));
|
|
6766
|
+
items = model([], ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
6767
|
+
filesAdded = output();
|
|
6768
|
+
filesRejected = output();
|
|
6769
|
+
removed = output();
|
|
6770
|
+
reordered = output();
|
|
6771
|
+
fileInput = viewChild.required('fileInput');
|
|
6772
|
+
grid = viewChild.required('grid');
|
|
6773
|
+
dragOver = signal(false, ...(ngDevMode ? [{ debugName: "dragOver" }] : []));
|
|
6774
|
+
/** Id of the tile currently grabbed for keyboard reordering */
|
|
6775
|
+
grabbedId = signal(null, ...(ngDevMode ? [{ debugName: "grabbedId" }] : []));
|
|
6776
|
+
/** Announcement for screen readers after a keyboard move */
|
|
6777
|
+
liveMessage = signal('', ...(ngDevMode ? [{ debugName: "liveMessage" }] : []));
|
|
6778
|
+
/** Id of the tile being pointer-dragged */
|
|
6779
|
+
dragId = signal(null, ...(ngDevMode ? [{ debugName: "dragId" }] : []));
|
|
6780
|
+
ghostX = signal(0, ...(ngDevMode ? [{ debugName: "ghostX" }] : []));
|
|
6781
|
+
ghostY = signal(0, ...(ngDevMode ? [{ debugName: "ghostY" }] : []));
|
|
6782
|
+
ghostSize = signal(0, ...(ngDevMode ? [{ debugName: "ghostSize" }] : []));
|
|
6783
|
+
pointerId = null;
|
|
6784
|
+
pointerDownX = 0;
|
|
6785
|
+
pointerDownY = 0;
|
|
6786
|
+
pointerDownId = null;
|
|
6787
|
+
dragStarted = false;
|
|
6788
|
+
canAdd = computed(() => {
|
|
6789
|
+
return !this.disabled() && this.items().length < this.maxCount();
|
|
6790
|
+
}, ...(ngDevMode ? [{ debugName: "canAdd" }] : []));
|
|
6791
|
+
dragItem = computed(() => {
|
|
6792
|
+
const id = this.dragId();
|
|
6793
|
+
return id === null ? null : this.items().find(item => item.id === id) ?? null;
|
|
6794
|
+
}, ...(ngDevMode ? [{ debugName: "dragItem" }] : []));
|
|
6795
|
+
tileLabel(item, index) {
|
|
6796
|
+
const position = `Image ${index + 1} of ${this.items().length}`;
|
|
6797
|
+
const cover = index === 0 ? ', cover image' : '';
|
|
6798
|
+
const error = item.error ? `, error: ${item.error}` : '';
|
|
6799
|
+
return `${position}${cover}${error}`;
|
|
6800
|
+
}
|
|
6801
|
+
// --- Adding files ---
|
|
6802
|
+
openFilePicker() {
|
|
6803
|
+
if (this.canAdd()) {
|
|
6804
|
+
this.fileInput().nativeElement.click();
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
onFileSelected(event) {
|
|
6808
|
+
const input = event.target;
|
|
6809
|
+
if (input.files && input.files.length > 0) {
|
|
6810
|
+
this.processFiles(Array.from(input.files));
|
|
6811
|
+
input.value = '';
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6814
|
+
onDragOver(event) {
|
|
6815
|
+
event.preventDefault();
|
|
6816
|
+
event.stopPropagation();
|
|
6817
|
+
if (this.canAdd()) {
|
|
6818
|
+
this.dragOver.set(true);
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
onDragLeave(event) {
|
|
6822
|
+
event.preventDefault();
|
|
6823
|
+
event.stopPropagation();
|
|
6824
|
+
this.dragOver.set(false);
|
|
6825
|
+
}
|
|
6826
|
+
onDrop(event) {
|
|
6827
|
+
event.preventDefault();
|
|
6828
|
+
event.stopPropagation();
|
|
6829
|
+
this.dragOver.set(false);
|
|
6830
|
+
if (!this.canAdd())
|
|
6831
|
+
return;
|
|
6832
|
+
const files = event.dataTransfer?.files;
|
|
6833
|
+
if (files && files.length > 0) {
|
|
6834
|
+
this.processFiles(Array.from(files));
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
processFiles(files) {
|
|
6838
|
+
const accepted = [];
|
|
6839
|
+
const rejected = [];
|
|
6840
|
+
const remaining = this.maxCount() - this.items().length;
|
|
6841
|
+
const maxBytes = this.maxFileSizeMb() != null ? this.maxFileSizeMb() * 1024 * 1024 : null;
|
|
6842
|
+
for (const file of files) {
|
|
6843
|
+
if (accepted.length >= remaining) {
|
|
6844
|
+
rejected.push({ file, reason: `Maximum ${this.maxCount()} image(s) allowed` });
|
|
6845
|
+
continue;
|
|
6846
|
+
}
|
|
6847
|
+
if (!this.isFileTypeAccepted(file)) {
|
|
6848
|
+
rejected.push({ file, reason: 'File type not accepted' });
|
|
6849
|
+
continue;
|
|
6850
|
+
}
|
|
6851
|
+
if (maxBytes !== null && file.size > maxBytes) {
|
|
6852
|
+
rejected.push({ file, reason: `File exceeds maximum size of ${this.maxFileSizeMb()} MB` });
|
|
6853
|
+
continue;
|
|
6854
|
+
}
|
|
6855
|
+
accepted.push(file);
|
|
6856
|
+
}
|
|
6857
|
+
if (accepted.length > 0) {
|
|
6858
|
+
this.filesAdded.emit(accepted);
|
|
6859
|
+
}
|
|
6860
|
+
if (rejected.length > 0) {
|
|
6861
|
+
this.filesRejected.emit(rejected);
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
isFileTypeAccepted(file) {
|
|
6865
|
+
const accept = this.accept();
|
|
6866
|
+
if (!accept)
|
|
6867
|
+
return true;
|
|
6868
|
+
const acceptedTypes = accept.split(',').map(t => t.trim().toLowerCase());
|
|
6869
|
+
for (const type of acceptedTypes) {
|
|
6870
|
+
if (type === file.type.toLowerCase())
|
|
6871
|
+
return true;
|
|
6872
|
+
if (type.endsWith('/*')) {
|
|
6873
|
+
const category = type.slice(0, -2);
|
|
6874
|
+
if (file.type.toLowerCase().startsWith(category + '/'))
|
|
6875
|
+
return true;
|
|
6876
|
+
}
|
|
6877
|
+
if (type.startsWith('.')) {
|
|
6878
|
+
const ext = file.name.toLowerCase().slice(file.name.lastIndexOf('.'));
|
|
6879
|
+
if (ext === type)
|
|
6880
|
+
return true;
|
|
6881
|
+
}
|
|
6882
|
+
}
|
|
6883
|
+
return false;
|
|
6884
|
+
}
|
|
6885
|
+
// --- Removing ---
|
|
6886
|
+
removeItem(event, id) {
|
|
6887
|
+
event.stopPropagation();
|
|
6888
|
+
if (this.disabled())
|
|
6889
|
+
return;
|
|
6890
|
+
this.items.update(items => items.filter(item => item.id !== id));
|
|
6891
|
+
this.removed.emit(id);
|
|
6892
|
+
}
|
|
6893
|
+
// --- Pointer drag to reorder ---
|
|
6894
|
+
onTilePointerDown(event, id) {
|
|
6895
|
+
if (this.disabled())
|
|
6896
|
+
return;
|
|
6897
|
+
// Ignore drags starting on the remove button
|
|
6898
|
+
if (event.target.closest('.ui-image-uploader__remove'))
|
|
6899
|
+
return;
|
|
6900
|
+
this.pointerId = event.pointerId;
|
|
6901
|
+
this.pointerDownX = event.clientX;
|
|
6902
|
+
this.pointerDownY = event.clientY;
|
|
6903
|
+
this.pointerDownId = id;
|
|
6904
|
+
this.dragStarted = false;
|
|
6905
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
6906
|
+
}
|
|
6907
|
+
onTilePointerMove(event) {
|
|
6908
|
+
if (this.pointerId !== event.pointerId || this.pointerDownId === null)
|
|
6909
|
+
return;
|
|
6910
|
+
if (!this.dragStarted) {
|
|
6911
|
+
const distance = Math.hypot(event.clientX - this.pointerDownX, event.clientY - this.pointerDownY);
|
|
6912
|
+
if (distance < 6)
|
|
6913
|
+
return;
|
|
6914
|
+
this.dragStarted = true;
|
|
6915
|
+
this.dragId.set(this.pointerDownId);
|
|
6916
|
+
const tile = this.findTileElement(this.pointerDownId);
|
|
6917
|
+
this.ghostSize.set(tile?.offsetWidth ?? 0);
|
|
6918
|
+
}
|
|
6919
|
+
this.ghostX.set(event.clientX);
|
|
6920
|
+
this.ghostY.set(event.clientY);
|
|
6921
|
+
const targetIndex = this.tileIndexAtPoint(event.clientX, event.clientY);
|
|
6922
|
+
if (targetIndex === null)
|
|
6923
|
+
return;
|
|
6924
|
+
const currentIndex = this.items().findIndex(item => item.id === this.dragId());
|
|
6925
|
+
if (currentIndex >= 0 && targetIndex !== currentIndex) {
|
|
6926
|
+
this.moveItem(currentIndex, targetIndex, false);
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
onTilePointerUp(event) {
|
|
6930
|
+
if (this.pointerId !== event.pointerId)
|
|
6931
|
+
return;
|
|
6932
|
+
if (this.dragStarted) {
|
|
6933
|
+
this.reordered.emit(this.items().map(item => item.id));
|
|
6934
|
+
}
|
|
6935
|
+
this.pointerId = null;
|
|
6936
|
+
this.pointerDownId = null;
|
|
6937
|
+
this.dragStarted = false;
|
|
6938
|
+
this.dragId.set(null);
|
|
6939
|
+
}
|
|
6940
|
+
findTileElement(id) {
|
|
6941
|
+
return this.grid().nativeElement.querySelector(`.ui-image-uploader__tile[data-id="${CSS.escape(id)}"]`);
|
|
6942
|
+
}
|
|
6943
|
+
tileIndexAtPoint(x, y) {
|
|
6944
|
+
const tiles = this.grid().nativeElement.querySelectorAll('.ui-image-uploader__tile');
|
|
6945
|
+
for (let i = 0; i < tiles.length; i++) {
|
|
6946
|
+
const rect = tiles[i].getBoundingClientRect();
|
|
6947
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
6948
|
+
return i;
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
return null;
|
|
6952
|
+
}
|
|
6953
|
+
// --- Keyboard reorder ---
|
|
6954
|
+
onTileKeyDown(event, id, index) {
|
|
6955
|
+
if (this.disabled())
|
|
6956
|
+
return;
|
|
6957
|
+
switch (event.key) {
|
|
6958
|
+
case ' ':
|
|
6959
|
+
case 'Enter':
|
|
6960
|
+
event.preventDefault();
|
|
6961
|
+
if (this.grabbedId() === id) {
|
|
6962
|
+
this.grabbedId.set(null);
|
|
6963
|
+
this.liveMessage.set(`Image dropped at position ${index + 1}`);
|
|
6964
|
+
}
|
|
6965
|
+
else {
|
|
6966
|
+
this.grabbedId.set(id);
|
|
6967
|
+
this.liveMessage.set(`Image ${index + 1} grabbed. Use arrow keys to move, Space to drop, Escape to cancel.`);
|
|
6968
|
+
}
|
|
6969
|
+
break;
|
|
6970
|
+
case 'Escape':
|
|
6971
|
+
if (this.grabbedId() === id) {
|
|
6972
|
+
event.preventDefault();
|
|
6973
|
+
this.grabbedId.set(null);
|
|
6974
|
+
this.liveMessage.set('Move cancelled');
|
|
6975
|
+
}
|
|
6976
|
+
break;
|
|
6977
|
+
case 'ArrowLeft':
|
|
6978
|
+
case 'ArrowUp':
|
|
6979
|
+
if (this.grabbedId() === id) {
|
|
6980
|
+
event.preventDefault();
|
|
6981
|
+
this.moveByKeyboard(index, index - 1, id);
|
|
6982
|
+
}
|
|
6983
|
+
break;
|
|
6984
|
+
case 'ArrowRight':
|
|
6985
|
+
case 'ArrowDown':
|
|
6986
|
+
if (this.grabbedId() === id) {
|
|
6987
|
+
event.preventDefault();
|
|
6988
|
+
this.moveByKeyboard(index, index + 1, id);
|
|
6989
|
+
}
|
|
6990
|
+
break;
|
|
6991
|
+
case 'Delete':
|
|
6992
|
+
case 'Backspace':
|
|
6993
|
+
event.preventDefault();
|
|
6994
|
+
this.removeItem(event, id);
|
|
6995
|
+
break;
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
moveByKeyboard(from, to, id) {
|
|
6999
|
+
if (to < 0 || to >= this.items().length)
|
|
7000
|
+
return;
|
|
7001
|
+
this.moveItem(from, to, true);
|
|
7002
|
+
this.liveMessage.set(`Image moved to position ${to + 1} of ${this.items().length}`);
|
|
7003
|
+
// Keep focus on the moved tile after the DOM reorders
|
|
7004
|
+
setTimeout(() => this.findTileElement(id)?.focus());
|
|
7005
|
+
}
|
|
7006
|
+
moveItem(from, to, emit) {
|
|
7007
|
+
this.items.update(items => {
|
|
7008
|
+
const next = [...items];
|
|
7009
|
+
const [moved] = next.splice(from, 1);
|
|
7010
|
+
next.splice(to, 0, moved);
|
|
7011
|
+
return next;
|
|
7012
|
+
});
|
|
7013
|
+
if (emit) {
|
|
7014
|
+
this.reordered.emit(this.items().map(item => item.id));
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ImageUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7018
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: ImageUploaderComponent, isStandalone: true, selector: "ui-image-uploader", inputs: { maxCount: { classPropertyName: "maxCount", publicName: "maxCount", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, maxFileSizeMb: { classPropertyName: "maxFileSizeMb", publicName: "maxFileSizeMb", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, coverLabel: { classPropertyName: "coverLabel", publicName: "coverLabel", isSignal: true, isRequired: false, transformFunction: null }, addLabel: { classPropertyName: "addLabel", publicName: "addLabel", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { items: "itemsChange", filesAdded: "filesAdded", filesRejected: "filesRejected", removed: "removed", reordered: "reordered" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }, { propertyName: "grid", first: true, predicate: ["grid"], descendants: true, isSignal: true }], ngImport: i0, template: "<ul\n #grid\n class=\"ui-image-uploader\"\n [class.ui-image-uploader--drag-over]=\"dragOver()\"\n [class.ui-image-uploader--disabled]=\"disabled()\"\n role=\"list\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n @for (item of items(); track item.id; let i = $index) {\n <li\n class=\"ui-image-uploader__tile\"\n [class.ui-image-uploader__tile--error]=\"item.error\"\n [class.ui-image-uploader__tile--grabbed]=\"grabbedId() === item.id\"\n [class.ui-image-uploader__tile--dragging]=\"dragId() === item.id\"\n [attr.data-id]=\"item.id\"\n [attr.tabindex]=\"disabled() ? null : 0\"\n [attr.aria-label]=\"tileLabel(item, i)\"\n [attr.title]=\"item.error || null\"\n (pointerdown)=\"onTilePointerDown($event, item.id)\"\n (pointermove)=\"onTilePointerMove($event)\"\n (pointerup)=\"onTilePointerUp($event)\"\n (pointercancel)=\"onTilePointerUp($event)\"\n (keydown)=\"onTileKeyDown($event, item.id, i)\"\n >\n <img class=\"ui-image-uploader__image\" [src]=\"item.previewUrl\" alt=\"\" draggable=\"false\" />\n\n @if (i === 0) {\n <span class=\"ui-image-uploader__cover-badge\">{{ coverLabel() }}</span>\n }\n\n @if (item.progress != null && item.progress < 100) {\n <span class=\"ui-image-uploader__progress\" aria-hidden=\"true\">\n <span class=\"ui-image-uploader__progress-bar\">\n <span\n class=\"ui-image-uploader__progress-fill\"\n [style.width.%]=\"item.progress\"\n ></span>\n </span>\n <span class=\"ui-image-uploader__progress-text\">{{ item.progress }}%</span>\n </span>\n }\n\n @if (item.error) {\n <span class=\"ui-image-uploader__error-icon\" aria-hidden=\"true\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"M12 8v4M12 16h.01\"/>\n </svg>\n </span>\n }\n\n @if (!disabled()) {\n <button\n type=\"button\"\n class=\"ui-image-uploader__remove\"\n aria-label=\"Remove image\"\n (click)=\"removeItem($event, item.id)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n }\n </li>\n }\n\n @if (canAdd()) {\n <li class=\"ui-image-uploader__add-item\">\n <button\n type=\"button\"\n class=\"ui-image-uploader__add\"\n [attr.aria-label]=\"addLabel()\"\n (click)=\"openFilePicker()\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12h14\"/>\n </svg>\n <span class=\"ui-image-uploader__add-text\">{{ addLabel() }}</span>\n </button>\n </li>\n }\n</ul>\n\n@if (dragItem(); as item) {\n <img\n class=\"ui-image-uploader__ghost\"\n [src]=\"item.previewUrl\"\n alt=\"\"\n aria-hidden=\"true\"\n [style.width.px]=\"ghostSize()\"\n [style.height.px]=\"ghostSize()\"\n [style.left.px]=\"ghostX()\"\n [style.top.px]=\"ghostY()\"\n />\n}\n\n<span class=\"ui-visually-hidden\" aria-live=\"polite\">{{ liveMessage() }}</span>\n\n<input\n #fileInput\n type=\"file\"\n class=\"ui-image-uploader__input\"\n [accept]=\"accept()\"\n multiple\n (change)=\"onFileSelected($event)\"\n/>\n", styles: [":host{display:block}.ui-image-uploader{display:grid;grid-template-columns:repeat(auto-fill,minmax(7.5rem,1fr));gap:var(--ui-spacing-sm);margin:0;padding:var(--ui-spacing-sm);list-style:none;border:2px dashed transparent;border-radius:var(--ui-radius-lg);transition:border-color var(--ui-transition-fast),background-color var(--ui-transition-fast)}.ui-image-uploader--drag-over{border-color:var(--ui-primary);background-color:color-mix(in srgb,var(--ui-primary) 6%,transparent)}.ui-image-uploader--disabled{opacity:.6}.ui-image-uploader__tile{position:relative;aspect-ratio:1;overflow:hidden;border:1px solid var(--ui-image-uploader-tile-border);border-radius:var(--ui-radius-md);background-color:var(--ui-image-uploader-tile-bg);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.ui-image-uploader__tile:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__tile--grabbed{outline:2px solid var(--ui-primary);outline-offset:2px;box-shadow:var(--ui-shadow-md)}.ui-image-uploader__tile--dragging{opacity:.4;cursor:grabbing}.ui-image-uploader__tile--error{border:2px solid var(--ui-danger)}.ui-image-uploader__image{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.ui-image-uploader__tile--error .ui-image-uploader__image{opacity:.5}.ui-image-uploader__cover-badge{position:absolute;left:var(--ui-spacing-xs);bottom:var(--ui-spacing-xs);padding:1px var(--ui-spacing-sm);border-radius:var(--ui-radius-sm);background-color:var(--ui-primary);color:var(--ui-primary-text);font-size:var(--ui-font-xs);font-weight:600;pointer-events:none}.ui-image-uploader__remove{position:absolute;top:var(--ui-spacing-xs);right:var(--ui-spacing-xs);display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:50%;background-color:var(--ui-image-uploader-overlay-bg);color:#fff;cursor:pointer;transition:background-color var(--ui-transition-fast)}.ui-image-uploader__remove:hover{background-color:var(--ui-danger)}.ui-image-uploader__remove:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__progress{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ui-spacing-xs);padding:var(--ui-spacing-sm);background-color:var(--ui-image-uploader-overlay-bg);pointer-events:none}.ui-image-uploader__progress-bar{width:80%;height:4px;border-radius:2px;background-color:#ffffff4d;overflow:hidden}.ui-image-uploader__progress-fill{display:block;height:100%;border-radius:2px;background-color:var(--ui-primary);transition:width var(--ui-transition-fast)}.ui-image-uploader__progress-text{color:#fff;font-size:var(--ui-font-xs);font-weight:600}.ui-image-uploader__error-icon{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--ui-danger);pointer-events:none}.ui-image-uploader__add-item{aspect-ratio:1}.ui-image-uploader__add{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ui-spacing-xs);width:100%;height:100%;border:2px dashed var(--ui-border);border-radius:var(--ui-radius-md);background:transparent;color:var(--ui-text-muted);cursor:pointer;transition:border-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-image-uploader__add:hover{border-color:var(--ui-border-hover);color:var(--ui-text)}.ui-image-uploader__add:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__add-text{font-size:var(--ui-font-xs);font-weight:500}.ui-image-uploader__ghost{position:fixed;z-index:1000;transform:translate(-50%,-50%) scale(.9);border-radius:var(--ui-radius-md);object-fit:cover;box-shadow:var(--ui-shadow-lg);opacity:.9;pointer-events:none}.ui-image-uploader__input{display:none}.ui-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7019
|
+
}
|
|
7020
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ImageUploaderComponent, decorators: [{
|
|
7021
|
+
type: Component,
|
|
7022
|
+
args: [{ selector: 'ui-image-uploader', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ul\n #grid\n class=\"ui-image-uploader\"\n [class.ui-image-uploader--drag-over]=\"dragOver()\"\n [class.ui-image-uploader--disabled]=\"disabled()\"\n role=\"list\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n @for (item of items(); track item.id; let i = $index) {\n <li\n class=\"ui-image-uploader__tile\"\n [class.ui-image-uploader__tile--error]=\"item.error\"\n [class.ui-image-uploader__tile--grabbed]=\"grabbedId() === item.id\"\n [class.ui-image-uploader__tile--dragging]=\"dragId() === item.id\"\n [attr.data-id]=\"item.id\"\n [attr.tabindex]=\"disabled() ? null : 0\"\n [attr.aria-label]=\"tileLabel(item, i)\"\n [attr.title]=\"item.error || null\"\n (pointerdown)=\"onTilePointerDown($event, item.id)\"\n (pointermove)=\"onTilePointerMove($event)\"\n (pointerup)=\"onTilePointerUp($event)\"\n (pointercancel)=\"onTilePointerUp($event)\"\n (keydown)=\"onTileKeyDown($event, item.id, i)\"\n >\n <img class=\"ui-image-uploader__image\" [src]=\"item.previewUrl\" alt=\"\" draggable=\"false\" />\n\n @if (i === 0) {\n <span class=\"ui-image-uploader__cover-badge\">{{ coverLabel() }}</span>\n }\n\n @if (item.progress != null && item.progress < 100) {\n <span class=\"ui-image-uploader__progress\" aria-hidden=\"true\">\n <span class=\"ui-image-uploader__progress-bar\">\n <span\n class=\"ui-image-uploader__progress-fill\"\n [style.width.%]=\"item.progress\"\n ></span>\n </span>\n <span class=\"ui-image-uploader__progress-text\">{{ item.progress }}%</span>\n </span>\n }\n\n @if (item.error) {\n <span class=\"ui-image-uploader__error-icon\" aria-hidden=\"true\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"M12 8v4M12 16h.01\"/>\n </svg>\n </span>\n }\n\n @if (!disabled()) {\n <button\n type=\"button\"\n class=\"ui-image-uploader__remove\"\n aria-label=\"Remove image\"\n (click)=\"removeItem($event, item.id)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n }\n </li>\n }\n\n @if (canAdd()) {\n <li class=\"ui-image-uploader__add-item\">\n <button\n type=\"button\"\n class=\"ui-image-uploader__add\"\n [attr.aria-label]=\"addLabel()\"\n (click)=\"openFilePicker()\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12h14\"/>\n </svg>\n <span class=\"ui-image-uploader__add-text\">{{ addLabel() }}</span>\n </button>\n </li>\n }\n</ul>\n\n@if (dragItem(); as item) {\n <img\n class=\"ui-image-uploader__ghost\"\n [src]=\"item.previewUrl\"\n alt=\"\"\n aria-hidden=\"true\"\n [style.width.px]=\"ghostSize()\"\n [style.height.px]=\"ghostSize()\"\n [style.left.px]=\"ghostX()\"\n [style.top.px]=\"ghostY()\"\n />\n}\n\n<span class=\"ui-visually-hidden\" aria-live=\"polite\">{{ liveMessage() }}</span>\n\n<input\n #fileInput\n type=\"file\"\n class=\"ui-image-uploader__input\"\n [accept]=\"accept()\"\n multiple\n (change)=\"onFileSelected($event)\"\n/>\n", styles: [":host{display:block}.ui-image-uploader{display:grid;grid-template-columns:repeat(auto-fill,minmax(7.5rem,1fr));gap:var(--ui-spacing-sm);margin:0;padding:var(--ui-spacing-sm);list-style:none;border:2px dashed transparent;border-radius:var(--ui-radius-lg);transition:border-color var(--ui-transition-fast),background-color var(--ui-transition-fast)}.ui-image-uploader--drag-over{border-color:var(--ui-primary);background-color:color-mix(in srgb,var(--ui-primary) 6%,transparent)}.ui-image-uploader--disabled{opacity:.6}.ui-image-uploader__tile{position:relative;aspect-ratio:1;overflow:hidden;border:1px solid var(--ui-image-uploader-tile-border);border-radius:var(--ui-radius-md);background-color:var(--ui-image-uploader-tile-bg);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.ui-image-uploader__tile:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__tile--grabbed{outline:2px solid var(--ui-primary);outline-offset:2px;box-shadow:var(--ui-shadow-md)}.ui-image-uploader__tile--dragging{opacity:.4;cursor:grabbing}.ui-image-uploader__tile--error{border:2px solid var(--ui-danger)}.ui-image-uploader__image{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.ui-image-uploader__tile--error .ui-image-uploader__image{opacity:.5}.ui-image-uploader__cover-badge{position:absolute;left:var(--ui-spacing-xs);bottom:var(--ui-spacing-xs);padding:1px var(--ui-spacing-sm);border-radius:var(--ui-radius-sm);background-color:var(--ui-primary);color:var(--ui-primary-text);font-size:var(--ui-font-xs);font-weight:600;pointer-events:none}.ui-image-uploader__remove{position:absolute;top:var(--ui-spacing-xs);right:var(--ui-spacing-xs);display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:50%;background-color:var(--ui-image-uploader-overlay-bg);color:#fff;cursor:pointer;transition:background-color var(--ui-transition-fast)}.ui-image-uploader__remove:hover{background-color:var(--ui-danger)}.ui-image-uploader__remove:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__progress{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ui-spacing-xs);padding:var(--ui-spacing-sm);background-color:var(--ui-image-uploader-overlay-bg);pointer-events:none}.ui-image-uploader__progress-bar{width:80%;height:4px;border-radius:2px;background-color:#ffffff4d;overflow:hidden}.ui-image-uploader__progress-fill{display:block;height:100%;border-radius:2px;background-color:var(--ui-primary);transition:width var(--ui-transition-fast)}.ui-image-uploader__progress-text{color:#fff;font-size:var(--ui-font-xs);font-weight:600}.ui-image-uploader__error-icon{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--ui-danger);pointer-events:none}.ui-image-uploader__add-item{aspect-ratio:1}.ui-image-uploader__add{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ui-spacing-xs);width:100%;height:100%;border:2px dashed var(--ui-border);border-radius:var(--ui-radius-md);background:transparent;color:var(--ui-text-muted);cursor:pointer;transition:border-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-image-uploader__add:hover{border-color:var(--ui-border-hover);color:var(--ui-text)}.ui-image-uploader__add:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px}.ui-image-uploader__add-text{font-size:var(--ui-font-xs);font-weight:500}.ui-image-uploader__ghost{position:fixed;z-index:1000;transform:translate(-50%,-50%) scale(.9);border-radius:var(--ui-radius-md);object-fit:cover;box-shadow:var(--ui-shadow-lg);opacity:.9;pointer-events:none}.ui-image-uploader__input{display:none}.ui-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
7023
|
+
}], propDecorators: { maxCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCount", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSizeMb", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], coverLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "coverLabel", required: false }] }], addLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "addLabel", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], filesAdded: [{ type: i0.Output, args: ["filesAdded"] }], filesRejected: [{ type: i0.Output, args: ["filesRejected"] }], removed: [{ type: i0.Output, args: ["removed"] }], reordered: [{ type: i0.Output, args: ["reordered"] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }], grid: [{ type: i0.ViewChild, args: ['grid', { isSignal: true }] }] } });
|
|
7024
|
+
|
|
6394
7025
|
class ShellComponent {
|
|
6395
7026
|
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
6396
7027
|
sidebarService = inject(SidebarService);
|
|
@@ -7729,5 +8360,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
7729
8360
|
* Generated bundle index. Do not edit.
|
|
7730
8361
|
*/
|
|
7731
8362
|
|
|
7732
|
-
export { AccordionComponent, AccordionHeaderDirective, AccordionItemComponent, AlertComponent, BadgeComponent, ButtonComponent, CardComponent, CellTemplateDirective, CellValuePipe, CheckboxComponent, ChipInputComponent, ChipTemplateDirective, CircularProgressComponent, ContentComponent, ContextMenuDirective, DIALOG_DATA, DIALOG_REF, DatepickerComponent, DatetimepickerComponent, DialogRef, DialogService, DropdownComponent, DropdownDividerComponent, DropdownItemComponent, DropdownTriggerDirective, DynamicTabsComponent, FileChooserComponent, FilePreviewPipe, FileSizePipe, FooterComponent, InputComponent, JsonTreeComponent, LOADABLE, LoadingDirective, LoadingService, ModalComponent, NavbarComponent, OptionComponent, OptionTemplateDirective, PaginationComponent, ProgressComponent, RadioComponent, RadioGroupComponent, RangeSliderComponent, SelectComponent, ShellComponent, SidebarComponent, SidebarService, SidebarToggleComponent, SliderComponent, SpinnerComponent, SplitComponent, SplitPaneComponent, SwitchComponent, TAB_DATA, TAB_REF, TREE_HOST, TabActivePipe, TabComponent, TabIconDirective, TabRef, TableComponent, TabsComponent, TabsService, TemplateInputComponent, TemplateInputSuffixDirective, TextareaComponent, TimepickerComponent, ToastRef, ToastService, TooltipDirective, TreeComponent, TreeNodeComponent, Validators, VariablePopoverDirective, jsonToTreeNodes };
|
|
8363
|
+
export { AccordionComponent, AccordionHeaderDirective, AccordionItemComponent, AlertComponent, BadgeComponent, BreadcrumbComponent, BreadcrumbSeparatorDirective, ButtonComponent, CardComponent, CarouselComponent, CellTemplateDirective, CellValuePipe, CheckboxComponent, ChipInputComponent, ChipTemplateDirective, CircularProgressComponent, ContentComponent, ContextMenuDirective, DIALOG_DATA, DIALOG_REF, DatepickerComponent, DatetimepickerComponent, DialogRef, DialogService, DropdownComponent, DropdownDividerComponent, DropdownItemComponent, DropdownTriggerDirective, DynamicTabsComponent, FileChooserComponent, FilePreviewPipe, FileSizePipe, FooterComponent, ImageUploaderComponent, InputComponent, JsonTreeComponent, LOADABLE, LightboxComponent, LightboxService, LoadingDirective, LoadingService, ModalComponent, NavbarComponent, OptionComponent, OptionTemplateDirective, PaginationComponent, ProgressComponent, RadioComponent, RadioGroupComponent, RangeSliderComponent, SegmentedComponent, SelectComponent, ShellComponent, SidebarComponent, SidebarService, SidebarToggleComponent, SkeletonComponent, SliderComponent, SpinnerComponent, SplitComponent, SplitPaneComponent, StepperComponent, SwitchComponent, TAB_DATA, TAB_REF, TREE_HOST, TabActivePipe, TabComponent, TabIconDirective, TabRef, TableComponent, TabsComponent, TabsService, TemplateInputComponent, TemplateInputSuffixDirective, TextareaComponent, TimepickerComponent, ToastRef, ToastService, TooltipDirective, TreeComponent, TreeNodeComponent, Validators, VariablePopoverDirective, jsonToTreeNodes };
|
|
7733
8364
|
//# sourceMappingURL=m1z23r-ngx-ui.mjs.map
|