adminator-admin-dashboard 2.7.0 → 2.7.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.
@@ -0,0 +1,740 @@
1
+ /**
2
+ * UI Bootstrap Components with TypeScript
3
+ * Vanilla JavaScript implementations for Bootstrap components
4
+ */
5
+
6
+ import type { ComponentInterface } from '../../types';
7
+
8
+ // Type definitions for UI components
9
+ export interface UIComponentOptions {
10
+ autoInit?: boolean;
11
+ selector?: string;
12
+ }
13
+
14
+ export interface TooltipOptions {
15
+ placement?: 'top' | 'bottom' | 'left' | 'right';
16
+ delay?: number;
17
+ animation?: boolean;
18
+ }
19
+
20
+ export interface PopoverOptions {
21
+ placement?: 'top' | 'bottom' | 'left' | 'right';
22
+ trigger?: 'click' | 'hover' | 'focus' | 'manual';
23
+ html?: boolean;
24
+ animation?: boolean;
25
+ }
26
+
27
+ export interface ModalOptions {
28
+ backdrop?: boolean | 'static';
29
+ keyboard?: boolean;
30
+ focus?: boolean;
31
+ show?: boolean;
32
+ }
33
+
34
+ export interface AccordionOptions {
35
+ parent?: string;
36
+ toggle?: boolean;
37
+ }
38
+
39
+ export interface DropdownOptions {
40
+ offset?: [number, number];
41
+ flip?: boolean;
42
+ boundary?: 'clippingParents' | 'viewport' | HTMLElement;
43
+ }
44
+
45
+ // Modal functionality
46
+ export class VanillaModal implements ComponentInterface {
47
+ public name: string = 'VanillaModal';
48
+ public element: HTMLElement;
49
+ public options: ModalOptions;
50
+ public isInitialized: boolean = false;
51
+
52
+ private modal: HTMLElement | null = null;
53
+ private backdrop: HTMLElement | null = null;
54
+ private isOpen: boolean = false;
55
+ private escapeHandler: ((e: KeyboardEvent) => void) | null = null;
56
+
57
+ constructor(element: HTMLElement, options: ModalOptions = {}) {
58
+ this.element = element;
59
+ this.options = {
60
+ backdrop: true,
61
+ keyboard: true,
62
+ focus: true,
63
+ show: false,
64
+ ...options,
65
+ };
66
+ this.init();
67
+ }
68
+
69
+ public init(): void {
70
+ const targetSelector = this.element.getAttribute('data-bs-target');
71
+ if (!targetSelector) {
72
+ console.warn('Modal: Missing data-bs-target attribute');
73
+ return;
74
+ }
75
+
76
+ this.modal = document.querySelector(targetSelector);
77
+ if (!this.modal) {
78
+ console.warn(`Modal: Target element ${targetSelector} not found`);
79
+ return;
80
+ }
81
+
82
+ this.element.addEventListener('click', this.handleElementClick.bind(this));
83
+
84
+ // Close button functionality
85
+ const closeButtons = this.modal.querySelectorAll<HTMLElement>('[data-bs-dismiss="modal"]');
86
+ closeButtons.forEach(btn => {
87
+ btn.addEventListener('click', this.hide.bind(this));
88
+ });
89
+
90
+ // Close on backdrop click
91
+ if (this.options.backdrop !== false) {
92
+ this.modal.addEventListener('click', this.handleBackdropClick.bind(this));
93
+ }
94
+
95
+ this.isInitialized = true;
96
+ }
97
+
98
+ public destroy(): void {
99
+ if (this.escapeHandler) {
100
+ document.removeEventListener('keydown', this.escapeHandler);
101
+ this.escapeHandler = null;
102
+ }
103
+ this.hide();
104
+ this.isInitialized = false;
105
+ }
106
+
107
+ private handleElementClick(e: Event): void {
108
+ e.preventDefault();
109
+ this.show();
110
+ }
111
+
112
+ private handleBackdropClick(e: Event): void {
113
+ if (e.target === this.modal && this.options.backdrop !== 'static') {
114
+ this.hide();
115
+ }
116
+ }
117
+
118
+ public show(): void {
119
+ if (this.isOpen || !this.modal) return;
120
+
121
+ // Create backdrop
122
+ if (this.options.backdrop !== false) {
123
+ this.backdrop = document.createElement('div');
124
+ this.backdrop.className = 'modal-backdrop fade show';
125
+ document.body.appendChild(this.backdrop);
126
+ }
127
+
128
+ // Show modal
129
+ this.modal.style.display = 'block';
130
+ this.modal.classList.add('show');
131
+ document.body.classList.add('modal-open');
132
+
133
+ this.isOpen = true;
134
+
135
+ // Focus the modal
136
+ if (this.options.focus) {
137
+ this.modal.setAttribute('tabindex', '-1');
138
+ this.modal.focus();
139
+ }
140
+
141
+ // Escape key handler
142
+ if (this.options.keyboard) {
143
+ this.escapeHandler = this.handleEscapeKey.bind(this);
144
+ document.addEventListener('keydown', this.escapeHandler);
145
+ }
146
+ }
147
+
148
+ public hide(): void {
149
+ if (!this.isOpen || !this.modal) return;
150
+
151
+ // Hide modal
152
+ this.modal.classList.remove('show');
153
+ this.modal.style.display = 'none';
154
+ document.body.classList.remove('modal-open');
155
+
156
+ // Remove backdrop
157
+ if (this.backdrop) {
158
+ this.backdrop.remove();
159
+ this.backdrop = null;
160
+ }
161
+
162
+ this.isOpen = false;
163
+
164
+ // Remove escape handler
165
+ if (this.escapeHandler) {
166
+ document.removeEventListener('keydown', this.escapeHandler);
167
+ this.escapeHandler = null;
168
+ }
169
+ }
170
+
171
+ private handleEscapeKey(e: KeyboardEvent): void {
172
+ if (e.key === 'Escape') {
173
+ this.hide();
174
+ }
175
+ }
176
+
177
+ public toggle(): void {
178
+ if (this.isOpen) {
179
+ this.hide();
180
+ } else {
181
+ this.show();
182
+ }
183
+ }
184
+
185
+ public isVisible(): boolean {
186
+ return this.isOpen;
187
+ }
188
+ }
189
+
190
+ // Dropdown functionality
191
+ export class VanillaDropdown implements ComponentInterface {
192
+ public name: string = 'VanillaDropdown';
193
+ public element: HTMLElement;
194
+ public options: DropdownOptions;
195
+ public isInitialized: boolean = false;
196
+
197
+ private menu: HTMLElement | null = null;
198
+ private isOpen: boolean = false;
199
+ private outsideClickHandler: ((e: Event) => void) | null = null;
200
+ private escapeHandler: ((e: KeyboardEvent) => void) | null = null;
201
+
202
+ constructor(element: HTMLElement, options: DropdownOptions = {}) {
203
+ this.element = element;
204
+ this.options = {
205
+ offset: [0, 2],
206
+ flip: true,
207
+ boundary: 'clippingParents',
208
+ ...options,
209
+ };
210
+ this.init();
211
+ }
212
+
213
+ public init(): void {
214
+ const parent = this.element.parentNode as HTMLElement;
215
+ if (!parent) return;
216
+
217
+ this.menu = parent.querySelector('.dropdown-menu');
218
+ if (!this.menu) return;
219
+
220
+ this.element.addEventListener('click', this.handleElementClick.bind(this));
221
+
222
+ // Setup event handlers
223
+ this.outsideClickHandler = this.handleOutsideClick.bind(this);
224
+ this.escapeHandler = this.handleEscapeKey.bind(this);
225
+
226
+ this.isInitialized = true;
227
+ }
228
+
229
+ public destroy(): void {
230
+ this.hide();
231
+ if (this.outsideClickHandler) {
232
+ document.removeEventListener('click', this.outsideClickHandler);
233
+ this.outsideClickHandler = null;
234
+ }
235
+ if (this.escapeHandler) {
236
+ document.removeEventListener('keydown', this.escapeHandler);
237
+ this.escapeHandler = null;
238
+ }
239
+ this.isInitialized = false;
240
+ }
241
+
242
+ private handleElementClick(e: Event): void {
243
+ e.preventDefault();
244
+ e.stopPropagation();
245
+ this.toggle();
246
+ }
247
+
248
+ private handleOutsideClick(e: Event): void {
249
+ const parent = this.element.parentNode as HTMLElement;
250
+ if (parent && !parent.contains(e.target as Node)) {
251
+ this.hide();
252
+ }
253
+ }
254
+
255
+ private handleEscapeKey(e: KeyboardEvent): void {
256
+ if (e.key === 'Escape' && this.isOpen) {
257
+ this.hide();
258
+ }
259
+ }
260
+
261
+ public toggle(): void {
262
+ if (this.isOpen) {
263
+ this.hide();
264
+ } else {
265
+ this.show();
266
+ }
267
+ }
268
+
269
+ public show(): void {
270
+ if (this.isOpen || !this.menu) return;
271
+
272
+ // Close other dropdowns
273
+ document.querySelectorAll<HTMLElement>('.dropdown-menu.show').forEach(menu => {
274
+ menu.classList.remove('show');
275
+ });
276
+
277
+ this.menu.classList.add('show');
278
+ this.element.setAttribute('aria-expanded', 'true');
279
+ this.isOpen = true;
280
+
281
+ // Add event listeners
282
+ if (this.outsideClickHandler) {
283
+ document.addEventListener('click', this.outsideClickHandler);
284
+ }
285
+ if (this.escapeHandler) {
286
+ document.addEventListener('keydown', this.escapeHandler);
287
+ }
288
+ }
289
+
290
+ public hide(): void {
291
+ if (!this.isOpen || !this.menu) return;
292
+
293
+ this.menu.classList.remove('show');
294
+ this.element.setAttribute('aria-expanded', 'false');
295
+ this.isOpen = false;
296
+
297
+ // Remove event listeners
298
+ if (this.outsideClickHandler) {
299
+ document.removeEventListener('click', this.outsideClickHandler);
300
+ }
301
+ if (this.escapeHandler) {
302
+ document.removeEventListener('keydown', this.escapeHandler);
303
+ }
304
+ }
305
+ }
306
+
307
+ // Popover functionality
308
+ export class VanillaPopover implements ComponentInterface {
309
+ public name: string = 'VanillaPopover';
310
+ public element: HTMLElement;
311
+ public options: PopoverOptions;
312
+ public isInitialized: boolean = false;
313
+
314
+ private popover: HTMLElement | null = null;
315
+ private isOpen: boolean = false;
316
+ private outsideClickHandler: ((e: Event) => void) | null = null;
317
+
318
+ constructor(element: HTMLElement, options: PopoverOptions = {}) {
319
+ this.element = element;
320
+ this.options = {
321
+ placement: 'top',
322
+ trigger: 'click',
323
+ html: false,
324
+ animation: true,
325
+ ...options,
326
+ };
327
+ this.init();
328
+ }
329
+
330
+ public init(): void {
331
+ if (this.options.trigger === 'click') {
332
+ this.element.addEventListener('click', this.handleElementClick.bind(this));
333
+ } else if (this.options.trigger === 'hover') {
334
+ this.element.addEventListener('mouseenter', this.show.bind(this));
335
+ this.element.addEventListener('mouseleave', this.hide.bind(this));
336
+ } else if (this.options.trigger === 'focus') {
337
+ this.element.addEventListener('focus', this.show.bind(this));
338
+ this.element.addEventListener('blur', this.hide.bind(this));
339
+ }
340
+
341
+ this.outsideClickHandler = this.handleOutsideClick.bind(this);
342
+ this.isInitialized = true;
343
+ }
344
+
345
+ public destroy(): void {
346
+ this.hide();
347
+ if (this.outsideClickHandler) {
348
+ document.removeEventListener('click', this.outsideClickHandler);
349
+ this.outsideClickHandler = null;
350
+ }
351
+ this.isInitialized = false;
352
+ }
353
+
354
+ private handleElementClick(e: Event): void {
355
+ e.preventDefault();
356
+ this.toggle();
357
+ }
358
+
359
+ private handleOutsideClick(e: Event): void {
360
+ if (!this.element.contains(e.target as Node) &&
361
+ (!this.popover || !this.popover.contains(e.target as Node))) {
362
+ this.hide();
363
+ }
364
+ }
365
+
366
+ public toggle(): void {
367
+ if (this.isOpen) {
368
+ this.hide();
369
+ } else {
370
+ this.show();
371
+ }
372
+ }
373
+
374
+ public show(): void {
375
+ if (this.isOpen) return;
376
+
377
+ // Close other popovers
378
+ document.querySelectorAll<HTMLElement>('.popover').forEach(popover => {
379
+ popover.remove();
380
+ });
381
+
382
+ const title = this.element.getAttribute('title') || this.element.getAttribute('data-bs-title');
383
+ const content = this.element.getAttribute('data-bs-content');
384
+
385
+ if (!content) return;
386
+
387
+ this.popover = document.createElement('div');
388
+ this.popover.className = `popover bs-popover-${this.options.placement} show`;
389
+ this.popover.style.position = 'absolute';
390
+ this.popover.style.zIndex = '1070';
391
+ this.popover.style.maxWidth = '276px';
392
+ this.popover.style.backgroundColor = '#fff';
393
+ this.popover.style.border = '1px solid rgba(0,0,0,.2)';
394
+ this.popover.style.borderRadius = '6px';
395
+ this.popover.style.boxShadow = '0 5px 10px rgba(0,0,0,.2)';
396
+
397
+ let popoverContent = '';
398
+ if (title) {
399
+ popoverContent += `<div class="popover-header" style="padding: 8px 14px; margin-bottom: 0; font-size: 14px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; font-weight: 600;">${title}</div>`;
400
+ }
401
+ popoverContent += `<div class="popover-body" style="padding: 9px 14px; word-wrap: break-word;">${content}</div>`;
402
+
403
+ this.popover.innerHTML = popoverContent;
404
+ document.body.appendChild(this.popover);
405
+
406
+ // Position popover
407
+ this.positionPopover();
408
+
409
+ this.isOpen = true;
410
+
411
+ // Add outside click handler
412
+ if (this.outsideClickHandler) {
413
+ document.addEventListener('click', this.outsideClickHandler);
414
+ }
415
+ }
416
+
417
+ public hide(): void {
418
+ if (!this.isOpen) return;
419
+
420
+ if (this.popover) {
421
+ this.popover.remove();
422
+ this.popover = null;
423
+ }
424
+ this.isOpen = false;
425
+
426
+ // Remove outside click handler
427
+ if (this.outsideClickHandler) {
428
+ document.removeEventListener('click', this.outsideClickHandler);
429
+ }
430
+ }
431
+
432
+ private positionPopover(): void {
433
+ if (!this.popover) return;
434
+
435
+ const rect = this.element.getBoundingClientRect();
436
+ const popoverRect = this.popover.getBoundingClientRect();
437
+
438
+ switch (this.options.placement) {
439
+ case 'top':
440
+ this.popover.style.left = `${rect.left + (rect.width / 2) - (popoverRect.width / 2)}px`;
441
+ this.popover.style.top = `${rect.top - popoverRect.height - 10}px`;
442
+ break;
443
+ case 'bottom':
444
+ this.popover.style.left = `${rect.left + (rect.width / 2) - (popoverRect.width / 2)}px`;
445
+ this.popover.style.top = `${rect.bottom + 10}px`;
446
+ break;
447
+ case 'left':
448
+ this.popover.style.left = `${rect.left - popoverRect.width - 10}px`;
449
+ this.popover.style.top = `${rect.top + (rect.height / 2) - (popoverRect.height / 2)}px`;
450
+ break;
451
+ case 'right':
452
+ this.popover.style.left = `${rect.right + 10}px`;
453
+ this.popover.style.top = `${rect.top + (rect.height / 2) - (popoverRect.height / 2)}px`;
454
+ break;
455
+ }
456
+ }
457
+ }
458
+
459
+ // Tooltip functionality
460
+ export class VanillaTooltip implements ComponentInterface {
461
+ public name: string = 'VanillaTooltip';
462
+ public element: HTMLElement;
463
+ public options: TooltipOptions;
464
+ public isInitialized: boolean = false;
465
+
466
+ private tooltip: HTMLElement | null = null;
467
+ private showTimeout: number | null = null;
468
+ private hideTimeout: number | null = null;
469
+
470
+ constructor(element: HTMLElement, options: TooltipOptions = {}) {
471
+ this.element = element;
472
+ this.options = {
473
+ placement: 'top',
474
+ delay: 0,
475
+ animation: true,
476
+ ...options,
477
+ };
478
+ this.init();
479
+ }
480
+
481
+ public init(): void {
482
+ this.element.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
483
+ this.element.addEventListener('mouseleave', this.handleMouseLeave.bind(this));
484
+ this.element.addEventListener('focus', this.handleFocus.bind(this));
485
+ this.element.addEventListener('blur', this.handleBlur.bind(this));
486
+ this.isInitialized = true;
487
+ }
488
+
489
+ public destroy(): void {
490
+ this.hide();
491
+ this.clearTimeouts();
492
+ this.isInitialized = false;
493
+ }
494
+
495
+ private handleMouseEnter(): void {
496
+ this.clearTimeouts();
497
+ this.showTimeout = window.setTimeout(() => this.show(), this.options.delay);
498
+ }
499
+
500
+ private handleMouseLeave(): void {
501
+ this.clearTimeouts();
502
+ this.hideTimeout = window.setTimeout(() => this.hide(), this.options.delay);
503
+ }
504
+
505
+ private handleFocus(): void {
506
+ this.show();
507
+ }
508
+
509
+ private handleBlur(): void {
510
+ this.hide();
511
+ }
512
+
513
+ private clearTimeouts(): void {
514
+ if (this.showTimeout) {
515
+ clearTimeout(this.showTimeout);
516
+ this.showTimeout = null;
517
+ }
518
+ if (this.hideTimeout) {
519
+ clearTimeout(this.hideTimeout);
520
+ this.hideTimeout = null;
521
+ }
522
+ }
523
+
524
+ public show(): void {
525
+ if (this.tooltip) return;
526
+
527
+ const title = this.element.getAttribute('title') || this.element.getAttribute('data-bs-title');
528
+ if (!title) return;
529
+
530
+ this.tooltip = document.createElement('div');
531
+ this.tooltip.className = `tooltip bs-tooltip-${this.options.placement} show`;
532
+ this.tooltip.style.position = 'absolute';
533
+ this.tooltip.style.zIndex = '1070';
534
+ this.tooltip.style.maxWidth = '200px';
535
+ this.tooltip.style.padding = '4px 8px';
536
+ this.tooltip.style.fontSize = '12px';
537
+ this.tooltip.style.backgroundColor = '#000';
538
+ this.tooltip.style.color = '#fff';
539
+ this.tooltip.style.borderRadius = '4px';
540
+ this.tooltip.style.pointerEvents = 'none';
541
+ this.tooltip.style.whiteSpace = 'nowrap';
542
+
543
+ this.tooltip.innerHTML = `<div class="tooltip-inner">${title}</div>`;
544
+ document.body.appendChild(this.tooltip);
545
+
546
+ // Position tooltip
547
+ this.positionTooltip();
548
+ }
549
+
550
+ public hide(): void {
551
+ if (this.tooltip) {
552
+ this.tooltip.remove();
553
+ this.tooltip = null;
554
+ }
555
+ }
556
+
557
+ private positionTooltip(): void {
558
+ if (!this.tooltip) return;
559
+
560
+ const rect = this.element.getBoundingClientRect();
561
+ const tooltipRect = this.tooltip.getBoundingClientRect();
562
+
563
+ switch (this.options.placement) {
564
+ case 'top':
565
+ this.tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltipRect.width / 2)}px`;
566
+ this.tooltip.style.top = `${rect.top - tooltipRect.height - 5}px`;
567
+ break;
568
+ case 'bottom':
569
+ this.tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltipRect.width / 2)}px`;
570
+ this.tooltip.style.top = `${rect.bottom + 5}px`;
571
+ break;
572
+ case 'left':
573
+ this.tooltip.style.left = `${rect.left - tooltipRect.width - 5}px`;
574
+ this.tooltip.style.top = `${rect.top + (rect.height / 2) - (tooltipRect.height / 2)}px`;
575
+ break;
576
+ case 'right':
577
+ this.tooltip.style.left = `${rect.right + 5}px`;
578
+ this.tooltip.style.top = `${rect.top + (rect.height / 2) - (tooltipRect.height / 2)}px`;
579
+ break;
580
+ }
581
+ }
582
+ }
583
+
584
+ // Accordion functionality
585
+ export class VanillaAccordion implements ComponentInterface {
586
+ public name: string = 'VanillaAccordion';
587
+ public element: HTMLElement;
588
+ public options: AccordionOptions;
589
+ public isInitialized: boolean = false;
590
+
591
+ private accordion: HTMLElement | null = null;
592
+ private target: HTMLElement | null = null;
593
+ private isOpen: boolean = false;
594
+
595
+ constructor(element: HTMLElement, options: AccordionOptions = {}) {
596
+ this.element = element;
597
+ this.options = {
598
+ toggle: true,
599
+ ...options,
600
+ };
601
+ this.init();
602
+ }
603
+
604
+ public init(): void {
605
+ this.accordion = this.element.closest('.accordion');
606
+ const targetSelector = this.element.getAttribute('data-bs-target');
607
+ if (!targetSelector) return;
608
+
609
+ this.target = document.querySelector(targetSelector);
610
+ if (!this.target) return;
611
+
612
+ this.isOpen = !this.element.classList.contains('collapsed');
613
+ this.element.addEventListener('click', this.handleElementClick.bind(this));
614
+ this.isInitialized = true;
615
+ }
616
+
617
+ public destroy(): void {
618
+ this.isInitialized = false;
619
+ }
620
+
621
+ private handleElementClick(e: Event): void {
622
+ e.preventDefault();
623
+ this.toggle();
624
+ }
625
+
626
+ public toggle(): void {
627
+ if (this.isOpen) {
628
+ this.hide();
629
+ } else {
630
+ this.show();
631
+ }
632
+ }
633
+
634
+ public show(): void {
635
+ if (this.isOpen || !this.target) return;
636
+
637
+ // Close other accordion items in the same parent
638
+ if (this.accordion) {
639
+ const otherItems = this.accordion.querySelectorAll<HTMLElement>('.accordion-collapse.show');
640
+ otherItems.forEach(item => {
641
+ if (item !== this.target) {
642
+ item.classList.remove('show');
643
+ const button = this.accordion!.querySelector<HTMLElement>(`[data-bs-target="#${item.id}"]`);
644
+ if (button) {
645
+ button.classList.add('collapsed');
646
+ button.setAttribute('aria-expanded', 'false');
647
+ }
648
+ }
649
+ });
650
+ }
651
+
652
+ // Show this item
653
+ this.target.classList.add('show');
654
+ this.element.classList.remove('collapsed');
655
+ this.element.setAttribute('aria-expanded', 'true');
656
+ this.isOpen = true;
657
+ }
658
+
659
+ public hide(): void {
660
+ if (!this.isOpen || !this.target) return;
661
+
662
+ this.target.classList.remove('show');
663
+ this.element.classList.add('collapsed');
664
+ this.element.setAttribute('aria-expanded', 'false');
665
+ this.isOpen = false;
666
+ }
667
+ }
668
+
669
+ // UI Manager Class
670
+ export class UIManager {
671
+ private components: Map<string, ComponentInterface> = new Map();
672
+
673
+ public initializeComponents(): void {
674
+ // Initialize modals
675
+ document.querySelectorAll<HTMLElement>('[data-bs-toggle="modal"]').forEach(element => {
676
+ const modal = new VanillaModal(element);
677
+ this.components.set(`modal-${element.id || Date.now()}`, modal);
678
+ });
679
+
680
+ // Initialize dropdowns
681
+ document.querySelectorAll<HTMLElement>('[data-bs-toggle="dropdown"]').forEach(element => {
682
+ const dropdown = new VanillaDropdown(element);
683
+ this.components.set(`dropdown-${element.id || Date.now()}`, dropdown);
684
+ });
685
+
686
+ // Initialize popovers
687
+ document.querySelectorAll<HTMLElement>('[data-bs-toggle="popover"]').forEach(element => {
688
+ const popover = new VanillaPopover(element);
689
+ this.components.set(`popover-${element.id || Date.now()}`, popover);
690
+ });
691
+
692
+ // Initialize tooltips
693
+ document.querySelectorAll<HTMLElement>('[data-bs-toggle="tooltip"]').forEach(element => {
694
+ const tooltip = new VanillaTooltip(element);
695
+ this.components.set(`tooltip-${element.id || Date.now()}`, tooltip);
696
+ });
697
+
698
+ // Initialize accordions
699
+ document.querySelectorAll<HTMLElement>('[data-bs-toggle="collapse"]').forEach(element => {
700
+ const accordion = new VanillaAccordion(element);
701
+ this.components.set(`accordion-${element.id || Date.now()}`, accordion);
702
+ });
703
+ }
704
+
705
+ public destroyComponents(): void {
706
+ this.components.forEach(component => {
707
+ component.destroy();
708
+ });
709
+ this.components.clear();
710
+ }
711
+
712
+ public getComponent(id: string): ComponentInterface | undefined {
713
+ return this.components.get(id);
714
+ }
715
+ }
716
+
717
+ // Create and export singleton instance
718
+ const uiManager = new UIManager();
719
+
720
+ // Initialize when DOM is ready
721
+ const initializeUI = (): void => {
722
+ uiManager.initializeComponents();
723
+ };
724
+
725
+ if (document.readyState === 'loading') {
726
+ document.addEventListener('DOMContentLoaded', initializeUI);
727
+ } else {
728
+ initializeUI();
729
+ }
730
+
731
+ // Export default object for compatibility
732
+ export default {
733
+ init: initializeUI,
734
+ manager: uiManager,
735
+ Modal: VanillaModal,
736
+ Dropdown: VanillaDropdown,
737
+ Popover: VanillaPopover,
738
+ Tooltip: VanillaTooltip,
739
+ Accordion: VanillaAccordion,
740
+ };