adminator-admin-dashboard 2.7.1 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -0
- package/CLAUDE.md +5 -5
- package/README.md +63 -24
- package/dist/main.js +1779 -2571
- package/dist/main.js.map +1 -1
- package/package.json +28 -36
- package/src/assets/scripts/app 2.js +645 -0
- package/src/assets/scripts/app.js +3 -3
- package/src/assets/scripts/utils/theme.js +4 -2
- package/src/assets/scripts/vectorMaps/index.js +5 -5
- package/dist/55b07f26c86c8e3d3754.svg +0 -1
- package/dist/9fad440d8ee7a949a9a9.svg +0 -1
- package/dist/test.html +0 -91
- package/src/assets/scripts/app.ts +0 -757
- package/src/assets/scripts/components/Chart.ts +0 -1350
- package/src/assets/scripts/components/Sidebar.ts +0 -388
- package/src/assets/scripts/datatable/index.ts +0 -707
- package/src/assets/scripts/datepicker/index.ts +0 -699
- package/src/assets/scripts/ui/index.ts +0 -740
- package/src/assets/scripts/utils/date.ts +0 -363
- package/src/assets/scripts/utils/dom.ts +0 -513
- package/src/assets/scripts/utils/theme.ts +0 -313
- package/src/assets/scripts/vectorMaps/index.ts +0 -542
- package/src/test.html +0 -96
- package/src/types/index.ts +0 -236
- /package/dist/assets/{c1e38fd9e0e74ba58f7a2b77ef29fdd3.svg → fontawesome-webfont.svg} +0 -0
- /package/dist/assets/{f0fc8c798eac5636249c4ea287832422.svg → themify.svg} +0 -0
|
@@ -1,740 +0,0 @@
|
|
|
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
|
-
};
|