@dodlhuat/basix 1.2.7 → 1.2.9
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 +1 -1
- package/js/bottom-sheet.d.ts +37 -0
- package/js/calendar.d.ts +115 -0
- package/js/carousel.d.ts +34 -0
- package/js/chart.d.ts +73 -0
- package/js/code-viewer.d.ts +16 -0
- package/js/context-menu.d.ts +31 -0
- package/js/datepicker.d.ts +55 -0
- package/js/dropdown.d.ts +30 -0
- package/js/editor.d.ts +41 -0
- package/js/file-uploader.d.ts +48 -0
- package/js/flyout-menu.d.ts +37 -0
- package/js/gallery.d.ts +35 -0
- package/js/group-picker.d.ts +59 -0
- package/js/lightbox.d.ts +46 -0
- package/js/modal.d.ts +28 -0
- package/js/popover.d.ts +46 -0
- package/js/position.d.ts +31 -0
- package/js/push-menu.d.ts +31 -0
- package/js/range-slider.d.ts +9 -0
- package/js/scroll.d.ts +15 -0
- package/js/scrollbar.d.ts +48 -0
- package/js/select.d.ts +16 -0
- package/js/sidebar-nav.d.ts +22 -0
- package/js/stepper.d.ts +26 -0
- package/js/table.d.ts +98 -0
- package/js/tabs.d.ts +57 -0
- package/js/theme.d.ts +65 -0
- package/js/timepicker.d.ts +37 -0
- package/js/toast.d.ts +26 -0
- package/js/tooltip.d.ts +34 -0
- package/js/tree.d.ts +40 -0
- package/js/utils.d.ts +24 -0
- package/js/virtual-dropdown.d.ts +55 -0
- package/package.json +1 -1
- package/js/bottom-sheet.ts +0 -224
- package/js/calendar.ts +0 -774
- package/js/carousel.ts +0 -222
- package/js/chart.ts +0 -694
- package/js/code-viewer.ts +0 -188
- package/js/context-menu.ts +0 -252
- package/js/datepicker.ts +0 -640
- package/js/dropdown.ts +0 -180
- package/js/editor.ts +0 -492
- package/js/file-uploader.ts +0 -361
- package/js/flyout-menu.ts +0 -255
- package/js/gallery.ts +0 -237
- package/js/group-picker.ts +0 -451
- package/js/lightbox.ts +0 -333
- package/js/modal.ts +0 -171
- package/js/popover.ts +0 -221
- package/js/position.ts +0 -111
- package/js/push-menu.ts +0 -286
- package/js/range-slider.ts +0 -33
- package/js/scroll.ts +0 -47
- package/js/scrollbar.ts +0 -335
- package/js/select.ts +0 -235
- package/js/sidebar-nav.ts +0 -66
- package/js/stepper.ts +0 -109
- package/js/table.ts +0 -459
- package/js/tabs.ts +0 -280
- package/js/theme.ts +0 -235
- package/js/timepicker.ts +0 -202
- package/js/toast.ts +0 -134
- package/js/tooltip.ts +0 -196
- package/js/tree.ts +0 -244
- package/js/tsconfig.json +0 -18
- package/js/utils.ts +0 -119
- package/js/virtual-dropdown.ts +0 -396
package/js/lightbox.ts
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
interface LightboxImage {
|
|
2
|
-
src: string;
|
|
3
|
-
alt?: string;
|
|
4
|
-
caption?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
interface LightboxOptions {
|
|
8
|
-
src?: string;
|
|
9
|
-
alt?: string;
|
|
10
|
-
caption?: string;
|
|
11
|
-
closeable?: boolean;
|
|
12
|
-
images?: LightboxImage[];
|
|
13
|
-
startIndex?: number;
|
|
14
|
-
onOpen?: () => void;
|
|
15
|
-
onClose?: () => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
class Lightbox {
|
|
19
|
-
private images: LightboxImage[];
|
|
20
|
-
private currentIndex: number;
|
|
21
|
-
private readonly closeable: boolean;
|
|
22
|
-
private readonly onOpen?: () => void;
|
|
23
|
-
private readonly onClose?: () => void;
|
|
24
|
-
private wrapper: HTMLElement | null = null;
|
|
25
|
-
private imgEl: HTMLImageElement | null = null;
|
|
26
|
-
private captionEl: HTMLElement | null = null;
|
|
27
|
-
private counterEl: HTMLElement | null = null;
|
|
28
|
-
private isZoomed = false;
|
|
29
|
-
private abortController = new AbortController();
|
|
30
|
-
|
|
31
|
-
constructor(options: LightboxOptions) {
|
|
32
|
-
if (options.images && options.images.length > 0) {
|
|
33
|
-
this.images = options.images;
|
|
34
|
-
} else {
|
|
35
|
-
this.images = [{ src: options.src ?? '', alt: options.alt, caption: options.caption }];
|
|
36
|
-
}
|
|
37
|
-
this.currentIndex = options.startIndex ?? 0;
|
|
38
|
-
this.closeable = options.closeable ?? true;
|
|
39
|
-
this.onOpen = options.onOpen;
|
|
40
|
-
this.onClose = options.onClose;
|
|
41
|
-
|
|
42
|
-
this.hide = this.hide.bind(this);
|
|
43
|
-
this.handleKeydown = this.handleKeydown.bind(this);
|
|
44
|
-
this.handleBackgroundClick = this.handleBackgroundClick.bind(this);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public show(): void {
|
|
48
|
-
this.hide();
|
|
49
|
-
|
|
50
|
-
const wrapper = document.createElement('div');
|
|
51
|
-
wrapper.className = 'lightbox-wrapper';
|
|
52
|
-
wrapper.setAttribute('role', 'dialog');
|
|
53
|
-
wrapper.setAttribute('aria-modal', 'true');
|
|
54
|
-
wrapper.setAttribute('aria-label', 'Image lightbox');
|
|
55
|
-
wrapper.setAttribute('tabindex', '-1');
|
|
56
|
-
wrapper.innerHTML = this.buildTemplate();
|
|
57
|
-
document.body.append(wrapper);
|
|
58
|
-
|
|
59
|
-
this.wrapper = wrapper;
|
|
60
|
-
this.imgEl = wrapper.querySelector('.lightbox-img');
|
|
61
|
-
this.captionEl = wrapper.querySelector('.lightbox-caption');
|
|
62
|
-
this.counterEl = wrapper.querySelector('.lightbox-counter');
|
|
63
|
-
|
|
64
|
-
const sig = { signal: this.abortController.signal };
|
|
65
|
-
|
|
66
|
-
if (this.closeable) {
|
|
67
|
-
wrapper.querySelector('.lightbox-close')?.addEventListener('click', this.hide, sig);
|
|
68
|
-
wrapper.querySelector('.lightbox-background')?.addEventListener('click', this.handleBackgroundClick, sig);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
document.addEventListener('keydown', this.handleKeydown, sig);
|
|
72
|
-
|
|
73
|
-
if (this.images.length > 1) {
|
|
74
|
-
wrapper.querySelector('.lightbox-prev')?.addEventListener('click', () => this.prev(), sig);
|
|
75
|
-
wrapper.querySelector('.lightbox-next')?.addEventListener('click', () => this.next(), sig);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
wrapper.querySelector('.lightbox-img-wrap')?.addEventListener('click', () => this.toggleZoom(), sig);
|
|
79
|
-
|
|
80
|
-
this.addTouchSupport();
|
|
81
|
-
|
|
82
|
-
document.body.style.overflow = 'hidden';
|
|
83
|
-
this.loadImage(this.currentIndex);
|
|
84
|
-
this.updateNav();
|
|
85
|
-
|
|
86
|
-
requestAnimationFrame(() => {
|
|
87
|
-
wrapper.classList.add('is-visible');
|
|
88
|
-
wrapper.focus();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
this.onOpen?.();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public hide(): void {
|
|
95
|
-
const wrapper = this.wrapper;
|
|
96
|
-
if (!wrapper) return;
|
|
97
|
-
|
|
98
|
-
this.abortController.abort();
|
|
99
|
-
this.abortController = new AbortController();
|
|
100
|
-
|
|
101
|
-
document.body.style.overflow = '';
|
|
102
|
-
wrapper.classList.remove('is-visible');
|
|
103
|
-
|
|
104
|
-
setTimeout(() => {
|
|
105
|
-
wrapper.remove();
|
|
106
|
-
if (this.wrapper === wrapper) {
|
|
107
|
-
this.wrapper = null;
|
|
108
|
-
this.imgEl = null;
|
|
109
|
-
this.captionEl = null;
|
|
110
|
-
this.counterEl = null;
|
|
111
|
-
this.isZoomed = false;
|
|
112
|
-
}
|
|
113
|
-
this.onClose?.();
|
|
114
|
-
}, 300);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
public next(): void {
|
|
118
|
-
if (this.images.length <= 1) return;
|
|
119
|
-
this.currentIndex = (this.currentIndex + 1) % this.images.length;
|
|
120
|
-
this.loadImage(this.currentIndex);
|
|
121
|
-
this.updateNav();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public prev(): void {
|
|
125
|
-
if (this.images.length <= 1) return;
|
|
126
|
-
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
|
|
127
|
-
this.loadImage(this.currentIndex);
|
|
128
|
-
this.updateNav();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
public isVisible(): boolean {
|
|
132
|
-
return this.wrapper !== null && document.body.contains(this.wrapper);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
public destroy(): void {
|
|
136
|
-
this.hide();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private loadImage(index: number): void {
|
|
140
|
-
const wrap = this.wrapper?.querySelector('.lightbox-img-wrap');
|
|
141
|
-
if (!wrap || !this.imgEl) return;
|
|
142
|
-
|
|
143
|
-
this.isZoomed = false;
|
|
144
|
-
this.imgEl.classList.remove('is-zoomed');
|
|
145
|
-
wrap.classList.remove('is-zoomed', 'is-error');
|
|
146
|
-
wrap.classList.add('is-loading');
|
|
147
|
-
this.imgEl.classList.remove('is-loaded');
|
|
148
|
-
|
|
149
|
-
const { src, alt, caption } = this.images[index];
|
|
150
|
-
|
|
151
|
-
const tempImg = new Image();
|
|
152
|
-
tempImg.onload = () => {
|
|
153
|
-
if (!this.imgEl) return;
|
|
154
|
-
this.imgEl.src = src;
|
|
155
|
-
this.imgEl.alt = alt ?? '';
|
|
156
|
-
wrap.classList.remove('is-loading');
|
|
157
|
-
this.imgEl.classList.add('is-loaded');
|
|
158
|
-
};
|
|
159
|
-
tempImg.onerror = () => {
|
|
160
|
-
wrap.classList.remove('is-loading');
|
|
161
|
-
wrap.classList.add('is-error');
|
|
162
|
-
};
|
|
163
|
-
tempImg.src = src;
|
|
164
|
-
|
|
165
|
-
if (this.captionEl) {
|
|
166
|
-
if (caption) {
|
|
167
|
-
this.captionEl.textContent = caption;
|
|
168
|
-
this.captionEl.hidden = false;
|
|
169
|
-
} else {
|
|
170
|
-
this.captionEl.hidden = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
this.preloadAdjacent(index);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private preloadAdjacent(index: number): void {
|
|
178
|
-
if (this.images.length <= 1) return;
|
|
179
|
-
const prevIdx = (index - 1 + this.images.length) % this.images.length;
|
|
180
|
-
const nextIdx = (index + 1) % this.images.length;
|
|
181
|
-
[prevIdx, nextIdx].forEach(i => {
|
|
182
|
-
const img = new Image();
|
|
183
|
-
img.src = this.images[i].src;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private updateNav(): void {
|
|
188
|
-
if (!this.wrapper) return;
|
|
189
|
-
const isGallery = this.images.length > 1;
|
|
190
|
-
|
|
191
|
-
if (this.counterEl) {
|
|
192
|
-
this.counterEl.textContent = isGallery ? `${this.currentIndex + 1} / ${this.images.length}` : '';
|
|
193
|
-
this.counterEl.hidden = !isGallery;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const prevBtn = this.wrapper.querySelector<HTMLButtonElement>('.lightbox-prev');
|
|
197
|
-
const nextBtn = this.wrapper.querySelector<HTMLButtonElement>('.lightbox-next');
|
|
198
|
-
if (prevBtn) prevBtn.hidden = !isGallery;
|
|
199
|
-
if (nextBtn) nextBtn.hidden = !isGallery;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private toggleZoom(): void {
|
|
203
|
-
if (!this.imgEl) return;
|
|
204
|
-
this.isZoomed = !this.isZoomed;
|
|
205
|
-
this.imgEl.classList.toggle('is-zoomed', this.isZoomed);
|
|
206
|
-
this.wrapper?.querySelector('.lightbox-img-wrap')?.classList.toggle('is-zoomed', this.isZoomed);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
private handleKeydown(e: KeyboardEvent): void {
|
|
210
|
-
switch (e.key) {
|
|
211
|
-
case 'Escape':
|
|
212
|
-
if (this.closeable) this.hide();
|
|
213
|
-
break;
|
|
214
|
-
case 'ArrowRight':
|
|
215
|
-
this.next();
|
|
216
|
-
break;
|
|
217
|
-
case 'ArrowLeft':
|
|
218
|
-
this.prev();
|
|
219
|
-
break;
|
|
220
|
-
case 'Tab':
|
|
221
|
-
this.trapFocus(e);
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private trapFocus(e: KeyboardEvent): void {
|
|
227
|
-
if (!this.wrapper) return;
|
|
228
|
-
const focusable = Array.from(
|
|
229
|
-
this.wrapper.querySelectorAll<HTMLElement>(
|
|
230
|
-
'button:not([hidden]), [tabindex]:not([tabindex="-1"]):not([hidden])'
|
|
231
|
-
)
|
|
232
|
-
).filter(el => el.offsetParent !== null);
|
|
233
|
-
|
|
234
|
-
if (focusable.length === 0) return;
|
|
235
|
-
const first = focusable[0];
|
|
236
|
-
const last = focusable[focusable.length - 1];
|
|
237
|
-
|
|
238
|
-
if (e.shiftKey) {
|
|
239
|
-
if (document.activeElement === first) {
|
|
240
|
-
e.preventDefault();
|
|
241
|
-
last.focus();
|
|
242
|
-
}
|
|
243
|
-
} else {
|
|
244
|
-
if (document.activeElement === last) {
|
|
245
|
-
e.preventDefault();
|
|
246
|
-
first.focus();
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
private handleBackgroundClick(e: Event): void {
|
|
252
|
-
if ((e.target as HTMLElement)?.classList.contains('lightbox-background')) {
|
|
253
|
-
this.hide();
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
private addTouchSupport(): void {
|
|
258
|
-
const wrap = this.wrapper?.querySelector('.lightbox-img-wrap');
|
|
259
|
-
if (!wrap) return;
|
|
260
|
-
let startX = 0;
|
|
261
|
-
let isDragging = false;
|
|
262
|
-
|
|
263
|
-
wrap.addEventListener('touchstart', (e: Event) => {
|
|
264
|
-
startX = (e as TouchEvent).touches[0].clientX;
|
|
265
|
-
isDragging = true;
|
|
266
|
-
}, { passive: true, signal: this.abortController.signal } as AddEventListenerOptions);
|
|
267
|
-
|
|
268
|
-
wrap.addEventListener('touchend', (e: Event) => {
|
|
269
|
-
if (!isDragging) return;
|
|
270
|
-
const deltaX = (e as TouchEvent).changedTouches[0].clientX - startX;
|
|
271
|
-
if (Math.abs(deltaX) > 50) {
|
|
272
|
-
deltaX < 0 ? this.next() : this.prev();
|
|
273
|
-
}
|
|
274
|
-
isDragging = false;
|
|
275
|
-
}, { signal: this.abortController.signal } as AddEventListenerOptions);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
private buildTemplate(): string {
|
|
279
|
-
return `
|
|
280
|
-
${this.closeable ? '<button class="lightbox-close" aria-label="Close lightbox"><span class="icon icon-close"></span></button>' : ''}
|
|
281
|
-
<div class="lightbox" role="document">
|
|
282
|
-
<div class="lightbox-img-wrap">
|
|
283
|
-
<div class="lightbox-spinner"><div class="spinner"></div></div>
|
|
284
|
-
<img class="lightbox-img" src="" alt="" draggable="false" />
|
|
285
|
-
</div>
|
|
286
|
-
<p class="lightbox-caption" hidden></p>
|
|
287
|
-
<div class="lightbox-counter" hidden></div>
|
|
288
|
-
</div>
|
|
289
|
-
<button class="lightbox-prev" aria-label="Previous image" hidden>
|
|
290
|
-
<span class="icon icon-navigate_before"></span>
|
|
291
|
-
</button>
|
|
292
|
-
<button class="lightbox-next" aria-label="Next image" hidden>
|
|
293
|
-
<span class="icon icon-navigate_next"></span>
|
|
294
|
-
</button>
|
|
295
|
-
<div class="lightbox-background"></div>
|
|
296
|
-
`;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
static bind(selector: string = '[data-lightbox]'): void {
|
|
300
|
-
const elements = document.querySelectorAll<HTMLElement>(selector);
|
|
301
|
-
const groups = new Map<string, { el: HTMLElement; image: LightboxImage }[]>();
|
|
302
|
-
|
|
303
|
-
elements.forEach(el => {
|
|
304
|
-
const groupKey = el.dataset.lightbox || `__solo__${el.dataset.lightboxId ?? Math.random()}`;
|
|
305
|
-
const src = el instanceof HTMLAnchorElement
|
|
306
|
-
? el.href
|
|
307
|
-
: el instanceof HTMLImageElement
|
|
308
|
-
? el.src
|
|
309
|
-
: (el.dataset.src ?? '');
|
|
310
|
-
const imgChild = el.querySelector<HTMLImageElement>('img');
|
|
311
|
-
const alt = el instanceof HTMLImageElement ? el.alt : (imgChild?.alt ?? '');
|
|
312
|
-
const caption = el.dataset.lightboxCaption;
|
|
313
|
-
|
|
314
|
-
if (!groups.has(groupKey)) groups.set(groupKey, []);
|
|
315
|
-
groups.get(groupKey)!.push({ el, image: { src, alt, caption } });
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
groups.forEach(items => {
|
|
319
|
-
items.forEach(({ el }, idx) => {
|
|
320
|
-
(el as HTMLElement).style.cursor = 'zoom-in';
|
|
321
|
-
el.addEventListener('click', e => {
|
|
322
|
-
e.preventDefault();
|
|
323
|
-
new Lightbox({
|
|
324
|
-
images: items.map(i => i.image),
|
|
325
|
-
startIndex: idx,
|
|
326
|
-
}).show();
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export { Lightbox, type LightboxOptions, type LightboxImage };
|
package/js/modal.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { sanitizeHtml } from './utils.js';
|
|
2
|
-
|
|
3
|
-
const CLOSE_ICON = '<div class="icon icon-close close"></div>';
|
|
4
|
-
|
|
5
|
-
type ModalType = 'default' | 'success' | 'error' | 'warning' | 'info';
|
|
6
|
-
|
|
7
|
-
interface ModalOptions {
|
|
8
|
-
content: string;
|
|
9
|
-
header?: string;
|
|
10
|
-
footer?: string;
|
|
11
|
-
closeable?: boolean;
|
|
12
|
-
type?: ModalType;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
class Modal {
|
|
16
|
-
private content: string;
|
|
17
|
-
private readonly header?: string;
|
|
18
|
-
private readonly footer?: string;
|
|
19
|
-
private readonly closeable: boolean;
|
|
20
|
-
private readonly type: ModalType;
|
|
21
|
-
private template: string;
|
|
22
|
-
private modalWrapper: HTMLElement | null = null;
|
|
23
|
-
|
|
24
|
-
constructor(options: ModalOptions);
|
|
25
|
-
constructor(content: string, header?: string, footer?: string, closeable?: boolean, type?: ModalType);
|
|
26
|
-
constructor(
|
|
27
|
-
contentOrOptions: string | ModalOptions,
|
|
28
|
-
header?: string,
|
|
29
|
-
footer?: string,
|
|
30
|
-
closeable: boolean = true,
|
|
31
|
-
type: ModalType = 'default'
|
|
32
|
-
) {
|
|
33
|
-
if (typeof contentOrOptions === 'object') {
|
|
34
|
-
this.content = contentOrOptions.content;
|
|
35
|
-
this.header = contentOrOptions.header;
|
|
36
|
-
this.footer = contentOrOptions.footer;
|
|
37
|
-
this.closeable = contentOrOptions.closeable ?? true;
|
|
38
|
-
this.type = contentOrOptions.type ?? 'default';
|
|
39
|
-
} else {
|
|
40
|
-
this.content = contentOrOptions;
|
|
41
|
-
this.header = header;
|
|
42
|
-
this.footer = footer;
|
|
43
|
-
this.closeable = closeable;
|
|
44
|
-
this.type = type;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
this.template = this.buildTemplate();
|
|
48
|
-
|
|
49
|
-
this.hide = this.hide.bind(this);
|
|
50
|
-
this.handleEscape = this.handleEscape.bind(this);
|
|
51
|
-
this.handleBackgroundClick = this.handleBackgroundClick.bind(this);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public show(): void {
|
|
55
|
-
this.hide();
|
|
56
|
-
|
|
57
|
-
const wrapper = document.createElement('div');
|
|
58
|
-
wrapper.className = 'modal-wrapper';
|
|
59
|
-
wrapper.innerHTML = this.template;
|
|
60
|
-
document.body.append(wrapper);
|
|
61
|
-
|
|
62
|
-
this.modalWrapper = wrapper;
|
|
63
|
-
|
|
64
|
-
if (this.closeable) {
|
|
65
|
-
const closeBtn = wrapper.querySelector('.close');
|
|
66
|
-
closeBtn?.addEventListener('click', this.hide);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const background = wrapper.querySelector('.modal-background');
|
|
70
|
-
if (this.closeable && background) {
|
|
71
|
-
background.addEventListener('click', this.handleBackgroundClick);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (this.closeable) {
|
|
75
|
-
document.addEventListener('keydown', this.handleEscape);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
document.body.style.overflow = 'hidden';
|
|
79
|
-
|
|
80
|
-
requestAnimationFrame(() => {
|
|
81
|
-
wrapper.classList.add('is-visible');
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public hide(): void {
|
|
86
|
-
const wrapper = this.modalWrapper;
|
|
87
|
-
if (!wrapper) return;
|
|
88
|
-
|
|
89
|
-
// Remove event listeners
|
|
90
|
-
const closeBtn = wrapper.querySelector('.close');
|
|
91
|
-
closeBtn?.removeEventListener('click', this.hide);
|
|
92
|
-
|
|
93
|
-
const background = wrapper.querySelector('.modal-background');
|
|
94
|
-
background?.removeEventListener('click', this.handleBackgroundClick);
|
|
95
|
-
|
|
96
|
-
document.removeEventListener('keydown', this.handleEscape);
|
|
97
|
-
document.body.style.overflow = '';
|
|
98
|
-
|
|
99
|
-
wrapper.classList.remove('is-visible');
|
|
100
|
-
|
|
101
|
-
setTimeout(() => {
|
|
102
|
-
wrapper.remove();
|
|
103
|
-
if (this.modalWrapper === wrapper) {
|
|
104
|
-
this.modalWrapper = null;
|
|
105
|
-
}
|
|
106
|
-
}, 300);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private handleEscape(e: KeyboardEvent): void {
|
|
110
|
-
if (e.key === 'Escape') {
|
|
111
|
-
this.hide();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private handleBackgroundClick(e: Event): void {
|
|
116
|
-
if ((e.target as HTMLElement)?.classList.contains('modal-background')) {
|
|
117
|
-
this.hide();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private buildTemplate(): string {
|
|
122
|
-
const parts: string[] = ['<div class="modal">'];
|
|
123
|
-
|
|
124
|
-
if (this.closeable) {
|
|
125
|
-
parts.push(CLOSE_ICON);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (this.header !== undefined) {
|
|
129
|
-
const headerClass = `header ${this.type}-bg`;
|
|
130
|
-
parts.push(`<div class="${headerClass}">${sanitizeHtml(this.header)}</div>`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
parts.push(sanitizeHtml(this.content));
|
|
134
|
-
|
|
135
|
-
if (this.footer !== undefined) {
|
|
136
|
-
parts.push(`<div class="footer">${sanitizeHtml(this.footer)}</div>`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
parts.push('</div>');
|
|
140
|
-
parts.push('<div class="modal-background"></div>');
|
|
141
|
-
|
|
142
|
-
return parts.join('');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public updateContent(content: string): void {
|
|
146
|
-
this.content = content;
|
|
147
|
-
this.template = this.buildTemplate();
|
|
148
|
-
|
|
149
|
-
if (this.modalWrapper) {
|
|
150
|
-
const modalElement = this.modalWrapper.querySelector('.modal');
|
|
151
|
-
if (modalElement) {
|
|
152
|
-
const tempWrapper = document.createElement('div');
|
|
153
|
-
tempWrapper.innerHTML = this.template;
|
|
154
|
-
const newModal = tempWrapper.querySelector('.modal');
|
|
155
|
-
if (newModal) {
|
|
156
|
-
modalElement.innerHTML = newModal.innerHTML;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
public isVisible(): boolean {
|
|
163
|
-
return this.modalWrapper !== null && document.body.contains(this.modalWrapper);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public destroy(): void {
|
|
167
|
-
this.hide();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export { Modal, type ModalOptions, type ModalType };
|