@devjuliovilla/jv-ui 1.10.0 → 1.11.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.
- package/README.md +262 -0
- package/fesm2022/devjuliovilla-jv-ui.mjs +1052 -176
- package/fesm2022/devjuliovilla-jv-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/devjuliovilla-jv-ui.d.ts +95 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, Component, forwardRef, inject, output, Directive, Injectable, signal, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, effect, contentChildren, HostListener, viewChild, viewChildren, afterNextRender, Renderer2, ElementRef } from '@angular/core';
|
|
2
|
+
import { input, computed, Component, forwardRef, inject, output, Directive, Injectable, signal, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, effect, contentChildren, HostListener, viewChild, viewChildren, numberAttribute, afterNextRender, Renderer2, ElementRef } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { NG_VALUE_ACCESSOR, FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
5
5
|
import { CommonModule, DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
@@ -18729,6 +18729,7 @@ const JV_UI_DEFAULT_CONFIG = {
|
|
|
18729
18729
|
percent: { style: 'percent', minimumFractionDigits: 0, maximumFractionDigits: 2 },
|
|
18730
18730
|
},
|
|
18731
18731
|
toastPosition: 'bottom-right',
|
|
18732
|
+
tabsTransition: 'none',
|
|
18732
18733
|
};
|
|
18733
18734
|
|
|
18734
18735
|
class JvFormatService {
|
|
@@ -19026,6 +19027,12 @@ const ES = {
|
|
|
19026
19027
|
'dialog.cancel': 'Cancelar',
|
|
19027
19028
|
'common.expanded': 'Expandido',
|
|
19028
19029
|
'common.collapsed': 'Colapsado',
|
|
19030
|
+
'slider.prevImage': 'Imagen anterior',
|
|
19031
|
+
'slider.nextImage': 'Imagen siguiente',
|
|
19032
|
+
'slider.thumbnailList': 'Miniaturas de imagenes',
|
|
19033
|
+
'gallery.close': 'Cerrar',
|
|
19034
|
+
'gallery.prevImage': 'Imagen anterior',
|
|
19035
|
+
'gallery.nextImage': 'Imagen siguiente',
|
|
19029
19036
|
};
|
|
19030
19037
|
|
|
19031
19038
|
const EN = {
|
|
@@ -19088,6 +19095,12 @@ const EN = {
|
|
|
19088
19095
|
'dialog.cancel': 'Cancel',
|
|
19089
19096
|
'common.expanded': 'Expanded',
|
|
19090
19097
|
'common.collapsed': 'Collapsed',
|
|
19098
|
+
'slider.prevImage': 'Previous image',
|
|
19099
|
+
'slider.nextImage': 'Next image',
|
|
19100
|
+
'slider.thumbnailList': 'Image thumbnails',
|
|
19101
|
+
'gallery.close': 'Close',
|
|
19102
|
+
'gallery.prevImage': 'Previous image',
|
|
19103
|
+
'gallery.nextImage': 'Next image',
|
|
19091
19104
|
};
|
|
19092
19105
|
|
|
19093
19106
|
const JV_LOCALE_DICTIONARIES = {
|
|
@@ -19923,10 +19936,10 @@ class JvDatePickerComponent {
|
|
|
19923
19936
|
visibleMonth = signal(firstOfMonth(new Date()), ...(ngDevMode ? [{ debugName: "visibleMonth" }] : /* istanbul ignore next */ []));
|
|
19924
19937
|
focusedDate = signal(startOfDay(new Date()), ...(ngDevMode ? [{ debugName: "focusedDate" }] : /* istanbul ignore next */ []));
|
|
19925
19938
|
dialogId = `jv-date-picker-dialog-${this.instanceId}`;
|
|
19926
|
-
value = null;
|
|
19939
|
+
value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
19927
19940
|
disabled = false;
|
|
19928
19941
|
resolvedDialogTitle = computed(() => this.dialogTitle() || this.translation.translate('datePicker.dialogTitle'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
|
|
19929
|
-
currentValue = computed(() => this.valueInput() !== undefined ? this.valueInput() : this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
|
|
19942
|
+
currentValue = computed(() => this.valueInput() !== undefined ? this.valueInput() : this.value(), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
|
|
19930
19943
|
triggerAriaLabel = computed(() => {
|
|
19931
19944
|
const name = this.label() || this.resolvedDialogTitle();
|
|
19932
19945
|
return `${name}. ${this.displayValue()}`;
|
|
@@ -19974,7 +19987,7 @@ class JvDatePickerComponent {
|
|
|
19974
19987
|
onChange = () => undefined;
|
|
19975
19988
|
onTouched = () => undefined;
|
|
19976
19989
|
writeValue(value) {
|
|
19977
|
-
this.value
|
|
19990
|
+
this.value.set(value ? startOfDay(value) : null);
|
|
19978
19991
|
}
|
|
19979
19992
|
registerOnChange(fn) {
|
|
19980
19993
|
this.onChange = fn;
|
|
@@ -20080,7 +20093,7 @@ class JvDatePickerComponent {
|
|
|
20080
20093
|
this.onTouched();
|
|
20081
20094
|
}
|
|
20082
20095
|
updateValue(value) {
|
|
20083
|
-
this.value
|
|
20096
|
+
this.value.set(value);
|
|
20084
20097
|
this.onChange(value);
|
|
20085
20098
|
this.selectionChange.emit(value);
|
|
20086
20099
|
}
|
|
@@ -21242,6 +21255,535 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
21242
21255
|
`, styles: [".jv-textarea{width:100%;min-height:var(--jv-density-control-height);padding:var(--jv-spacing-sm) var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);resize:vertical;font-family:inherit;font-size:inherit;line-height:inherit;box-sizing:border-box}.jv-textarea::placeholder{color:var(--jv-color-foreground-muted)}.jv-textarea.is-invalid{border-color:var(--jv-color-danger)}.jv-textarea:disabled{opacity:.7;cursor:not-allowed}.jv-textarea:read-only{background:var(--jv-color-surface-muted);cursor:default}\n"] }]
|
|
21243
21256
|
}], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], cols: [{ type: i0.Input, args: [{ isSignal: true, alias: "cols", required: false }] }], maxlength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxlength", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }] } });
|
|
21244
21257
|
|
|
21258
|
+
class JvImageGalleryComponent {
|
|
21259
|
+
translation = inject(JvTranslationService);
|
|
21260
|
+
images = input([], ...(ngDevMode ? [{ debugName: "images" }] : /* istanbul ignore next */ []));
|
|
21261
|
+
stripHeightVh = input(25, { ...(ngDevMode ? { debugName: "stripHeightVh" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
21262
|
+
actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
|
|
21263
|
+
imageClick = output();
|
|
21264
|
+
actionClick = output();
|
|
21265
|
+
strip = viewChild('strip', ...(ngDevMode ? [{ debugName: "strip" }] : /* istanbul ignore next */ []));
|
|
21266
|
+
dialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "dialogOpen" }] : /* istanbul ignore next */ []));
|
|
21267
|
+
currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
|
|
21268
|
+
slideDirection = signal(null, ...(ngDevMode ? [{ debugName: "slideDirection" }] : /* istanbul ignore next */ []));
|
|
21269
|
+
hasMultiple = computed(() => this.images().length > 1, ...(ngDevMode ? [{ debugName: "hasMultiple" }] : /* istanbul ignore next */ []));
|
|
21270
|
+
stripHeightStyle = computed(() => `${this.clampStripHeight(this.stripHeightVh())}vh`, ...(ngDevMode ? [{ debugName: "stripHeightStyle" }] : /* istanbul ignore next */ []));
|
|
21271
|
+
currentImage = computed(() => this.images()[this.currentIndex()] ?? null, ...(ngDevMode ? [{ debugName: "currentImage" }] : /* istanbul ignore next */ []));
|
|
21272
|
+
dialogTitle = computed(() => {
|
|
21273
|
+
const image = this.currentImage();
|
|
21274
|
+
if (!image)
|
|
21275
|
+
return '';
|
|
21276
|
+
const counter = this.hasMultiple() ? ` (${this.currentIndex() + 1}/${this.images().length})` : '';
|
|
21277
|
+
return `${image.alt}${counter}`;
|
|
21278
|
+
}, ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
|
|
21279
|
+
translatedLabels = computed(() => ({
|
|
21280
|
+
close: this.translation.translate('gallery.close'),
|
|
21281
|
+
prev: this.translation.translate('gallery.prevImage'),
|
|
21282
|
+
next: this.translation.translate('gallery.nextImage'),
|
|
21283
|
+
}), ...(ngDevMode ? [{ debugName: "translatedLabels" }] : /* istanbul ignore next */ []));
|
|
21284
|
+
handleKeydown(event) {
|
|
21285
|
+
if (!this.dialogOpen())
|
|
21286
|
+
return;
|
|
21287
|
+
switch (event.key) {
|
|
21288
|
+
case 'ArrowLeft':
|
|
21289
|
+
event.preventDefault();
|
|
21290
|
+
this.previous();
|
|
21291
|
+
break;
|
|
21292
|
+
case 'ArrowRight':
|
|
21293
|
+
event.preventDefault();
|
|
21294
|
+
this.next();
|
|
21295
|
+
break;
|
|
21296
|
+
}
|
|
21297
|
+
}
|
|
21298
|
+
onWheel(event) {
|
|
21299
|
+
const el = this.strip()?.nativeElement;
|
|
21300
|
+
if (!el)
|
|
21301
|
+
return;
|
|
21302
|
+
if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
21303
|
+
const delta = event.deltaMode === WheelEvent.DOM_DELTA_LINE
|
|
21304
|
+
? event.deltaY * 32
|
|
21305
|
+
: event.deltaMode === WheelEvent.DOM_DELTA_PAGE
|
|
21306
|
+
? event.deltaY * el.clientWidth
|
|
21307
|
+
: event.deltaY;
|
|
21308
|
+
const firstThumb = el.querySelector('.jv-gallery-thumb');
|
|
21309
|
+
const gap = Number.parseFloat(getComputedStyle(el).columnGap || getComputedStyle(el).gap || '0') || 0;
|
|
21310
|
+
const step = firstThumb ? firstThumb.getBoundingClientRect().width + gap : 0;
|
|
21311
|
+
const offset = Math.sign(delta) * Math.max(Math.abs(delta), step);
|
|
21312
|
+
event.preventDefault();
|
|
21313
|
+
el.scrollLeft += offset;
|
|
21314
|
+
}
|
|
21315
|
+
}
|
|
21316
|
+
openDialog(index) {
|
|
21317
|
+
this.currentIndex.set(index);
|
|
21318
|
+
this.slideDirection.set(null);
|
|
21319
|
+
this.dialogOpen.set(true);
|
|
21320
|
+
this.imageClick.emit(this.images()[index]);
|
|
21321
|
+
}
|
|
21322
|
+
closeDialog() {
|
|
21323
|
+
this.dialogOpen.set(false);
|
|
21324
|
+
this.slideDirection.set(null);
|
|
21325
|
+
}
|
|
21326
|
+
previous() {
|
|
21327
|
+
if (!this.hasMultiple())
|
|
21328
|
+
return;
|
|
21329
|
+
const newIndex = this.currentIndex() === 0
|
|
21330
|
+
? this.images().length - 1
|
|
21331
|
+
: this.currentIndex() - 1;
|
|
21332
|
+
this.showImage(newIndex, 'right');
|
|
21333
|
+
}
|
|
21334
|
+
next() {
|
|
21335
|
+
if (!this.hasMultiple())
|
|
21336
|
+
return;
|
|
21337
|
+
const newIndex = this.currentIndex() === this.images().length - 1
|
|
21338
|
+
? 0
|
|
21339
|
+
: this.currentIndex() + 1;
|
|
21340
|
+
this.showImage(newIndex, 'left');
|
|
21341
|
+
}
|
|
21342
|
+
onAction(action) {
|
|
21343
|
+
const image = this.currentImage();
|
|
21344
|
+
if (image) {
|
|
21345
|
+
this.actionClick.emit({ actionId: action.id, image });
|
|
21346
|
+
}
|
|
21347
|
+
}
|
|
21348
|
+
clampStripHeight(value) {
|
|
21349
|
+
if (!Number.isFinite(value)) {
|
|
21350
|
+
return 25;
|
|
21351
|
+
}
|
|
21352
|
+
return Math.min(100, Math.max(0, value));
|
|
21353
|
+
}
|
|
21354
|
+
showImage(index, direction) {
|
|
21355
|
+
this.slideDirection.set(null);
|
|
21356
|
+
this.currentIndex.set(index);
|
|
21357
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
21358
|
+
requestAnimationFrame(() => this.slideDirection.set(direction));
|
|
21359
|
+
return;
|
|
21360
|
+
}
|
|
21361
|
+
this.slideDirection.set(direction);
|
|
21362
|
+
}
|
|
21363
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvImageGalleryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
21364
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvImageGalleryComponent, isStandalone: true, selector: "jv-image-gallery", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: false, transformFunction: null }, stripHeightVh: { classPropertyName: "stripHeightVh", publicName: "stripHeightVh", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageClick: "imageClick", actionClick: "actionClick" }, host: { listeners: { "document:keydown": "handleKeydown($event)" } }, viewQueries: [{ propertyName: "strip", first: true, predicate: ["strip"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
21365
|
+
<div class="jv-gallery">
|
|
21366
|
+
<div class="jv-gallery-strip" #strip [style.height]="stripHeightStyle()" (wheel)="onWheel($event)">
|
|
21367
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21368
|
+
<button
|
|
21369
|
+
type="button"
|
|
21370
|
+
class="jv-gallery-thumb"
|
|
21371
|
+
[class.jv-gallery-thumb--active]="dialogOpen() && currentIndex() === i"
|
|
21372
|
+
[attr.aria-label]="image.alt"
|
|
21373
|
+
(wheel)="onWheel($event)"
|
|
21374
|
+
(click)="openDialog(i)"
|
|
21375
|
+
>
|
|
21376
|
+
<img
|
|
21377
|
+
class="jv-gallery-thumb-image"
|
|
21378
|
+
[src]="image.thumbnail || image.src"
|
|
21379
|
+
[alt]="image.alt"
|
|
21380
|
+
loading="lazy"
|
|
21381
|
+
/>
|
|
21382
|
+
</button>
|
|
21383
|
+
}
|
|
21384
|
+
</div>
|
|
21385
|
+
|
|
21386
|
+
<jv-dialog
|
|
21387
|
+
[open]="dialogOpen()"
|
|
21388
|
+
[title]="dialogTitle()"
|
|
21389
|
+
(closed)="closeDialog()"
|
|
21390
|
+
>
|
|
21391
|
+
<div class="jv-gallery-viewer" tabindex="0">
|
|
21392
|
+
@if (currentImage(); as image) {
|
|
21393
|
+
<img
|
|
21394
|
+
class="jv-gallery-viewer-image"
|
|
21395
|
+
[class.jv-gallery-viewer-image--slide-left]="slideDirection() === 'left'"
|
|
21396
|
+
[class.jv-gallery-viewer-image--slide-right]="slideDirection() === 'right'"
|
|
21397
|
+
[src]="image.src"
|
|
21398
|
+
[alt]="image.alt"
|
|
21399
|
+
/>
|
|
21400
|
+
}
|
|
21401
|
+
|
|
21402
|
+
@if (hasMultiple()) {
|
|
21403
|
+
<button
|
|
21404
|
+
type="button"
|
|
21405
|
+
class="jv-gallery-viewer-nav jv-gallery-viewer-nav--prev"
|
|
21406
|
+
[attr.aria-label]="translatedLabels().prev"
|
|
21407
|
+
(click)="previous(); $event.stopPropagation()"
|
|
21408
|
+
>
|
|
21409
|
+
<jv-icon name="chevron-left" [size]="24" />
|
|
21410
|
+
</button>
|
|
21411
|
+
|
|
21412
|
+
<button
|
|
21413
|
+
type="button"
|
|
21414
|
+
class="jv-gallery-viewer-nav jv-gallery-viewer-nav--next"
|
|
21415
|
+
[attr.aria-label]="translatedLabels().next"
|
|
21416
|
+
(click)="next(); $event.stopPropagation()"
|
|
21417
|
+
>
|
|
21418
|
+
<jv-icon name="chevron-right" [size]="24" />
|
|
21419
|
+
</button>
|
|
21420
|
+
|
|
21421
|
+
<span class="jv-gallery-viewer-counter">
|
|
21422
|
+
{{ currentIndex() + 1 }} / {{ images().length }}
|
|
21423
|
+
</span>
|
|
21424
|
+
}
|
|
21425
|
+
</div>
|
|
21426
|
+
|
|
21427
|
+
<div dialog-actions>
|
|
21428
|
+
@for (action of actions(); track action.id) {
|
|
21429
|
+
<jv-button
|
|
21430
|
+
[variant]="action.tone ?? 'outline'"
|
|
21431
|
+
(click)="onAction(action)"
|
|
21432
|
+
>
|
|
21433
|
+
@if (action.icon) {
|
|
21434
|
+
<jv-icon [name]="action.icon" [size]="16" />
|
|
21435
|
+
}
|
|
21436
|
+
{{ action.label }}
|
|
21437
|
+
</jv-button>
|
|
21438
|
+
}
|
|
21439
|
+
<jv-button variant="ghost" (click)="closeDialog()">
|
|
21440
|
+
{{ translatedLabels().close }}
|
|
21441
|
+
</jv-button>
|
|
21442
|
+
</div>
|
|
21443
|
+
</jv-dialog>
|
|
21444
|
+
</div>
|
|
21445
|
+
`, isInline: true, styles: [".jv-gallery{display:block;width:100%;max-width:100vw}.jv-gallery-strip{display:flex;gap:5px;width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none;scroll-snap-type:x proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.jv-gallery-strip::-webkit-scrollbar{display:none}.jv-gallery-thumb{flex:0 0 auto;width:calc((100vw - 20px)/4);min-width:120px;height:100%;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;scroll-snap-align:start;transition:border-color .16s ease,opacity .16s ease}.jv-gallery-thumb:hover{opacity:.85}.jv-gallery-thumb--active{border-color:var(--jv-color-primary)}.jv-gallery-thumb:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-thumb-image{display:block;width:100%;height:100%;object-fit:cover;pointer-events:none}.jv-gallery-viewer{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:70vh;min-height:300px;background:#000;border-radius:var(--jv-radius-md);overflow:hidden}.jv-gallery-viewer:focus{outline:none}.jv-gallery-viewer-image{display:block;max-width:100%;max-height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}@keyframes gallery-slide-left{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes gallery-slide-right{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.jv-gallery-viewer-image--slide-left{animation:gallery-slide-left .28s ease-out}.jv-gallery-viewer-image--slide-right{animation:gallery-slide-right .28s ease-out}.jv-gallery-viewer-nav{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:3rem;height:3rem;border:none;border-radius:var(--jv-radius-md);background:#00000080;color:#fff;cursor:pointer;transition:background-color .16s ease;z-index:1}.jv-gallery-viewer-nav:hover{background:#000000bf}.jv-gallery-viewer-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-md)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-md)}.jv-gallery-viewer-counter{position:absolute;bottom:var(--jv-spacing-md);left:50%;transform:translate(-50%);padding:var(--jv-spacing-xs) var(--jv-spacing-sm);border-radius:var(--jv-radius-sm);background:#0009;color:#fff;font-size:.8125rem;font-variant-numeric:tabular-nums;pointer-events:none}@media(max-width:768px){.jv-gallery-thumb{width:calc(100vw - 20px);min-width:200px}.jv-gallery-viewer-nav{width:2.5rem;height:2.5rem}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-sm)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-sm)}}@media(prefers-reduced-motion:reduce){.jv-gallery-strip{scroll-behavior:auto}.jv-gallery-viewer-image--slide-left,.jv-gallery-viewer-image--slide-right{animation:none}}\n"], dependencies: [{ kind: "component", type: JvButtonComponent, selector: "jv-button", inputs: ["type", "variant", "icon", "iconPosition", "loading", "disabled"] }, { kind: "component", type: JvDialogComponent, selector: "jv-dialog", inputs: ["open", "title"], outputs: ["closed"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
|
|
21446
|
+
}
|
|
21447
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvImageGalleryComponent, decorators: [{
|
|
21448
|
+
type: Component,
|
|
21449
|
+
args: [{ selector: 'jv-image-gallery', standalone: true, imports: [JvButtonComponent, JvDialogComponent, JvIconComponent], template: `
|
|
21450
|
+
<div class="jv-gallery">
|
|
21451
|
+
<div class="jv-gallery-strip" #strip [style.height]="stripHeightStyle()" (wheel)="onWheel($event)">
|
|
21452
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21453
|
+
<button
|
|
21454
|
+
type="button"
|
|
21455
|
+
class="jv-gallery-thumb"
|
|
21456
|
+
[class.jv-gallery-thumb--active]="dialogOpen() && currentIndex() === i"
|
|
21457
|
+
[attr.aria-label]="image.alt"
|
|
21458
|
+
(wheel)="onWheel($event)"
|
|
21459
|
+
(click)="openDialog(i)"
|
|
21460
|
+
>
|
|
21461
|
+
<img
|
|
21462
|
+
class="jv-gallery-thumb-image"
|
|
21463
|
+
[src]="image.thumbnail || image.src"
|
|
21464
|
+
[alt]="image.alt"
|
|
21465
|
+
loading="lazy"
|
|
21466
|
+
/>
|
|
21467
|
+
</button>
|
|
21468
|
+
}
|
|
21469
|
+
</div>
|
|
21470
|
+
|
|
21471
|
+
<jv-dialog
|
|
21472
|
+
[open]="dialogOpen()"
|
|
21473
|
+
[title]="dialogTitle()"
|
|
21474
|
+
(closed)="closeDialog()"
|
|
21475
|
+
>
|
|
21476
|
+
<div class="jv-gallery-viewer" tabindex="0">
|
|
21477
|
+
@if (currentImage(); as image) {
|
|
21478
|
+
<img
|
|
21479
|
+
class="jv-gallery-viewer-image"
|
|
21480
|
+
[class.jv-gallery-viewer-image--slide-left]="slideDirection() === 'left'"
|
|
21481
|
+
[class.jv-gallery-viewer-image--slide-right]="slideDirection() === 'right'"
|
|
21482
|
+
[src]="image.src"
|
|
21483
|
+
[alt]="image.alt"
|
|
21484
|
+
/>
|
|
21485
|
+
}
|
|
21486
|
+
|
|
21487
|
+
@if (hasMultiple()) {
|
|
21488
|
+
<button
|
|
21489
|
+
type="button"
|
|
21490
|
+
class="jv-gallery-viewer-nav jv-gallery-viewer-nav--prev"
|
|
21491
|
+
[attr.aria-label]="translatedLabels().prev"
|
|
21492
|
+
(click)="previous(); $event.stopPropagation()"
|
|
21493
|
+
>
|
|
21494
|
+
<jv-icon name="chevron-left" [size]="24" />
|
|
21495
|
+
</button>
|
|
21496
|
+
|
|
21497
|
+
<button
|
|
21498
|
+
type="button"
|
|
21499
|
+
class="jv-gallery-viewer-nav jv-gallery-viewer-nav--next"
|
|
21500
|
+
[attr.aria-label]="translatedLabels().next"
|
|
21501
|
+
(click)="next(); $event.stopPropagation()"
|
|
21502
|
+
>
|
|
21503
|
+
<jv-icon name="chevron-right" [size]="24" />
|
|
21504
|
+
</button>
|
|
21505
|
+
|
|
21506
|
+
<span class="jv-gallery-viewer-counter">
|
|
21507
|
+
{{ currentIndex() + 1 }} / {{ images().length }}
|
|
21508
|
+
</span>
|
|
21509
|
+
}
|
|
21510
|
+
</div>
|
|
21511
|
+
|
|
21512
|
+
<div dialog-actions>
|
|
21513
|
+
@for (action of actions(); track action.id) {
|
|
21514
|
+
<jv-button
|
|
21515
|
+
[variant]="action.tone ?? 'outline'"
|
|
21516
|
+
(click)="onAction(action)"
|
|
21517
|
+
>
|
|
21518
|
+
@if (action.icon) {
|
|
21519
|
+
<jv-icon [name]="action.icon" [size]="16" />
|
|
21520
|
+
}
|
|
21521
|
+
{{ action.label }}
|
|
21522
|
+
</jv-button>
|
|
21523
|
+
}
|
|
21524
|
+
<jv-button variant="ghost" (click)="closeDialog()">
|
|
21525
|
+
{{ translatedLabels().close }}
|
|
21526
|
+
</jv-button>
|
|
21527
|
+
</div>
|
|
21528
|
+
</jv-dialog>
|
|
21529
|
+
</div>
|
|
21530
|
+
`, styles: [".jv-gallery{display:block;width:100%;max-width:100vw}.jv-gallery-strip{display:flex;gap:5px;width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none;scroll-snap-type:x proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.jv-gallery-strip::-webkit-scrollbar{display:none}.jv-gallery-thumb{flex:0 0 auto;width:calc((100vw - 20px)/4);min-width:120px;height:100%;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;scroll-snap-align:start;transition:border-color .16s ease,opacity .16s ease}.jv-gallery-thumb:hover{opacity:.85}.jv-gallery-thumb--active{border-color:var(--jv-color-primary)}.jv-gallery-thumb:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-thumb-image{display:block;width:100%;height:100%;object-fit:cover;pointer-events:none}.jv-gallery-viewer{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:70vh;min-height:300px;background:#000;border-radius:var(--jv-radius-md);overflow:hidden}.jv-gallery-viewer:focus{outline:none}.jv-gallery-viewer-image{display:block;max-width:100%;max-height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}@keyframes gallery-slide-left{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes gallery-slide-right{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.jv-gallery-viewer-image--slide-left{animation:gallery-slide-left .28s ease-out}.jv-gallery-viewer-image--slide-right{animation:gallery-slide-right .28s ease-out}.jv-gallery-viewer-nav{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:3rem;height:3rem;border:none;border-radius:var(--jv-radius-md);background:#00000080;color:#fff;cursor:pointer;transition:background-color .16s ease;z-index:1}.jv-gallery-viewer-nav:hover{background:#000000bf}.jv-gallery-viewer-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-md)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-md)}.jv-gallery-viewer-counter{position:absolute;bottom:var(--jv-spacing-md);left:50%;transform:translate(-50%);padding:var(--jv-spacing-xs) var(--jv-spacing-sm);border-radius:var(--jv-radius-sm);background:#0009;color:#fff;font-size:.8125rem;font-variant-numeric:tabular-nums;pointer-events:none}@media(max-width:768px){.jv-gallery-thumb{width:calc(100vw - 20px);min-width:200px}.jv-gallery-viewer-nav{width:2.5rem;height:2.5rem}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-sm)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-sm)}}@media(prefers-reduced-motion:reduce){.jv-gallery-strip{scroll-behavior:auto}.jv-gallery-viewer-image--slide-left,.jv-gallery-viewer-image--slide-right{animation:none}}\n"] }]
|
|
21531
|
+
}], propDecorators: { images: [{ type: i0.Input, args: [{ isSignal: true, alias: "images", required: false }] }], stripHeightVh: [{ type: i0.Input, args: [{ isSignal: true, alias: "stripHeightVh", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], imageClick: [{ type: i0.Output, args: ["imageClick"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }], strip: [{ type: i0.ViewChild, args: ['strip', { isSignal: true }] }], handleKeydown: [{
|
|
21532
|
+
type: HostListener,
|
|
21533
|
+
args: ['document:keydown', ['$event']]
|
|
21534
|
+
}] } });
|
|
21535
|
+
|
|
21536
|
+
class JvImageSliderComponent {
|
|
21537
|
+
thumbnailStripRef = viewChild('thumbnailStrip', ...(ngDevMode ? [{ debugName: "thumbnailStripRef" }] : /* istanbul ignore next */ []));
|
|
21538
|
+
translation = inject(JvTranslationService);
|
|
21539
|
+
images = input([], ...(ngDevMode ? [{ debugName: "images" }] : /* istanbul ignore next */ []));
|
|
21540
|
+
showThumbnails = input(true, ...(ngDevMode ? [{ debugName: "showThumbnails" }] : /* istanbul ignore next */ []));
|
|
21541
|
+
showDescription = input(true, ...(ngDevMode ? [{ debugName: "showDescription" }] : /* istanbul ignore next */ []));
|
|
21542
|
+
imageChange = output();
|
|
21543
|
+
currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
|
|
21544
|
+
hasMultiple = computed(() => this.images().length > 1, ...(ngDevMode ? [{ debugName: "hasMultiple" }] : /* istanbul ignore next */ []));
|
|
21545
|
+
currentImage = computed(() => this.images()[this.currentIndex()] ?? null, ...(ngDevMode ? [{ debugName: "currentImage" }] : /* istanbul ignore next */ []));
|
|
21546
|
+
translatedAriaLabels = computed(() => ({
|
|
21547
|
+
prev: this.translation.translate('slider.prevImage'),
|
|
21548
|
+
next: this.translation.translate('slider.nextImage'),
|
|
21549
|
+
thumbnailList: this.translation.translate('slider.thumbnailList'),
|
|
21550
|
+
}), ...(ngDevMode ? [{ debugName: "translatedAriaLabels" }] : /* istanbul ignore next */ []));
|
|
21551
|
+
constructor() {
|
|
21552
|
+
effect(() => {
|
|
21553
|
+
const images = this.images();
|
|
21554
|
+
if (images.length > 0 && this.currentIndex() >= images.length) {
|
|
21555
|
+
this.currentIndex.set(0);
|
|
21556
|
+
}
|
|
21557
|
+
});
|
|
21558
|
+
}
|
|
21559
|
+
handleKeydown(event) {
|
|
21560
|
+
if (!this.hasMultiple()) {
|
|
21561
|
+
return;
|
|
21562
|
+
}
|
|
21563
|
+
switch (event.key) {
|
|
21564
|
+
case 'ArrowLeft':
|
|
21565
|
+
event.preventDefault();
|
|
21566
|
+
this.previous();
|
|
21567
|
+
break;
|
|
21568
|
+
case 'ArrowRight':
|
|
21569
|
+
event.preventDefault();
|
|
21570
|
+
this.next();
|
|
21571
|
+
break;
|
|
21572
|
+
case 'Home':
|
|
21573
|
+
event.preventDefault();
|
|
21574
|
+
this.goTo(0);
|
|
21575
|
+
break;
|
|
21576
|
+
case 'End':
|
|
21577
|
+
event.preventDefault();
|
|
21578
|
+
this.goTo(this.images().length - 1);
|
|
21579
|
+
break;
|
|
21580
|
+
}
|
|
21581
|
+
}
|
|
21582
|
+
previous() {
|
|
21583
|
+
if (!this.hasMultiple()) {
|
|
21584
|
+
return;
|
|
21585
|
+
}
|
|
21586
|
+
const newIndex = this.currentIndex() === 0
|
|
21587
|
+
? this.images().length - 1
|
|
21588
|
+
: this.currentIndex() - 1;
|
|
21589
|
+
this.goTo(newIndex);
|
|
21590
|
+
}
|
|
21591
|
+
next() {
|
|
21592
|
+
if (!this.hasMultiple()) {
|
|
21593
|
+
return;
|
|
21594
|
+
}
|
|
21595
|
+
const newIndex = this.currentIndex() === this.images().length - 1
|
|
21596
|
+
? 0
|
|
21597
|
+
: this.currentIndex() + 1;
|
|
21598
|
+
this.goTo(newIndex);
|
|
21599
|
+
}
|
|
21600
|
+
goTo(index) {
|
|
21601
|
+
if (index === this.currentIndex() || index < 0 || index >= this.images().length) {
|
|
21602
|
+
return;
|
|
21603
|
+
}
|
|
21604
|
+
this.currentIndex.set(index);
|
|
21605
|
+
this.imageChange.emit(index);
|
|
21606
|
+
this.scrollThumbnailIntoView(index);
|
|
21607
|
+
}
|
|
21608
|
+
scrollThumbnailIntoView(index) {
|
|
21609
|
+
const strip = this.thumbnailStripRef()?.nativeElement;
|
|
21610
|
+
if (!strip) {
|
|
21611
|
+
return;
|
|
21612
|
+
}
|
|
21613
|
+
const thumbnails = strip.children;
|
|
21614
|
+
if (thumbnails[index]) {
|
|
21615
|
+
thumbnails[index].scrollIntoView({
|
|
21616
|
+
behavior: 'smooth',
|
|
21617
|
+
block: 'nearest',
|
|
21618
|
+
inline: 'center',
|
|
21619
|
+
});
|
|
21620
|
+
}
|
|
21621
|
+
}
|
|
21622
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvImageSliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
21623
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvImageSliderComponent, isStandalone: true, selector: "jv-image-slider", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: false, transformFunction: null }, showThumbnails: { classPropertyName: "showThumbnails", publicName: "showThumbnails", isSignal: true, isRequired: false, transformFunction: null }, showDescription: { classPropertyName: "showDescription", publicName: "showDescription", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageChange: "imageChange" }, host: { listeners: { "keydown": "handleKeydown($event)" } }, viewQueries: [{ propertyName: "thumbnailStripRef", first: true, predicate: ["thumbnailStrip"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
21624
|
+
<div class="jv-slider">
|
|
21625
|
+
<div class="jv-slider-main">
|
|
21626
|
+
@if (hasMultiple()) {
|
|
21627
|
+
<button
|
|
21628
|
+
type="button"
|
|
21629
|
+
class="jv-slider-nav jv-slider-nav--prev"
|
|
21630
|
+
[attr.aria-label]="translatedAriaLabels().prev"
|
|
21631
|
+
(click)="previous()"
|
|
21632
|
+
>
|
|
21633
|
+
<jv-icon name="chevron-left" [size]="20" />
|
|
21634
|
+
</button>
|
|
21635
|
+
}
|
|
21636
|
+
|
|
21637
|
+
<div class="jv-slider-stage">
|
|
21638
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21639
|
+
<div
|
|
21640
|
+
class="jv-slider-slide"
|
|
21641
|
+
[class.jv-slider-slide--active]="i === currentIndex()"
|
|
21642
|
+
>
|
|
21643
|
+
<img
|
|
21644
|
+
class="jv-slider-image"
|
|
21645
|
+
[src]="image.src"
|
|
21646
|
+
[alt]="image.alt"
|
|
21647
|
+
loading="lazy"
|
|
21648
|
+
/>
|
|
21649
|
+
</div>
|
|
21650
|
+
}
|
|
21651
|
+
</div>
|
|
21652
|
+
|
|
21653
|
+
@if (hasMultiple()) {
|
|
21654
|
+
<button
|
|
21655
|
+
type="button"
|
|
21656
|
+
class="jv-slider-nav jv-slider-nav--next"
|
|
21657
|
+
[attr.aria-label]="translatedAriaLabels().next"
|
|
21658
|
+
(click)="next()"
|
|
21659
|
+
>
|
|
21660
|
+
<jv-icon name="chevron-right" [size]="20" />
|
|
21661
|
+
</button>
|
|
21662
|
+
}
|
|
21663
|
+
</div>
|
|
21664
|
+
|
|
21665
|
+
@if (currentImage(); as image) {
|
|
21666
|
+
@if (showDescription() && image.description) {
|
|
21667
|
+
<div class="jv-slider-description">
|
|
21668
|
+
<p>{{ image.description }}</p>
|
|
21669
|
+
</div>
|
|
21670
|
+
}
|
|
21671
|
+
}
|
|
21672
|
+
|
|
21673
|
+
@if (hasMultiple() && showThumbnails()) {
|
|
21674
|
+
<div class="jv-slider-thumbnails" #thumbnailStrip role="tablist" [attr.aria-label]="translatedAriaLabels().thumbnailList">
|
|
21675
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21676
|
+
<button
|
|
21677
|
+
type="button"
|
|
21678
|
+
class="jv-slider-thumbnail"
|
|
21679
|
+
[class.jv-slider-thumbnail--active]="i === currentIndex()"
|
|
21680
|
+
role="tab"
|
|
21681
|
+
[attr.aria-selected]="i === currentIndex() ? 'true' : 'false'"
|
|
21682
|
+
[attr.aria-label]="image.alt"
|
|
21683
|
+
(click)="goTo(i)"
|
|
21684
|
+
>
|
|
21685
|
+
<img
|
|
21686
|
+
class="jv-slider-thumbnail-image"
|
|
21687
|
+
[src]="image.thumbnail || image.src"
|
|
21688
|
+
[alt]="image.alt"
|
|
21689
|
+
loading="lazy"
|
|
21690
|
+
/>
|
|
21691
|
+
</button>
|
|
21692
|
+
}
|
|
21693
|
+
</div>
|
|
21694
|
+
|
|
21695
|
+
<div class="jv-slider-counter">
|
|
21696
|
+
{{ currentIndex() + 1 }} / {{ images().length }}
|
|
21697
|
+
</div>
|
|
21698
|
+
}
|
|
21699
|
+
</div>
|
|
21700
|
+
`, isInline: true, styles: [".jv-slider{display:grid;gap:var(--jv-spacing-sm);width:100%;max-width:100%}.jv-slider-main{position:relative;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--jv-spacing-xs)}.jv-slider-stage{position:relative;aspect-ratio:16 / 9;overflow:hidden;border-radius:var(--jv-radius-md);background:var(--jv-color-surface-muted)}.jv-slider-slide{position:absolute;inset:0;opacity:0;transition:opacity .2s ease;pointer-events:none}.jv-slider-slide--active{opacity:1;pointer-events:auto}.jv-slider-image{display:block;width:100%;height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}.jv-slider-nav{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease;flex-shrink:0;z-index:1}.jv-slider-nav:hover{background:var(--jv-color-surface-muted);border-color:var(--jv-color-foreground-muted)}.jv-slider-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-slider-description{padding:var(--jv-spacing-sm) 0}.jv-slider-description p{margin:0;font-size:var(--jv-density-font-size);color:var(--jv-color-foreground-muted);line-height:1.5}.jv-slider-thumbnails{display:flex;gap:var(--jv-spacing-xs);overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;padding:var(--jv-spacing-xs) 0}.jv-slider-thumbnails::-webkit-scrollbar{display:none}.jv-slider-thumbnail{flex:0 0 auto;width:3.5rem;height:3.5rem;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;transition:border-color .16s ease,opacity .16s ease;opacity:.6}.jv-slider-thumbnail:hover{opacity:.85}.jv-slider-thumbnail--active{border-color:var(--jv-color-primary);opacity:1}.jv-slider-thumbnail:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-slider-thumbnail-image{display:block;width:100%;height:100%;object-fit:cover}.jv-slider-counter{text-align:center;font-size:.8125rem;color:var(--jv-color-foreground-muted);font-variant-numeric:tabular-nums}@media(max-width:640px){.jv-slider-nav{width:2rem;height:2rem}.jv-slider-thumbnail{width:2.75rem;height:2.75rem}}@media(prefers-reduced-motion:reduce){.jv-slider-slide{transition:none}}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
|
|
21701
|
+
}
|
|
21702
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvImageSliderComponent, decorators: [{
|
|
21703
|
+
type: Component,
|
|
21704
|
+
args: [{ selector: 'jv-image-slider', standalone: true, imports: [JvIconComponent], template: `
|
|
21705
|
+
<div class="jv-slider">
|
|
21706
|
+
<div class="jv-slider-main">
|
|
21707
|
+
@if (hasMultiple()) {
|
|
21708
|
+
<button
|
|
21709
|
+
type="button"
|
|
21710
|
+
class="jv-slider-nav jv-slider-nav--prev"
|
|
21711
|
+
[attr.aria-label]="translatedAriaLabels().prev"
|
|
21712
|
+
(click)="previous()"
|
|
21713
|
+
>
|
|
21714
|
+
<jv-icon name="chevron-left" [size]="20" />
|
|
21715
|
+
</button>
|
|
21716
|
+
}
|
|
21717
|
+
|
|
21718
|
+
<div class="jv-slider-stage">
|
|
21719
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21720
|
+
<div
|
|
21721
|
+
class="jv-slider-slide"
|
|
21722
|
+
[class.jv-slider-slide--active]="i === currentIndex()"
|
|
21723
|
+
>
|
|
21724
|
+
<img
|
|
21725
|
+
class="jv-slider-image"
|
|
21726
|
+
[src]="image.src"
|
|
21727
|
+
[alt]="image.alt"
|
|
21728
|
+
loading="lazy"
|
|
21729
|
+
/>
|
|
21730
|
+
</div>
|
|
21731
|
+
}
|
|
21732
|
+
</div>
|
|
21733
|
+
|
|
21734
|
+
@if (hasMultiple()) {
|
|
21735
|
+
<button
|
|
21736
|
+
type="button"
|
|
21737
|
+
class="jv-slider-nav jv-slider-nav--next"
|
|
21738
|
+
[attr.aria-label]="translatedAriaLabels().next"
|
|
21739
|
+
(click)="next()"
|
|
21740
|
+
>
|
|
21741
|
+
<jv-icon name="chevron-right" [size]="20" />
|
|
21742
|
+
</button>
|
|
21743
|
+
}
|
|
21744
|
+
</div>
|
|
21745
|
+
|
|
21746
|
+
@if (currentImage(); as image) {
|
|
21747
|
+
@if (showDescription() && image.description) {
|
|
21748
|
+
<div class="jv-slider-description">
|
|
21749
|
+
<p>{{ image.description }}</p>
|
|
21750
|
+
</div>
|
|
21751
|
+
}
|
|
21752
|
+
}
|
|
21753
|
+
|
|
21754
|
+
@if (hasMultiple() && showThumbnails()) {
|
|
21755
|
+
<div class="jv-slider-thumbnails" #thumbnailStrip role="tablist" [attr.aria-label]="translatedAriaLabels().thumbnailList">
|
|
21756
|
+
@for (image of images(); track image.src; let i = $index) {
|
|
21757
|
+
<button
|
|
21758
|
+
type="button"
|
|
21759
|
+
class="jv-slider-thumbnail"
|
|
21760
|
+
[class.jv-slider-thumbnail--active]="i === currentIndex()"
|
|
21761
|
+
role="tab"
|
|
21762
|
+
[attr.aria-selected]="i === currentIndex() ? 'true' : 'false'"
|
|
21763
|
+
[attr.aria-label]="image.alt"
|
|
21764
|
+
(click)="goTo(i)"
|
|
21765
|
+
>
|
|
21766
|
+
<img
|
|
21767
|
+
class="jv-slider-thumbnail-image"
|
|
21768
|
+
[src]="image.thumbnail || image.src"
|
|
21769
|
+
[alt]="image.alt"
|
|
21770
|
+
loading="lazy"
|
|
21771
|
+
/>
|
|
21772
|
+
</button>
|
|
21773
|
+
}
|
|
21774
|
+
</div>
|
|
21775
|
+
|
|
21776
|
+
<div class="jv-slider-counter">
|
|
21777
|
+
{{ currentIndex() + 1 }} / {{ images().length }}
|
|
21778
|
+
</div>
|
|
21779
|
+
}
|
|
21780
|
+
</div>
|
|
21781
|
+
`, styles: [".jv-slider{display:grid;gap:var(--jv-spacing-sm);width:100%;max-width:100%}.jv-slider-main{position:relative;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--jv-spacing-xs)}.jv-slider-stage{position:relative;aspect-ratio:16 / 9;overflow:hidden;border-radius:var(--jv-radius-md);background:var(--jv-color-surface-muted)}.jv-slider-slide{position:absolute;inset:0;opacity:0;transition:opacity .2s ease;pointer-events:none}.jv-slider-slide--active{opacity:1;pointer-events:auto}.jv-slider-image{display:block;width:100%;height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}.jv-slider-nav{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease;flex-shrink:0;z-index:1}.jv-slider-nav:hover{background:var(--jv-color-surface-muted);border-color:var(--jv-color-foreground-muted)}.jv-slider-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-slider-description{padding:var(--jv-spacing-sm) 0}.jv-slider-description p{margin:0;font-size:var(--jv-density-font-size);color:var(--jv-color-foreground-muted);line-height:1.5}.jv-slider-thumbnails{display:flex;gap:var(--jv-spacing-xs);overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;padding:var(--jv-spacing-xs) 0}.jv-slider-thumbnails::-webkit-scrollbar{display:none}.jv-slider-thumbnail{flex:0 0 auto;width:3.5rem;height:3.5rem;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;transition:border-color .16s ease,opacity .16s ease;opacity:.6}.jv-slider-thumbnail:hover{opacity:.85}.jv-slider-thumbnail--active{border-color:var(--jv-color-primary);opacity:1}.jv-slider-thumbnail:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-slider-thumbnail-image{display:block;width:100%;height:100%;object-fit:cover}.jv-slider-counter{text-align:center;font-size:.8125rem;color:var(--jv-color-foreground-muted);font-variant-numeric:tabular-nums}@media(max-width:640px){.jv-slider-nav{width:2rem;height:2rem}.jv-slider-thumbnail{width:2.75rem;height:2.75rem}}@media(prefers-reduced-motion:reduce){.jv-slider-slide{transition:none}}\n"] }]
|
|
21782
|
+
}], ctorParameters: () => [], propDecorators: { thumbnailStripRef: [{ type: i0.ViewChild, args: ['thumbnailStrip', { isSignal: true }] }], images: [{ type: i0.Input, args: [{ isSignal: true, alias: "images", required: false }] }], showThumbnails: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThumbnails", required: false }] }], showDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDescription", required: false }] }], imageChange: [{ type: i0.Output, args: ["imageChange"] }], handleKeydown: [{
|
|
21783
|
+
type: HostListener,
|
|
21784
|
+
args: ['keydown', ['$event']]
|
|
21785
|
+
}] } });
|
|
21786
|
+
|
|
21245
21787
|
class JvDialogService {
|
|
21246
21788
|
announcementService = inject(JvAnnouncementService);
|
|
21247
21789
|
translation = inject(JvTranslationService);
|
|
@@ -22125,12 +22667,25 @@ class JvTabsComponent {
|
|
|
22125
22667
|
instanceId = ++tabsSequence;
|
|
22126
22668
|
panelTemplates = contentChildren(JvTabPanelDirective, ...(ngDevMode ? [{ debugName: "panelTemplates" }] : /* istanbul ignore next */ []));
|
|
22127
22669
|
tabListRef = viewChild('tabList', ...(ngDevMode ? [{ debugName: "tabListRef" }] : /* istanbul ignore next */ []));
|
|
22670
|
+
config = inject(JvUiConfigService);
|
|
22128
22671
|
tabs = input([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
22129
22672
|
activeTabId = input(null, ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
|
|
22130
22673
|
keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
|
|
22674
|
+
transition = input(null, ...(ngDevMode ? [{ debugName: "transition" }] : /* istanbul ignore next */ []));
|
|
22131
22675
|
activeTabChange = output();
|
|
22132
22676
|
showScrollLeft = signal(false, ...(ngDevMode ? [{ debugName: "showScrollLeft" }] : /* istanbul ignore next */ []));
|
|
22133
22677
|
showScrollRight = signal(false, ...(ngDevMode ? [{ debugName: "showScrollRight" }] : /* istanbul ignore next */ []));
|
|
22678
|
+
slideDirection = signal('right', ...(ngDevMode ? [{ debugName: "slideDirection" }] : /* istanbul ignore next */ []));
|
|
22679
|
+
outgoingPanelId = signal(null, ...(ngDevMode ? [{ debugName: "outgoingPanelId" }] : /* istanbul ignore next */ []));
|
|
22680
|
+
previousActiveTabId = signal(null, ...(ngDevMode ? [{ debugName: "previousActiveTabId" }] : /* istanbul ignore next */ []));
|
|
22681
|
+
effectiveTransition = computed(() => {
|
|
22682
|
+
const local = this.transition();
|
|
22683
|
+
if (local !== null) {
|
|
22684
|
+
return local === 'slide' && this.keepMounted() ? 'fade' : local;
|
|
22685
|
+
}
|
|
22686
|
+
const global = this.config.snapshot.tabsTransition;
|
|
22687
|
+
return global === 'slide' && this.keepMounted() ? 'fade' : global;
|
|
22688
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveTransition" }] : /* istanbul ignore next */ []));
|
|
22134
22689
|
resolvedActiveTabId = computed(() => {
|
|
22135
22690
|
const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
|
|
22136
22691
|
const requestedId = this.activeTabId();
|
|
@@ -22150,6 +22705,20 @@ class JvTabsComponent {
|
|
|
22150
22705
|
this.updateScrollControls();
|
|
22151
22706
|
});
|
|
22152
22707
|
});
|
|
22708
|
+
effect(() => {
|
|
22709
|
+
const activeId = this.resolvedActiveTabId();
|
|
22710
|
+
const prevId = this.previousActiveTabId();
|
|
22711
|
+
if (activeId !== prevId && prevId !== null && !this.keepMounted()) {
|
|
22712
|
+
const enabledTabs = this.tabs().filter((t) => !t.disabled);
|
|
22713
|
+
const newIndex = enabledTabs.findIndex((t) => t.id === activeId);
|
|
22714
|
+
const oldIndex = enabledTabs.findIndex((t) => t.id === prevId);
|
|
22715
|
+
if (newIndex !== oldIndex) {
|
|
22716
|
+
this.slideDirection.set(newIndex > oldIndex ? 'left' : 'right');
|
|
22717
|
+
}
|
|
22718
|
+
this.outgoingPanelId.set(prevId);
|
|
22719
|
+
}
|
|
22720
|
+
this.previousActiveTabId.set(activeId);
|
|
22721
|
+
});
|
|
22153
22722
|
}
|
|
22154
22723
|
ngAfterViewInit() {
|
|
22155
22724
|
this.updateScrollControls();
|
|
@@ -22174,6 +22743,14 @@ class JvTabsComponent {
|
|
|
22174
22743
|
this.activeTabChange.emit(tabId);
|
|
22175
22744
|
queueMicrotask(() => this.focusTabButton(tabId));
|
|
22176
22745
|
}
|
|
22746
|
+
handleTransitionEnd(event) {
|
|
22747
|
+
if (event.propertyName !== 'opacity' && event.propertyName !== 'transform') {
|
|
22748
|
+
return;
|
|
22749
|
+
}
|
|
22750
|
+
if (!this.keepMounted() && this.outgoingPanelId()) {
|
|
22751
|
+
this.outgoingPanelId.set(null);
|
|
22752
|
+
}
|
|
22753
|
+
}
|
|
22177
22754
|
handleKeydown(event, currentTabId) {
|
|
22178
22755
|
const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
|
|
22179
22756
|
if (enabledTabs.length === 0) {
|
|
@@ -22254,148 +22831,194 @@ class JvTabsComponent {
|
|
|
22254
22831
|
return (Array.from(tabList.querySelectorAll('[data-jv-tab-id]')).find((button) => button.dataset['jvTabId'] === tabId) ?? null);
|
|
22255
22832
|
}
|
|
22256
22833
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22257
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvTabsComponent, isStandalone: true, selector: "jv-tabs", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, activeTabId: { classPropertyName: "activeTabId", publicName: "activeTabId", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeTabChange: "activeTabChange" }, host: { listeners: { "window:resize": "handleWindowResize()" } }, queries: [{ propertyName: "panelTemplates", predicate: JvTabPanelDirective, isSignal: true }], viewQueries: [{ propertyName: "tabListRef", first: true, predicate: ["tabList"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
22258
|
-
<div class="jv-tabs">
|
|
22259
|
-
<div class="jv-tabs-header">
|
|
22260
|
-
<div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
|
|
22261
|
-
@for (tab of tabs(); track tab.id) {
|
|
22262
|
-
<button
|
|
22263
|
-
type="button"
|
|
22264
|
-
class="jv-tab"
|
|
22265
|
-
[class.jv-tab--active]="isActive(tab.id)"
|
|
22266
|
-
[class.jv-tab--disabled]="tab.disabled ?? false"
|
|
22267
|
-
[id]="tabButtonId(tab.id)"
|
|
22268
|
-
[attr.data-jv-tab-id]="tab.id"
|
|
22269
|
-
role="tab"
|
|
22270
|
-
[attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
|
|
22271
|
-
[attr.aria-controls]="panelId(tab.id)"
|
|
22272
|
-
[attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
|
|
22273
|
-
[disabled]="tab.disabled ?? false"
|
|
22274
|
-
(click)="activateTab(tab.id)"
|
|
22275
|
-
(keydown)="handleKeydown($event, tab.id)"
|
|
22276
|
-
>
|
|
22277
|
-
@if (tab.icon) {
|
|
22278
|
-
<jv-icon [name]="tab.icon" [size]="16" />
|
|
22279
|
-
}
|
|
22280
|
-
<span>{{ tab.label }}</span>
|
|
22281
|
-
</button>
|
|
22282
|
-
}
|
|
22283
|
-
</div>
|
|
22284
|
-
|
|
22285
|
-
@if (showScrollLeft() || showScrollRight()) {
|
|
22286
|
-
<div class="jv-tabs-controls" aria-hidden="true">
|
|
22287
|
-
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
|
|
22288
|
-
<jv-icon name="chevron-left" [size]="16" />
|
|
22289
|
-
</button>
|
|
22290
|
-
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
|
|
22291
|
-
<jv-icon name="chevron-right" [size]="16" />
|
|
22292
|
-
</button>
|
|
22293
|
-
</div>
|
|
22294
|
-
}
|
|
22295
|
-
</div>
|
|
22296
|
-
|
|
22297
|
-
<div class="jv-tabs-panels">
|
|
22298
|
-
@if (keepMounted()) {
|
|
22299
|
-
@for (tab of tabs(); track tab.id) {
|
|
22300
|
-
<div
|
|
22301
|
-
class="jv-tab-panel"
|
|
22302
|
-
[class.jv-tab-panel--hidden]="!isActive(tab.id)"
|
|
22303
|
-
[
|
|
22304
|
-
|
|
22305
|
-
[
|
|
22306
|
-
[
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22834
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvTabsComponent, isStandalone: true, selector: "jv-tabs", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, activeTabId: { classPropertyName: "activeTabId", publicName: "activeTabId", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null }, transition: { classPropertyName: "transition", publicName: "transition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeTabChange: "activeTabChange" }, host: { listeners: { "window:resize": "handleWindowResize()" } }, queries: [{ propertyName: "panelTemplates", predicate: JvTabPanelDirective, isSignal: true }], viewQueries: [{ propertyName: "tabListRef", first: true, predicate: ["tabList"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
22835
|
+
<div class="jv-tabs">
|
|
22836
|
+
<div class="jv-tabs-header">
|
|
22837
|
+
<div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
|
|
22838
|
+
@for (tab of tabs(); track tab.id) {
|
|
22839
|
+
<button
|
|
22840
|
+
type="button"
|
|
22841
|
+
class="jv-tab"
|
|
22842
|
+
[class.jv-tab--active]="isActive(tab.id)"
|
|
22843
|
+
[class.jv-tab--disabled]="tab.disabled ?? false"
|
|
22844
|
+
[id]="tabButtonId(tab.id)"
|
|
22845
|
+
[attr.data-jv-tab-id]="tab.id"
|
|
22846
|
+
role="tab"
|
|
22847
|
+
[attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
|
|
22848
|
+
[attr.aria-controls]="panelId(tab.id)"
|
|
22849
|
+
[attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
|
|
22850
|
+
[disabled]="tab.disabled ?? false"
|
|
22851
|
+
(click)="activateTab(tab.id)"
|
|
22852
|
+
(keydown)="handleKeydown($event, tab.id)"
|
|
22853
|
+
>
|
|
22854
|
+
@if (tab.icon) {
|
|
22855
|
+
<jv-icon [name]="tab.icon" [size]="16" />
|
|
22856
|
+
}
|
|
22857
|
+
<span>{{ tab.label }}</span>
|
|
22858
|
+
</button>
|
|
22859
|
+
}
|
|
22860
|
+
</div>
|
|
22861
|
+
|
|
22862
|
+
@if (showScrollLeft() || showScrollRight()) {
|
|
22863
|
+
<div class="jv-tabs-controls" aria-hidden="true">
|
|
22864
|
+
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
|
|
22865
|
+
<jv-icon name="chevron-left" [size]="16" />
|
|
22866
|
+
</button>
|
|
22867
|
+
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
|
|
22868
|
+
<jv-icon name="chevron-right" [size]="16" />
|
|
22869
|
+
</button>
|
|
22870
|
+
</div>
|
|
22871
|
+
}
|
|
22872
|
+
</div>
|
|
22873
|
+
|
|
22874
|
+
<div class="jv-tabs-panels">
|
|
22875
|
+
@if (keepMounted()) {
|
|
22876
|
+
@for (tab of tabs(); track tab.id) {
|
|
22877
|
+
<div
|
|
22878
|
+
class="jv-tab-panel"
|
|
22879
|
+
[class.jv-tab-panel--hidden]="!isActive(tab.id)"
|
|
22880
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
22881
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
22882
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
22883
|
+
[id]="panelId(tab.id)"
|
|
22884
|
+
role="tabpanel"
|
|
22885
|
+
[attr.aria-labelledby]="tabButtonId(tab.id)"
|
|
22886
|
+
[attr.tabindex]="isActive(tab.id) ? 0 : null"
|
|
22887
|
+
>
|
|
22888
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
|
|
22889
|
+
</div>
|
|
22890
|
+
}
|
|
22891
|
+
} @else {
|
|
22892
|
+
@if (outgoingPanelId(); as outgoingId) {
|
|
22893
|
+
<div
|
|
22894
|
+
class="jv-tab-panel jv-tab-panel--exit"
|
|
22895
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
22896
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
22897
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
22898
|
+
[id]="panelId(outgoingId)"
|
|
22899
|
+
role="tabpanel"
|
|
22900
|
+
[attr.aria-labelledby]="tabButtonId(outgoingId)"
|
|
22901
|
+
tabindex="-1"
|
|
22902
|
+
(transitionend)="handleTransitionEnd($event)"
|
|
22903
|
+
>
|
|
22904
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(outgoingId) ?? null" />
|
|
22905
|
+
</div>
|
|
22906
|
+
}
|
|
22907
|
+
|
|
22908
|
+
@if (resolvedActiveTabId(); as activeId) {
|
|
22909
|
+
<div
|
|
22910
|
+
class="jv-tab-panel jv-tab-panel--entering"
|
|
22911
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
22912
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
22913
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
22914
|
+
[id]="panelId(activeId)"
|
|
22915
|
+
role="tabpanel"
|
|
22916
|
+
[attr.aria-labelledby]="tabButtonId(activeId)"
|
|
22917
|
+
tabindex="0"
|
|
22918
|
+
>
|
|
22919
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
|
|
22920
|
+
</div>
|
|
22921
|
+
}
|
|
22922
|
+
}
|
|
22923
|
+
</div>
|
|
22924
|
+
</div>
|
|
22925
|
+
`, isInline: true, styles: [".jv-tabs{display:grid;gap:var(--jv-spacing-md)}.jv-tabs-header{display:flex;align-items:center;gap:var(--jv-spacing-sm);min-width:0}.jv-tabs-scroll{flex:1;min-width:0;display:flex;align-items:stretch;gap:var(--jv-spacing-xs);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--jv-color-border);scroll-behavior:smooth}.jv-tabs-scroll::-webkit-scrollbar{display:none}.jv-tab{position:relative;display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);flex:0 0 auto;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:0;border-bottom:2px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer;transition:color .16s ease,border-color .16s ease,background-color .16s ease}.jv-tab:hover:not(:disabled){color:var(--jv-color-foreground);background:color-mix(in srgb,var(--jv-color-primary) 6%,transparent)}.jv-tab:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px;border-radius:var(--jv-radius-sm) var(--jv-radius-sm) 0 0}.jv-tab--active{color:var(--jv-color-primary);border-bottom-color:var(--jv-color-primary)}.jv-tab--disabled{opacity:.5;cursor:not-allowed}.jv-tabs-controls{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs)}.jv-tabs-scroll-button{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer}.jv-tabs-scroll-button:disabled{opacity:.4;cursor:default}.jv-tab-panel{outline:none}.jv-tab-panel--hidden{display:none}.jv-tab-panel--fade{transition:opacity .18s ease}.jv-tab-panel--hidden.jv-tab-panel--fade{display:block;opacity:0;pointer-events:none}.jv-tab-panel--slide-left,.jv-tab-panel--slide-right{transition:opacity .18s ease,transform .18s ease}.jv-tab-panel--hidden.jv-tab-panel--slide-left{display:block;opacity:0;transform:translate(20px);pointer-events:none}.jv-tab-panel--hidden.jv-tab-panel--slide-right{display:block;opacity:0;transform:translate(-20px);pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--fade{position:absolute;opacity:0;pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--slide-left{position:absolute;opacity:0;transform:translate(-20px);pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--slide-right{position:absolute;opacity:0;transform:translate(20px);pointer-events:none}.jv-tab-panel--entering{animation:jv-tab-enter .18s ease forwards}.jv-tab-panel--entering.jv-tab-panel--slide-left{animation-name:jv-tab-slide-from-right}.jv-tab-panel--entering.jv-tab-panel--slide-right{animation-name:jv-tab-slide-from-left}@keyframes jv-tab-enter{0%{opacity:0}to{opacity:1}}@keyframes jv-tab-slide-from-right{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}@keyframes jv-tab-slide-from-left{0%{opacity:0;transform:translate(-20px)}to{opacity:1;transform:translate(0)}}@media(prefers-reduced-motion:reduce){.jv-tab-panel--fade,.jv-tab-panel--slide-left,.jv-tab-panel--slide-right,.jv-tab-panel--entering{transition:none;animation:none}.jv-tab-panel--hidden.jv-tab-panel--fade,.jv-tab-panel--hidden.jv-tab-panel--slide-left,.jv-tab-panel--hidden.jv-tab-panel--slide-right{opacity:0}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
|
|
22326
22926
|
}
|
|
22327
22927
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, decorators: [{
|
|
22328
22928
|
type: Component,
|
|
22329
|
-
args: [{ selector: 'jv-tabs', standalone: true, imports: [NgTemplateOutlet, JvIconComponent], template: `
|
|
22330
|
-
<div class="jv-tabs">
|
|
22331
|
-
<div class="jv-tabs-header">
|
|
22332
|
-
<div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
|
|
22333
|
-
@for (tab of tabs(); track tab.id) {
|
|
22334
|
-
<button
|
|
22335
|
-
type="button"
|
|
22336
|
-
class="jv-tab"
|
|
22337
|
-
[class.jv-tab--active]="isActive(tab.id)"
|
|
22338
|
-
[class.jv-tab--disabled]="tab.disabled ?? false"
|
|
22339
|
-
[id]="tabButtonId(tab.id)"
|
|
22340
|
-
[attr.data-jv-tab-id]="tab.id"
|
|
22341
|
-
role="tab"
|
|
22342
|
-
[attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
|
|
22343
|
-
[attr.aria-controls]="panelId(tab.id)"
|
|
22344
|
-
[attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
|
|
22345
|
-
[disabled]="tab.disabled ?? false"
|
|
22346
|
-
(click)="activateTab(tab.id)"
|
|
22347
|
-
(keydown)="handleKeydown($event, tab.id)"
|
|
22348
|
-
>
|
|
22349
|
-
@if (tab.icon) {
|
|
22350
|
-
<jv-icon [name]="tab.icon" [size]="16" />
|
|
22351
|
-
}
|
|
22352
|
-
<span>{{ tab.label }}</span>
|
|
22353
|
-
</button>
|
|
22354
|
-
}
|
|
22355
|
-
</div>
|
|
22356
|
-
|
|
22357
|
-
@if (showScrollLeft() || showScrollRight()) {
|
|
22358
|
-
<div class="jv-tabs-controls" aria-hidden="true">
|
|
22359
|
-
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
|
|
22360
|
-
<jv-icon name="chevron-left" [size]="16" />
|
|
22361
|
-
</button>
|
|
22362
|
-
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
|
|
22363
|
-
<jv-icon name="chevron-right" [size]="16" />
|
|
22364
|
-
</button>
|
|
22365
|
-
</div>
|
|
22366
|
-
}
|
|
22367
|
-
</div>
|
|
22368
|
-
|
|
22369
|
-
<div class="jv-tabs-panels">
|
|
22370
|
-
@if (keepMounted()) {
|
|
22371
|
-
@for (tab of tabs(); track tab.id) {
|
|
22372
|
-
<div
|
|
22373
|
-
class="jv-tab-panel"
|
|
22374
|
-
[class.jv-tab-panel--hidden]="!isActive(tab.id)"
|
|
22375
|
-
[
|
|
22376
|
-
|
|
22377
|
-
[
|
|
22378
|
-
[
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
|
|
22385
|
-
|
|
22386
|
-
|
|
22387
|
-
|
|
22388
|
-
|
|
22389
|
-
|
|
22390
|
-
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22394
|
-
|
|
22395
|
-
|
|
22396
|
-
|
|
22397
|
-
|
|
22398
|
-
|
|
22929
|
+
args: [{ selector: 'jv-tabs', standalone: true, imports: [NgTemplateOutlet, JvIconComponent], template: `
|
|
22930
|
+
<div class="jv-tabs">
|
|
22931
|
+
<div class="jv-tabs-header">
|
|
22932
|
+
<div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
|
|
22933
|
+
@for (tab of tabs(); track tab.id) {
|
|
22934
|
+
<button
|
|
22935
|
+
type="button"
|
|
22936
|
+
class="jv-tab"
|
|
22937
|
+
[class.jv-tab--active]="isActive(tab.id)"
|
|
22938
|
+
[class.jv-tab--disabled]="tab.disabled ?? false"
|
|
22939
|
+
[id]="tabButtonId(tab.id)"
|
|
22940
|
+
[attr.data-jv-tab-id]="tab.id"
|
|
22941
|
+
role="tab"
|
|
22942
|
+
[attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
|
|
22943
|
+
[attr.aria-controls]="panelId(tab.id)"
|
|
22944
|
+
[attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
|
|
22945
|
+
[disabled]="tab.disabled ?? false"
|
|
22946
|
+
(click)="activateTab(tab.id)"
|
|
22947
|
+
(keydown)="handleKeydown($event, tab.id)"
|
|
22948
|
+
>
|
|
22949
|
+
@if (tab.icon) {
|
|
22950
|
+
<jv-icon [name]="tab.icon" [size]="16" />
|
|
22951
|
+
}
|
|
22952
|
+
<span>{{ tab.label }}</span>
|
|
22953
|
+
</button>
|
|
22954
|
+
}
|
|
22955
|
+
</div>
|
|
22956
|
+
|
|
22957
|
+
@if (showScrollLeft() || showScrollRight()) {
|
|
22958
|
+
<div class="jv-tabs-controls" aria-hidden="true">
|
|
22959
|
+
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
|
|
22960
|
+
<jv-icon name="chevron-left" [size]="16" />
|
|
22961
|
+
</button>
|
|
22962
|
+
<button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
|
|
22963
|
+
<jv-icon name="chevron-right" [size]="16" />
|
|
22964
|
+
</button>
|
|
22965
|
+
</div>
|
|
22966
|
+
}
|
|
22967
|
+
</div>
|
|
22968
|
+
|
|
22969
|
+
<div class="jv-tabs-panels">
|
|
22970
|
+
@if (keepMounted()) {
|
|
22971
|
+
@for (tab of tabs(); track tab.id) {
|
|
22972
|
+
<div
|
|
22973
|
+
class="jv-tab-panel"
|
|
22974
|
+
[class.jv-tab-panel--hidden]="!isActive(tab.id)"
|
|
22975
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
22976
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
22977
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
22978
|
+
[id]="panelId(tab.id)"
|
|
22979
|
+
role="tabpanel"
|
|
22980
|
+
[attr.aria-labelledby]="tabButtonId(tab.id)"
|
|
22981
|
+
[attr.tabindex]="isActive(tab.id) ? 0 : null"
|
|
22982
|
+
>
|
|
22983
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
|
|
22984
|
+
</div>
|
|
22985
|
+
}
|
|
22986
|
+
} @else {
|
|
22987
|
+
@if (outgoingPanelId(); as outgoingId) {
|
|
22988
|
+
<div
|
|
22989
|
+
class="jv-tab-panel jv-tab-panel--exit"
|
|
22990
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
22991
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
22992
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
22993
|
+
[id]="panelId(outgoingId)"
|
|
22994
|
+
role="tabpanel"
|
|
22995
|
+
[attr.aria-labelledby]="tabButtonId(outgoingId)"
|
|
22996
|
+
tabindex="-1"
|
|
22997
|
+
(transitionend)="handleTransitionEnd($event)"
|
|
22998
|
+
>
|
|
22999
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(outgoingId) ?? null" />
|
|
23000
|
+
</div>
|
|
23001
|
+
}
|
|
23002
|
+
|
|
23003
|
+
@if (resolvedActiveTabId(); as activeId) {
|
|
23004
|
+
<div
|
|
23005
|
+
class="jv-tab-panel jv-tab-panel--entering"
|
|
23006
|
+
[class.jv-tab-panel--fade]="effectiveTransition() === 'fade'"
|
|
23007
|
+
[class.jv-tab-panel--slide-left]="effectiveTransition() === 'slide' && slideDirection() === 'left'"
|
|
23008
|
+
[class.jv-tab-panel--slide-right]="effectiveTransition() === 'slide' && slideDirection() === 'right'"
|
|
23009
|
+
[id]="panelId(activeId)"
|
|
23010
|
+
role="tabpanel"
|
|
23011
|
+
[attr.aria-labelledby]="tabButtonId(activeId)"
|
|
23012
|
+
tabindex="0"
|
|
23013
|
+
>
|
|
23014
|
+
<ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
|
|
23015
|
+
</div>
|
|
23016
|
+
}
|
|
23017
|
+
}
|
|
23018
|
+
</div>
|
|
23019
|
+
</div>
|
|
23020
|
+
`, styles: [".jv-tabs{display:grid;gap:var(--jv-spacing-md)}.jv-tabs-header{display:flex;align-items:center;gap:var(--jv-spacing-sm);min-width:0}.jv-tabs-scroll{flex:1;min-width:0;display:flex;align-items:stretch;gap:var(--jv-spacing-xs);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--jv-color-border);scroll-behavior:smooth}.jv-tabs-scroll::-webkit-scrollbar{display:none}.jv-tab{position:relative;display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);flex:0 0 auto;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:0;border-bottom:2px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer;transition:color .16s ease,border-color .16s ease,background-color .16s ease}.jv-tab:hover:not(:disabled){color:var(--jv-color-foreground);background:color-mix(in srgb,var(--jv-color-primary) 6%,transparent)}.jv-tab:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px;border-radius:var(--jv-radius-sm) var(--jv-radius-sm) 0 0}.jv-tab--active{color:var(--jv-color-primary);border-bottom-color:var(--jv-color-primary)}.jv-tab--disabled{opacity:.5;cursor:not-allowed}.jv-tabs-controls{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs)}.jv-tabs-scroll-button{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer}.jv-tabs-scroll-button:disabled{opacity:.4;cursor:default}.jv-tab-panel{outline:none}.jv-tab-panel--hidden{display:none}.jv-tab-panel--fade{transition:opacity .18s ease}.jv-tab-panel--hidden.jv-tab-panel--fade{display:block;opacity:0;pointer-events:none}.jv-tab-panel--slide-left,.jv-tab-panel--slide-right{transition:opacity .18s ease,transform .18s ease}.jv-tab-panel--hidden.jv-tab-panel--slide-left{display:block;opacity:0;transform:translate(20px);pointer-events:none}.jv-tab-panel--hidden.jv-tab-panel--slide-right{display:block;opacity:0;transform:translate(-20px);pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--fade{position:absolute;opacity:0;pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--slide-left{position:absolute;opacity:0;transform:translate(-20px);pointer-events:none}.jv-tab-panel--exit.jv-tab-panel--slide-right{position:absolute;opacity:0;transform:translate(20px);pointer-events:none}.jv-tab-panel--entering{animation:jv-tab-enter .18s ease forwards}.jv-tab-panel--entering.jv-tab-panel--slide-left{animation-name:jv-tab-slide-from-right}.jv-tab-panel--entering.jv-tab-panel--slide-right{animation-name:jv-tab-slide-from-left}@keyframes jv-tab-enter{0%{opacity:0}to{opacity:1}}@keyframes jv-tab-slide-from-right{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}@keyframes jv-tab-slide-from-left{0%{opacity:0;transform:translate(-20px)}to{opacity:1;transform:translate(0)}}@media(prefers-reduced-motion:reduce){.jv-tab-panel--fade,.jv-tab-panel--slide-left,.jv-tab-panel--slide-right,.jv-tab-panel--entering{transition:none;animation:none}.jv-tab-panel--hidden.jv-tab-panel--fade,.jv-tab-panel--hidden.jv-tab-panel--slide-left,.jv-tab-panel--hidden.jv-tab-panel--slide-right{opacity:0}}\n"] }]
|
|
23021
|
+
}], ctorParameters: () => [], propDecorators: { panelTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => JvTabPanelDirective), { isSignal: true }] }], tabListRef: [{ type: i0.ViewChild, args: ['tabList', { isSignal: true }] }], tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], activeTabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTabId", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }], transition: [{ type: i0.Input, args: [{ isSignal: true, alias: "transition", required: false }] }], activeTabChange: [{ type: i0.Output, args: ["activeTabChange"] }], handleWindowResize: [{
|
|
22399
23022
|
type: HostListener,
|
|
22400
23023
|
args: ['window:resize']
|
|
22401
23024
|
}] } });
|
|
@@ -22593,6 +23216,280 @@ const JV_GRID_DEFAULT_OPTIONS = {
|
|
|
22593
23216
|
rowDoubleClick: false,
|
|
22594
23217
|
};
|
|
22595
23218
|
|
|
23219
|
+
const MAIN_NS = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
|
|
23220
|
+
const REL_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
23221
|
+
const PKG_REL_NS = 'http://schemas.openxmlformats.org/package/2006/relationships';
|
|
23222
|
+
const XML_DECL = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
|
|
23223
|
+
/**
|
|
23224
|
+
* Builds a real Office Open XML (.xlsx) spreadsheet as a Blob.
|
|
23225
|
+
*
|
|
23226
|
+
* The package is assembled in memory with the ZIP "store" method (no
|
|
23227
|
+
* compression) so no third-party dependency is required. All cell text is
|
|
23228
|
+
* written as `inlineStr`, which keeps every character (including UTF-8
|
|
23229
|
+
* accented glyphs) intact and is understood by Microsoft Excel and Apple
|
|
23230
|
+
* Numbers alike.
|
|
23231
|
+
*/
|
|
23232
|
+
function buildXlsxBlob(sheets) {
|
|
23233
|
+
return new Blob([buildXlsxBuffer(sheets)], {
|
|
23234
|
+
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
23235
|
+
});
|
|
23236
|
+
}
|
|
23237
|
+
/**
|
|
23238
|
+
* Returns the raw bytes of a minimal valid .xlsx package. Exposed separately
|
|
23239
|
+
* from the Blob wrapper to make the package easy to assert on in tests.
|
|
23240
|
+
*/
|
|
23241
|
+
function buildXlsxBuffer(sheets) {
|
|
23242
|
+
const parts = [];
|
|
23243
|
+
const sheetXml = sheets.map((sheet, index) => buildSheetXml(sheet, index));
|
|
23244
|
+
const sheetNames = sheets.length > 0 ? sheets.map((s) => s.name) : ['Sheet1'];
|
|
23245
|
+
parts.push({ name: '[Content_Types].xml', data: utf8(buildContentTypes(sheets.length)) });
|
|
23246
|
+
parts.push({ name: '_rels/.rels', data: utf8(buildRootRels()) });
|
|
23247
|
+
parts.push({ name: 'xl/workbook.xml', data: utf8(buildWorkbook(sheetNames)) });
|
|
23248
|
+
parts.push({ name: 'xl/_rels/workbook.xml.rels', data: utf8(buildWorkbookRels(sheets.length)) });
|
|
23249
|
+
parts.push({ name: 'xl/styles.xml', data: utf8(buildStyles()) });
|
|
23250
|
+
for (let i = 0; i < sheetXml.length; i++) {
|
|
23251
|
+
parts.push({ name: `xl/worksheets/sheet${i + 1}.xml`, data: utf8(sheetXml[i]) });
|
|
23252
|
+
}
|
|
23253
|
+
return toZip(parts);
|
|
23254
|
+
}
|
|
23255
|
+
function buildContentTypes(sheetCount) {
|
|
23256
|
+
let xml = XML_DECL;
|
|
23257
|
+
xml += `<Types xmlns="${PKG_REL_NS}">`;
|
|
23258
|
+
xml += `<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>`;
|
|
23259
|
+
xml += `<Default Extension="xml" ContentType="application/xml"/>`;
|
|
23260
|
+
xml += `<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>`;
|
|
23261
|
+
for (let i = 0; i < sheetCount; i++) {
|
|
23262
|
+
xml += `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`;
|
|
23263
|
+
}
|
|
23264
|
+
xml += `<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>`;
|
|
23265
|
+
xml += `</Types>`;
|
|
23266
|
+
return xml;
|
|
23267
|
+
}
|
|
23268
|
+
function buildRootRels() {
|
|
23269
|
+
return XML_DECL +
|
|
23270
|
+
`<Relationships xmlns="${PKG_REL_NS}">` +
|
|
23271
|
+
`<Relationship Id="rId1" Type="${REL_NS}/officeDocument" Target="xl/workbook.xml"/>` +
|
|
23272
|
+
`</Relationships>`;
|
|
23273
|
+
}
|
|
23274
|
+
function buildWorkbook(sheetNames) {
|
|
23275
|
+
let xml = XML_DECL;
|
|
23276
|
+
xml += `<workbook xmlns="${MAIN_NS}" xmlns:r="${REL_NS}">`;
|
|
23277
|
+
xml += `<sheets>`;
|
|
23278
|
+
for (let i = 0; i < sheetNames.length; i++) {
|
|
23279
|
+
const name = sanitizeSheetName(sheetNames[i]) || `Sheet${i + 1}`;
|
|
23280
|
+
xml += `<sheet name="${escapeXml(name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`;
|
|
23281
|
+
}
|
|
23282
|
+
xml += `</sheets></workbook>`;
|
|
23283
|
+
return xml;
|
|
23284
|
+
}
|
|
23285
|
+
function buildWorkbookRels(sheetCount) {
|
|
23286
|
+
let xml = XML_DECL;
|
|
23287
|
+
xml += `<Relationships xmlns="${PKG_REL_NS}">`;
|
|
23288
|
+
for (let i = 0; i < sheetCount; i++) {
|
|
23289
|
+
xml += `<Relationship Id="rId${i + 1}" Type="${REL_NS}/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`;
|
|
23290
|
+
}
|
|
23291
|
+
xml += `<Relationship Id="rId${sheetCount + 1}" Type="${REL_NS}/styles" Target="styles.xml"/>`;
|
|
23292
|
+
xml += `</Relationships>`;
|
|
23293
|
+
return xml;
|
|
23294
|
+
}
|
|
23295
|
+
function buildSheetXml(sheet, index) {
|
|
23296
|
+
const rows = sheet.rows;
|
|
23297
|
+
const rowCount = rows.length;
|
|
23298
|
+
const colCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
23299
|
+
const dimension = rowCount === 0 || colCount === 0 ? 'A1' : `A1:${columnName(colCount - 1)}${rowCount}`;
|
|
23300
|
+
let xml = XML_DECL;
|
|
23301
|
+
xml += `<worksheet xmlns="${MAIN_NS}">`;
|
|
23302
|
+
xml += `<dimension ref="${dimension}"/>`;
|
|
23303
|
+
xml += `<sheetViews><sheetView workbookViewId="0"/></sheetViews>`;
|
|
23304
|
+
xml += `<sheetFormatPr defaultRowHeight="15"/>`;
|
|
23305
|
+
xml += `<sheetData>`;
|
|
23306
|
+
for (let r = 0; r < rowCount; r++) {
|
|
23307
|
+
const row = rows[r];
|
|
23308
|
+
xml += `<row r="${r + 1}">`;
|
|
23309
|
+
for (let c = 0; c < row.length; c++) {
|
|
23310
|
+
xml += `<c r="${columnName(c)}${r + 1}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(String(row[c]))}</t></is></c>`;
|
|
23311
|
+
}
|
|
23312
|
+
xml += `</row>`;
|
|
23313
|
+
}
|
|
23314
|
+
xml += `</sheetData></worksheet>`;
|
|
23315
|
+
void index;
|
|
23316
|
+
return xml;
|
|
23317
|
+
}
|
|
23318
|
+
function buildStyles() {
|
|
23319
|
+
return XML_DECL +
|
|
23320
|
+
`<styleSheet xmlns="${MAIN_NS}">` +
|
|
23321
|
+
`<fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts>` +
|
|
23322
|
+
`<fills count="1"><fill><patternFill patternType="none"/></fill></fills>` +
|
|
23323
|
+
`<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>` +
|
|
23324
|
+
`<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>` +
|
|
23325
|
+
`<cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs>` +
|
|
23326
|
+
`<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>` +
|
|
23327
|
+
`</styleSheet>`;
|
|
23328
|
+
}
|
|
23329
|
+
function columnName(index) {
|
|
23330
|
+
let value = '';
|
|
23331
|
+
let n = index;
|
|
23332
|
+
while (n >= 0) {
|
|
23333
|
+
value = String.fromCharCode((n % 26) + 65) + value;
|
|
23334
|
+
n = Math.floor(n / 26) - 1;
|
|
23335
|
+
}
|
|
23336
|
+
return value;
|
|
23337
|
+
}
|
|
23338
|
+
function sanitizeSheetName(name) {
|
|
23339
|
+
return name.replace(/[\[\]:*?/\\]/g, '').slice(0, 31);
|
|
23340
|
+
}
|
|
23341
|
+
function escapeXml(value) {
|
|
23342
|
+
return value
|
|
23343
|
+
.replace(/&/g, '&')
|
|
23344
|
+
.replace(/</g, '<')
|
|
23345
|
+
.replace(/>/g, '>')
|
|
23346
|
+
.replace(/"/g, '"')
|
|
23347
|
+
.replace(/'/g, ''');
|
|
23348
|
+
}
|
|
23349
|
+
function utf8(value) {
|
|
23350
|
+
return new TextEncoder().encode(value);
|
|
23351
|
+
}
|
|
23352
|
+
// ---------------------------------------------------------------------------
|
|
23353
|
+
// Minimal ZIP writer ("store" method, no compression)
|
|
23354
|
+
// ---------------------------------------------------------------------------
|
|
23355
|
+
const CRC_TABLE = (() => {
|
|
23356
|
+
const table = new Array(256);
|
|
23357
|
+
for (let n = 0; n < 256; n++) {
|
|
23358
|
+
let c = n;
|
|
23359
|
+
for (let k = 0; k < 8; k++) {
|
|
23360
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
23361
|
+
}
|
|
23362
|
+
table[n] = c >>> 0;
|
|
23363
|
+
}
|
|
23364
|
+
return table;
|
|
23365
|
+
})();
|
|
23366
|
+
function crc32(bytes) {
|
|
23367
|
+
let crc = 0xffffffff;
|
|
23368
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
23369
|
+
crc = CRC_TABLE[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
|
|
23370
|
+
}
|
|
23371
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
23372
|
+
}
|
|
23373
|
+
function toZip(files) {
|
|
23374
|
+
const encoder = new TextEncoder();
|
|
23375
|
+
const chunks = [];
|
|
23376
|
+
const centralDirectory = new Uint8Array(0);
|
|
23377
|
+
const centralRecords = [];
|
|
23378
|
+
let offset = 0;
|
|
23379
|
+
for (const file of files) {
|
|
23380
|
+
const nameBytes = encoder.encode(file.name);
|
|
23381
|
+
const crc = crc32(file.data);
|
|
23382
|
+
// Local file header
|
|
23383
|
+
const local = new DataView(new ArrayBuffer(30));
|
|
23384
|
+
local.setUint32(0, 0x04034b50, true);
|
|
23385
|
+
local.setUint16(4, 20, true);
|
|
23386
|
+
local.setUint16(6, 0x0800, true);
|
|
23387
|
+
local.setUint16(8, 0, true); // store (no compression)
|
|
23388
|
+
local.setUint16(10, 0, true);
|
|
23389
|
+
local.setUint16(12, 0, true);
|
|
23390
|
+
local.setUint32(14, crc, true);
|
|
23391
|
+
local.setUint32(18, file.data.length, true);
|
|
23392
|
+
local.setUint32(22, file.data.length, true);
|
|
23393
|
+
local.setUint16(26, nameBytes.length, true);
|
|
23394
|
+
local.setUint16(28, 0, true);
|
|
23395
|
+
chunks.push(new Uint8Array(local.buffer), nameBytes, file.data);
|
|
23396
|
+
// Central directory header
|
|
23397
|
+
const central = new DataView(new ArrayBuffer(46));
|
|
23398
|
+
central.setUint32(0, 0x02014b50, true);
|
|
23399
|
+
central.setUint16(4, 20, true); // version made by
|
|
23400
|
+
central.setUint16(6, 20, true); // version needed
|
|
23401
|
+
central.setUint16(8, 0x0800, true); // UTF-8 filename flag
|
|
23402
|
+
central.setUint16(10, 0, true); // store (no compression)
|
|
23403
|
+
central.setUint16(12, 0, true); // mod time
|
|
23404
|
+
central.setUint16(14, 0, true); // mod date
|
|
23405
|
+
central.setUint32(16, crc, true);
|
|
23406
|
+
central.setUint32(20, file.data.length, true); // compressed size
|
|
23407
|
+
central.setUint32(24, file.data.length, true); // uncompressed size
|
|
23408
|
+
central.setUint16(28, nameBytes.length, true);
|
|
23409
|
+
central.setUint16(30, 0, true); // extra field length
|
|
23410
|
+
central.setUint16(32, 0, true); // comment length
|
|
23411
|
+
central.setUint16(34, 0, true); // disk number start
|
|
23412
|
+
central.setUint16(36, 0, true); // internal file attributes
|
|
23413
|
+
central.setUint32(38, 0, true); // external file attributes
|
|
23414
|
+
central.setUint32(42, offset, true); // local header offset
|
|
23415
|
+
centralRecords.push(new Uint8Array(central.buffer), nameBytes);
|
|
23416
|
+
offset += 30 + nameBytes.length + file.data.length;
|
|
23417
|
+
}
|
|
23418
|
+
const centralSize = centralRecords.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
23419
|
+
const eocd = new DataView(new ArrayBuffer(22));
|
|
23420
|
+
eocd.setUint32(0, 0x06054b50, true);
|
|
23421
|
+
eocd.setUint16(4, 0, true);
|
|
23422
|
+
eocd.setUint16(6, 0, true);
|
|
23423
|
+
eocd.setUint16(8, files.length, true);
|
|
23424
|
+
eocd.setUint16(10, files.length, true);
|
|
23425
|
+
eocd.setUint32(12, centralSize, true);
|
|
23426
|
+
eocd.setUint32(16, offset, true);
|
|
23427
|
+
eocd.setUint16(20, 0, true);
|
|
23428
|
+
const result = new Uint8Array(offset + centralSize + 22);
|
|
23429
|
+
let pointer = 0;
|
|
23430
|
+
for (const chunk of chunks) {
|
|
23431
|
+
result.set(chunk, pointer);
|
|
23432
|
+
pointer += chunk.length;
|
|
23433
|
+
}
|
|
23434
|
+
for (const chunk of centralRecords) {
|
|
23435
|
+
result.set(chunk, pointer);
|
|
23436
|
+
pointer += chunk.length;
|
|
23437
|
+
}
|
|
23438
|
+
result.set(new Uint8Array(eocd.buffer), pointer);
|
|
23439
|
+
void centralDirectory;
|
|
23440
|
+
return result;
|
|
23441
|
+
}
|
|
23442
|
+
|
|
23443
|
+
/**
|
|
23444
|
+
* Triggers a browser download for the given Blob.
|
|
23445
|
+
*/
|
|
23446
|
+
function downloadBlob(blob, filename) {
|
|
23447
|
+
const url = URL.createObjectURL(blob);
|
|
23448
|
+
const a = document.createElement('a');
|
|
23449
|
+
a.href = url;
|
|
23450
|
+
a.download = filename;
|
|
23451
|
+
a.click();
|
|
23452
|
+
URL.revokeObjectURL(url);
|
|
23453
|
+
}
|
|
23454
|
+
const CSV_BOM = '\uFEFF';
|
|
23455
|
+
/**
|
|
23456
|
+
* Builds the raw CSV text (including the UTF-8 BOM) for the given header and
|
|
23457
|
+
* rows. Exposed separately from the downloader to keep it easy to test.
|
|
23458
|
+
*/
|
|
23459
|
+
function buildCsv(header, rows) {
|
|
23460
|
+
const escapeCell = (value) => {
|
|
23461
|
+
const text = value == null ? '' : String(value);
|
|
23462
|
+
return `"${text.replace(/"/g, '""')}"`;
|
|
23463
|
+
};
|
|
23464
|
+
const lines = [];
|
|
23465
|
+
lines.push(header.map(escapeCell).join(','));
|
|
23466
|
+
for (const row of rows) {
|
|
23467
|
+
lines.push(row.map(escapeCell).join(','));
|
|
23468
|
+
}
|
|
23469
|
+
return CSV_BOM + lines.join('\r\n');
|
|
23470
|
+
}
|
|
23471
|
+
/**
|
|
23472
|
+
* Serializes a grid/table to CSV and downloads it.
|
|
23473
|
+
*
|
|
23474
|
+
* A UTF-8 BOM is prepended so Microsoft Excel recognises the file as UTF-8
|
|
23475
|
+
* and renders accented characters correctly.
|
|
23476
|
+
*/
|
|
23477
|
+
function exportGridToCsv(header, rows, filename = 'export.csv') {
|
|
23478
|
+
const csv = buildCsv(header, rows);
|
|
23479
|
+
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
23480
|
+
downloadBlob(blob, filename);
|
|
23481
|
+
}
|
|
23482
|
+
/**
|
|
23483
|
+
* Serializes a grid/table to a real Open XML (.xlsx) spreadsheet and downloads it.
|
|
23484
|
+
*
|
|
23485
|
+
* Generates an actual xlsx package (not an HTML file with an .xls extension),
|
|
23486
|
+
* preserving UTF-8 characters. Comma-separated columns are exported as-is.
|
|
23487
|
+
*/
|
|
23488
|
+
function exportGridToExcel(header, rows, filename = 'export.xlsx') {
|
|
23489
|
+
const blob = buildXlsxBlob([{ name: 'Sheet1', rows: [header, ...rows] }]);
|
|
23490
|
+
downloadBlob(blob, filename);
|
|
23491
|
+
}
|
|
23492
|
+
|
|
22596
23493
|
let gridIdSequence = 0;
|
|
22597
23494
|
class JvGridComponent {
|
|
22598
23495
|
gridId = `jv-grid-${++gridIdSequence}`;
|
|
@@ -23104,41 +24001,20 @@ class JvGridComponent {
|
|
|
23104
24001
|
}
|
|
23105
24002
|
// Export
|
|
23106
24003
|
exportCsv() {
|
|
23107
|
-
const
|
|
23108
|
-
|
|
23109
|
-
const headerRow = cols.map(c => `"${c.header}"`).join(',');
|
|
23110
|
-
const dataRows = allData.map(row => cols.map(col => {
|
|
23111
|
-
const val = row[col.key];
|
|
23112
|
-
if (val == null)
|
|
23113
|
-
return '';
|
|
23114
|
-
return `"${this.getExportValue(val, col.column).replace(/"/g, '""')}"`;
|
|
23115
|
-
}).join(','));
|
|
23116
|
-
const csv = [headerRow, ...dataRows].join('\r\n');
|
|
23117
|
-
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
23118
|
-
const url = URL.createObjectURL(blob);
|
|
23119
|
-
const a = document.createElement('a');
|
|
23120
|
-
a.href = url;
|
|
23121
|
-
a.download = 'grid-export.csv';
|
|
23122
|
-
a.click();
|
|
23123
|
-
URL.revokeObjectURL(url);
|
|
24004
|
+
const { header, rows } = this.buildExportMatrix();
|
|
24005
|
+
exportGridToCsv(header, rows, 'grid-export.csv');
|
|
23124
24006
|
}
|
|
23125
24007
|
exportExcel() {
|
|
24008
|
+
const { header, rows } = this.buildExportMatrix();
|
|
24009
|
+
exportGridToExcel(header, rows, 'grid-export.xlsx');
|
|
24010
|
+
}
|
|
24011
|
+
buildExportMatrix() {
|
|
23126
24012
|
const cols = this.visibleColumns().map(c => ({ key: this.colKey(c), header: c.header, column: c }));
|
|
23127
24013
|
const allData = this.effectiveOptions().serverSide ? this.data() : this.sortedData();
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23132
|
-
html += '<tr>' + cols.map(col => `<td>${this.getExportValue(row[col.key], col.column)}</td>`).join('') + '</tr>';
|
|
23133
|
-
}
|
|
23134
|
-
html += '</tbody></table>';
|
|
23135
|
-
const blob = new Blob([html], { type: 'application/vnd.ms-excel' });
|
|
23136
|
-
const url = URL.createObjectURL(blob);
|
|
23137
|
-
const a = document.createElement('a');
|
|
23138
|
-
a.href = url;
|
|
23139
|
-
a.download = 'grid-export.xls';
|
|
23140
|
-
a.click();
|
|
23141
|
-
URL.revokeObjectURL(url);
|
|
24014
|
+
return {
|
|
24015
|
+
header: cols.map(c => c.header),
|
|
24016
|
+
rows: allData.map(row => cols.map(col => this.getExportValue(row[col.key], col.column))),
|
|
24017
|
+
};
|
|
23142
24018
|
}
|
|
23143
24019
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
23144
24020
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvGridComponent, isStandalone: true, selector: "jv-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", rowDoubleClick: "rowDoubleClick", actionClick: "actionClick", selectionChange: "selectionChange", pageChange: "pageChange", searchChange: "searchChange", sortChange: "sortChange", columnFilter: "columnFilter", columnResize: "columnResize", columnReorder: "columnReorder", rowEdit: "rowEdit" }, ngImport: i0, template: `
|
|
@@ -24010,5 +24886,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
24010
24886
|
* Generated bundle index. Do not edit.
|
|
24011
24887
|
*/
|
|
24012
24888
|
|
|
24013
|
-
export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAccordionComponent, JvAccordionPanelDirective, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvCodeComponent, JvCollapsiblePanelComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDatePickerComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvFileInputComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvFormatService, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, provideJvUi };
|
|
24889
|
+
export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAccordionComponent, JvAccordionPanelDirective, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvCodeComponent, JvCollapsiblePanelComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDatePickerComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvFileInputComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvFormatService, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvImageGalleryComponent, JvImageSliderComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, provideJvUi };
|
|
24014
24890
|
//# sourceMappingURL=devjuliovilla-jv-ui.mjs.map
|