admin-lte 4.0.0-rc6 → 4.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.
- package/CHANGELOG.md +96 -0
- package/README.md +44 -18
- package/dist/UI/general.html +311 -92
- package/dist/UI/icons.html +311 -92
- package/dist/UI/timeline.html +311 -92
- package/dist/css/adminlte.css +2814 -1138
- package/dist/css/adminlte.css.map +1 -1
- package/dist/css/adminlte.min.css +2 -2
- package/dist/css/adminlte.min.css.map +1 -1
- package/dist/css/adminlte.rtl.css +2814 -1138
- package/dist/css/adminlte.rtl.css.map +1 -1
- package/dist/css/adminlte.rtl.min.css +2 -2
- package/dist/css/adminlte.rtl.min.css.map +1 -1
- package/dist/docs/browser-support.html +291 -385
- package/dist/docs/color-mode.html +283 -381
- package/dist/docs/components/main-header.html +269 -363
- package/dist/docs/components/main-sidebar.html +284 -378
- package/dist/docs/customization.html +1056 -0
- package/dist/docs/deployment.html +1121 -0
- package/dist/docs/faq.html +887 -397
- package/dist/docs/getting-started.html +967 -0
- package/dist/docs/how-to-contribute.html +365 -450
- package/dist/docs/index.html +16 -0
- package/dist/docs/integrations.html +1207 -0
- package/dist/docs/introduction.html +483 -434
- package/dist/docs/javascript/accessibility.html +925 -0
- package/dist/docs/javascript/card-widget.html +995 -0
- package/dist/docs/javascript/direct-chat.html +872 -0
- package/dist/docs/javascript/fullscreen.html +881 -0
- package/dist/docs/javascript/layout.html +884 -0
- package/dist/docs/javascript/plugins-overview.html +1120 -0
- package/dist/docs/javascript/pushmenu.html +522 -377
- package/dist/docs/javascript/treeview.html +498 -390
- package/dist/docs/layout-blueprint.html +1063 -0
- package/dist/docs/layout.html +316 -402
- package/dist/docs/license.html +282 -375
- package/dist/docs/migration.html +1125 -0
- package/dist/docs/recipes.html +1161 -0
- package/dist/docs/rtl.html +933 -0
- package/dist/examples/lockscreen.html +66 -2
- package/dist/examples/login-v2.html +65 -1
- package/dist/examples/login.html +65 -1
- package/dist/examples/register-v2.html +65 -1
- package/dist/examples/register.html +65 -1
- package/dist/forms/elements.html +1381 -0
- package/dist/forms/{general.html → layout.html} +382 -452
- package/dist/forms/validation.html +1251 -0
- package/dist/forms/wizard.html +1386 -0
- package/dist/generate/theme.html +135 -5
- package/dist/index.html +311 -92
- package/dist/index2.html +314 -92
- package/dist/index3.html +311 -92
- package/dist/js/adminlte.js +135 -314
- package/dist/js/adminlte.js.map +1 -1
- package/dist/js/adminlte.min.js +3 -3
- package/dist/js/adminlte.min.js.map +1 -1
- package/dist/layout/collapsed-sidebar-without-hover.html +1120 -0
- package/dist/layout/collapsed-sidebar.html +311 -92
- package/dist/layout/fixed-complete.html +311 -92
- package/dist/layout/fixed-footer.html +311 -92
- package/dist/layout/fixed-header.html +311 -92
- package/dist/layout/fixed-sidebar.html +311 -92
- package/dist/layout/layout-custom-area.html +311 -92
- package/dist/layout/layout-rtl.html +311 -92
- package/dist/layout/logo-switch.html +135 -5
- package/dist/layout/sidebar-mini.html +311 -92
- package/dist/layout/unfixed-sidebar.html +311 -92
- package/dist/mailbox/compose.html +1127 -0
- package/dist/mailbox/inbox.html +1477 -0
- package/dist/mailbox/read.html +1178 -0
- package/dist/pages/404.html +213 -0
- package/dist/pages/500.html +201 -0
- package/dist/pages/calendar.html +1242 -0
- package/dist/pages/chat.html +1403 -0
- package/dist/pages/faq.html +1359 -0
- package/dist/pages/file-manager.html +1811 -0
- package/dist/pages/invoice.html +1175 -0
- package/dist/pages/kanban.html +1415 -0
- package/dist/pages/maintenance.html +204 -0
- package/dist/pages/pricing.html +1255 -0
- package/dist/pages/profile.html +1358 -0
- package/dist/pages/projects.html +1620 -0
- package/dist/pages/settings.html +1363 -0
- package/dist/tables/data.html +1295 -0
- package/dist/tables/simple.html +311 -92
- package/dist/widgets/cards.html +311 -92
- package/dist/widgets/info-box.html +311 -92
- package/dist/widgets/small-box.html +311 -92
- package/netlify.toml +16 -0
- package/package.json +32 -26
- package/src/scss/_accessibility.scss +44 -0
- package/src/scss/_app-footer.scss +9 -0
- package/src/scss/_app-header.scss +27 -0
- package/src/scss/_app-sidebar.scss +73 -74
- package/src/scss/_bootstrap-variables.scss +12 -5
- package/src/scss/_docs.scss +816 -0
- package/src/scss/_table.scss +4 -4
- package/src/scss/adminlte.scss +4 -1
- package/src/scss/pages/_login_and_register.scss +1 -1
- package/src/ts/accessibility.ts +9 -7
- package/src/ts/adminlte.ts +2 -14
- package/src/ts/fullscreen.ts +8 -4
- package/src/ts/layout.ts +34 -16
- package/src/ts/push-menu.ts +257 -104
- package/src/ts/util/index.ts +10 -0
- package/.claude/settings.local.json +0 -33
- package/CLAUDE.md +0 -79
package/dist/js/adminlte.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* AdminLTE v4.0.0
|
|
3
|
-
* Copyright 2014-
|
|
2
|
+
* AdminLTE v4.0.0 (https://adminlte.io)
|
|
3
|
+
* Copyright 2014-2026 Colorlib <https://colorlib.com>
|
|
4
4
|
* Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
const domContentLoadedCallbacks = [];
|
|
13
13
|
const onDOMContentLoaded = (callback) => {
|
|
14
14
|
if (document.readyState === 'loading') {
|
|
15
|
-
// add listener on the first call when the document is in loading state
|
|
16
15
|
if (!domContentLoadedCallbacks.length) {
|
|
17
16
|
document.addEventListener('DOMContentLoaded', () => {
|
|
18
17
|
for (const callback of domContentLoadedCallbacks) {
|
|
@@ -26,8 +25,11 @@
|
|
|
26
25
|
callback();
|
|
27
26
|
}
|
|
28
27
|
};
|
|
29
|
-
/* SLIDE UP */
|
|
30
28
|
const slideUp = (target, duration = 500) => {
|
|
29
|
+
if (duration <= 1) {
|
|
30
|
+
target.style.display = 'none';
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
31
33
|
target.style.transitionProperty = 'height, margin, padding';
|
|
32
34
|
target.style.transitionDuration = `${duration}ms`;
|
|
33
35
|
target.style.boxSizing = 'border-box';
|
|
@@ -52,7 +54,6 @@
|
|
|
52
54
|
target.style.removeProperty('transition-property');
|
|
53
55
|
}, duration);
|
|
54
56
|
};
|
|
55
|
-
/* SLIDE DOWN */
|
|
56
57
|
const slideDown = (target, duration = 500) => {
|
|
57
58
|
target.style.removeProperty('display');
|
|
58
59
|
let { display } = globalThis.getComputedStyle(target);
|
|
@@ -60,6 +61,9 @@
|
|
|
60
61
|
display = 'block';
|
|
61
62
|
}
|
|
62
63
|
target.style.display = display;
|
|
64
|
+
if (duration <= 1) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
63
67
|
const height = target.offsetHeight;
|
|
64
68
|
target.style.overflow = 'hidden';
|
|
65
69
|
target.style.height = '0';
|
|
@@ -85,59 +89,33 @@
|
|
|
85
89
|
}, duration);
|
|
86
90
|
};
|
|
87
91
|
|
|
88
|
-
/**
|
|
89
|
-
* --------------------------------------------
|
|
90
|
-
* @file AdminLTE layout.ts
|
|
91
|
-
* @description Layout for AdminLTE.
|
|
92
|
-
* @license MIT
|
|
93
|
-
* --------------------------------------------
|
|
94
|
-
*/
|
|
95
|
-
/**
|
|
96
|
-
* ------------------------------------------------------------------------
|
|
97
|
-
* Constants
|
|
98
|
-
* ------------------------------------------------------------------------
|
|
99
|
-
*/
|
|
100
92
|
const CLASS_NAME_HOLD_TRANSITIONS = 'hold-transition';
|
|
101
93
|
const CLASS_NAME_APP_LOADED = 'app-loaded';
|
|
102
|
-
/**
|
|
103
|
-
* Class Definition
|
|
104
|
-
* ====================================================
|
|
105
|
-
*/
|
|
106
94
|
class Layout {
|
|
107
95
|
_element;
|
|
96
|
+
_holdTransitionTimer;
|
|
108
97
|
constructor(element) {
|
|
109
98
|
this._element = element;
|
|
99
|
+
this._holdTransitionTimer = undefined;
|
|
110
100
|
}
|
|
111
|
-
holdTransition() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
101
|
+
holdTransition(time = 100) {
|
|
102
|
+
if (this._holdTransitionTimer) {
|
|
103
|
+
clearTimeout(this._holdTransitionTimer);
|
|
104
|
+
}
|
|
105
|
+
document.body.classList.add(CLASS_NAME_HOLD_TRANSITIONS);
|
|
106
|
+
this._holdTransitionTimer = setTimeout(() => {
|
|
107
|
+
document.body.classList.remove(CLASS_NAME_HOLD_TRANSITIONS);
|
|
108
|
+
}, time);
|
|
120
109
|
}
|
|
121
110
|
}
|
|
122
111
|
onDOMContentLoaded(() => {
|
|
123
|
-
const
|
|
124
|
-
|
|
112
|
+
const layout = new Layout(document.body);
|
|
113
|
+
window.addEventListener('resize', () => layout.holdTransition(200));
|
|
125
114
|
setTimeout(() => {
|
|
126
115
|
document.body.classList.add(CLASS_NAME_APP_LOADED);
|
|
127
116
|
}, 400);
|
|
128
117
|
});
|
|
129
118
|
|
|
130
|
-
/**
|
|
131
|
-
* --------------------------------------------
|
|
132
|
-
* @file AdminLTE card-widget.ts
|
|
133
|
-
* @description Card widget for AdminLTE.
|
|
134
|
-
* @license MIT
|
|
135
|
-
* --------------------------------------------
|
|
136
|
-
*/
|
|
137
|
-
/**
|
|
138
|
-
* Constants
|
|
139
|
-
* ====================================================
|
|
140
|
-
*/
|
|
141
119
|
const DATA_KEY$4 = 'lte.card-widget';
|
|
142
120
|
const EVENT_KEY$4 = `.${DATA_KEY$4}`;
|
|
143
121
|
const EVENT_COLLAPSED$2 = `collapsed${EVENT_KEY$4}`;
|
|
@@ -180,7 +158,6 @@
|
|
|
180
158
|
const event = new Event(EVENT_COLLAPSED$2);
|
|
181
159
|
if (this._parent) {
|
|
182
160
|
this._parent.classList.add(CLASS_NAME_COLLAPSING);
|
|
183
|
-
// Only target direct children to avoid affecting nested cards
|
|
184
161
|
const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
|
|
185
162
|
elm.forEach(el => {
|
|
186
163
|
if (el instanceof HTMLElement) {
|
|
@@ -200,7 +177,6 @@
|
|
|
200
177
|
const event = new Event(EVENT_EXPANDED$2);
|
|
201
178
|
if (this._parent) {
|
|
202
179
|
this._parent.classList.add(CLASS_NAME_EXPANDING);
|
|
203
|
-
// Only target direct children to avoid affecting nested cards
|
|
204
180
|
const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
|
|
205
181
|
elm.forEach(el => {
|
|
206
182
|
if (el instanceof HTMLElement) {
|
|
@@ -279,11 +255,6 @@
|
|
|
279
255
|
this.maximize();
|
|
280
256
|
}
|
|
281
257
|
}
|
|
282
|
-
/**
|
|
283
|
-
*
|
|
284
|
-
* Data Api implementation
|
|
285
|
-
* ====================================================
|
|
286
|
-
*/
|
|
287
258
|
onDOMContentLoaded(() => {
|
|
288
259
|
const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE);
|
|
289
260
|
collapseBtn.forEach(btn => {
|
|
@@ -314,26 +285,13 @@
|
|
|
314
285
|
});
|
|
315
286
|
});
|
|
316
287
|
|
|
317
|
-
/**
|
|
318
|
-
* --------------------------------------------
|
|
319
|
-
* @file AdminLTE treeview.ts
|
|
320
|
-
* @description Treeview plugin for AdminLTE.
|
|
321
|
-
* @license MIT
|
|
322
|
-
* --------------------------------------------
|
|
323
|
-
*/
|
|
324
|
-
/**
|
|
325
|
-
* ------------------------------------------------------------------------
|
|
326
|
-
* Constants
|
|
327
|
-
* ------------------------------------------------------------------------
|
|
328
|
-
*/
|
|
329
|
-
// const NAME = 'Treeview'
|
|
330
288
|
const DATA_KEY$3 = 'lte.treeview';
|
|
331
289
|
const EVENT_KEY$3 = `.${DATA_KEY$3}`;
|
|
332
290
|
const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$3}`;
|
|
333
291
|
const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$3}`;
|
|
334
292
|
const EVENT_LOAD_DATA_API = `load${EVENT_KEY$3}`;
|
|
335
|
-
const CLASS_NAME_MENU_OPEN
|
|
336
|
-
const SELECTOR_NAV_ITEM
|
|
293
|
+
const CLASS_NAME_MENU_OPEN = 'menu-open';
|
|
294
|
+
const SELECTOR_NAV_ITEM = '.nav-item';
|
|
337
295
|
const SELECTOR_NAV_LINK = '.nav-link';
|
|
338
296
|
const SELECTOR_TREEVIEW_MENU = '.nav-treeview';
|
|
339
297
|
const SELECTOR_DATA_TOGGLE$1 = '[data-lte-toggle="treeview"]';
|
|
@@ -341,10 +299,6 @@
|
|
|
341
299
|
animationSpeed: 300,
|
|
342
300
|
accordion: true
|
|
343
301
|
};
|
|
344
|
-
/**
|
|
345
|
-
* Class Definition
|
|
346
|
-
* ====================================================
|
|
347
|
-
*/
|
|
348
302
|
class Treeview {
|
|
349
303
|
_element;
|
|
350
304
|
_config;
|
|
@@ -355,10 +309,10 @@
|
|
|
355
309
|
open() {
|
|
356
310
|
const event = new Event(EVENT_EXPANDED$1);
|
|
357
311
|
if (this._config.accordion) {
|
|
358
|
-
const openMenuList = this._element.parentElement?.querySelectorAll(`${SELECTOR_NAV_ITEM
|
|
312
|
+
const openMenuList = this._element.parentElement?.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
|
|
359
313
|
openMenuList?.forEach(openMenu => {
|
|
360
314
|
if (openMenu !== this._element.parentElement) {
|
|
361
|
-
openMenu.classList.remove(CLASS_NAME_MENU_OPEN
|
|
315
|
+
openMenu.classList.remove(CLASS_NAME_MENU_OPEN);
|
|
362
316
|
const childElement = openMenu?.querySelector(SELECTOR_TREEVIEW_MENU);
|
|
363
317
|
if (childElement) {
|
|
364
318
|
slideUp(childElement, this._config.animationSpeed);
|
|
@@ -366,7 +320,7 @@
|
|
|
366
320
|
}
|
|
367
321
|
});
|
|
368
322
|
}
|
|
369
|
-
this._element.classList.add(CLASS_NAME_MENU_OPEN
|
|
323
|
+
this._element.classList.add(CLASS_NAME_MENU_OPEN);
|
|
370
324
|
const childElement = this._element?.querySelector(SELECTOR_TREEVIEW_MENU);
|
|
371
325
|
if (childElement) {
|
|
372
326
|
slideDown(childElement, this._config.animationSpeed);
|
|
@@ -375,7 +329,7 @@
|
|
|
375
329
|
}
|
|
376
330
|
close() {
|
|
377
331
|
const event = new Event(EVENT_COLLAPSED$1);
|
|
378
|
-
this._element.classList.remove(CLASS_NAME_MENU_OPEN
|
|
332
|
+
this._element.classList.remove(CLASS_NAME_MENU_OPEN);
|
|
379
333
|
const childElement = this._element?.querySelector(SELECTOR_TREEVIEW_MENU);
|
|
380
334
|
if (childElement) {
|
|
381
335
|
slideUp(childElement, this._config.animationSpeed);
|
|
@@ -383,7 +337,7 @@
|
|
|
383
337
|
this._element.dispatchEvent(event);
|
|
384
338
|
}
|
|
385
339
|
toggle() {
|
|
386
|
-
if (this._element.classList.contains(CLASS_NAME_MENU_OPEN
|
|
340
|
+
if (this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
|
|
387
341
|
this.close();
|
|
388
342
|
}
|
|
389
343
|
else {
|
|
@@ -391,13 +345,8 @@
|
|
|
391
345
|
}
|
|
392
346
|
}
|
|
393
347
|
}
|
|
394
|
-
/**
|
|
395
|
-
* ------------------------------------------------------------------------
|
|
396
|
-
* Data Api implementation
|
|
397
|
-
* ------------------------------------------------------------------------
|
|
398
|
-
*/
|
|
399
348
|
onDOMContentLoaded(() => {
|
|
400
|
-
const openMenuItems = document.querySelectorAll(`${SELECTOR_NAV_ITEM
|
|
349
|
+
const openMenuItems = document.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
|
|
401
350
|
openMenuItems.forEach(menuItem => {
|
|
402
351
|
const childElement = menuItem.querySelector(SELECTOR_TREEVIEW_MENU);
|
|
403
352
|
if (childElement) {
|
|
@@ -410,11 +359,10 @@
|
|
|
410
359
|
button.forEach(btn => {
|
|
411
360
|
btn.addEventListener('click', event => {
|
|
412
361
|
const target = event.target;
|
|
413
|
-
const targetItem = target.closest(SELECTOR_NAV_ITEM
|
|
362
|
+
const targetItem = target.closest(SELECTOR_NAV_ITEM);
|
|
414
363
|
const targetLink = target.closest(SELECTOR_NAV_LINK);
|
|
415
364
|
const targetTreeviewMenu = targetItem?.querySelector(SELECTOR_TREEVIEW_MENU);
|
|
416
365
|
const lteToggleElement = event.currentTarget;
|
|
417
|
-
// Avoid creating Treeview instances on non menu elements
|
|
418
366
|
if (!targetTreeviewMenu) {
|
|
419
367
|
return;
|
|
420
368
|
}
|
|
@@ -422,10 +370,8 @@
|
|
|
422
370
|
event.preventDefault();
|
|
423
371
|
}
|
|
424
372
|
if (targetItem) {
|
|
425
|
-
// Read data attributes
|
|
426
373
|
const accordionAttr = lteToggleElement.dataset.accordion;
|
|
427
374
|
const animationSpeedAttr = lteToggleElement.dataset.animationSpeed;
|
|
428
|
-
// Build config from data attributes, fallback to Default
|
|
429
375
|
const config = {
|
|
430
376
|
accordion: accordionAttr === undefined ? Default.accordion : accordionAttr === 'true',
|
|
431
377
|
animationSpeed: animationSpeedAttr === undefined ? Default.animationSpeed : Number(animationSpeedAttr)
|
|
@@ -437,17 +383,6 @@
|
|
|
437
383
|
});
|
|
438
384
|
});
|
|
439
385
|
|
|
440
|
-
/**
|
|
441
|
-
* --------------------------------------------
|
|
442
|
-
* @file AdminLTE direct-chat.ts
|
|
443
|
-
* @description Direct chat for AdminLTE.
|
|
444
|
-
* @license MIT
|
|
445
|
-
* --------------------------------------------
|
|
446
|
-
*/
|
|
447
|
-
/**
|
|
448
|
-
* Constants
|
|
449
|
-
* ====================================================
|
|
450
|
-
*/
|
|
451
386
|
const DATA_KEY$2 = 'lte.direct-chat';
|
|
452
387
|
const EVENT_KEY$2 = `.${DATA_KEY$2}`;
|
|
453
388
|
const EVENT_EXPANDED = `expanded${EVENT_KEY$2}`;
|
|
@@ -455,10 +390,6 @@
|
|
|
455
390
|
const SELECTOR_DATA_TOGGLE = '[data-lte-toggle="chat-pane"]';
|
|
456
391
|
const SELECTOR_DIRECT_CHAT = '.direct-chat';
|
|
457
392
|
const CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open';
|
|
458
|
-
/**
|
|
459
|
-
* Class Definition
|
|
460
|
-
* ====================================================
|
|
461
|
-
*/
|
|
462
393
|
class DirectChat {
|
|
463
394
|
_element;
|
|
464
395
|
constructor(element) {
|
|
@@ -477,11 +408,6 @@
|
|
|
477
408
|
}
|
|
478
409
|
}
|
|
479
410
|
}
|
|
480
|
-
/**
|
|
481
|
-
*
|
|
482
|
-
* Data Api implementation
|
|
483
|
-
* ====================================================
|
|
484
|
-
*/
|
|
485
411
|
onDOMContentLoaded(() => {
|
|
486
412
|
const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE);
|
|
487
413
|
button.forEach(btn => {
|
|
@@ -497,17 +423,6 @@
|
|
|
497
423
|
});
|
|
498
424
|
});
|
|
499
425
|
|
|
500
|
-
/**
|
|
501
|
-
* --------------------------------------------
|
|
502
|
-
* @file AdminLTE fullscreen.ts
|
|
503
|
-
* @description Fullscreen plugin for AdminLTE.
|
|
504
|
-
* @license MIT
|
|
505
|
-
* --------------------------------------------
|
|
506
|
-
*/
|
|
507
|
-
/**
|
|
508
|
-
* Constants
|
|
509
|
-
* ============================================================================
|
|
510
|
-
*/
|
|
511
426
|
const DATA_KEY$1 = 'lte.fullscreen';
|
|
512
427
|
const EVENT_KEY$1 = `.${DATA_KEY$1}`;
|
|
513
428
|
const EVENT_MAXIMIZED = `maximized${EVENT_KEY$1}`;
|
|
@@ -515,10 +430,6 @@
|
|
|
515
430
|
const SELECTOR_FULLSCREEN_TOGGLE = '[data-lte-toggle="fullscreen"]';
|
|
516
431
|
const SELECTOR_MAXIMIZE_ICON = '[data-lte-icon="maximize"]';
|
|
517
432
|
const SELECTOR_MINIMIZE_ICON = '[data-lte-icon="minimize"]';
|
|
518
|
-
/**
|
|
519
|
-
* Class Definition.
|
|
520
|
-
* ============================================================================
|
|
521
|
-
*/
|
|
522
433
|
class FullScreen {
|
|
523
434
|
_element;
|
|
524
435
|
_config;
|
|
@@ -532,10 +443,10 @@
|
|
|
532
443
|
const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
|
|
533
444
|
void document.documentElement.requestFullscreen();
|
|
534
445
|
if (iconMaximize) {
|
|
535
|
-
iconMaximize.
|
|
446
|
+
iconMaximize.classList.add('d-none');
|
|
536
447
|
}
|
|
537
448
|
if (iconMinimize) {
|
|
538
|
-
iconMinimize.
|
|
449
|
+
iconMinimize.classList.remove('d-none');
|
|
539
450
|
}
|
|
540
451
|
this._element.dispatchEvent(event);
|
|
541
452
|
}
|
|
@@ -545,10 +456,10 @@
|
|
|
545
456
|
const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
|
|
546
457
|
void document.exitFullscreen();
|
|
547
458
|
if (iconMaximize) {
|
|
548
|
-
iconMaximize.
|
|
459
|
+
iconMaximize.classList.remove('d-none');
|
|
549
460
|
}
|
|
550
461
|
if (iconMinimize) {
|
|
551
|
-
iconMinimize.
|
|
462
|
+
iconMinimize.classList.add('d-none');
|
|
552
463
|
}
|
|
553
464
|
this._element.dispatchEvent(event);
|
|
554
465
|
}
|
|
@@ -563,10 +474,6 @@
|
|
|
563
474
|
}
|
|
564
475
|
}
|
|
565
476
|
}
|
|
566
|
-
/**
|
|
567
|
-
* Data Api implementation
|
|
568
|
-
* ============================================================================
|
|
569
|
-
*/
|
|
570
477
|
onDOMContentLoaded(() => {
|
|
571
478
|
const buttons = document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE);
|
|
572
479
|
buttons.forEach(btn => {
|
|
@@ -582,44 +489,24 @@
|
|
|
582
489
|
});
|
|
583
490
|
});
|
|
584
491
|
|
|
585
|
-
/**
|
|
586
|
-
* --------------------------------------------
|
|
587
|
-
* @file AdminLTE push-menu.ts
|
|
588
|
-
* @description Push menu for AdminLTE.
|
|
589
|
-
* @license MIT
|
|
590
|
-
* --------------------------------------------
|
|
591
|
-
*/
|
|
592
|
-
/**
|
|
593
|
-
* ------------------------------------------------------------------------
|
|
594
|
-
* Constants
|
|
595
|
-
* ------------------------------------------------------------------------
|
|
596
|
-
*/
|
|
597
492
|
const DATA_KEY = 'lte.push-menu';
|
|
598
493
|
const EVENT_KEY = `.${DATA_KEY}`;
|
|
599
494
|
const EVENT_OPEN = `open${EVENT_KEY}`;
|
|
600
495
|
const EVENT_COLLAPSE = `collapse${EVENT_KEY}`;
|
|
601
496
|
const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini';
|
|
602
|
-
const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse';
|
|
603
|
-
const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open';
|
|
604
497
|
const CLASS_NAME_SIDEBAR_EXPAND = 'sidebar-expand';
|
|
605
498
|
const CLASS_NAME_SIDEBAR_OVERLAY = 'sidebar-overlay';
|
|
606
|
-
const
|
|
499
|
+
const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse';
|
|
500
|
+
const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open';
|
|
607
501
|
const SELECTOR_APP_SIDEBAR = '.app-sidebar';
|
|
608
|
-
const SELECTOR_SIDEBAR_MENU = '.sidebar-menu';
|
|
609
|
-
const SELECTOR_NAV_ITEM = '.nav-item';
|
|
610
|
-
const SELECTOR_NAV_TREEVIEW = '.nav-treeview';
|
|
611
502
|
const SELECTOR_APP_WRAPPER = '.app-wrapper';
|
|
612
503
|
const SELECTOR_SIDEBAR_EXPAND = `[class*="${CLASS_NAME_SIDEBAR_EXPAND}"]`;
|
|
613
504
|
const SELECTOR_SIDEBAR_TOGGLE = '[data-lte-toggle="sidebar"]';
|
|
614
505
|
const STORAGE_KEY_SIDEBAR_STATE = 'lte.sidebar.state';
|
|
615
506
|
const Defaults = {
|
|
616
507
|
sidebarBreakpoint: 992,
|
|
617
|
-
enablePersistence:
|
|
508
|
+
enablePersistence: false
|
|
618
509
|
};
|
|
619
|
-
/**
|
|
620
|
-
* Class Definition
|
|
621
|
-
* ====================================================
|
|
622
|
-
*/
|
|
623
510
|
class PushMenu {
|
|
624
511
|
_element;
|
|
625
512
|
_config;
|
|
@@ -627,100 +514,82 @@
|
|
|
627
514
|
this._element = element;
|
|
628
515
|
this._config = { ...Defaults, ...config };
|
|
629
516
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
});
|
|
642
|
-
}
|
|
517
|
+
isCollapsed() {
|
|
518
|
+
return document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE);
|
|
519
|
+
}
|
|
520
|
+
isExplicitlyOpen() {
|
|
521
|
+
return document.body.classList.contains(CLASS_NAME_SIDEBAR_OPEN);
|
|
522
|
+
}
|
|
523
|
+
isMiniMode() {
|
|
524
|
+
return document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI);
|
|
525
|
+
}
|
|
526
|
+
isMobileSize() {
|
|
527
|
+
return globalThis.innerWidth <= this._config.sidebarBreakpoint;
|
|
643
528
|
}
|
|
644
529
|
expand() {
|
|
645
|
-
const event = new Event(EVENT_OPEN);
|
|
646
530
|
document.body.classList.remove(CLASS_NAME_SIDEBAR_COLLAPSE);
|
|
647
|
-
|
|
648
|
-
|
|
531
|
+
if (this.isMobileSize()) {
|
|
532
|
+
document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN);
|
|
533
|
+
}
|
|
534
|
+
this._element.dispatchEvent(new Event(EVENT_OPEN));
|
|
649
535
|
}
|
|
650
536
|
collapse() {
|
|
651
|
-
const event = new Event(EVENT_COLLAPSE);
|
|
652
537
|
document.body.classList.remove(CLASS_NAME_SIDEBAR_OPEN);
|
|
653
538
|
document.body.classList.add(CLASS_NAME_SIDEBAR_COLLAPSE);
|
|
654
|
-
this._element.dispatchEvent(
|
|
655
|
-
}
|
|
656
|
-
addSidebarBreakPoint() {
|
|
657
|
-
const sidebarExpandList = document.querySelector(SELECTOR_SIDEBAR_EXPAND)?.classList ?? [];
|
|
658
|
-
const sidebarExpand = Array.from(sidebarExpandList).find(className => className.startsWith(CLASS_NAME_SIDEBAR_EXPAND)) ?? '';
|
|
659
|
-
const sidebar = document.getElementsByClassName(sidebarExpand)[0];
|
|
660
|
-
const sidebarContent = globalThis.getComputedStyle(sidebar, '::before').getPropertyValue('content');
|
|
661
|
-
this._config = { ...this._config, sidebarBreakpoint: Number(sidebarContent.replace(/[^\d.-]/g, '')) };
|
|
662
|
-
// FIXED: Don't auto-collapse on mobile if sidebar is currently open
|
|
663
|
-
// This prevents resize events (triggered by scrolling) from closing the sidebar
|
|
664
|
-
const isCurrentlyOpen = document.body.classList.contains(CLASS_NAME_SIDEBAR_OPEN);
|
|
665
|
-
if (window.innerWidth <= this._config.sidebarBreakpoint) {
|
|
666
|
-
// Only collapse if not currently open (prevents scroll-triggered closes)
|
|
667
|
-
if (!isCurrentlyOpen) {
|
|
668
|
-
this.collapse();
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
if (!document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI)) {
|
|
673
|
-
this.expand();
|
|
674
|
-
}
|
|
675
|
-
if (document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI) && document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
|
|
676
|
-
this.collapse();
|
|
677
|
-
}
|
|
678
|
-
}
|
|
539
|
+
this._element.dispatchEvent(new Event(EVENT_COLLAPSE));
|
|
679
540
|
}
|
|
680
541
|
toggle() {
|
|
681
|
-
|
|
542
|
+
const isCollapsed = this.isCollapsed();
|
|
543
|
+
if (isCollapsed) {
|
|
682
544
|
this.expand();
|
|
683
545
|
}
|
|
684
546
|
else {
|
|
685
547
|
this.collapse();
|
|
686
548
|
}
|
|
687
|
-
this.
|
|
549
|
+
if (this._config.enablePersistence) {
|
|
550
|
+
this.saveSidebarState(isCollapsed ? CLASS_NAME_SIDEBAR_OPEN : CLASS_NAME_SIDEBAR_COLLAPSE);
|
|
551
|
+
}
|
|
688
552
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
saveSidebarState() {
|
|
693
|
-
if (!this._config.enablePersistence) {
|
|
553
|
+
setupSidebarBreakPoint() {
|
|
554
|
+
const sidebarExpand = document.querySelector(SELECTOR_SIDEBAR_EXPAND);
|
|
555
|
+
if (!sidebarExpand) {
|
|
694
556
|
return;
|
|
695
557
|
}
|
|
696
|
-
|
|
697
|
-
|
|
558
|
+
const content = globalThis.getComputedStyle(sidebarExpand, '::before')
|
|
559
|
+
.getPropertyValue('content');
|
|
560
|
+
if (!content || content === 'none') {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const breakpointValue = Number(content.replace(/[^\d.-]/g, ''));
|
|
564
|
+
if (Number.isNaN(breakpointValue)) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
this._config = { ...this._config, sidebarBreakpoint: breakpointValue };
|
|
568
|
+
}
|
|
569
|
+
updateStateByResponsiveLogic() {
|
|
570
|
+
if (this.isMobileSize()) {
|
|
571
|
+
if (!this.isExplicitlyOpen()) {
|
|
572
|
+
this.collapse();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
if (!(this.isMiniMode() && this.isCollapsed())) {
|
|
577
|
+
this.expand();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
saveSidebarState(state) {
|
|
582
|
+
if (globalThis.localStorage === undefined) {
|
|
698
583
|
return;
|
|
699
584
|
}
|
|
700
585
|
try {
|
|
701
|
-
const state = document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE) ?
|
|
702
|
-
CLASS_NAME_SIDEBAR_COLLAPSE :
|
|
703
|
-
CLASS_NAME_SIDEBAR_OPEN;
|
|
704
586
|
localStorage.setItem(STORAGE_KEY_SIDEBAR_STATE, state);
|
|
705
587
|
}
|
|
706
588
|
catch {
|
|
707
|
-
// localStorage may be unavailable (private browsing, quota exceeded, etc.)
|
|
708
589
|
}
|
|
709
590
|
}
|
|
710
|
-
/**
|
|
711
|
-
* Load sidebar state from localStorage
|
|
712
|
-
* Only applies on desktop; mobile always starts collapsed
|
|
713
|
-
*/
|
|
714
591
|
loadSidebarState() {
|
|
715
|
-
if (
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
// Check for SSR environment
|
|
719
|
-
if (globalThis.window === undefined || globalThis.localStorage === undefined) {
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
// Don't restore state on mobile - let responsive behavior handle it
|
|
723
|
-
if (globalThis.innerWidth <= this._config.sidebarBreakpoint) {
|
|
592
|
+
if (globalThis.localStorage === undefined) {
|
|
724
593
|
return;
|
|
725
594
|
}
|
|
726
595
|
try {
|
|
@@ -731,36 +600,62 @@
|
|
|
731
600
|
else if (storedState === CLASS_NAME_SIDEBAR_OPEN) {
|
|
732
601
|
this.expand();
|
|
733
602
|
}
|
|
734
|
-
|
|
603
|
+
else {
|
|
604
|
+
this.updateStateByResponsiveLogic();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
this.updateStateByResponsiveLogic();
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
clearSidebarState() {
|
|
612
|
+
if (globalThis.localStorage === undefined) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
try {
|
|
616
|
+
localStorage.removeItem(STORAGE_KEY_SIDEBAR_STATE);
|
|
735
617
|
}
|
|
736
618
|
catch {
|
|
737
|
-
// localStorage may be unavailable
|
|
738
619
|
}
|
|
739
620
|
}
|
|
740
621
|
init() {
|
|
741
|
-
this.
|
|
742
|
-
this.
|
|
622
|
+
this.setupSidebarBreakPoint();
|
|
623
|
+
if (!this._config.enablePersistence) {
|
|
624
|
+
this.clearSidebarState();
|
|
625
|
+
}
|
|
626
|
+
if (this._config.enablePersistence && !this.isMobileSize()) {
|
|
627
|
+
this.loadSidebarState();
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
this.updateStateByResponsiveLogic();
|
|
631
|
+
}
|
|
743
632
|
}
|
|
744
633
|
}
|
|
745
|
-
/**
|
|
746
|
-
* ------------------------------------------------------------------------
|
|
747
|
-
* Data Api implementation
|
|
748
|
-
* ------------------------------------------------------------------------
|
|
749
|
-
*/
|
|
750
634
|
onDOMContentLoaded(() => {
|
|
751
635
|
const sidebar = document?.querySelector(SELECTOR_APP_SIDEBAR);
|
|
752
|
-
if (sidebar) {
|
|
753
|
-
|
|
754
|
-
data.init();
|
|
755
|
-
window.addEventListener('resize', () => {
|
|
756
|
-
data.init();
|
|
757
|
-
});
|
|
636
|
+
if (!sidebar) {
|
|
637
|
+
return;
|
|
758
638
|
}
|
|
639
|
+
const sidebarBreakpointAttr = sidebar.dataset.sidebarBreakpoint;
|
|
640
|
+
const enablePersistenceAttr = sidebar.dataset.enablePersistence;
|
|
641
|
+
const config = {
|
|
642
|
+
sidebarBreakpoint: sidebarBreakpointAttr === undefined ?
|
|
643
|
+
Defaults.sidebarBreakpoint :
|
|
644
|
+
Number(sidebarBreakpointAttr),
|
|
645
|
+
enablePersistence: enablePersistenceAttr === undefined ?
|
|
646
|
+
Defaults.enablePersistence :
|
|
647
|
+
enablePersistenceAttr === 'true'
|
|
648
|
+
};
|
|
649
|
+
const pushMenu = new PushMenu(sidebar, config);
|
|
650
|
+
pushMenu.init();
|
|
651
|
+
window.addEventListener('resize', () => {
|
|
652
|
+
pushMenu.setupSidebarBreakPoint();
|
|
653
|
+
pushMenu.updateStateByResponsiveLogic();
|
|
654
|
+
});
|
|
759
655
|
const sidebarOverlay = document.createElement('div');
|
|
760
656
|
sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY;
|
|
761
657
|
document.querySelector(SELECTOR_APP_WRAPPER)?.append(sidebarOverlay);
|
|
762
658
|
let overlayTouchMoved = false;
|
|
763
|
-
// Handle touch events on overlay (area outside sidebar)
|
|
764
659
|
sidebarOverlay.addEventListener('touchstart', () => {
|
|
765
660
|
overlayTouchMoved = false;
|
|
766
661
|
}, { passive: true });
|
|
@@ -770,17 +665,13 @@
|
|
|
770
665
|
sidebarOverlay.addEventListener('touchend', event => {
|
|
771
666
|
if (!overlayTouchMoved) {
|
|
772
667
|
event.preventDefault();
|
|
773
|
-
|
|
774
|
-
const data = new PushMenu(target, Defaults);
|
|
775
|
-
data.collapse();
|
|
668
|
+
pushMenu.collapse();
|
|
776
669
|
}
|
|
777
670
|
overlayTouchMoved = false;
|
|
778
671
|
}, { passive: false });
|
|
779
672
|
sidebarOverlay.addEventListener('click', event => {
|
|
780
673
|
event.preventDefault();
|
|
781
|
-
|
|
782
|
-
const data = new PushMenu(target, Defaults);
|
|
783
|
-
data.collapse();
|
|
674
|
+
pushMenu.collapse();
|
|
784
675
|
});
|
|
785
676
|
const fullBtn = document.querySelectorAll(SELECTOR_SIDEBAR_TOGGLE);
|
|
786
677
|
fullBtn.forEach(btn => {
|
|
@@ -792,17 +683,12 @@
|
|
|
792
683
|
}
|
|
793
684
|
if (button) {
|
|
794
685
|
event?.preventDefault();
|
|
795
|
-
|
|
796
|
-
data.toggle();
|
|
686
|
+
pushMenu.toggle();
|
|
797
687
|
}
|
|
798
688
|
});
|
|
799
689
|
});
|
|
800
690
|
});
|
|
801
691
|
|
|
802
|
-
/**
|
|
803
|
-
* AdminLTE Accessibility Module
|
|
804
|
-
* WCAG 2.1 AA Compliance Features
|
|
805
|
-
*/
|
|
806
692
|
class AccessibilityManager {
|
|
807
693
|
config;
|
|
808
694
|
liveRegion = null;
|
|
@@ -838,7 +724,6 @@
|
|
|
838
724
|
this.initTableAccessibility();
|
|
839
725
|
this.initFormAccessibility();
|
|
840
726
|
}
|
|
841
|
-
// WCAG 4.1.3: Status Messages
|
|
842
727
|
createLiveRegion() {
|
|
843
728
|
if (this.liveRegion)
|
|
844
729
|
return;
|
|
@@ -850,7 +735,6 @@
|
|
|
850
735
|
this.liveRegion.setAttribute('role', 'status');
|
|
851
736
|
document.body.append(this.liveRegion);
|
|
852
737
|
}
|
|
853
|
-
// WCAG 2.4.1: Bypass Blocks
|
|
854
738
|
addSkipLinks() {
|
|
855
739
|
const skipLinksContainer = document.createElement('div');
|
|
856
740
|
skipLinksContainer.className = 'skip-links';
|
|
@@ -865,7 +749,6 @@
|
|
|
865
749
|
skipLinksContainer.append(skipToMain);
|
|
866
750
|
skipLinksContainer.append(skipToNav);
|
|
867
751
|
document.body.insertBefore(skipLinksContainer, document.body.firstChild);
|
|
868
|
-
// Ensure targets exist and are focusable
|
|
869
752
|
this.ensureSkipTargets();
|
|
870
753
|
}
|
|
871
754
|
ensureSkipTargets() {
|
|
@@ -884,7 +767,6 @@
|
|
|
884
767
|
nav.setAttribute('tabindex', '-1');
|
|
885
768
|
}
|
|
886
769
|
}
|
|
887
|
-
// WCAG 2.4.3: Focus Order & 2.4.7: Focus Visible
|
|
888
770
|
initFocusManagement() {
|
|
889
771
|
document.addEventListener('keydown', (event) => {
|
|
890
772
|
if (event.key === 'Tab') {
|
|
@@ -894,7 +776,6 @@
|
|
|
894
776
|
this.handleEscapeKey(event);
|
|
895
777
|
}
|
|
896
778
|
});
|
|
897
|
-
// Focus management for modals and dropdowns
|
|
898
779
|
this.initModalFocusManagement();
|
|
899
780
|
this.initDropdownFocusManagement();
|
|
900
781
|
}
|
|
@@ -902,14 +783,12 @@
|
|
|
902
783
|
const focusableElements = this.getFocusableElements();
|
|
903
784
|
const currentIndex = focusableElements.indexOf(document.activeElement);
|
|
904
785
|
if (event.shiftKey) {
|
|
905
|
-
// Shift+Tab (backward)
|
|
906
786
|
if (currentIndex <= 0) {
|
|
907
787
|
event.preventDefault();
|
|
908
788
|
focusableElements.at(-1)?.focus();
|
|
909
789
|
}
|
|
910
790
|
}
|
|
911
791
|
else if (currentIndex >= focusableElements.length - 1) {
|
|
912
|
-
// Tab (forward)
|
|
913
792
|
event.preventDefault();
|
|
914
793
|
focusableElements[0]?.focus();
|
|
915
794
|
}
|
|
@@ -927,30 +806,23 @@
|
|
|
927
806
|
return Array.from(document.querySelectorAll(selector));
|
|
928
807
|
}
|
|
929
808
|
handleEscapeKey(event) {
|
|
930
|
-
// Close modals, dropdowns, etc.
|
|
931
809
|
const activeModal = document.querySelector('.modal.show');
|
|
932
|
-
const activeDropdown = document.querySelector('.dropdown-menu.show');
|
|
933
810
|
if (activeModal) {
|
|
934
|
-
|
|
935
|
-
closeButton?.click();
|
|
936
|
-
event.preventDefault();
|
|
811
|
+
return;
|
|
937
812
|
}
|
|
938
|
-
|
|
813
|
+
const activeDropdown = document.querySelector('.dropdown-menu.show');
|
|
814
|
+
if (activeDropdown) {
|
|
939
815
|
const toggleButton = document.querySelector('[data-bs-toggle="dropdown"][aria-expanded="true"]');
|
|
940
816
|
toggleButton?.click();
|
|
941
817
|
event.preventDefault();
|
|
942
818
|
}
|
|
943
819
|
}
|
|
944
|
-
// WCAG 2.1.1: Keyboard Access
|
|
945
820
|
initKeyboardNavigation() {
|
|
946
|
-
// Add keyboard support for custom components
|
|
947
821
|
document.addEventListener('keydown', (event) => {
|
|
948
822
|
const target = event.target;
|
|
949
|
-
// Handle arrow key navigation for menus
|
|
950
823
|
if (target.closest('.nav, .navbar-nav, .dropdown-menu')) {
|
|
951
824
|
this.handleMenuNavigation(event);
|
|
952
825
|
}
|
|
953
|
-
// Handle Enter and Space for custom buttons
|
|
954
826
|
if ((event.key === 'Enter' || event.key === ' ') && target.hasAttribute('role') && target.getAttribute('role') === 'button' && !target.matches('button, input[type="button"], input[type="submit"]')) {
|
|
955
827
|
event.preventDefault();
|
|
956
828
|
target.click();
|
|
@@ -991,14 +863,11 @@
|
|
|
991
863
|
event.preventDefault();
|
|
992
864
|
menuItems[nextIndex]?.focus();
|
|
993
865
|
}
|
|
994
|
-
// WCAG 2.3.3: Animation from Interactions
|
|
995
866
|
respectReducedMotion() {
|
|
996
867
|
const prefersReducedMotion = globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
997
868
|
if (prefersReducedMotion) {
|
|
998
869
|
document.body.classList.add('reduce-motion');
|
|
999
|
-
// Disable smooth scrolling
|
|
1000
870
|
document.documentElement.style.scrollBehavior = 'auto';
|
|
1001
|
-
// Reduce animation duration
|
|
1002
871
|
const style = document.createElement('style');
|
|
1003
872
|
style.textContent = `
|
|
1004
873
|
*, *::before, *::after {
|
|
@@ -1010,18 +879,15 @@
|
|
|
1010
879
|
document.head.append(style);
|
|
1011
880
|
}
|
|
1012
881
|
}
|
|
1013
|
-
// WCAG 3.3.1: Error Identification
|
|
1014
882
|
initErrorAnnouncements() {
|
|
1015
883
|
const observer = new MutationObserver((mutations) => {
|
|
1016
884
|
mutations.forEach((mutation) => {
|
|
1017
885
|
mutation.addedNodes.forEach((node) => {
|
|
1018
886
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1019
887
|
const element = node;
|
|
1020
|
-
// Check for error messages
|
|
1021
888
|
if (element.matches('.alert-danger, .invalid-feedback, .error')) {
|
|
1022
889
|
this.announce(element.textContent || 'Error occurred', 'assertive');
|
|
1023
890
|
}
|
|
1024
|
-
// Check for success messages
|
|
1025
891
|
if (element.matches('.alert-success, .success')) {
|
|
1026
892
|
this.announce(element.textContent || 'Success', 'polite');
|
|
1027
893
|
}
|
|
@@ -1034,14 +900,11 @@
|
|
|
1034
900
|
subtree: true
|
|
1035
901
|
});
|
|
1036
902
|
}
|
|
1037
|
-
// WCAG 1.3.1: Info and Relationships
|
|
1038
903
|
initTableAccessibility() {
|
|
1039
904
|
document.querySelectorAll('table').forEach((table) => {
|
|
1040
|
-
// Add table role if missing
|
|
1041
905
|
if (!table.hasAttribute('role')) {
|
|
1042
906
|
table.setAttribute('role', 'table');
|
|
1043
907
|
}
|
|
1044
|
-
// Ensure headers have proper scope
|
|
1045
908
|
table.querySelectorAll('th').forEach((th) => {
|
|
1046
909
|
if (!th.hasAttribute('scope')) {
|
|
1047
910
|
const isInThead = th.closest('thead');
|
|
@@ -1054,7 +917,6 @@
|
|
|
1054
917
|
}
|
|
1055
918
|
}
|
|
1056
919
|
});
|
|
1057
|
-
// Add caption if missing but title exists
|
|
1058
920
|
if (!table.querySelector('caption') && table.hasAttribute('title')) {
|
|
1059
921
|
const caption = document.createElement('caption');
|
|
1060
922
|
caption.textContent = table.getAttribute('title') || '';
|
|
@@ -1062,18 +924,15 @@
|
|
|
1062
924
|
}
|
|
1063
925
|
});
|
|
1064
926
|
}
|
|
1065
|
-
// WCAG 3.3.2: Labels or Instructions
|
|
1066
927
|
initFormAccessibility() {
|
|
1067
928
|
document.querySelectorAll('input, select, textarea').forEach((input) => {
|
|
1068
929
|
const htmlInput = input;
|
|
1069
|
-
// Ensure all inputs have labels
|
|
1070
930
|
if (!htmlInput.labels?.length && !htmlInput.hasAttribute('aria-label') && !htmlInput.hasAttribute('aria-labelledby')) {
|
|
1071
931
|
const placeholder = htmlInput.getAttribute('placeholder');
|
|
1072
932
|
if (placeholder) {
|
|
1073
933
|
htmlInput.setAttribute('aria-label', placeholder);
|
|
1074
934
|
}
|
|
1075
935
|
}
|
|
1076
|
-
// Add required indicators
|
|
1077
936
|
if (htmlInput.hasAttribute('required')) {
|
|
1078
937
|
const label = htmlInput.labels?.[0];
|
|
1079
938
|
if (label && !label.querySelector('.required-indicator')) {
|
|
@@ -1083,8 +942,6 @@
|
|
|
1083
942
|
label.append(indicator);
|
|
1084
943
|
}
|
|
1085
944
|
}
|
|
1086
|
-
// Handle invalid state unless the element explicitly opts out via the
|
|
1087
|
-
// 'disable-adminlte-validations' class.
|
|
1088
945
|
if (!htmlInput.classList.contains('disable-adminlte-validations')) {
|
|
1089
946
|
htmlInput.addEventListener('invalid', () => {
|
|
1090
947
|
this.handleFormError(htmlInput);
|
|
@@ -1100,10 +957,6 @@
|
|
|
1100
957
|
errorElement.id = errorId;
|
|
1101
958
|
errorElement.className = 'invalid-feedback';
|
|
1102
959
|
errorElement.setAttribute('role', 'alert');
|
|
1103
|
-
// Always append the error element as the last child of the parent.
|
|
1104
|
-
// This prevents breaking layouts where inputs use Bootstrap's
|
|
1105
|
-
// `.input-group-text` decorators, ensuring the error stays below
|
|
1106
|
-
// the entire input group.
|
|
1107
960
|
input.parentNode?.append(errorElement);
|
|
1108
961
|
}
|
|
1109
962
|
errorElement.textContent = input.validationMessage;
|
|
@@ -1111,7 +964,6 @@
|
|
|
1111
964
|
input.classList.add('is-invalid');
|
|
1112
965
|
this.announce(`Error in ${input.labels?.[0]?.textContent || input.name}: ${input.validationMessage}`, 'assertive');
|
|
1113
966
|
}
|
|
1114
|
-
// Modal focus management
|
|
1115
967
|
initModalFocusManagement() {
|
|
1116
968
|
document.addEventListener('shown.bs.modal', (event) => {
|
|
1117
969
|
const modal = event.target;
|
|
@@ -1119,18 +971,15 @@
|
|
|
1119
971
|
if (focusableElements.length > 0) {
|
|
1120
972
|
focusableElements[0].focus();
|
|
1121
973
|
}
|
|
1122
|
-
// Store previous focus
|
|
1123
974
|
this.focusHistory.push(document.activeElement);
|
|
1124
975
|
});
|
|
1125
976
|
document.addEventListener('hidden.bs.modal', () => {
|
|
1126
|
-
// Restore previous focus
|
|
1127
977
|
const previousElement = this.focusHistory.pop();
|
|
1128
978
|
if (previousElement) {
|
|
1129
979
|
previousElement.focus();
|
|
1130
980
|
}
|
|
1131
981
|
});
|
|
1132
982
|
}
|
|
1133
|
-
// Dropdown focus management
|
|
1134
983
|
initDropdownFocusManagement() {
|
|
1135
984
|
document.addEventListener('shown.bs.dropdown', (event) => {
|
|
1136
985
|
const dropdown = event.target;
|
|
@@ -1141,7 +990,6 @@
|
|
|
1141
990
|
}
|
|
1142
991
|
});
|
|
1143
992
|
}
|
|
1144
|
-
// Public API methods
|
|
1145
993
|
announce(message, priority = 'polite') {
|
|
1146
994
|
if (!this.liveRegion) {
|
|
1147
995
|
this.createLiveRegion();
|
|
@@ -1149,7 +997,6 @@
|
|
|
1149
997
|
if (this.liveRegion) {
|
|
1150
998
|
this.liveRegion.setAttribute('aria-live', priority);
|
|
1151
999
|
this.liveRegion.textContent = message;
|
|
1152
|
-
// Clear after announcement
|
|
1153
1000
|
setTimeout(() => {
|
|
1154
1001
|
if (this.liveRegion) {
|
|
1155
1002
|
this.liveRegion.textContent = '';
|
|
@@ -1161,7 +1008,6 @@
|
|
|
1161
1008
|
const element = document.querySelector(selector);
|
|
1162
1009
|
if (element) {
|
|
1163
1010
|
element.focus();
|
|
1164
|
-
// Ensure element is visible
|
|
1165
1011
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1166
1012
|
}
|
|
1167
1013
|
}
|
|
@@ -1186,7 +1032,6 @@
|
|
|
1186
1032
|
});
|
|
1187
1033
|
}
|
|
1188
1034
|
addLandmarks() {
|
|
1189
|
-
// Add main landmark if missing
|
|
1190
1035
|
const main = document.querySelector('main');
|
|
1191
1036
|
if (!main) {
|
|
1192
1037
|
const appMain = document.querySelector('.app-main');
|
|
@@ -1195,7 +1040,6 @@
|
|
|
1195
1040
|
appMain.id = 'main';
|
|
1196
1041
|
}
|
|
1197
1042
|
}
|
|
1198
|
-
// Add navigation landmarks
|
|
1199
1043
|
document.querySelectorAll('.navbar-nav, .nav').forEach((nav, index) => {
|
|
1200
1044
|
if (!nav.hasAttribute('role')) {
|
|
1201
1045
|
nav.setAttribute('role', 'navigation');
|
|
@@ -1204,35 +1048,17 @@
|
|
|
1204
1048
|
nav.setAttribute('aria-label', `Navigation ${index + 1}`);
|
|
1205
1049
|
}
|
|
1206
1050
|
});
|
|
1207
|
-
// Add search landmark
|
|
1208
1051
|
const searchForm = document.querySelector('form[role="search"], .navbar-search');
|
|
1209
1052
|
if (searchForm && !searchForm.hasAttribute('role')) {
|
|
1210
1053
|
searchForm.setAttribute('role', 'search');
|
|
1211
1054
|
}
|
|
1212
1055
|
}
|
|
1213
1056
|
}
|
|
1214
|
-
// Initialize accessibility when DOM is ready
|
|
1215
1057
|
const initAccessibility = (config) => {
|
|
1216
1058
|
return new AccessibilityManager(config);
|
|
1217
1059
|
};
|
|
1218
1060
|
|
|
1219
|
-
/**
|
|
1220
|
-
* AdminLTE v4.0.0-rc5
|
|
1221
|
-
* Author: Colorlib
|
|
1222
|
-
* Website: AdminLTE.io <https://adminlte.io>
|
|
1223
|
-
* License: Open source - MIT <https://opensource.org/licenses/MIT>
|
|
1224
|
-
*/
|
|
1225
1061
|
onDOMContentLoaded(() => {
|
|
1226
|
-
/**
|
|
1227
|
-
* Initialize AdminLTE Core Components
|
|
1228
|
-
* -------------------------------
|
|
1229
|
-
*/
|
|
1230
|
-
const layout = new Layout(document.body);
|
|
1231
|
-
layout.holdTransition();
|
|
1232
|
-
/**
|
|
1233
|
-
* Initialize Accessibility Features - WCAG 2.1 AA Compliance
|
|
1234
|
-
* --------------------------------------------------------
|
|
1235
|
-
*/
|
|
1236
1062
|
const accessibilityManager = initAccessibility({
|
|
1237
1063
|
announcements: true,
|
|
1238
1064
|
skipLinks: true,
|
|
@@ -1240,12 +1066,7 @@
|
|
|
1240
1066
|
keyboardNavigation: true,
|
|
1241
1067
|
reducedMotion: true
|
|
1242
1068
|
});
|
|
1243
|
-
// Add semantic landmarks
|
|
1244
1069
|
accessibilityManager.addLandmarks();
|
|
1245
|
-
// Mark app as loaded after initialization
|
|
1246
|
-
setTimeout(() => {
|
|
1247
|
-
document.body.classList.add('app-loaded');
|
|
1248
|
-
}, 400);
|
|
1249
1070
|
});
|
|
1250
1071
|
|
|
1251
1072
|
exports.CardWidget = CardWidget;
|