admin-lte 4.0.2 → 4.1.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.
Files changed (146) hide show
  1. package/ACCESSIBILITY-COMPLIANCE.md +60 -339
  2. package/CHANGELOG.md +66 -0
  3. package/README.md +15 -2
  4. package/dist/css/adminlte-docs.css +878 -0
  5. package/dist/css/adminlte-docs.css.map +1 -0
  6. package/dist/css/adminlte-docs.min.css +7 -0
  7. package/dist/css/adminlte-docs.min.css.map +1 -0
  8. package/dist/css/adminlte-docs.rtl.css +877 -0
  9. package/dist/css/adminlte-docs.rtl.css.map +1 -0
  10. package/dist/css/adminlte-docs.rtl.min.css +7 -0
  11. package/dist/css/adminlte-docs.rtl.min.css.map +1 -0
  12. package/dist/css/adminlte.css +1176 -2718
  13. package/dist/css/adminlte.css.map +1 -1
  14. package/dist/css/adminlte.min.css +2 -2
  15. package/dist/css/adminlte.min.css.map +1 -1
  16. package/dist/css/adminlte.rtl.css +1176 -2718
  17. package/dist/css/adminlte.rtl.css.map +1 -1
  18. package/dist/css/adminlte.rtl.min.css +2 -2
  19. package/dist/css/adminlte.rtl.min.css.map +1 -1
  20. package/dist/js/adminlte.esm.js +1365 -0
  21. package/dist/js/adminlte.esm.js.map +1 -0
  22. package/dist/js/adminlte.esm.min.js +7 -0
  23. package/dist/js/adminlte.esm.min.js.map +1 -0
  24. package/dist/js/adminlte.js +606 -310
  25. package/dist/js/adminlte.js.map +1 -1
  26. package/dist/js/adminlte.min.js +2 -2
  27. package/dist/js/adminlte.min.js.map +1 -1
  28. package/dist/js/types/accessibility.d.ts +46 -0
  29. package/dist/js/types/adminlte.d.ts +9 -0
  30. package/dist/js/types/base-component.d.ts +36 -0
  31. package/dist/js/types/card-widget.d.ts +30 -0
  32. package/dist/js/types/color-mode.d.ts +56 -0
  33. package/dist/js/types/direct-chat.d.ts +19 -0
  34. package/dist/js/types/fullscreen.d.ts +21 -0
  35. package/dist/js/types/layout.d.ts +19 -0
  36. package/dist/js/types/push-menu.d.ts +102 -0
  37. package/dist/js/types/treeview.d.ts +27 -0
  38. package/dist/js/types/util/index.d.ts +42 -0
  39. package/package.json +60 -21
  40. package/src/scss/_app-header.scss +3 -1
  41. package/src/scss/_app-main.scss +4 -0
  42. package/src/scss/_app-sidebar.scss +3 -1
  43. package/src/scss/_bootstrap-overrides.scss +60 -0
  44. package/src/scss/_callouts.scss +13 -3
  45. package/src/scss/_dropdown.scss +3 -1
  46. package/src/scss/_info-box.scss +3 -1
  47. package/src/scss/_progress-bars.scss +3 -1
  48. package/src/scss/_table.scss +4 -2
  49. package/src/scss/adminlte-docs.scss +25 -0
  50. package/src/scss/adminlte.scss +5 -4
  51. package/src/scss/pages/_login_and_register.scss +3 -1
  52. package/src/scss/parts/_core.scss +0 -1
  53. package/src/ts/accessibility.ts +129 -69
  54. package/src/ts/adminlte.ts +3 -1
  55. package/src/ts/base-component.ts +84 -0
  56. package/src/ts/card-widget.ts +134 -85
  57. package/src/ts/color-mode.ts +174 -0
  58. package/src/ts/direct-chat.ts +38 -32
  59. package/src/ts/fullscreen.ts +78 -63
  60. package/src/ts/layout.ts +2 -1
  61. package/src/ts/push-menu.ts +93 -44
  62. package/src/ts/treeview.ts +107 -47
  63. package/src/ts/util/index.ts +114 -29
  64. package/.browserslistrc +0 -17
  65. package/.prettierrc +0 -6
  66. package/CODE_OF_CONDUCT.md +0 -43
  67. package/SECURITY.md +0 -99
  68. package/dist/UI/general.html +0 -2317
  69. package/dist/UI/icons.html +0 -1126
  70. package/dist/UI/timeline.html +0 -1212
  71. package/dist/docs/browser-support.html +0 -826
  72. package/dist/docs/color-mode.html +0 -1142
  73. package/dist/docs/components/main-header.html +0 -1209
  74. package/dist/docs/components/main-sidebar.html +0 -924
  75. package/dist/docs/customization.html +0 -1080
  76. package/dist/docs/deployment.html +0 -1145
  77. package/dist/docs/faq.html +0 -1392
  78. package/dist/docs/getting-started.html +0 -991
  79. package/dist/docs/how-to-contribute.html +0 -890
  80. package/dist/docs/index.html +0 -16
  81. package/dist/docs/integrations.html +0 -1231
  82. package/dist/docs/introduction.html +0 -1008
  83. package/dist/docs/javascript/accessibility.html +0 -949
  84. package/dist/docs/javascript/card-widget.html +0 -1019
  85. package/dist/docs/javascript/direct-chat.html +0 -896
  86. package/dist/docs/javascript/fullscreen.html +0 -905
  87. package/dist/docs/javascript/layout.html +0 -908
  88. package/dist/docs/javascript/plugins-overview.html +0 -1144
  89. package/dist/docs/javascript/pushmenu.html +0 -1045
  90. package/dist/docs/javascript/treeview.html +0 -1024
  91. package/dist/docs/layout-blueprint.html +0 -1087
  92. package/dist/docs/layout.html +0 -842
  93. package/dist/docs/license.html +0 -807
  94. package/dist/docs/migration.html +0 -1149
  95. package/dist/docs/recipes.html +0 -1185
  96. package/dist/docs/rtl.html +0 -957
  97. package/dist/examples/lockscreen.html +0 -249
  98. package/dist/examples/login-v2.html +0 -282
  99. package/dist/examples/login.html +0 -272
  100. package/dist/examples/register-v2.html +0 -291
  101. package/dist/examples/register.html +0 -277
  102. package/dist/forms/elements.html +0 -1405
  103. package/dist/forms/layout.html +0 -1267
  104. package/dist/forms/validation.html +0 -1275
  105. package/dist/forms/wizard.html +0 -1410
  106. package/dist/generate/theme.html +0 -1082
  107. package/dist/index.html +0 -1745
  108. package/dist/index2.html +0 -2277
  109. package/dist/index3.html +0 -1532
  110. package/dist/layout/collapsed-sidebar-without-hover.html +0 -1144
  111. package/dist/layout/collapsed-sidebar.html +0 -1137
  112. package/dist/layout/fixed-complete.html +0 -1138
  113. package/dist/layout/fixed-footer.html +0 -1136
  114. package/dist/layout/fixed-header.html +0 -1136
  115. package/dist/layout/fixed-sidebar.html +0 -1138
  116. package/dist/layout/layout-custom-area.html +0 -1177
  117. package/dist/layout/layout-rtl.html +0 -1137
  118. package/dist/layout/logo-switch.html +0 -683
  119. package/dist/layout/sidebar-mini.html +0 -1137
  120. package/dist/layout/unfixed-sidebar.html +0 -1136
  121. package/dist/mailbox/compose.html +0 -1151
  122. package/dist/mailbox/inbox.html +0 -1509
  123. package/dist/mailbox/read.html +0 -1202
  124. package/dist/pages/404.html +0 -239
  125. package/dist/pages/500.html +0 -227
  126. package/dist/pages/calendar.html +0 -1266
  127. package/dist/pages/chat.html +0 -1427
  128. package/dist/pages/faq.html +0 -1383
  129. package/dist/pages/file-manager.html +0 -1835
  130. package/dist/pages/invoice.html +0 -1199
  131. package/dist/pages/kanban.html +0 -1439
  132. package/dist/pages/maintenance.html +0 -230
  133. package/dist/pages/pricing.html +0 -1279
  134. package/dist/pages/profile.html +0 -1382
  135. package/dist/pages/projects.html +0 -1644
  136. package/dist/pages/settings.html +0 -1387
  137. package/dist/tables/data.html +0 -1319
  138. package/dist/tables/simple.html +0 -1435
  139. package/dist/widgets/cards.html +0 -1356
  140. package/dist/widgets/info-box.html +0 -1429
  141. package/dist/widgets/small-box.html +0 -1225
  142. package/eslint.config.js +0 -82
  143. package/github-issue-response.md +0 -40
  144. package/index.html +0 -12
  145. package/netlify.toml +0 -16
  146. package/src/scss/_bootstrap-variables.scss +0 -1766
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * AdminLTE v4.0.2 (https://adminlte.io)
2
+ * AdminLTE v4.1.0 (https://adminlte.io)
3
3
  * Copyright 2014-2026 Colorlib <https://colorlib.com>
4
4
  * Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
5
5
  */
@@ -9,25 +9,57 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.adminlte = {}));
10
10
  })(this, (function (exports) { 'use strict';
11
11
 
12
- const domContentLoadedCallbacks = [];
13
- const onDOMContentLoaded = (callback) => {
14
- if (document.readyState === 'loading') {
15
- if (!domContentLoadedCallbacks.length) {
16
- document.addEventListener('DOMContentLoaded', () => {
17
- for (const callback of domContentLoadedCallbacks) {
18
- callback();
19
- }
20
- });
21
- }
22
- domContentLoadedCallbacks.push(callback);
12
+ const lifecycleCallbacks = [];
13
+ const lifecycleState = {
14
+ controller: new AbortController(),
15
+ hasInitialized: false
16
+ };
17
+ const getLifecycleSignal = () => lifecycleState.controller.signal;
18
+ const runLifecycleCallbacks = () => {
19
+ if (lifecycleState.hasInitialized) {
20
+ return;
23
21
  }
24
- else {
22
+ lifecycleState.hasInitialized = true;
23
+ for (const callback of lifecycleCallbacks) {
24
+ callback();
25
+ }
26
+ };
27
+ const onDOMContentLoaded = (callback) => {
28
+ lifecycleCallbacks.push(callback);
29
+ if (lifecycleState.hasInitialized) {
25
30
  callback();
26
31
  }
27
32
  };
33
+ if (document.readyState === 'loading') {
34
+ document.addEventListener('DOMContentLoaded', runLifecycleCallbacks, { once: true });
35
+ }
36
+ else {
37
+ runLifecycleCallbacks();
38
+ }
39
+ document.addEventListener('turbo:before-render', () => {
40
+ lifecycleState.controller.abort();
41
+ lifecycleState.controller = new AbortController();
42
+ lifecycleState.hasInitialized = false;
43
+ });
44
+ document.addEventListener('turbo:load', runLifecycleCallbacks);
45
+ const slideTimers = new WeakMap();
46
+ const cancelSlide = (target) => {
47
+ const timers = slideTimers.get(target) ?? [];
48
+ for (const timer of timers) {
49
+ globalThis.clearTimeout(timer);
50
+ }
51
+ slideTimers.delete(target);
52
+ };
53
+ const clearSlideStyles = (target) => {
54
+ for (const property of ['height', 'padding-top', 'padding-bottom', 'margin-top', 'margin-bottom', 'overflow', 'transition-duration', 'transition-property']) {
55
+ target.style.removeProperty(property);
56
+ }
57
+ };
28
58
  const slideUp = (target, duration = 500) => {
59
+ cancelSlide(target);
29
60
  if (duration <= 1) {
30
61
  target.style.display = 'none';
62
+ clearSlideStyles(target);
31
63
  return;
32
64
  }
33
65
  target.style.transitionProperty = 'height, margin, padding';
@@ -35,26 +67,23 @@
35
67
  target.style.boxSizing = 'border-box';
36
68
  target.style.height = `${target.offsetHeight}px`;
37
69
  target.style.overflow = 'hidden';
38
- globalThis.setTimeout(() => {
70
+ const stepTimer = globalThis.setTimeout(() => {
39
71
  target.style.height = '0';
40
72
  target.style.paddingTop = '0';
41
73
  target.style.paddingBottom = '0';
42
74
  target.style.marginTop = '0';
43
75
  target.style.marginBottom = '0';
44
76
  }, 1);
45
- globalThis.setTimeout(() => {
77
+ const cleanupTimer = globalThis.setTimeout(() => {
46
78
  target.style.display = 'none';
47
- target.style.removeProperty('height');
48
- target.style.removeProperty('padding-top');
49
- target.style.removeProperty('padding-bottom');
50
- target.style.removeProperty('margin-top');
51
- target.style.removeProperty('margin-bottom');
52
- target.style.removeProperty('overflow');
53
- target.style.removeProperty('transition-duration');
54
- target.style.removeProperty('transition-property');
79
+ clearSlideStyles(target);
80
+ slideTimers.delete(target);
55
81
  }, duration);
82
+ slideTimers.set(target, [stepTimer, cleanupTimer]);
56
83
  };
57
84
  const slideDown = (target, duration = 500) => {
85
+ cancelSlide(target);
86
+ clearSlideStyles(target);
58
87
  target.style.removeProperty('display');
59
88
  let { display } = globalThis.getComputedStyle(target);
60
89
  if (display === 'none') {
@@ -71,7 +100,7 @@
71
100
  target.style.paddingBottom = '0';
72
101
  target.style.marginTop = '0';
73
102
  target.style.marginBottom = '0';
74
- globalThis.setTimeout(() => {
103
+ const stepTimer = globalThis.setTimeout(() => {
75
104
  target.style.boxSizing = 'border-box';
76
105
  target.style.transitionProperty = 'height, margin, padding';
77
106
  target.style.transitionDuration = `${duration}ms`;
@@ -81,12 +110,11 @@
81
110
  target.style.removeProperty('margin-top');
82
111
  target.style.removeProperty('margin-bottom');
83
112
  }, 1);
84
- globalThis.setTimeout(() => {
85
- target.style.removeProperty('height');
86
- target.style.removeProperty('overflow');
87
- target.style.removeProperty('transition-duration');
88
- target.style.removeProperty('transition-property');
113
+ const cleanupTimer = globalThis.setTimeout(() => {
114
+ clearSlideStyles(target);
115
+ slideTimers.delete(target);
89
116
  }, duration);
117
+ slideTimers.set(target, [stepTimer, cleanupTimer]);
90
118
  };
91
119
 
92
120
  const CLASS_NAME_HOLD_TRANSITIONS = 'hold-transition';
@@ -110,19 +138,61 @@
110
138
  }
111
139
  onDOMContentLoaded(() => {
112
140
  const layout = new Layout(document.body);
113
- window.addEventListener('resize', () => layout.holdTransition(200));
141
+ window.addEventListener('resize', () => layout.holdTransition(200), { signal: getLifecycleSignal() });
114
142
  setTimeout(() => {
115
143
  document.body.classList.add(CLASS_NAME_APP_LOADED);
116
144
  }, 400);
117
145
  });
118
146
 
119
- const DATA_KEY$4 = 'lte.card-widget';
120
- const EVENT_KEY$4 = `.${DATA_KEY$4}`;
121
- const EVENT_COLLAPSED$2 = `collapsed${EVENT_KEY$4}`;
122
- const EVENT_EXPANDED$2 = `expanded${EVENT_KEY$4}`;
123
- const EVENT_REMOVE = `remove${EVENT_KEY$4}`;
124
- const EVENT_MAXIMIZED$1 = `maximized${EVENT_KEY$4}`;
125
- const EVENT_MINIMIZED$1 = `minimized${EVENT_KEY$4}`;
147
+ const componentRegistry = new WeakMap();
148
+ class BaseComponent {
149
+ static get NAME() {
150
+ throw new Error('Component subclasses must override the static NAME getter.');
151
+ }
152
+ static get DATA_KEY() {
153
+ return `lte.${this.NAME}`;
154
+ }
155
+ static _getInstance(element) {
156
+ if (!element) {
157
+ return null;
158
+ }
159
+ return componentRegistry.get(element)?.get(this.DATA_KEY) ?? null;
160
+ }
161
+ _element;
162
+ constructor(element) {
163
+ this._element = element;
164
+ const instances = componentRegistry.get(element) ?? new Map();
165
+ componentRegistry.set(element, instances);
166
+ instances.set(this.constructor.DATA_KEY, this);
167
+ }
168
+ dispose() {
169
+ const instances = componentRegistry.get(this._element);
170
+ instances?.delete(this.constructor.DATA_KEY);
171
+ if (instances?.size === 0) {
172
+ componentRegistry.delete(this._element);
173
+ }
174
+ }
175
+ }
176
+ const dispatchCustomEvent = (element, name, options = {}) => {
177
+ const event = new CustomEvent(name, {
178
+ bubbles: true,
179
+ cancelable: options.cancelable ?? false,
180
+ detail: options.detail
181
+ });
182
+ element.dispatchEvent(event);
183
+ return event;
184
+ };
185
+
186
+ const NAME$4 = 'card-widget';
187
+ const EVENT_KEY$5 = `.lte.${NAME$4}`;
188
+ const EVENT_COLLAPSE$2 = `collapse${EVENT_KEY$5}`;
189
+ const EVENT_EXPAND$1 = `expand${EVENT_KEY$5}`;
190
+ const EVENT_REMOVE = `remove${EVENT_KEY$5}`;
191
+ const EVENT_COLLAPSED$3 = `collapsed${EVENT_KEY$5}`;
192
+ const EVENT_EXPANDED$2 = `expanded${EVENT_KEY$5}`;
193
+ const EVENT_REMOVED = `removed${EVENT_KEY$5}`;
194
+ const EVENT_MAXIMIZED$1 = `maximized${EVENT_KEY$5}`;
195
+ const EVENT_MINIMIZED$1 = `minimized${EVENT_KEY$5}`;
126
196
  const CLASS_NAME_CARD = 'card';
127
197
  const CLASS_NAME_COLLAPSED = 'collapsed-card';
128
198
  const CLASS_NAME_COLLAPSING = 'collapsing-card';
@@ -141,13 +211,20 @@
141
211
  removeTrigger: SELECTOR_DATA_REMOVE,
142
212
  maximizeTrigger: SELECTOR_DATA_MAXIMIZE
143
213
  };
144
- class CardWidget {
145
- _element;
214
+ class CardWidget extends BaseComponent {
215
+ static get NAME() {
216
+ return NAME$4;
217
+ }
218
+ static getInstance(element) {
219
+ return this._getInstance(element);
220
+ }
221
+ static getOrCreateInstance(element, config = {}) {
222
+ return this.getInstance(element) ?? new this(element, config);
223
+ }
146
224
  _parent;
147
- _clone;
148
225
  _config;
149
- constructor(element, config) {
150
- this._element = element;
226
+ constructor(element, config = {}) {
227
+ super(element);
151
228
  this._parent = element.closest(SELECTOR_CARD);
152
229
  if (element.classList.contains(CLASS_NAME_CARD)) {
153
230
  this._parent = element;
@@ -155,58 +232,73 @@
155
232
  this._config = { ...Default$1, ...config };
156
233
  }
157
234
  collapse() {
158
- const event = new Event(EVENT_COLLAPSED$2);
159
- if (this._parent) {
160
- this._parent.classList.add(CLASS_NAME_COLLAPSING);
161
- const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
162
- elm.forEach(el => {
163
- if (el instanceof HTMLElement) {
164
- slideUp(el, this._config.animationSpeed);
165
- }
166
- });
167
- setTimeout(() => {
168
- if (this._parent) {
169
- this._parent.classList.add(CLASS_NAME_COLLAPSED);
170
- this._parent.classList.remove(CLASS_NAME_COLLAPSING);
171
- }
172
- }, this._config.animationSpeed);
235
+ if (!this._parent) {
236
+ return;
237
+ }
238
+ if (dispatchCustomEvent(this._parent, EVENT_COLLAPSE$2, { cancelable: true }).defaultPrevented) {
239
+ return;
173
240
  }
174
- this._element?.dispatchEvent(event);
241
+ this._parent.classList.add(CLASS_NAME_COLLAPSING);
242
+ this._parent.classList.remove(CLASS_NAME_EXPANDING);
243
+ const elm = this._parent.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
244
+ elm.forEach(el => {
245
+ if (el instanceof HTMLElement) {
246
+ slideUp(el, this._config.animationSpeed);
247
+ }
248
+ });
249
+ setTimeout(() => {
250
+ if (this._parent?.classList.contains(CLASS_NAME_COLLAPSING)) {
251
+ this._parent.classList.add(CLASS_NAME_COLLAPSED);
252
+ this._parent.classList.remove(CLASS_NAME_COLLAPSING);
253
+ dispatchCustomEvent(this._parent, EVENT_COLLAPSED$3);
254
+ }
255
+ }, this._config.animationSpeed);
175
256
  }
176
257
  expand() {
177
- const event = new Event(EVENT_EXPANDED$2);
178
- if (this._parent) {
179
- this._parent.classList.add(CLASS_NAME_EXPANDING);
180
- const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
181
- elm.forEach(el => {
182
- if (el instanceof HTMLElement) {
183
- slideDown(el, this._config.animationSpeed);
184
- }
185
- });
186
- setTimeout(() => {
187
- if (this._parent) {
188
- this._parent.classList.remove(CLASS_NAME_COLLAPSED, CLASS_NAME_EXPANDING);
189
- }
190
- }, this._config.animationSpeed);
258
+ if (!this._parent) {
259
+ return;
191
260
  }
192
- this._element?.dispatchEvent(event);
261
+ if (dispatchCustomEvent(this._parent, EVENT_EXPAND$1, { cancelable: true }).defaultPrevented) {
262
+ return;
263
+ }
264
+ this._parent.classList.add(CLASS_NAME_EXPANDING);
265
+ this._parent.classList.remove(CLASS_NAME_COLLAPSING, CLASS_NAME_COLLAPSED);
266
+ const elm = this._parent.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`);
267
+ elm.forEach(el => {
268
+ if (el instanceof HTMLElement) {
269
+ slideDown(el, this._config.animationSpeed);
270
+ }
271
+ });
272
+ setTimeout(() => {
273
+ if (this._parent?.classList.contains(CLASS_NAME_EXPANDING)) {
274
+ this._parent.classList.remove(CLASS_NAME_EXPANDING);
275
+ dispatchCustomEvent(this._parent, EVENT_EXPANDED$2);
276
+ }
277
+ }, this._config.animationSpeed);
193
278
  }
194
279
  remove() {
195
- const event = new Event(EVENT_REMOVE);
196
- if (this._parent) {
197
- slideUp(this._parent, this._config.animationSpeed);
280
+ if (!this._parent) {
281
+ return;
198
282
  }
199
- this._element?.dispatchEvent(event);
283
+ if (dispatchCustomEvent(this._parent, EVENT_REMOVE, { cancelable: true }).defaultPrevented) {
284
+ return;
285
+ }
286
+ const parent = this._parent;
287
+ slideUp(parent, this._config.animationSpeed);
288
+ setTimeout(() => {
289
+ dispatchCustomEvent(parent, EVENT_REMOVED);
290
+ parent.remove();
291
+ this.dispose();
292
+ }, this._config.animationSpeed);
200
293
  }
201
294
  toggle() {
202
- if (this._parent?.classList.contains(CLASS_NAME_COLLAPSED)) {
295
+ if (this._parent?.classList.contains(CLASS_NAME_COLLAPSED) || this._parent?.classList.contains(CLASS_NAME_COLLAPSING)) {
203
296
  this.expand();
204
297
  return;
205
298
  }
206
299
  this.collapse();
207
300
  }
208
301
  maximize() {
209
- const event = new Event(EVENT_MAXIMIZED$1);
210
302
  if (this._parent) {
211
303
  this._parent.style.height = `${this._parent.offsetHeight}px`;
212
304
  this._parent.style.width = `${this._parent.offsetWidth}px`;
@@ -221,13 +313,12 @@
221
313
  if (this._parent.classList.contains(CLASS_NAME_COLLAPSED)) {
222
314
  this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED);
223
315
  }
316
+ dispatchCustomEvent(this._parent, EVENT_MAXIMIZED$1);
224
317
  }
225
318
  }, 150);
226
319
  }
227
- this._element?.dispatchEvent(event);
228
320
  }
229
321
  minimize() {
230
- const event = new Event(EVENT_MINIMIZED$1);
231
322
  if (this._parent) {
232
323
  this._parent.style.height = 'auto';
233
324
  this._parent.style.width = 'auto';
@@ -242,10 +333,15 @@
242
333
  if (this._parent?.classList.contains(CLASS_NAME_WAS_COLLAPSED)) {
243
334
  this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED);
244
335
  }
336
+ dispatchCustomEvent(this._parent, EVENT_MINIMIZED$1);
337
+ setTimeout(() => {
338
+ this._parent?.style.removeProperty('height');
339
+ this._parent?.style.removeProperty('width');
340
+ this._parent?.style.removeProperty('transition');
341
+ }, 150);
245
342
  }
246
343
  }, 10);
247
344
  }
248
- this._element?.dispatchEvent(event);
249
345
  }
250
346
  toggleMaximize() {
251
347
  if (this._parent?.classList.contains(CLASS_NAME_MAXIMIZED)) {
@@ -255,41 +351,42 @@
255
351
  this.maximize();
256
352
  }
257
353
  }
258
- onDOMContentLoaded(() => {
259
- const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE);
260
- collapseBtn.forEach(btn => {
261
- btn.addEventListener('click', event => {
262
- event.preventDefault();
263
- const target = event.target;
264
- const data = new CardWidget(target, Default$1);
265
- data.toggle();
266
- });
267
- });
268
- const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE);
269
- removeBtn.forEach(btn => {
270
- btn.addEventListener('click', event => {
271
- event.preventDefault();
272
- const target = event.target;
273
- const data = new CardWidget(target, Default$1);
274
- data.remove();
275
- });
276
- });
277
- const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE);
278
- maxBtn.forEach(btn => {
279
- btn.addEventListener('click', event => {
280
- event.preventDefault();
281
- const target = event.target;
282
- const data = new CardWidget(target, Default$1);
283
- data.toggleMaximize();
284
- });
285
- });
354
+ document.addEventListener('click', event => {
355
+ const target = event.target;
356
+ if (!(target instanceof Element)) {
357
+ return;
358
+ }
359
+ const collapseTrigger = target.closest(SELECTOR_DATA_COLLAPSE);
360
+ const removeTrigger = target.closest(SELECTOR_DATA_REMOVE);
361
+ const maximizeTrigger = target.closest(SELECTOR_DATA_MAXIMIZE);
362
+ const trigger = collapseTrigger ?? removeTrigger ?? maximizeTrigger;
363
+ if (!trigger) {
364
+ return;
365
+ }
366
+ event.preventDefault();
367
+ const card = trigger.closest(SELECTOR_CARD);
368
+ if (!card) {
369
+ return;
370
+ }
371
+ const widget = CardWidget.getOrCreateInstance(card);
372
+ if (collapseTrigger) {
373
+ widget.toggle();
374
+ }
375
+ else if (removeTrigger) {
376
+ widget.remove();
377
+ }
378
+ else {
379
+ widget.toggleMaximize();
380
+ }
286
381
  });
287
382
 
288
- const DATA_KEY$3 = 'lte.treeview';
289
- const EVENT_KEY$3 = `.${DATA_KEY$3}`;
290
- const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$3}`;
291
- const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$3}`;
292
- const EVENT_LOAD_DATA_API = `load${EVENT_KEY$3}`;
383
+ const NAME$3 = 'treeview';
384
+ const EVENT_KEY$4 = `.lte.${NAME$3}`;
385
+ const EVENT_EXPAND = `expand${EVENT_KEY$4}`;
386
+ const EVENT_COLLAPSE$1 = `collapse${EVENT_KEY$4}`;
387
+ const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$4}`;
388
+ const EVENT_COLLAPSED$2 = `collapsed${EVENT_KEY$4}`;
389
+ const EVENT_LOAD_DATA_API = `load${EVENT_KEY$4}`;
293
390
  const CLASS_NAME_MENU_OPEN = 'menu-open';
294
391
  const SELECTOR_NAV_ITEM = '.nav-item';
295
392
  const SELECTOR_NAV_LINK = '.nav-link';
@@ -299,20 +396,35 @@
299
396
  animationSpeed: 300,
300
397
  accordion: true
301
398
  };
302
- class Treeview {
303
- _element;
399
+ const setAriaExpanded = (navItem, expanded) => {
400
+ const link = navItem.querySelector(`:scope > ${SELECTOR_NAV_LINK}`);
401
+ link?.setAttribute('aria-expanded', String(expanded));
402
+ };
403
+ class Treeview extends BaseComponent {
404
+ static get NAME() {
405
+ return NAME$3;
406
+ }
407
+ static getInstance(element) {
408
+ return this._getInstance(element);
409
+ }
410
+ static getOrCreateInstance(element, config = {}) {
411
+ return this.getInstance(element) ?? new this(element, config);
412
+ }
304
413
  _config;
305
- constructor(element, config) {
306
- this._element = element;
414
+ constructor(element, config = {}) {
415
+ super(element);
307
416
  this._config = { ...Default, ...config };
308
417
  }
309
418
  open() {
310
- const event = new Event(EVENT_EXPANDED$1);
419
+ if (dispatchCustomEvent(this._element, EVENT_EXPAND, { cancelable: true }).defaultPrevented) {
420
+ return;
421
+ }
311
422
  if (this._config.accordion) {
312
423
  const openMenuList = this._element.parentElement?.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
313
424
  openMenuList?.forEach(openMenu => {
314
- if (openMenu !== this._element.parentElement) {
425
+ if (!this._element.contains(openMenu)) {
315
426
  openMenu.classList.remove(CLASS_NAME_MENU_OPEN);
427
+ setAriaExpanded(openMenu, false);
316
428
  const childElement = openMenu?.querySelector(SELECTOR_TREEVIEW_MENU);
317
429
  if (childElement) {
318
430
  slideUp(childElement, this._config.animationSpeed);
@@ -321,20 +433,32 @@
321
433
  });
322
434
  }
323
435
  this._element.classList.add(CLASS_NAME_MENU_OPEN);
324
- const childElement = this._element?.querySelector(SELECTOR_TREEVIEW_MENU);
436
+ setAriaExpanded(this._element, true);
437
+ const childElement = this._element.querySelector(SELECTOR_TREEVIEW_MENU);
325
438
  if (childElement) {
326
439
  slideDown(childElement, this._config.animationSpeed);
327
440
  }
328
- this._element.dispatchEvent(event);
441
+ setTimeout(() => {
442
+ if (this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
443
+ dispatchCustomEvent(this._element, EVENT_EXPANDED$1);
444
+ }
445
+ }, this._config.animationSpeed);
329
446
  }
330
447
  close() {
331
- const event = new Event(EVENT_COLLAPSED$1);
448
+ if (dispatchCustomEvent(this._element, EVENT_COLLAPSE$1, { cancelable: true }).defaultPrevented) {
449
+ return;
450
+ }
332
451
  this._element.classList.remove(CLASS_NAME_MENU_OPEN);
333
- const childElement = this._element?.querySelector(SELECTOR_TREEVIEW_MENU);
452
+ setAriaExpanded(this._element, false);
453
+ const childElement = this._element.querySelector(SELECTOR_TREEVIEW_MENU);
334
454
  if (childElement) {
335
455
  slideUp(childElement, this._config.animationSpeed);
336
456
  }
337
- this._element.dispatchEvent(event);
457
+ setTimeout(() => {
458
+ if (!this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
459
+ dispatchCustomEvent(this._element, EVENT_COLLAPSED$2);
460
+ }
461
+ }, this._config.animationSpeed);
338
462
  }
339
463
  toggle() {
340
464
  if (this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
@@ -345,6 +469,31 @@
345
469
  }
346
470
  }
347
471
  }
472
+ document.addEventListener('click', event => {
473
+ const target = event.target;
474
+ if (!(target instanceof Element)) {
475
+ return;
476
+ }
477
+ const toggleRoot = target.closest(SELECTOR_DATA_TOGGLE$1);
478
+ if (!toggleRoot) {
479
+ return;
480
+ }
481
+ const targetItem = target.closest(SELECTOR_NAV_ITEM);
482
+ const targetLink = target.closest(SELECTOR_NAV_LINK);
483
+ if (!targetItem?.querySelector(SELECTOR_TREEVIEW_MENU)) {
484
+ return;
485
+ }
486
+ if (target.getAttribute('href') === '#' || targetLink?.getAttribute('href') === '#') {
487
+ event.preventDefault();
488
+ }
489
+ const accordionAttr = toggleRoot.dataset.accordion;
490
+ const animationSpeedAttr = toggleRoot.dataset.animationSpeed;
491
+ const config = {
492
+ accordion: accordionAttr === undefined ? Default.accordion : accordionAttr === 'true',
493
+ animationSpeed: animationSpeedAttr === undefined ? Default.animationSpeed : Number(animationSpeedAttr)
494
+ };
495
+ Treeview.getOrCreateInstance(targetItem, config).toggle();
496
+ });
348
497
  onDOMContentLoaded(() => {
349
498
  const openMenuItems = document.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
350
499
  openMenuItems.forEach(menuItem => {
@@ -355,144 +504,129 @@
355
504
  menuItem.dispatchEvent(event);
356
505
  }
357
506
  });
358
- const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE$1);
359
- button.forEach(btn => {
360
- btn.addEventListener('click', event => {
361
- const target = event.target;
362
- const targetItem = target.closest(SELECTOR_NAV_ITEM);
363
- const targetLink = target.closest(SELECTOR_NAV_LINK);
364
- const targetTreeviewMenu = targetItem?.querySelector(SELECTOR_TREEVIEW_MENU);
365
- const lteToggleElement = event.currentTarget;
366
- if (!targetTreeviewMenu) {
367
- return;
368
- }
369
- if (target?.getAttribute('href') === '#' || targetLink?.getAttribute('href') === '#') {
370
- event.preventDefault();
371
- }
372
- if (targetItem) {
373
- const accordionAttr = lteToggleElement.dataset.accordion;
374
- const animationSpeedAttr = lteToggleElement.dataset.animationSpeed;
375
- const config = {
376
- accordion: accordionAttr === undefined ? Default.accordion : accordionAttr === 'true',
377
- animationSpeed: animationSpeedAttr === undefined ? Default.animationSpeed : Number(animationSpeedAttr)
378
- };
379
- const data = new Treeview(targetItem, config);
380
- data.toggle();
507
+ document.querySelectorAll(SELECTOR_DATA_TOGGLE$1).forEach(root => {
508
+ root.querySelectorAll(SELECTOR_NAV_ITEM).forEach(item => {
509
+ if (item.querySelector(`:scope > ${SELECTOR_TREEVIEW_MENU}`)) {
510
+ setAriaExpanded(item, item.classList.contains(CLASS_NAME_MENU_OPEN));
381
511
  }
382
512
  });
383
513
  });
384
514
  });
385
515
 
386
- const DATA_KEY$2 = 'lte.direct-chat';
387
- const EVENT_KEY$2 = `.${DATA_KEY$2}`;
388
- const EVENT_EXPANDED = `expanded${EVENT_KEY$2}`;
389
- const EVENT_COLLAPSED = `collapsed${EVENT_KEY$2}`;
516
+ const NAME$2 = 'direct-chat';
517
+ const EVENT_KEY$3 = `.lte.${NAME$2}`;
518
+ const EVENT_EXPANDED = `expanded${EVENT_KEY$3}`;
519
+ const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$3}`;
390
520
  const SELECTOR_DATA_TOGGLE = '[data-lte-toggle="chat-pane"]';
391
521
  const SELECTOR_DIRECT_CHAT = '.direct-chat';
392
522
  const CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open';
393
- class DirectChat {
394
- _element;
395
- constructor(element) {
396
- this._element = element;
523
+ class DirectChat extends BaseComponent {
524
+ static get NAME() {
525
+ return NAME$2;
526
+ }
527
+ static getInstance(element) {
528
+ return this._getInstance(element);
529
+ }
530
+ static getOrCreateInstance(element) {
531
+ return this.getInstance(element) ?? new this(element);
397
532
  }
398
533
  toggle() {
399
534
  if (this._element.classList.contains(CLASS_NAME_DIRECT_CHAT_OPEN)) {
400
- const event = new Event(EVENT_COLLAPSED);
401
535
  this._element.classList.remove(CLASS_NAME_DIRECT_CHAT_OPEN);
402
- this._element.dispatchEvent(event);
536
+ dispatchCustomEvent(this._element, EVENT_COLLAPSED$1);
403
537
  }
404
538
  else {
405
- const event = new Event(EVENT_EXPANDED);
406
539
  this._element.classList.add(CLASS_NAME_DIRECT_CHAT_OPEN);
407
- this._element.dispatchEvent(event);
540
+ dispatchCustomEvent(this._element, EVENT_EXPANDED);
408
541
  }
409
542
  }
410
543
  }
411
- onDOMContentLoaded(() => {
412
- const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE);
413
- button.forEach(btn => {
414
- btn.addEventListener('click', event => {
415
- event.preventDefault();
416
- const target = event.target;
417
- const chatPane = target.closest(SELECTOR_DIRECT_CHAT);
418
- if (chatPane) {
419
- const data = new DirectChat(chatPane);
420
- data.toggle();
421
- }
422
- });
423
- });
544
+ document.addEventListener('click', event => {
545
+ const target = event.target;
546
+ if (!(target instanceof Element)) {
547
+ return;
548
+ }
549
+ const trigger = target.closest(SELECTOR_DATA_TOGGLE);
550
+ if (!trigger) {
551
+ return;
552
+ }
553
+ event.preventDefault();
554
+ const chatPane = trigger.closest(SELECTOR_DIRECT_CHAT);
555
+ if (chatPane) {
556
+ DirectChat.getOrCreateInstance(chatPane).toggle();
557
+ }
424
558
  });
425
559
 
426
- const DATA_KEY$1 = 'lte.fullscreen';
427
- const EVENT_KEY$1 = `.${DATA_KEY$1}`;
428
- const EVENT_MAXIMIZED = `maximized${EVENT_KEY$1}`;
429
- const EVENT_MINIMIZED = `minimized${EVENT_KEY$1}`;
560
+ const NAME$1 = 'fullscreen';
561
+ const EVENT_KEY$2 = `.lte.${NAME$1}`;
562
+ const EVENT_MAXIMIZED = `maximized${EVENT_KEY$2}`;
563
+ const EVENT_MINIMIZED = `minimized${EVENT_KEY$2}`;
430
564
  const SELECTOR_FULLSCREEN_TOGGLE = '[data-lte-toggle="fullscreen"]';
431
565
  const SELECTOR_MAXIMIZE_ICON = '[data-lte-icon="maximize"]';
432
566
  const SELECTOR_MINIMIZE_ICON = '[data-lte-icon="minimize"]';
433
- class FullScreen {
434
- _element;
435
- _config;
436
- constructor(element, config) {
437
- this._element = element;
438
- this._config = config;
567
+ function syncFullScreenState() {
568
+ const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
569
+ const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
570
+ const isFullScreen = Boolean(document.fullscreenElement);
571
+ iconMaximize?.classList.toggle('d-none', isFullScreen);
572
+ iconMinimize?.classList.toggle('d-none', !isFullScreen);
573
+ const eventName = isFullScreen ? EVENT_MAXIMIZED : EVENT_MINIMIZED;
574
+ document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE).forEach(button => {
575
+ dispatchCustomEvent(button, eventName);
576
+ });
577
+ }
578
+ class FullScreen extends BaseComponent {
579
+ static get NAME() {
580
+ return NAME$1;
581
+ }
582
+ static getInstance(element) {
583
+ return this._getInstance(element);
584
+ }
585
+ static getOrCreateInstance(element) {
586
+ return this.getInstance(element) ?? new this(element);
439
587
  }
440
588
  inFullScreen() {
441
- const event = new Event(EVENT_MAXIMIZED);
442
- const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
443
- const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
444
- void document.documentElement.requestFullscreen();
445
- if (iconMaximize) {
446
- iconMaximize.classList.add('d-none');
447
- }
448
- if (iconMinimize) {
449
- iconMinimize.classList.remove('d-none');
450
- }
451
- this._element.dispatchEvent(event);
589
+ void document.documentElement.requestFullscreen().catch(() => {
590
+ });
452
591
  }
453
592
  outFullscreen() {
454
- const event = new Event(EVENT_MINIMIZED);
455
- const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
456
- const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
457
- void document.exitFullscreen();
458
- if (iconMaximize) {
459
- iconMaximize.classList.remove('d-none');
460
- }
461
- if (iconMinimize) {
462
- iconMinimize.classList.add('d-none');
463
- }
464
- this._element.dispatchEvent(event);
593
+ void document.exitFullscreen().catch(() => {
594
+ });
465
595
  }
466
596
  toggleFullScreen() {
467
- if (document.fullscreenEnabled) {
468
- if (document.fullscreenElement) {
469
- this.outFullscreen();
470
- }
471
- else {
472
- this.inFullScreen();
473
- }
597
+ if (!document.fullscreenEnabled) {
598
+ return;
599
+ }
600
+ if (document.fullscreenElement) {
601
+ this.outFullscreen();
602
+ }
603
+ else {
604
+ this.inFullScreen();
474
605
  }
475
606
  }
476
607
  }
608
+ document.addEventListener('click', event => {
609
+ const target = event.target;
610
+ if (!(target instanceof Element)) {
611
+ return;
612
+ }
613
+ const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE);
614
+ if (!button) {
615
+ return;
616
+ }
617
+ event.preventDefault();
618
+ FullScreen.getOrCreateInstance(button).toggleFullScreen();
619
+ });
477
620
  onDOMContentLoaded(() => {
478
- const buttons = document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE);
479
- buttons.forEach(btn => {
480
- btn.addEventListener('click', event => {
481
- event.preventDefault();
482
- const target = event.target;
483
- const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE);
484
- if (button) {
485
- const data = new FullScreen(button, undefined);
486
- data.toggleFullScreen();
487
- }
488
- });
489
- });
621
+ document.addEventListener('fullscreenchange', syncFullScreenState, { signal: getLifecycleSignal() });
490
622
  });
491
623
 
492
- const DATA_KEY = 'lte.push-menu';
493
- const EVENT_KEY = `.${DATA_KEY}`;
494
- const EVENT_OPEN = `open${EVENT_KEY}`;
495
- const EVENT_COLLAPSE = `collapse${EVENT_KEY}`;
624
+ const NAME = 'push-menu';
625
+ const EVENT_KEY$1 = `.lte.${NAME}`;
626
+ const EVENT_OPEN = `open${EVENT_KEY$1}`;
627
+ const EVENT_COLLAPSE = `collapse${EVENT_KEY$1}`;
628
+ const EVENT_OPENED = `opened${EVENT_KEY$1}`;
629
+ const EVENT_COLLAPSED = `collapsed${EVENT_KEY$1}`;
496
630
  const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini';
497
631
  const CLASS_NAME_SIDEBAR_EXPAND = 'sidebar-expand';
498
632
  const CLASS_NAME_SIDEBAR_OVERLAY = 'sidebar-overlay';
@@ -504,14 +638,22 @@
504
638
  const SELECTOR_SIDEBAR_TOGGLE = '[data-lte-toggle="sidebar"]';
505
639
  const STORAGE_KEY_SIDEBAR_STATE = 'lte.sidebar.state';
506
640
  const Defaults = {
507
- sidebarBreakpoint: 992,
641
+ sidebarBreakpoint: 991.98,
508
642
  enablePersistence: false
509
643
  };
510
- class PushMenu {
511
- _element;
644
+ class PushMenu extends BaseComponent {
645
+ static get NAME() {
646
+ return NAME;
647
+ }
648
+ static getInstance(element) {
649
+ return this._getInstance(element);
650
+ }
651
+ static getOrCreateInstance(element, config = {}) {
652
+ return this.getInstance(element) ?? new this(element, config);
653
+ }
512
654
  _config;
513
- constructor(element, config) {
514
- this._element = element;
655
+ constructor(element, config = {}) {
656
+ super(element);
515
657
  this._config = { ...Defaults, ...config };
516
658
  }
517
659
  isCollapsed() {
@@ -527,16 +669,22 @@
527
669
  return globalThis.innerWidth <= this._config.sidebarBreakpoint;
528
670
  }
529
671
  expand() {
672
+ if (dispatchCustomEvent(this._element, EVENT_OPEN, { cancelable: true }).defaultPrevented) {
673
+ return;
674
+ }
530
675
  document.body.classList.remove(CLASS_NAME_SIDEBAR_COLLAPSE);
531
676
  if (this.isMobileSize()) {
532
677
  document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN);
533
678
  }
534
- this._element.dispatchEvent(new Event(EVENT_OPEN));
679
+ dispatchCustomEvent(this._element, EVENT_OPENED);
535
680
  }
536
681
  collapse() {
682
+ if (dispatchCustomEvent(this._element, EVENT_COLLAPSE, { cancelable: true }).defaultPrevented) {
683
+ return;
684
+ }
537
685
  document.body.classList.remove(CLASS_NAME_SIDEBAR_OPEN);
538
686
  document.body.classList.add(CLASS_NAME_SIDEBAR_COLLAPSE);
539
- this._element.dispatchEvent(new Event(EVENT_COLLAPSE));
687
+ dispatchCustomEvent(this._element, EVENT_COLLAPSED);
540
688
  }
541
689
  toggle() {
542
690
  const isCollapsed = this.isCollapsed();
@@ -631,8 +779,23 @@
631
779
  }
632
780
  }
633
781
  }
782
+ document.addEventListener('click', event => {
783
+ const target = event.target;
784
+ if (!(target instanceof Element)) {
785
+ return;
786
+ }
787
+ const button = target.closest(SELECTOR_SIDEBAR_TOGGLE);
788
+ if (!button) {
789
+ return;
790
+ }
791
+ event.preventDefault();
792
+ const sidebar = document.querySelector(SELECTOR_APP_SIDEBAR);
793
+ if (sidebar) {
794
+ PushMenu.getOrCreateInstance(sidebar).toggle();
795
+ }
796
+ });
634
797
  onDOMContentLoaded(() => {
635
- const sidebar = document?.querySelector(SELECTOR_APP_SIDEBAR);
798
+ const sidebar = document.querySelector(SELECTOR_APP_SIDEBAR);
636
799
  if (!sidebar) {
637
800
  return;
638
801
  }
@@ -646,15 +809,19 @@
646
809
  Defaults.enablePersistence :
647
810
  enablePersistenceAttr === 'true'
648
811
  };
649
- const pushMenu = new PushMenu(sidebar, config);
812
+ const pushMenu = PushMenu.getOrCreateInstance(sidebar, config);
650
813
  pushMenu.init();
651
- window.addEventListener('resize', () => {
652
- pushMenu.setupSidebarBreakPoint();
814
+ const breakpointQuery = globalThis.matchMedia(`(max-width: ${pushMenu._config.sidebarBreakpoint}px)`);
815
+ breakpointQuery.addEventListener('change', () => {
653
816
  pushMenu.updateStateByResponsiveLogic();
654
- });
655
- const sidebarOverlay = document.createElement('div');
656
- sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY;
657
- document.querySelector(SELECTOR_APP_WRAPPER)?.append(sidebarOverlay);
817
+ }, { signal: getLifecycleSignal() });
818
+ const appWrapper = document.querySelector(SELECTOR_APP_WRAPPER);
819
+ let sidebarOverlay = appWrapper?.querySelector(`:scope > .${CLASS_NAME_SIDEBAR_OVERLAY}`);
820
+ if (!sidebarOverlay) {
821
+ sidebarOverlay = document.createElement('div');
822
+ sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY;
823
+ appWrapper?.append(sidebarOverlay);
824
+ }
658
825
  let overlayTouchMoved = false;
659
826
  sidebarOverlay.addEventListener('touchstart', () => {
660
827
  overlayTouchMoved = false;
@@ -673,26 +840,102 @@
673
840
  event.preventDefault();
674
841
  pushMenu.collapse();
675
842
  });
676
- const fullBtn = document.querySelectorAll(SELECTOR_SIDEBAR_TOGGLE);
677
- fullBtn.forEach(btn => {
678
- btn.addEventListener('click', event => {
679
- event.preventDefault();
680
- let button = event.currentTarget;
681
- if (button?.dataset.lteToggle !== 'sidebar') {
682
- button = button?.closest(SELECTOR_SIDEBAR_TOGGLE);
683
- }
684
- if (button) {
685
- event?.preventDefault();
686
- pushMenu.toggle();
687
- }
843
+ });
844
+
845
+ const DATA_KEY = 'lte.color-mode';
846
+ const EVENT_KEY = `.${DATA_KEY}`;
847
+ const EVENT_CHANGED = `changed${EVENT_KEY}`;
848
+ const STORAGE_KEY = 'lte-theme';
849
+ const SELECTOR_TOGGLE = '[data-bs-theme-value]';
850
+ const SELECTOR_ICON = '[data-lte-theme-icon]';
851
+ class ColorMode {
852
+ getStoredTheme() {
853
+ try {
854
+ const stored = localStorage.getItem(STORAGE_KEY);
855
+ return stored && ['light', 'dark', 'auto'].includes(stored) ? stored : null;
856
+ }
857
+ catch {
858
+ return null;
859
+ }
860
+ }
861
+ getPreferredTheme() {
862
+ const stored = this.getStoredTheme();
863
+ if (stored) {
864
+ return stored;
865
+ }
866
+ return this._prefersDark() ? 'dark' : 'light';
867
+ }
868
+ resolveTheme(theme) {
869
+ if (theme === 'auto') {
870
+ return this._prefersDark() ? 'dark' : 'light';
871
+ }
872
+ return theme;
873
+ }
874
+ setTheme(theme) {
875
+ try {
876
+ localStorage.setItem(STORAGE_KEY, theme);
877
+ }
878
+ catch {
879
+ }
880
+ this._applyTheme(theme);
881
+ this._showActiveTheme(theme);
882
+ document.dispatchEvent(new CustomEvent(EVENT_CHANGED, {
883
+ detail: { theme, resolved: this.resolveTheme(theme) }
884
+ }));
885
+ }
886
+ _applyTheme(theme) {
887
+ const resolved = this.resolveTheme(theme);
888
+ document.documentElement.setAttribute('data-bs-theme', resolved);
889
+ document.documentElement.style.colorScheme = resolved;
890
+ }
891
+ _prefersDark() {
892
+ return globalThis.matchMedia('(prefers-color-scheme: dark)').matches;
893
+ }
894
+ _showActiveTheme(theme) {
895
+ document.querySelectorAll(SELECTOR_TOGGLE).forEach(toggle => {
896
+ const isActive = toggle.getAttribute('data-bs-theme-value') === theme;
897
+ toggle.classList.toggle('active', isActive);
898
+ toggle.setAttribute('aria-pressed', String(isActive));
899
+ toggle.querySelector('.bi-check-lg')?.classList.toggle('d-none', !isActive);
688
900
  });
689
- });
901
+ document.querySelectorAll(SELECTOR_ICON).forEach(icon => {
902
+ icon.classList.toggle('d-none', icon.dataset.lteThemeIcon !== theme);
903
+ });
904
+ }
905
+ init() {
906
+ const theme = this.getPreferredTheme();
907
+ this._applyTheme(theme);
908
+ this._showActiveTheme(theme);
909
+ }
910
+ }
911
+ document.addEventListener('click', event => {
912
+ const target = event.target;
913
+ if (!(target instanceof Element)) {
914
+ return;
915
+ }
916
+ const toggle = target.closest(SELECTOR_TOGGLE);
917
+ const theme = toggle?.getAttribute('data-bs-theme-value');
918
+ if (theme) {
919
+ new ColorMode().setTheme(theme);
920
+ }
921
+ });
922
+ onDOMContentLoaded(() => {
923
+ const colorMode = new ColorMode();
924
+ colorMode.init();
925
+ globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
926
+ const stored = colorMode.getStoredTheme();
927
+ if (!stored || stored === 'auto') {
928
+ colorMode._applyTheme('auto');
929
+ colorMode._showActiveTheme(stored ?? 'auto');
930
+ }
931
+ }, { signal: getLifecycleSignal() });
690
932
  });
691
933
 
692
934
  class AccessibilityManager {
693
935
  config;
694
936
  liveRegion = null;
695
937
  focusHistory = [];
938
+ signal = getLifecycleSignal();
696
939
  constructor(config = {}) {
697
940
  this.config = {
698
941
  announcements: true,
@@ -727,6 +970,11 @@
727
970
  createLiveRegion() {
728
971
  if (this.liveRegion)
729
972
  return;
973
+ const existingRegion = document.getElementById('live-region');
974
+ if (existingRegion) {
975
+ this.liveRegion = existingRegion;
976
+ return;
977
+ }
730
978
  this.liveRegion = document.createElement('div');
731
979
  this.liveRegion.id = 'live-region';
732
980
  this.liveRegion.className = 'live-region';
@@ -736,6 +984,10 @@
736
984
  document.body.append(this.liveRegion);
737
985
  }
738
986
  addSkipLinks() {
987
+ if (document.querySelector('.skip-links')) {
988
+ this.ensureSkipTargets();
989
+ return;
990
+ }
739
991
  const skipLinksContainer = document.createElement('div');
740
992
  skipLinksContainer.className = 'skip-links';
741
993
  const skipToMain = document.createElement('a');
@@ -769,42 +1021,13 @@
769
1021
  }
770
1022
  initFocusManagement() {
771
1023
  document.addEventListener('keydown', (event) => {
772
- if (event.key === 'Tab') {
773
- this.handleTabNavigation(event);
774
- }
775
1024
  if (event.key === 'Escape') {
776
1025
  this.handleEscapeKey(event);
777
1026
  }
778
- });
1027
+ }, { signal: this.signal });
779
1028
  this.initModalFocusManagement();
780
1029
  this.initDropdownFocusManagement();
781
1030
  }
782
- handleTabNavigation(event) {
783
- const focusableElements = this.getFocusableElements();
784
- const currentIndex = focusableElements.indexOf(document.activeElement);
785
- if (event.shiftKey) {
786
- if (currentIndex <= 0) {
787
- event.preventDefault();
788
- focusableElements.at(-1)?.focus();
789
- }
790
- }
791
- else if (currentIndex >= focusableElements.length - 1) {
792
- event.preventDefault();
793
- focusableElements[0]?.focus();
794
- }
795
- }
796
- getFocusableElements() {
797
- const selector = [
798
- 'a[href]',
799
- 'button:not([disabled])',
800
- 'input:not([disabled])',
801
- 'select:not([disabled])',
802
- 'textarea:not([disabled])',
803
- '[tabindex]:not([tabindex="-1"])',
804
- '[contenteditable="true"]'
805
- ].join(', ');
806
- return Array.from(document.querySelectorAll(selector));
807
- }
808
1031
  handleEscapeKey(event) {
809
1032
  const activeModal = document.querySelector('.modal.show');
810
1033
  if (activeModal) {
@@ -820,6 +1043,9 @@
820
1043
  initKeyboardNavigation() {
821
1044
  document.addEventListener('keydown', (event) => {
822
1045
  const target = event.target;
1046
+ if (target.matches('input, textarea, select, [contenteditable], [contenteditable] *')) {
1047
+ return;
1048
+ }
823
1049
  if (target.closest('.nav, .navbar-nav, .dropdown-menu')) {
824
1050
  this.handleMenuNavigation(event);
825
1051
  }
@@ -827,15 +1053,19 @@
827
1053
  event.preventDefault();
828
1054
  target.click();
829
1055
  }
830
- });
1056
+ }, { signal: this.signal });
831
1057
  }
832
1058
  handleMenuNavigation(event) {
833
1059
  if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) {
834
1060
  return;
835
1061
  }
836
1062
  const currentElement = event.target;
837
- const menuItems = Array.from(currentElement.closest('.nav, .navbar-nav, .dropdown-menu')?.querySelectorAll('a, button') || []);
1063
+ const menuItems = Array.from(currentElement.closest('.nav, .navbar-nav, .dropdown-menu')?.querySelectorAll('a, button') || [])
1064
+ .filter(item => item.offsetParent !== null);
838
1065
  const currentIndex = menuItems.indexOf(currentElement);
1066
+ if (currentIndex === -1) {
1067
+ return;
1068
+ }
839
1069
  let nextIndex;
840
1070
  switch (event.key) {
841
1071
  case 'ArrowDown':
@@ -868,15 +1098,18 @@
868
1098
  if (prefersReducedMotion) {
869
1099
  document.body.classList.add('reduce-motion');
870
1100
  document.documentElement.style.scrollBehavior = 'auto';
871
- const style = document.createElement('style');
872
- style.textContent = `
873
- *, *::before, *::after {
874
- animation-duration: 0.01ms !important;
875
- animation-iteration-count: 1 !important;
876
- transition-duration: 0.01ms !important;
877
- }
878
- `;
879
- document.head.append(style);
1101
+ if (!document.getElementById('adminlte-reduce-motion')) {
1102
+ const style = document.createElement('style');
1103
+ style.id = 'adminlte-reduce-motion';
1104
+ style.textContent = `
1105
+ *, *::before, *::after {
1106
+ animation-duration: 0.01ms !important;
1107
+ animation-iteration-count: 1 !important;
1108
+ transition-duration: 0.01ms !important;
1109
+ }
1110
+ `;
1111
+ document.head.append(style);
1112
+ }
880
1113
  }
881
1114
  }
882
1115
  initErrorAnnouncements() {
@@ -899,6 +1132,9 @@
899
1132
  childList: true,
900
1133
  subtree: true
901
1134
  });
1135
+ this.signal.addEventListener('abort', () => {
1136
+ observer.disconnect();
1137
+ }, { once: true });
902
1138
  }
903
1139
  initTableAccessibility() {
904
1140
  document.querySelectorAll('table').forEach((table) => {
@@ -950,6 +1186,9 @@
950
1186
  });
951
1187
  }
952
1188
  handleFormError(input) {
1189
+ if (!input.id && !input.name) {
1190
+ input.id = accessibilityUtils.generateId('field');
1191
+ }
953
1192
  const errorId = `${input.id || input.name}-error`;
954
1193
  let errorElement = document.getElementById(errorId);
955
1194
  if (!errorElement) {
@@ -960,25 +1199,31 @@
960
1199
  input.parentNode?.append(errorElement);
961
1200
  }
962
1201
  errorElement.textContent = input.validationMessage;
963
- input.setAttribute('aria-describedby', errorId);
1202
+ const describedBy = (input.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
1203
+ if (!describedBy.includes(errorId)) {
1204
+ describedBy.push(errorId);
1205
+ }
1206
+ input.setAttribute('aria-describedby', describedBy.join(' '));
964
1207
  input.classList.add('is-invalid');
965
1208
  this.announce(`Error in ${input.labels?.[0]?.textContent || input.name}: ${input.validationMessage}`, 'assertive');
966
1209
  }
967
1210
  initModalFocusManagement() {
1211
+ document.addEventListener('show.bs.modal', () => {
1212
+ this.focusHistory.push(document.activeElement);
1213
+ }, { signal: this.signal });
968
1214
  document.addEventListener('shown.bs.modal', (event) => {
969
1215
  const modal = event.target;
970
- const focusableElements = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
971
- if (focusableElements.length > 0) {
972
- focusableElements[0].focus();
973
- }
974
- this.focusHistory.push(document.activeElement);
975
- });
1216
+ const autofocusElement = modal.querySelector('[autofocus]');
1217
+ const firstFocusable = autofocusElement ||
1218
+ modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
1219
+ firstFocusable?.focus();
1220
+ }, { signal: this.signal });
976
1221
  document.addEventListener('hidden.bs.modal', () => {
977
1222
  const previousElement = this.focusHistory.pop();
978
- if (previousElement) {
1223
+ if (previousElement?.isConnected) {
979
1224
  previousElement.focus();
980
1225
  }
981
- });
1226
+ }, { signal: this.signal });
982
1227
  }
983
1228
  initDropdownFocusManagement() {
984
1229
  document.addEventListener('shown.bs.dropdown', (event) => {
@@ -988,7 +1233,7 @@
988
1233
  if (firstItem) {
989
1234
  firstItem.focus();
990
1235
  }
991
- });
1236
+ }, { signal: this.signal });
992
1237
  }
993
1238
  announce(message, priority = 'polite') {
994
1239
  if (!this.liveRegion) {
@@ -1029,7 +1274,7 @@
1029
1274
  event.preventDefault();
1030
1275
  }
1031
1276
  }
1032
- });
1277
+ }, { signal: this.signal });
1033
1278
  }
1034
1279
  addLandmarks() {
1035
1280
  const main = document.querySelector('main');
@@ -1037,7 +1282,9 @@
1037
1282
  const appMain = document.querySelector('.app-main');
1038
1283
  if (appMain) {
1039
1284
  appMain.setAttribute('role', 'main');
1040
- appMain.id = 'main';
1285
+ if (!appMain.id) {
1286
+ appMain.id = 'main';
1287
+ }
1041
1288
  }
1042
1289
  }
1043
1290
  document.querySelectorAll('.navbar-nav, .nav').forEach((nav, index) => {
@@ -1060,6 +1307,54 @@
1060
1307
  const initAccessibility = (config) => {
1061
1308
  return new AccessibilityManager(config);
1062
1309
  };
1310
+ const parseColorChannels = (color) => {
1311
+ const hexMatch = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(color.trim());
1312
+ if (hexMatch) {
1313
+ let hex = hexMatch[1];
1314
+ if (hex.length === 3) {
1315
+ hex = [...hex].map(character => character + character).join('');
1316
+ }
1317
+ return [
1318
+ Number.parseInt(hex.slice(0, 2), 16),
1319
+ Number.parseInt(hex.slice(2, 4), 16),
1320
+ Number.parseInt(hex.slice(4, 6), 16)
1321
+ ];
1322
+ }
1323
+ return color.match(/\d+/g)?.map(Number) || [0, 0, 0];
1324
+ };
1325
+ const getLuminance = (color) => {
1326
+ const [r, g, b] = parseColorChannels(color).map(c => {
1327
+ c = c / 255;
1328
+ return c <= 0.039_28 ? c / 12.92 : (c + 0.055) ** 2.4 / (1.055 ** 2.4);
1329
+ });
1330
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
1331
+ };
1332
+ const accessibilityUtils = {
1333
+ checkColorContrast: (foreground, background) => {
1334
+ const l1 = getLuminance(foreground);
1335
+ const l2 = getLuminance(background);
1336
+ const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
1337
+ return {
1338
+ ratio: Math.round(ratio * 100) / 100,
1339
+ passes: ratio >= 4.5
1340
+ };
1341
+ },
1342
+ generateId: (prefix = 'a11y') => {
1343
+ return `${prefix}-${Math.random().toString(36).slice(2, 11)}`;
1344
+ },
1345
+ isFocusable: (element) => {
1346
+ const focusableSelectors = [
1347
+ 'a[href]',
1348
+ 'button:not([disabled])',
1349
+ 'input:not([disabled])',
1350
+ 'select:not([disabled])',
1351
+ 'textarea:not([disabled])',
1352
+ '[tabindex]:not([tabindex="-1"])',
1353
+ '[contenteditable="true"]'
1354
+ ];
1355
+ return focusableSelectors.some(selector => element.matches(selector));
1356
+ }
1357
+ };
1063
1358
 
1064
1359
  onDOMContentLoaded(() => {
1065
1360
  const accessibilityManager = initAccessibility({
@@ -1073,6 +1368,7 @@
1073
1368
  });
1074
1369
 
1075
1370
  exports.CardWidget = CardWidget;
1371
+ exports.ColorMode = ColorMode;
1076
1372
  exports.DirectChat = DirectChat;
1077
1373
  exports.FullScreen = FullScreen;
1078
1374
  exports.Layout = Layout;