ngx-radiant 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,721 @@
1
+ import { NgClass, DOCUMENT, isPlatformBrowser } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, output, inject, viewChild, signal, computed, effect, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, DestroyRef, PLATFORM_ID, createComponent, HostListener, Directive } from '@angular/core';
4
+ import { DomSanitizer } from '@angular/platform-browser';
5
+
6
+ const NGX_RADIANT_DEFAULT_CONFIG = {
7
+ ariaLabel: 'Image gallery lightbox',
8
+ closeOnEscape: true,
9
+ loop: true,
10
+ showThumbnails: true,
11
+ showCounter: true,
12
+ showNavigation: true,
13
+ zoomable: true,
14
+ initialZoom: 1,
15
+ minZoom: 1,
16
+ maxZoom: 3,
17
+ zoomStep: 0.25,
18
+ showZoomSlider: false,
19
+ iframeAspectRatio: '16 / 9',
20
+ iframeAutoplay: false,
21
+ iframeMuted: false,
22
+ iframeAllowedOrigins: [],
23
+ };
24
+ class NgxRadiantLightbox {
25
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
26
+ config = input(null, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
27
+ ariaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
28
+ closeOnEscape = input(undefined, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []));
29
+ loop = input(undefined, ...(ngDevMode ? [{ debugName: "loop" }] : /* istanbul ignore next */ []));
30
+ showThumbnails = input(undefined, ...(ngDevMode ? [{ debugName: "showThumbnails" }] : /* istanbul ignore next */ []));
31
+ showCounter = input(undefined, ...(ngDevMode ? [{ debugName: "showCounter" }] : /* istanbul ignore next */ []));
32
+ showNavigation = input(undefined, ...(ngDevMode ? [{ debugName: "showNavigation" }] : /* istanbul ignore next */ []));
33
+ zoomable = input(undefined, ...(ngDevMode ? [{ debugName: "zoomable" }] : /* istanbul ignore next */ []));
34
+ initialZoom = input(undefined, ...(ngDevMode ? [{ debugName: "initialZoom" }] : /* istanbul ignore next */ []));
35
+ minZoom = input(undefined, ...(ngDevMode ? [{ debugName: "minZoom" }] : /* istanbul ignore next */ []));
36
+ maxZoom = input(undefined, ...(ngDevMode ? [{ debugName: "maxZoom" }] : /* istanbul ignore next */ []));
37
+ zoomStep = input(undefined, ...(ngDevMode ? [{ debugName: "zoomStep" }] : /* istanbul ignore next */ []));
38
+ showZoomSlider = input(undefined, ...(ngDevMode ? [{ debugName: "showZoomSlider" }] : /* istanbul ignore next */ []));
39
+ iframeAspectRatio = input(undefined, ...(ngDevMode ? [{ debugName: "iframeAspectRatio" }] : /* istanbul ignore next */ []));
40
+ iframeAutoplay = input(undefined, ...(ngDevMode ? [{ debugName: "iframeAutoplay" }] : /* istanbul ignore next */ []));
41
+ iframeMuted = input(undefined, ...(ngDevMode ? [{ debugName: "iframeMuted" }] : /* istanbul ignore next */ []));
42
+ iframeAllowedOrigins = input(undefined, ...(ngDevMode ? [{ debugName: "iframeAllowedOrigins" }] : /* istanbul ignore next */ []));
43
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
44
+ index = input(0, ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
45
+ openChange = output();
46
+ indexChange = output();
47
+ closed = output();
48
+ sanitizer = inject(DomSanitizer);
49
+ zoomMedia = viewChild('zoomMedia', ...(ngDevMode ? [{ debugName: "zoomMedia" }] : /* istanbul ignore next */ []));
50
+ zoomLevel = signal(1, ...(ngDevMode ? [{ debugName: "zoomLevel" }] : /* istanbul ignore next */ []));
51
+ panX = signal(0, ...(ngDevMode ? [{ debugName: "panX" }] : /* istanbul ignore next */ []));
52
+ panY = signal(0, ...(ngDevMode ? [{ debugName: "panY" }] : /* istanbul ignore next */ []));
53
+ dragging = signal(false, ...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
54
+ dragStart = null;
55
+ resolvedConfig = computed(() => {
56
+ const config = this.config() ?? {};
57
+ return {
58
+ ...NGX_RADIANT_DEFAULT_CONFIG,
59
+ ...config,
60
+ ariaLabel: this.ariaLabel() ?? config.ariaLabel ?? NGX_RADIANT_DEFAULT_CONFIG.ariaLabel,
61
+ closeOnEscape: this.closeOnEscape() ?? config.closeOnEscape ?? NGX_RADIANT_DEFAULT_CONFIG.closeOnEscape,
62
+ loop: this.loop() ?? config.loop ?? NGX_RADIANT_DEFAULT_CONFIG.loop,
63
+ showThumbnails: this.showThumbnails() ?? config.showThumbnails ?? NGX_RADIANT_DEFAULT_CONFIG.showThumbnails,
64
+ showCounter: this.showCounter() ?? config.showCounter ?? NGX_RADIANT_DEFAULT_CONFIG.showCounter,
65
+ showNavigation: this.showNavigation() ?? config.showNavigation ?? NGX_RADIANT_DEFAULT_CONFIG.showNavigation,
66
+ zoomable: this.zoomable() ?? config.zoomable ?? NGX_RADIANT_DEFAULT_CONFIG.zoomable,
67
+ initialZoom: this.initialZoom() ?? config.initialZoom ?? NGX_RADIANT_DEFAULT_CONFIG.initialZoom,
68
+ minZoom: this.minZoom() ?? config.minZoom ?? NGX_RADIANT_DEFAULT_CONFIG.minZoom,
69
+ maxZoom: this.maxZoom() ?? config.maxZoom ?? NGX_RADIANT_DEFAULT_CONFIG.maxZoom,
70
+ zoomStep: this.zoomStep() ?? config.zoomStep ?? NGX_RADIANT_DEFAULT_CONFIG.zoomStep,
71
+ showZoomSlider: this.showZoomSlider() ?? config.showZoomSlider ?? NGX_RADIANT_DEFAULT_CONFIG.showZoomSlider,
72
+ iframeAspectRatio: this.iframeAspectRatio() ?? config.iframeAspectRatio ?? NGX_RADIANT_DEFAULT_CONFIG.iframeAspectRatio,
73
+ iframeAutoplay: this.iframeAutoplay() ?? config.iframeAutoplay ?? NGX_RADIANT_DEFAULT_CONFIG.iframeAutoplay,
74
+ iframeMuted: this.iframeMuted() ?? config.iframeMuted ?? NGX_RADIANT_DEFAULT_CONFIG.iframeMuted,
75
+ iframeAllowedOrigins: this.iframeAllowedOrigins() ?? config.iframeAllowedOrigins ?? NGX_RADIANT_DEFAULT_CONFIG.iframeAllowedOrigins,
76
+ };
77
+ }, ...(ngDevMode ? [{ debugName: "resolvedConfig" }] : /* istanbul ignore next */ []));
78
+ currentIndex = computed(() => this.normalizeIndex(this.index()), ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
79
+ currentItem = computed(() => this.items()[this.currentIndex()] ?? { src: '' }, ...(ngDevMode ? [{ debugName: "currentItem" }] : /* istanbul ignore next */ []));
80
+ canNavigate = computed(() => this.items().length > 1, ...(ngDevMode ? [{ debugName: "canNavigate" }] : /* istanbul ignore next */ []));
81
+ canZoom = computed(() => this.resolvedConfig().zoomable && (this.currentItem().type ?? 'image') === 'image', ...(ngDevMode ? [{ debugName: "canZoom" }] : /* istanbul ignore next */ []));
82
+ zoomPercent = computed(() => Math.round(this.zoomLevel() * 100), ...(ngDevMode ? [{ debugName: "zoomPercent" }] : /* istanbul ignore next */ []));
83
+ zoomTransform = computed(() => `translate(${this.panX()}px, ${this.panY()}px) scale(${this.zoomLevel()})`, ...(ngDevMode ? [{ debugName: "zoomTransform" }] : /* istanbul ignore next */ []));
84
+ resolvedMinZoom = computed(() => Math.max(this.resolvedConfig().minZoom, 0.1), ...(ngDevMode ? [{ debugName: "resolvedMinZoom" }] : /* istanbul ignore next */ []));
85
+ resolvedMaxZoom = computed(() => Math.max(this.resolvedConfig().maxZoom, this.resolvedMinZoom()), ...(ngDevMode ? [{ debugName: "resolvedMaxZoom" }] : /* istanbul ignore next */ []));
86
+ resolvedZoomStep = computed(() => Math.max(this.resolvedConfig().zoomStep, 0.01), ...(ngDevMode ? [{ debugName: "resolvedZoomStep" }] : /* istanbul ignore next */ []));
87
+ resolvedIframeAspectRatio = computed(() => this.resolvedConfig().iframeAspectRatio, ...(ngDevMode ? [{ debugName: "resolvedIframeAspectRatio" }] : /* istanbul ignore next */ []));
88
+ trustedFrameSrc = computed(() => this.sanitizer.bypassSecurityTrustResourceUrl(this.resolveFrameSrc()), ...(ngDevMode ? [{ debugName: "trustedFrameSrc" }] : /* istanbul ignore next */ []));
89
+ resolvedAriaLabel = computed(() => this.resolvedConfig().ariaLabel, ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
90
+ resolvedShowThumbnails = computed(() => this.resolvedConfig().showThumbnails, ...(ngDevMode ? [{ debugName: "resolvedShowThumbnails" }] : /* istanbul ignore next */ []));
91
+ showCounterControl = computed(() => this.resolvedConfig().showCounter, ...(ngDevMode ? [{ debugName: "showCounterControl" }] : /* istanbul ignore next */ []));
92
+ showNavigationControl = computed(() => this.resolvedConfig().showNavigation, ...(ngDevMode ? [{ debugName: "showNavigationControl" }] : /* istanbul ignore next */ []));
93
+ showZoomSliderControl = computed(() => this.resolvedConfig().showZoomSlider, ...(ngDevMode ? [{ debugName: "showZoomSliderControl" }] : /* istanbul ignore next */ []));
94
+ constructor() {
95
+ effect(() => {
96
+ this.open();
97
+ this.currentIndex();
98
+ this.currentItem().src;
99
+ const config = this.resolvedConfig();
100
+ this.zoomLevel.set(this.clampZoom(config.initialZoom));
101
+ this.resetPan();
102
+ });
103
+ }
104
+ openAt(index) {
105
+ this.indexChange.emit(this.normalizeIndex(index));
106
+ this.openChange.emit(this.items().length > 0);
107
+ }
108
+ close() {
109
+ if (!this.open()) {
110
+ return;
111
+ }
112
+ this.openChange.emit(false);
113
+ this.closed.emit();
114
+ }
115
+ next() {
116
+ this.goTo(this.currentIndex() + 1);
117
+ }
118
+ previous() {
119
+ this.goTo(this.currentIndex() - 1);
120
+ }
121
+ goTo(index) {
122
+ if (!this.canNavigate()) {
123
+ return;
124
+ }
125
+ const itemCount = this.items().length;
126
+ const nextIndex = this.resolvedConfig().loop
127
+ ? (index + itemCount) % itemCount
128
+ : Math.min(Math.max(index, 0), itemCount - 1);
129
+ this.indexChange.emit(nextIndex);
130
+ }
131
+ zoomIn() {
132
+ if (!this.canZoom()) {
133
+ return;
134
+ }
135
+ this.setZoom(this.zoomLevel() + this.resolvedZoomStep());
136
+ }
137
+ zoomOut() {
138
+ if (!this.canZoom()) {
139
+ return;
140
+ }
141
+ this.setZoom(this.zoomLevel() - this.resolvedZoomStep());
142
+ }
143
+ resetZoom() {
144
+ this.zoomLevel.set(this.clampZoom(this.resolvedConfig().initialZoom));
145
+ this.resetPan();
146
+ }
147
+ setZoomFromInput(event) {
148
+ const input = event.target;
149
+ this.setZoom(Number(input.value));
150
+ }
151
+ setZoom(zoom) {
152
+ const nextZoom = this.clampZoom(zoom);
153
+ this.zoomLevel.set(nextZoom);
154
+ if (nextZoom <= this.resolvedMinZoom()) {
155
+ this.resetPan();
156
+ return;
157
+ }
158
+ this.clampCurrentPan();
159
+ }
160
+ toggleZoom() {
161
+ if (!this.canZoom()) {
162
+ return;
163
+ }
164
+ this.setZoom(this.zoomLevel() > this.resolvedMinZoom() ? this.resolvedMinZoom() : this.resolvedMaxZoom());
165
+ }
166
+ startPan(event) {
167
+ if (!this.canPan()) {
168
+ return;
169
+ }
170
+ event.preventDefault();
171
+ event.currentTarget.setPointerCapture(event.pointerId);
172
+ this.dragStart = {
173
+ pointerId: event.pointerId,
174
+ x: event.clientX,
175
+ y: event.clientY,
176
+ panX: this.panX(),
177
+ panY: this.panY(),
178
+ };
179
+ this.dragging.set(true);
180
+ }
181
+ movePan(event) {
182
+ if (!this.dragStart || this.dragStart.pointerId !== event.pointerId) {
183
+ return;
184
+ }
185
+ event.preventDefault();
186
+ const bounds = this.getPanBounds(event.currentTarget);
187
+ this.panX.set(this.clamp(this.dragStart.panX + event.clientX - this.dragStart.x, -bounds.x, bounds.x));
188
+ this.panY.set(this.clamp(this.dragStart.panY + event.clientY - this.dragStart.y, -bounds.y, bounds.y));
189
+ }
190
+ endPan(event) {
191
+ if (this.dragStart?.pointerId === event.pointerId) {
192
+ this.dragStart = null;
193
+ this.dragging.set(false);
194
+ }
195
+ }
196
+ handleKeydown(event) {
197
+ if (event.key === 'Escape' && this.resolvedConfig().closeOnEscape) {
198
+ event.preventDefault();
199
+ this.close();
200
+ return;
201
+ }
202
+ if (event.key === 'ArrowRight') {
203
+ event.preventDefault();
204
+ this.next();
205
+ return;
206
+ }
207
+ if (event.key === 'ArrowLeft') {
208
+ event.preventDefault();
209
+ this.previous();
210
+ return;
211
+ }
212
+ if (event.key === '+' || event.key === '=') {
213
+ event.preventDefault();
214
+ this.zoomIn();
215
+ return;
216
+ }
217
+ if (event.key === '-' || event.key === '_') {
218
+ event.preventDefault();
219
+ this.zoomOut();
220
+ return;
221
+ }
222
+ if (event.key === '0') {
223
+ event.preventDefault();
224
+ this.resetZoom();
225
+ }
226
+ }
227
+ resolveFrameSrc() {
228
+ const config = this.resolvedConfig();
229
+ const source = this.currentItem().src;
230
+ const baseHref = this.getBaseHref();
231
+ try {
232
+ const url = new URL(source, baseHref);
233
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
234
+ return 'about:blank';
235
+ }
236
+ if (config.iframeAllowedOrigins.length > 0 && !config.iframeAllowedOrigins.includes(url.origin)) {
237
+ return 'about:blank';
238
+ }
239
+ if (config.iframeAutoplay) {
240
+ url.searchParams.set('autoplay', '1');
241
+ }
242
+ if (config.iframeMuted) {
243
+ url.searchParams.set('mute', '1');
244
+ url.searchParams.set('muted', '1');
245
+ }
246
+ return this.isRelativeUrl(source) ? `${url.pathname}${url.search}${url.hash}` : url.toString();
247
+ }
248
+ catch {
249
+ return 'about:blank';
250
+ }
251
+ }
252
+ getBaseHref() {
253
+ return typeof globalThis.location !== 'undefined' ? globalThis.location.origin : 'http://localhost';
254
+ }
255
+ isRelativeUrl(url) {
256
+ return !/^[a-z][a-z\d+.-]*:/i.test(url) && !url.startsWith('//');
257
+ }
258
+ clampCurrentPan() {
259
+ if (!this.open()) {
260
+ return;
261
+ }
262
+ const media = this.zoomMedia()?.nativeElement;
263
+ if (!media) {
264
+ return;
265
+ }
266
+ const bounds = this.getPanBounds(media);
267
+ this.panX.set(this.clamp(this.panX(), -bounds.x, bounds.x));
268
+ this.panY.set(this.clamp(this.panY(), -bounds.y, bounds.y));
269
+ }
270
+ getPanBounds(media) {
271
+ const stage = media.parentElement;
272
+ const zoom = this.zoomLevel();
273
+ const mediaWidth = media.clientWidth || media.getBoundingClientRect().width;
274
+ const mediaHeight = media.clientHeight || media.getBoundingClientRect().height;
275
+ const stageWidth = stage?.clientWidth || mediaWidth;
276
+ const stageHeight = stage?.clientHeight || mediaHeight;
277
+ return {
278
+ x: Math.max((mediaWidth * zoom - stageWidth) / 2, 0),
279
+ y: Math.max((mediaHeight * zoom - stageHeight) / 2, 0),
280
+ };
281
+ }
282
+ clamp(value, min, max) {
283
+ return Math.min(Math.max(value, min), max);
284
+ }
285
+ normalizeIndex(index) {
286
+ const itemCount = this.items().length;
287
+ if (itemCount === 0) {
288
+ return 0;
289
+ }
290
+ return Math.min(Math.max(Math.trunc(index) || 0, 0), itemCount - 1);
291
+ }
292
+ clampZoom(zoom) {
293
+ const minZoom = this.resolvedMinZoom();
294
+ const maxZoom = this.resolvedMaxZoom();
295
+ return Math.min(Math.max(Number.isFinite(zoom) ? zoom : this.resolvedConfig().initialZoom, minZoom), maxZoom);
296
+ }
297
+ canPan() {
298
+ return this.canZoom() && this.zoomLevel() > this.resolvedMinZoom();
299
+ }
300
+ resetPan() {
301
+ this.panX.set(0);
302
+ this.panY.set(0);
303
+ this.dragStart = null;
304
+ this.dragging.set(false);
305
+ }
306
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NgxRadiantLightbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
307
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: NgxRadiantLightbox, isStandalone: true, selector: "ngx-radiant-lightbox", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, showThumbnails: { classPropertyName: "showThumbnails", publicName: "showThumbnails", isSignal: true, isRequired: false, transformFunction: null }, showCounter: { classPropertyName: "showCounter", publicName: "showCounter", isSignal: true, isRequired: false, transformFunction: null }, showNavigation: { classPropertyName: "showNavigation", publicName: "showNavigation", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, initialZoom: { classPropertyName: "initialZoom", publicName: "initialZoom", isSignal: true, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: true, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: true, isRequired: false, transformFunction: null }, zoomStep: { classPropertyName: "zoomStep", publicName: "zoomStep", isSignal: true, isRequired: false, transformFunction: null }, showZoomSlider: { classPropertyName: "showZoomSlider", publicName: "showZoomSlider", isSignal: true, isRequired: false, transformFunction: null }, iframeAspectRatio: { classPropertyName: "iframeAspectRatio", publicName: "iframeAspectRatio", isSignal: true, isRequired: false, transformFunction: null }, iframeAutoplay: { classPropertyName: "iframeAutoplay", publicName: "iframeAutoplay", isSignal: true, isRequired: false, transformFunction: null }, iframeMuted: { classPropertyName: "iframeMuted", publicName: "iframeMuted", isSignal: true, isRequired: false, transformFunction: null }, iframeAllowedOrigins: { classPropertyName: "iframeAllowedOrigins", publicName: "iframeAllowedOrigins", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openChange: "openChange", indexChange: "indexChange", closed: "closed" }, host: { properties: { "class.ngx-radiant-host-open": "open()" } }, viewQueries: [{ propertyName: "zoomMedia", first: true, predicate: ["zoomMedia"], descendants: true, isSignal: true }], ngImport: i0, template: `
308
+ @if (open()) {
309
+ <div class="ngx-radiant" role="dialog" aria-modal="true" [attr.aria-label]="resolvedAriaLabel()">
310
+ <button
311
+ type="button"
312
+ class="ngx-radiant__backdrop"
313
+ aria-label="Close lightbox"
314
+ (click)="close()"
315
+ ></button>
316
+
317
+ <div class="ngx-radiant__shell" (keydown)="handleKeydown($event)" tabindex="-1">
318
+ <header class="ngx-radiant__toolbar">
319
+ <div class="ngx-radiant__toolbar-start">
320
+ @if (showCounterControl() && canNavigate()) {
321
+ <div class="ngx-radiant__counter" aria-live="polite">
322
+ {{ currentIndex() + 1 }} / {{ items().length }}
323
+ </div>
324
+ }
325
+ </div>
326
+
327
+ <div class="ngx-radiant__toolbar-actions">
328
+ @if (canZoom()) {
329
+ <div class="ngx-radiant__zoom-controls" aria-label="Image zoom controls">
330
+ <button type="button" class="ngx-radiant__button" aria-label="Zoom out" (click)="zoomOut()">−</button>
331
+ <button type="button" class="ngx-radiant__zoom-value" aria-label="Reset zoom" (click)="resetZoom()">
332
+ {{ zoomPercent() }}%
333
+ </button>
334
+ <button type="button" class="ngx-radiant__button" aria-label="Zoom in" (click)="zoomIn()">+</button>
335
+ @if (showZoomSliderControl()) {
336
+ <label class="ngx-radiant__zoom-slider-label">
337
+ <span>Zoom</span>
338
+ <input
339
+ class="ngx-radiant__zoom-slider"
340
+ type="range"
341
+ [min]="resolvedMinZoom()"
342
+ [max]="resolvedMaxZoom()"
343
+ [step]="resolvedZoomStep()"
344
+ [value]="zoomLevel()"
345
+ aria-label="Zoom level"
346
+ (input)="setZoomFromInput($event)"
347
+ />
348
+ </label>
349
+ }
350
+ </div>
351
+ }
352
+
353
+ <button type="button" class="ngx-radiant__button" aria-label="Close" (click)="close()">
354
+ ×
355
+ </button>
356
+ </div>
357
+ </header>
358
+
359
+ @if (showNavigationControl() && canNavigate()) {
360
+ <button
361
+ type="button"
362
+ class="ngx-radiant__nav ngx-radiant__nav--prev"
363
+ aria-label="Previous item"
364
+ (click)="previous()"
365
+ >
366
+
367
+ </button>
368
+ }
369
+
370
+ <figure class="ngx-radiant__stage">
371
+ @switch (currentItem().type ?? 'image') {
372
+ @case ('video') {
373
+ <video class="ngx-radiant__media" [src]="currentItem().src" controls playsinline></video>
374
+ }
375
+ @case ('iframe') {
376
+ <iframe
377
+ class="ngx-radiant__frame"
378
+ [src]="trustedFrameSrc()"
379
+ [style.aspect-ratio]="resolvedIframeAspectRatio()"
380
+ title="Ngx Radiant embedded content"
381
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
382
+ allowfullscreen
383
+ ></iframe>
384
+ }
385
+ @default {
386
+ <img
387
+ #zoomMedia
388
+ class="ngx-radiant__media"
389
+ [style.transform]="zoomTransform()"
390
+ [class.ngx-radiant__media--zoomed]="zoomLevel() > 1"
391
+ [class.ngx-radiant__media--dragging]="dragging()"
392
+ [src]="currentItem().src"
393
+ [alt]="currentItem().alt ?? currentItem().caption ?? ''"
394
+ draggable="false"
395
+ (dblclick)="toggleZoom()"
396
+ (pointerdown)="startPan($event)"
397
+ (pointermove)="movePan($event)"
398
+ (pointerup)="endPan($event)"
399
+ (pointercancel)="endPan($event)"
400
+ (lostpointercapture)="endPan($event)"
401
+ />
402
+ }
403
+ }
404
+
405
+ @if (currentItem().caption) {
406
+ <figcaption class="ngx-radiant__caption">{{ currentItem().caption }}</figcaption>
407
+ }
408
+ </figure>
409
+
410
+ @if (showNavigationControl() && canNavigate()) {
411
+ <button
412
+ type="button"
413
+ class="ngx-radiant__nav ngx-radiant__nav--next"
414
+ aria-label="Next item"
415
+ (click)="next()"
416
+ >
417
+
418
+ </button>
419
+ }
420
+
421
+ @if (resolvedShowThumbnails() && items().length > 1) {
422
+ <footer class="ngx-radiant__thumbs" aria-label="Lightbox thumbnails">
423
+ @for (item of items(); track item.src; let index = $index) {
424
+ <button
425
+ type="button"
426
+ class="ngx-radiant__thumb"
427
+ [ngClass]="{ 'ngx-radiant__thumb--active': index === currentIndex() }"
428
+ [attr.aria-label]="'Open item ' + (index + 1)"
429
+ [attr.aria-current]="index === currentIndex() ? 'true' : null"
430
+ (click)="goTo(index)"
431
+ >
432
+ <img [src]="item.thumb ?? item.src" [alt]="item.alt ?? ''" />
433
+ </button>
434
+ }
435
+ </footer>
436
+ }
437
+ </div>
438
+ </div>
439
+ }
440
+ `, isInline: true, styles: [":host{display:contents}.ngx-radiant{position:fixed;inset:0;z-index:var(--ngx-radiant-z-index, 1000);display:grid;place-items:center;color:#fff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ngx-radiant__backdrop{position:absolute;inset:0;border:0;background:color-mix(in srgb,#020617 88%,transparent);cursor:zoom-out}.ngx-radiant__shell{position:relative;z-index:1;display:grid;grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:auto minmax(0,1fr) auto;width:min(94vw,1180px);height:min(92vh,840px);gap:1rem;outline:none}.ngx-radiant__toolbar{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:1rem}.ngx-radiant__toolbar-start,.ngx-radiant__toolbar-actions,.ngx-radiant__zoom-controls{display:flex;align-items:center;gap:.5rem}.ngx-radiant__zoom-slider-label{display:inline-flex;align-items:center;gap:.5rem;border:1px solid rgb(255 255 255 / 18%);border-radius:999px;background:#0f172ab8;padding:.35rem .65rem;box-shadow:0 18px 50px #00000047;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.ngx-radiant__zoom-slider-label span{font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.ngx-radiant__zoom-slider{width:min(22vw,12rem);accent-color:var(--ngx-radiant-accent, #67e8f9)}.ngx-radiant__toolbar-start{min-width:0}.ngx-radiant__toolbar-actions{margin-left:auto}.ngx-radiant__counter,.ngx-radiant__button,.ngx-radiant__zoom-value,.ngx-radiant__nav{border:1px solid rgb(255 255 255 / 18%);border-radius:999px;background:#0f172ab8;color:inherit;box-shadow:0 18px 50px #00000047;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.ngx-radiant__counter,.ngx-radiant__zoom-value{padding:.55rem .9rem;font-size:.86rem;letter-spacing:.08em;text-transform:uppercase}.ngx-radiant__button,.ngx-radiant__zoom-value,.ngx-radiant__nav{cursor:pointer;transition:transform .16s ease,background .16s ease,opacity .16s ease}.ngx-radiant__button{width:2.6rem;height:2.6rem;font-size:1.35rem;line-height:1}.ngx-radiant__zoom-value{min-width:4.4rem}.ngx-radiant__nav{align-self:center;width:3rem;height:3rem;border:0;font-size:2.6rem;line-height:1}.ngx-radiant__button:hover,.ngx-radiant__zoom-value:hover,.ngx-radiant__nav:hover,.ngx-radiant__thumb:hover{transform:translateY(-1px);background:#1e293bdb}.ngx-radiant__stage{grid-column:2;grid-row:2;display:grid;place-items:center;overflow:hidden;min-width:0;min-height:0;margin:0}.ngx-radiant__media,.ngx-radiant__frame{max-width:100%;max-height:100%;border:0;border-radius:1.25rem;background:#020617;box-shadow:0 28px 80px #0000006b}.ngx-radiant__media{transform-origin:center center;transition:transform .18s ease}.ngx-radiant__media--zoomed{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.ngx-radiant__media--dragging{cursor:grabbing;transition:none}.ngx-radiant__frame{width:min(100%,960px);height:auto;max-height:100%}.ngx-radiant__caption{justify-self:center;max-width:min(80ch,84vw);margin-top:.85rem;padding:.6rem .9rem;border-radius:999px;background:#0f172aad;color:#e2e8f0;text-align:center;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.ngx-radiant__thumbs{grid-column:1 / -1;display:flex;justify-content:center;gap:.5rem;overflow-x:auto;padding:.25rem}.ngx-radiant__thumb{width:4rem;height:3rem;flex:0 0 auto;overflow:hidden;border:1px solid rgb(255 255 255 / 14%);border-radius:.7rem;background:#0f172ab3;cursor:pointer;opacity:.68;padding:0;transition:.16s ease}.ngx-radiant__thumb--active{border-color:var(--ngx-radiant-accent, #67e8f9);box-shadow:0 0 0 2px color-mix(in srgb,var(--ngx-radiant-accent, #67e8f9) 30%,transparent);opacity:1}.ngx-radiant__thumb img{width:100%;height:100%;object-fit:cover;display:block}@media(max-width:720px){.ngx-radiant__shell{width:96vw;height:94vh;grid-template-columns:1fr 1fr}.ngx-radiant__toolbar{align-items:flex-start}.ngx-radiant__toolbar-actions,.ngx-radiant__zoom-controls{flex-wrap:wrap;justify-content:flex-end}.ngx-radiant__zoom-slider{width:8rem}.ngx-radiant__stage{grid-column:1 / -1}.ngx-radiant__nav{grid-row:3;justify-self:center}.ngx-radiant__nav--prev{grid-column:1}.ngx-radiant__nav--next{grid-column:2}.ngx-radiant__thumbs{display:none}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
441
+ }
442
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NgxRadiantLightbox, decorators: [{
443
+ type: Component,
444
+ args: [{ selector: 'ngx-radiant-lightbox', imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, host: {
445
+ '[class.ngx-radiant-host-open]': 'open()',
446
+ }, template: `
447
+ @if (open()) {
448
+ <div class="ngx-radiant" role="dialog" aria-modal="true" [attr.aria-label]="resolvedAriaLabel()">
449
+ <button
450
+ type="button"
451
+ class="ngx-radiant__backdrop"
452
+ aria-label="Close lightbox"
453
+ (click)="close()"
454
+ ></button>
455
+
456
+ <div class="ngx-radiant__shell" (keydown)="handleKeydown($event)" tabindex="-1">
457
+ <header class="ngx-radiant__toolbar">
458
+ <div class="ngx-radiant__toolbar-start">
459
+ @if (showCounterControl() && canNavigate()) {
460
+ <div class="ngx-radiant__counter" aria-live="polite">
461
+ {{ currentIndex() + 1 }} / {{ items().length }}
462
+ </div>
463
+ }
464
+ </div>
465
+
466
+ <div class="ngx-radiant__toolbar-actions">
467
+ @if (canZoom()) {
468
+ <div class="ngx-radiant__zoom-controls" aria-label="Image zoom controls">
469
+ <button type="button" class="ngx-radiant__button" aria-label="Zoom out" (click)="zoomOut()">−</button>
470
+ <button type="button" class="ngx-radiant__zoom-value" aria-label="Reset zoom" (click)="resetZoom()">
471
+ {{ zoomPercent() }}%
472
+ </button>
473
+ <button type="button" class="ngx-radiant__button" aria-label="Zoom in" (click)="zoomIn()">+</button>
474
+ @if (showZoomSliderControl()) {
475
+ <label class="ngx-radiant__zoom-slider-label">
476
+ <span>Zoom</span>
477
+ <input
478
+ class="ngx-radiant__zoom-slider"
479
+ type="range"
480
+ [min]="resolvedMinZoom()"
481
+ [max]="resolvedMaxZoom()"
482
+ [step]="resolvedZoomStep()"
483
+ [value]="zoomLevel()"
484
+ aria-label="Zoom level"
485
+ (input)="setZoomFromInput($event)"
486
+ />
487
+ </label>
488
+ }
489
+ </div>
490
+ }
491
+
492
+ <button type="button" class="ngx-radiant__button" aria-label="Close" (click)="close()">
493
+ ×
494
+ </button>
495
+ </div>
496
+ </header>
497
+
498
+ @if (showNavigationControl() && canNavigate()) {
499
+ <button
500
+ type="button"
501
+ class="ngx-radiant__nav ngx-radiant__nav--prev"
502
+ aria-label="Previous item"
503
+ (click)="previous()"
504
+ >
505
+
506
+ </button>
507
+ }
508
+
509
+ <figure class="ngx-radiant__stage">
510
+ @switch (currentItem().type ?? 'image') {
511
+ @case ('video') {
512
+ <video class="ngx-radiant__media" [src]="currentItem().src" controls playsinline></video>
513
+ }
514
+ @case ('iframe') {
515
+ <iframe
516
+ class="ngx-radiant__frame"
517
+ [src]="trustedFrameSrc()"
518
+ [style.aspect-ratio]="resolvedIframeAspectRatio()"
519
+ title="Ngx Radiant embedded content"
520
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
521
+ allowfullscreen
522
+ ></iframe>
523
+ }
524
+ @default {
525
+ <img
526
+ #zoomMedia
527
+ class="ngx-radiant__media"
528
+ [style.transform]="zoomTransform()"
529
+ [class.ngx-radiant__media--zoomed]="zoomLevel() > 1"
530
+ [class.ngx-radiant__media--dragging]="dragging()"
531
+ [src]="currentItem().src"
532
+ [alt]="currentItem().alt ?? currentItem().caption ?? ''"
533
+ draggable="false"
534
+ (dblclick)="toggleZoom()"
535
+ (pointerdown)="startPan($event)"
536
+ (pointermove)="movePan($event)"
537
+ (pointerup)="endPan($event)"
538
+ (pointercancel)="endPan($event)"
539
+ (lostpointercapture)="endPan($event)"
540
+ />
541
+ }
542
+ }
543
+
544
+ @if (currentItem().caption) {
545
+ <figcaption class="ngx-radiant__caption">{{ currentItem().caption }}</figcaption>
546
+ }
547
+ </figure>
548
+
549
+ @if (showNavigationControl() && canNavigate()) {
550
+ <button
551
+ type="button"
552
+ class="ngx-radiant__nav ngx-radiant__nav--next"
553
+ aria-label="Next item"
554
+ (click)="next()"
555
+ >
556
+
557
+ </button>
558
+ }
559
+
560
+ @if (resolvedShowThumbnails() && items().length > 1) {
561
+ <footer class="ngx-radiant__thumbs" aria-label="Lightbox thumbnails">
562
+ @for (item of items(); track item.src; let index = $index) {
563
+ <button
564
+ type="button"
565
+ class="ngx-radiant__thumb"
566
+ [ngClass]="{ 'ngx-radiant__thumb--active': index === currentIndex() }"
567
+ [attr.aria-label]="'Open item ' + (index + 1)"
568
+ [attr.aria-current]="index === currentIndex() ? 'true' : null"
569
+ (click)="goTo(index)"
570
+ >
571
+ <img [src]="item.thumb ?? item.src" [alt]="item.alt ?? ''" />
572
+ </button>
573
+ }
574
+ </footer>
575
+ }
576
+ </div>
577
+ </div>
578
+ }
579
+ `, styles: [":host{display:contents}.ngx-radiant{position:fixed;inset:0;z-index:var(--ngx-radiant-z-index, 1000);display:grid;place-items:center;color:#fff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ngx-radiant__backdrop{position:absolute;inset:0;border:0;background:color-mix(in srgb,#020617 88%,transparent);cursor:zoom-out}.ngx-radiant__shell{position:relative;z-index:1;display:grid;grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:auto minmax(0,1fr) auto;width:min(94vw,1180px);height:min(92vh,840px);gap:1rem;outline:none}.ngx-radiant__toolbar{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:1rem}.ngx-radiant__toolbar-start,.ngx-radiant__toolbar-actions,.ngx-radiant__zoom-controls{display:flex;align-items:center;gap:.5rem}.ngx-radiant__zoom-slider-label{display:inline-flex;align-items:center;gap:.5rem;border:1px solid rgb(255 255 255 / 18%);border-radius:999px;background:#0f172ab8;padding:.35rem .65rem;box-shadow:0 18px 50px #00000047;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.ngx-radiant__zoom-slider-label span{font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.ngx-radiant__zoom-slider{width:min(22vw,12rem);accent-color:var(--ngx-radiant-accent, #67e8f9)}.ngx-radiant__toolbar-start{min-width:0}.ngx-radiant__toolbar-actions{margin-left:auto}.ngx-radiant__counter,.ngx-radiant__button,.ngx-radiant__zoom-value,.ngx-radiant__nav{border:1px solid rgb(255 255 255 / 18%);border-radius:999px;background:#0f172ab8;color:inherit;box-shadow:0 18px 50px #00000047;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.ngx-radiant__counter,.ngx-radiant__zoom-value{padding:.55rem .9rem;font-size:.86rem;letter-spacing:.08em;text-transform:uppercase}.ngx-radiant__button,.ngx-radiant__zoom-value,.ngx-radiant__nav{cursor:pointer;transition:transform .16s ease,background .16s ease,opacity .16s ease}.ngx-radiant__button{width:2.6rem;height:2.6rem;font-size:1.35rem;line-height:1}.ngx-radiant__zoom-value{min-width:4.4rem}.ngx-radiant__nav{align-self:center;width:3rem;height:3rem;border:0;font-size:2.6rem;line-height:1}.ngx-radiant__button:hover,.ngx-radiant__zoom-value:hover,.ngx-radiant__nav:hover,.ngx-radiant__thumb:hover{transform:translateY(-1px);background:#1e293bdb}.ngx-radiant__stage{grid-column:2;grid-row:2;display:grid;place-items:center;overflow:hidden;min-width:0;min-height:0;margin:0}.ngx-radiant__media,.ngx-radiant__frame{max-width:100%;max-height:100%;border:0;border-radius:1.25rem;background:#020617;box-shadow:0 28px 80px #0000006b}.ngx-radiant__media{transform-origin:center center;transition:transform .18s ease}.ngx-radiant__media--zoomed{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.ngx-radiant__media--dragging{cursor:grabbing;transition:none}.ngx-radiant__frame{width:min(100%,960px);height:auto;max-height:100%}.ngx-radiant__caption{justify-self:center;max-width:min(80ch,84vw);margin-top:.85rem;padding:.6rem .9rem;border-radius:999px;background:#0f172aad;color:#e2e8f0;text-align:center;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.ngx-radiant__thumbs{grid-column:1 / -1;display:flex;justify-content:center;gap:.5rem;overflow-x:auto;padding:.25rem}.ngx-radiant__thumb{width:4rem;height:3rem;flex:0 0 auto;overflow:hidden;border:1px solid rgb(255 255 255 / 14%);border-radius:.7rem;background:#0f172ab3;cursor:pointer;opacity:.68;padding:0;transition:.16s ease}.ngx-radiant__thumb--active{border-color:var(--ngx-radiant-accent, #67e8f9);box-shadow:0 0 0 2px color-mix(in srgb,var(--ngx-radiant-accent, #67e8f9) 30%,transparent);opacity:1}.ngx-radiant__thumb img{width:100%;height:100%;object-fit:cover;display:block}@media(max-width:720px){.ngx-radiant__shell{width:96vw;height:94vh;grid-template-columns:1fr 1fr}.ngx-radiant__toolbar{align-items:flex-start}.ngx-radiant__toolbar-actions,.ngx-radiant__zoom-controls{flex-wrap:wrap;justify-content:flex-end}.ngx-radiant__zoom-slider{width:8rem}.ngx-radiant__stage{grid-column:1 / -1}.ngx-radiant__nav{grid-row:3;justify-self:center}.ngx-radiant__nav--prev{grid-column:1}.ngx-radiant__nav--next{grid-column:2}.ngx-radiant__thumbs{display:none}}\n"] }]
580
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], showThumbnails: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThumbnails", required: false }] }], showCounter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCounter", required: false }] }], showNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNavigation", required: false }] }], zoomable: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomable", required: false }] }], initialZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialZoom", required: false }] }], minZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "minZoom", required: false }] }], maxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxZoom", required: false }] }], zoomStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomStep", required: false }] }], showZoomSlider: [{ type: i0.Input, args: [{ isSignal: true, alias: "showZoomSlider", required: false }] }], iframeAspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "iframeAspectRatio", required: false }] }], iframeAutoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "iframeAutoplay", required: false }] }], iframeMuted: [{ type: i0.Input, args: [{ isSignal: true, alias: "iframeMuted", required: false }] }], iframeAllowedOrigins: [{ type: i0.Input, args: [{ isSignal: true, alias: "iframeAllowedOrigins", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }], openChange: [{ type: i0.Output, args: ["openChange"] }], indexChange: [{ type: i0.Output, args: ["indexChange"] }], closed: [{ type: i0.Output, args: ["closed"] }], zoomMedia: [{ type: i0.ViewChild, args: ['zoomMedia', { isSignal: true }] }] } });
581
+
582
+ class NgxRadiantDirective {
583
+ ngxRadiant = input(null, ...(ngDevMode ? [{ debugName: "ngxRadiant" }] : /* istanbul ignore next */ []));
584
+ radiantItems = input(null, ...(ngDevMode ? [{ debugName: "radiantItems" }] : /* istanbul ignore next */ []));
585
+ radiantIndex = input(0, ...(ngDevMode ? [{ debugName: "radiantIndex" }] : /* istanbul ignore next */ []));
586
+ radiantType = input(undefined, ...(ngDevMode ? [{ debugName: "radiantType" }] : /* istanbul ignore next */ []));
587
+ radiantAlt = input(undefined, ...(ngDevMode ? [{ debugName: "radiantAlt" }] : /* istanbul ignore next */ []));
588
+ radiantCaption = input(undefined, ...(ngDevMode ? [{ debugName: "radiantCaption" }] : /* istanbul ignore next */ []));
589
+ radiantThumb = input(undefined, ...(ngDevMode ? [{ debugName: "radiantThumb" }] : /* istanbul ignore next */ []));
590
+ radiantAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "radiantAriaLabel" }] : /* istanbul ignore next */ []));
591
+ radiantConfig = input(null, ...(ngDevMode ? [{ debugName: "radiantConfig" }] : /* istanbul ignore next */ []));
592
+ radiantCloseOnEscape = input(undefined, ...(ngDevMode ? [{ debugName: "radiantCloseOnEscape" }] : /* istanbul ignore next */ []));
593
+ radiantLoop = input(undefined, ...(ngDevMode ? [{ debugName: "radiantLoop" }] : /* istanbul ignore next */ []));
594
+ radiantShowThumbnails = input(undefined, ...(ngDevMode ? [{ debugName: "radiantShowThumbnails" }] : /* istanbul ignore next */ []));
595
+ appRef = inject(ApplicationRef);
596
+ environmentInjector = inject(EnvironmentInjector);
597
+ document = inject(DOCUMENT);
598
+ destroyRef = inject(DestroyRef);
599
+ platformId = inject(PLATFORM_ID);
600
+ lightboxRef = null;
601
+ hostElement = null;
602
+ subscriptions = [];
603
+ activeIndex = 0;
604
+ constructor() {
605
+ this.destroyRef.onDestroy(() => this.destroyLightbox());
606
+ }
607
+ open(event) {
608
+ event?.preventDefault();
609
+ const items = this.resolveItems();
610
+ if (!items.length || !isPlatformBrowser(this.platformId)) {
611
+ return;
612
+ }
613
+ this.activeIndex = this.normalizeIndex(this.radiantIndex(), items.length);
614
+ const lightboxRef = this.ensureLightbox();
615
+ this.syncLightboxInputs(lightboxRef, items, true);
616
+ }
617
+ close() {
618
+ this.destroyLightbox();
619
+ }
620
+ ensureLightbox() {
621
+ if (this.lightboxRef) {
622
+ return this.lightboxRef;
623
+ }
624
+ this.hostElement = this.document.createElement('ngx-radiant-overlay');
625
+ this.document.body.appendChild(this.hostElement);
626
+ this.lightboxRef = createComponent(NgxRadiantLightbox, {
627
+ environmentInjector: this.environmentInjector,
628
+ hostElement: this.hostElement,
629
+ });
630
+ this.appRef.attachView(this.lightboxRef.hostView);
631
+ this.subscriptions = [
632
+ this.lightboxRef.instance.indexChange.subscribe((index) => {
633
+ this.activeIndex = index;
634
+ this.lightboxRef?.setInput('index', index);
635
+ this.lightboxRef?.changeDetectorRef.detectChanges();
636
+ }),
637
+ this.lightboxRef.instance.openChange.subscribe((open) => {
638
+ if (!open) {
639
+ this.destroyLightbox();
640
+ }
641
+ }),
642
+ this.lightboxRef.instance.closed.subscribe(() => this.destroyLightbox()),
643
+ ];
644
+ return this.lightboxRef;
645
+ }
646
+ syncLightboxInputs(lightboxRef, items, open) {
647
+ lightboxRef.setInput('items', items);
648
+ lightboxRef.setInput('index', this.activeIndex);
649
+ lightboxRef.setInput('open', open);
650
+ lightboxRef.setInput('config', this.radiantConfig());
651
+ lightboxRef.setInput('ariaLabel', this.radiantAriaLabel());
652
+ lightboxRef.setInput('closeOnEscape', this.radiantCloseOnEscape());
653
+ lightboxRef.setInput('loop', this.radiantLoop());
654
+ lightboxRef.setInput('showThumbnails', this.radiantShowThumbnails());
655
+ lightboxRef.changeDetectorRef.detectChanges();
656
+ }
657
+ destroyLightbox() {
658
+ for (const subscription of this.subscriptions) {
659
+ subscription.unsubscribe();
660
+ }
661
+ this.subscriptions = [];
662
+ if (this.lightboxRef) {
663
+ this.appRef.detachView(this.lightboxRef.hostView);
664
+ this.lightboxRef.destroy();
665
+ this.lightboxRef = null;
666
+ }
667
+ this.hostElement?.remove();
668
+ this.hostElement = null;
669
+ }
670
+ resolveItems() {
671
+ const explicitItems = this.radiantItems();
672
+ if (explicitItems?.length) {
673
+ return explicitItems;
674
+ }
675
+ const value = this.ngxRadiant();
676
+ if (!value) {
677
+ return [];
678
+ }
679
+ if (Array.isArray(value)) {
680
+ return value;
681
+ }
682
+ if (typeof value === 'string') {
683
+ return [
684
+ {
685
+ src: value,
686
+ type: this.radiantType(),
687
+ alt: this.radiantAlt(),
688
+ caption: this.radiantCaption(),
689
+ thumb: this.radiantThumb(),
690
+ },
691
+ ];
692
+ }
693
+ return [value];
694
+ }
695
+ normalizeIndex(index, itemCount) {
696
+ return Math.min(Math.max(Math.trunc(index) || 0, 0), itemCount - 1);
697
+ }
698
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NgxRadiantDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
699
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: NgxRadiantDirective, isStandalone: true, selector: "[ngxRadiant]", inputs: { ngxRadiant: { classPropertyName: "ngxRadiant", publicName: "ngxRadiant", isSignal: true, isRequired: false, transformFunction: null }, radiantItems: { classPropertyName: "radiantItems", publicName: "radiantItems", isSignal: true, isRequired: false, transformFunction: null }, radiantIndex: { classPropertyName: "radiantIndex", publicName: "radiantIndex", isSignal: true, isRequired: false, transformFunction: null }, radiantType: { classPropertyName: "radiantType", publicName: "radiantType", isSignal: true, isRequired: false, transformFunction: null }, radiantAlt: { classPropertyName: "radiantAlt", publicName: "radiantAlt", isSignal: true, isRequired: false, transformFunction: null }, radiantCaption: { classPropertyName: "radiantCaption", publicName: "radiantCaption", isSignal: true, isRequired: false, transformFunction: null }, radiantThumb: { classPropertyName: "radiantThumb", publicName: "radiantThumb", isSignal: true, isRequired: false, transformFunction: null }, radiantAriaLabel: { classPropertyName: "radiantAriaLabel", publicName: "radiantAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, radiantConfig: { classPropertyName: "radiantConfig", publicName: "radiantConfig", isSignal: true, isRequired: false, transformFunction: null }, radiantCloseOnEscape: { classPropertyName: "radiantCloseOnEscape", publicName: "radiantCloseOnEscape", isSignal: true, isRequired: false, transformFunction: null }, radiantLoop: { classPropertyName: "radiantLoop", publicName: "radiantLoop", isSignal: true, isRequired: false, transformFunction: null }, radiantShowThumbnails: { classPropertyName: "radiantShowThumbnails", publicName: "radiantShowThumbnails", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "open($event)" } }, exportAs: ["ngxRadiant"], ngImport: i0 });
700
+ }
701
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NgxRadiantDirective, decorators: [{
702
+ type: Directive,
703
+ args: [{
704
+ selector: '[ngxRadiant]',
705
+ exportAs: 'ngxRadiant',
706
+ }]
707
+ }], ctorParameters: () => [], propDecorators: { ngxRadiant: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxRadiant", required: false }] }], radiantItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantItems", required: false }] }], radiantIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantIndex", required: false }] }], radiantType: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantType", required: false }] }], radiantAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantAlt", required: false }] }], radiantCaption: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantCaption", required: false }] }], radiantThumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantThumb", required: false }] }], radiantAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantAriaLabel", required: false }] }], radiantConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantConfig", required: false }] }], radiantCloseOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantCloseOnEscape", required: false }] }], radiantLoop: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantLoop", required: false }] }], radiantShowThumbnails: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiantShowThumbnails", required: false }] }], open: [{
708
+ type: HostListener,
709
+ args: ['click', ['$event']]
710
+ }] } });
711
+
712
+ /*
713
+ * Public API Surface of ngx-radiant
714
+ */
715
+
716
+ /**
717
+ * Generated bundle index. Do not edit.
718
+ */
719
+
720
+ export { NgxRadiantDirective, NgxRadiantLightbox };
721
+ //# sourceMappingURL=ngx-radiant.mjs.map