admin-lte 4.0.4 → 4.2.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 (59) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/README.md +6 -6
  3. package/dist/css/adminlte-docs.css +878 -0
  4. package/dist/css/adminlte-docs.css.map +1 -0
  5. package/dist/css/adminlte-docs.min.css +7 -0
  6. package/dist/css/adminlte-docs.min.css.map +1 -0
  7. package/dist/css/adminlte-docs.rtl.css +877 -0
  8. package/dist/css/adminlte-docs.rtl.css.map +1 -0
  9. package/dist/css/adminlte-docs.rtl.min.css +7 -0
  10. package/dist/css/adminlte-docs.rtl.min.css.map +1 -0
  11. package/dist/css/adminlte.css +132 -912
  12. package/dist/css/adminlte.css.map +1 -1
  13. package/dist/css/adminlte.min.css +2 -2
  14. package/dist/css/adminlte.min.css.map +1 -1
  15. package/dist/css/adminlte.rtl.css +132 -912
  16. package/dist/css/adminlte.rtl.css.map +1 -1
  17. package/dist/css/adminlte.rtl.min.css +2 -2
  18. package/dist/css/adminlte.rtl.min.css.map +1 -1
  19. package/dist/js/adminlte.esm.js +1406 -0
  20. package/dist/js/adminlte.esm.js.map +1 -0
  21. package/dist/js/adminlte.esm.min.js +7 -0
  22. package/dist/js/adminlte.esm.min.js.map +1 -0
  23. package/dist/js/adminlte.js +449 -215
  24. package/dist/js/adminlte.js.map +1 -1
  25. package/dist/js/adminlte.min.js +2 -2
  26. package/dist/js/adminlte.min.js.map +1 -1
  27. package/dist/js/types/accessibility.d.ts +46 -0
  28. package/dist/js/types/adminlte.d.ts +10 -0
  29. package/dist/js/types/base-component.d.ts +60 -0
  30. package/dist/js/types/card-widget.d.ts +30 -0
  31. package/dist/js/types/color-mode.d.ts +74 -0
  32. package/dist/js/types/direct-chat.d.ts +19 -0
  33. package/dist/js/types/fullscreen.d.ts +21 -0
  34. package/dist/js/types/layout.d.ts +19 -0
  35. package/dist/js/types/push-menu.d.ts +123 -0
  36. package/dist/js/types/treeview.d.ts +27 -0
  37. package/dist/js/types/util/index.d.ts +79 -0
  38. package/package.json +52 -25
  39. package/src/scss/_app-header.scss +3 -1
  40. package/src/scss/_app-main.scss +4 -0
  41. package/src/scss/_app-sidebar.scss +3 -1
  42. package/src/scss/_bootstrap-overrides.scss +60 -0
  43. package/src/scss/_info-box.scss +3 -1
  44. package/src/scss/_progress-bars.scss +3 -1
  45. package/src/scss/_table.scss +4 -2
  46. package/src/scss/adminlte-docs.scss +25 -0
  47. package/src/scss/adminlte.scss +5 -4
  48. package/src/scss/pages/_login_and_register.scss +3 -1
  49. package/src/ts/accessibility.ts +6 -2
  50. package/src/ts/adminlte.ts +7 -3
  51. package/src/ts/base-component.ts +111 -0
  52. package/src/ts/card-widget.ts +124 -100
  53. package/src/ts/color-mode.ts +254 -0
  54. package/src/ts/direct-chat.ts +38 -32
  55. package/src/ts/fullscreen.ts +32 -23
  56. package/src/ts/push-menu.ts +106 -43
  57. package/src/ts/treeview.ts +84 -50
  58. package/src/ts/util/index.ts +80 -16
  59. package/src/scss/_bootstrap-variables.scss +0 -1766
@@ -1,3 +1,5 @@
1
+ @use "sass:map";
2
+
1
3
  //
2
4
  // Component: Progress Bar
3
5
  //
@@ -40,7 +42,7 @@
40
42
  }
41
43
 
42
44
  .progress-group {
43
- margin-bottom: map-get($spacers, 2);
45
+ margin-bottom: map.get($spacers, 2);
44
46
  }
45
47
 
46
48
  // size variation
@@ -1,3 +1,5 @@
1
+ @use "sass:map";
2
+
1
3
  //
2
4
  // Component: Table
3
5
  //
@@ -56,11 +58,11 @@
56
58
  tbody > tr > th,
57
59
  tbody > tr > td {
58
60
  &:first-of-type {
59
- padding-left: map-get($spacers, 4);
61
+ padding-left: map.get($spacers, 4);
60
62
  }
61
63
 
62
64
  &:last-of-type {
63
- padding-right: map-get($spacers, 4);
65
+ padding-right: map.get($spacers, 4);
64
66
  }
65
67
  }
66
68
  }
@@ -0,0 +1,25 @@
1
+ /*!
2
+ * AdminLTE v4.2.0 — documentation styles
3
+ * Styles for the documentation and FAQ pages only (body.docs-page /
4
+ * body.faq-page). Applications never need this file; it is loaded by the
5
+ * docs pages in addition to adminlte.css.
6
+ */
7
+
8
+ // Bootstrap Configuration
9
+ // ---------------------------------------------------
10
+ @import "bootstrap/scss/functions";
11
+ @import "bootstrap-overrides";
12
+ @import "bootstrap/scss/variables";
13
+ @import "bootstrap/scss/variables-dark";
14
+ @import "bootstrap/scss/maps";
15
+ @import "bootstrap/scss/mixins";
16
+
17
+ // AdminLTE Configuration
18
+ // ---------------------------------------------------
19
+ @import "variables";
20
+ @import "variables-dark";
21
+ @import "mixins";
22
+
23
+ // Documentation styles
24
+ // ---------------------------------------------------
25
+ @import "docs";
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * AdminLTE v4.0.4
2
+ * AdminLTE v4.2.0
3
3
  * Author: Colorlib
4
4
  * Website: AdminLTE.io <https://adminlte.io>
5
5
  * License: Open source - MIT <https://opensource.org/licenses/MIT>
@@ -11,7 +11,7 @@
11
11
 
12
12
  // AdminLTE Configuration
13
13
  // ---------------------------------------------------
14
- @import "bootstrap-variables"; // little modified are here
14
+ @import "bootstrap-overrides"; // AdminLTE's ~10 changes to Bootstrap defaults
15
15
 
16
16
  // Bootstrap Configuration
17
17
  // ---------------------------------------------------
@@ -77,5 +77,6 @@
77
77
  // AdminLTE Accessibility Styles - WCAG 2.1 AA Compliance
78
78
  @import "accessibility";
79
79
 
80
- // AdminLTE Documentation Styles (only applies to pages with body.docs-page)
81
- @import "docs";
80
+ // Documentation styles live in their own bundle (adminlte-docs.scss) so
81
+ // applications don't ship them — the docs pages load it in addition to
82
+ // this file.
@@ -1,3 +1,5 @@
1
+ @use "sass:map";
2
+
1
3
  //
2
4
  // Pages: Login & Register
3
5
  //
@@ -28,7 +30,7 @@
28
30
  .register-box {
29
31
  width: 400px;
30
32
 
31
- @media (max-width: map-get($grid-breakpoints, sm)) {
33
+ @media (max-width: map.get($grid-breakpoints, sm)) {
32
34
  width: 90%;
33
35
  margin-top: .5rem;
34
36
  }
@@ -357,11 +357,15 @@ export class AccessibilityManager {
357
357
  }
358
358
 
359
359
  // Handle invalid state unless the element explicitly opts out via the
360
- // 'disable-adminlte-validations' class.
360
+ // 'disable-adminlte-validations' class. Registered with the lifecycle
361
+ // signal: this runs once per cycle, and under a framework that
362
+ // re-initialises against a persistent <body> (initialize()) the inputs
363
+ // survive the cycle — an unsignalled listener would stack one handler
364
+ // per re-init on every field.
361
365
  if (!htmlInput.classList.contains('disable-adminlte-validations')) {
362
366
  htmlInput.addEventListener('invalid', () => {
363
367
  this.handleFormError(htmlInput)
364
- })
368
+ }, { signal: this.signal })
365
369
  }
366
370
  })
367
371
  }
@@ -1,14 +1,15 @@
1
- import { onDOMContentLoaded } from './util/index.js'
1
+ import { initialize, teardown, onDOMContentLoaded } from './util/index.js'
2
2
  import Layout from './layout.js'
3
3
  import CardWidget from './card-widget.js'
4
4
  import Treeview from './treeview.js'
5
5
  import DirectChat from './direct-chat.js'
6
6
  import FullScreen from './fullscreen.js'
7
7
  import PushMenu from './push-menu.js'
8
+ import ColorMode from './color-mode.js'
8
9
  import { initAccessibility } from './accessibility.js'
9
10
 
10
11
  /**
11
- * AdminLTE v4.0.4
12
+ * AdminLTE v4.2.0
12
13
  * Author: Colorlib
13
14
  * Website: AdminLTE.io <https://adminlte.io>
14
15
  * License: Open source - MIT <https://opensource.org/licenses/MIT>
@@ -38,5 +39,8 @@ export {
38
39
  DirectChat,
39
40
  FullScreen,
40
41
  PushMenu,
41
- initAccessibility
42
+ ColorMode,
43
+ initAccessibility,
44
+ initialize,
45
+ teardown
42
46
  }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * --------------------------------------------
3
+ * @file AdminLTE base-component.ts
4
+ * @description Shared component lifecycle for AdminLTE plugins: a per-element
5
+ * instance registry (getInstance / getOrCreateInstance / dispose) and a
6
+ * consistent custom-event contract, mirroring Bootstrap's component API.
7
+ * @license MIT
8
+ * --------------------------------------------
9
+ */
10
+
11
+ /**
12
+ * Component registry: element -> (data key -> component instance). A single
13
+ * element can host several components at once, each stored under its own
14
+ * DATA_KEY. WeakMap keys don't prevent garbage collection, so instances die
15
+ * with their elements — important under Hotwired Turbo, which swaps the whole
16
+ * <body> on navigation.
17
+ */
18
+ const componentRegistry = new WeakMap<Element, Map<string, BaseComponent>>()
19
+
20
+ class BaseComponent {
21
+ /**
22
+ * Subclasses must override this getter to declare their own name.
23
+ */
24
+ static get NAME(): string {
25
+ throw new Error('Component subclasses must override the static NAME getter.')
26
+ }
27
+
28
+ /**
29
+ * Key this component is registered under: `lte.<name>`.
30
+ */
31
+ static get DATA_KEY(): string {
32
+ return `lte.${this.NAME}`
33
+ }
34
+
35
+ /**
36
+ * Untyped registry lookup. Every component exposes a typed wrapper
37
+ * (e.g. CardWidget.getInstance()) built on top of this.
38
+ *
39
+ * @param element The element to look up.
40
+ * @returns The instance for this component, or null if there is none.
41
+ */
42
+ protected static _getInstance(element: Element | null | undefined): BaseComponent | null {
43
+ if (!element) {
44
+ return null
45
+ }
46
+
47
+ return componentRegistry.get(element)?.get(this.DATA_KEY) ?? null
48
+ }
49
+
50
+ /**
51
+ * The element this instance is attached to.
52
+ */
53
+ _element: HTMLElement
54
+
55
+ /**
56
+ * Attach a new instance to the given element and register it under the
57
+ * subclass's DATA_KEY.
58
+ *
59
+ * @param element The element to attach this instance to.
60
+ */
61
+ constructor(element: HTMLElement) {
62
+ this._element = element
63
+
64
+ const instances = componentRegistry.get(element) ?? new Map<string, BaseComponent>()
65
+ componentRegistry.set(element, instances)
66
+ instances.set((this.constructor as typeof BaseComponent).DATA_KEY, this)
67
+ }
68
+
69
+ /**
70
+ * Remove this instance from the registry so getInstance() no longer
71
+ * returns it. Subclasses release their own resources, then call
72
+ * super.dispose().
73
+ */
74
+ dispose(): void {
75
+ const instances = componentRegistry.get(this._element)
76
+ instances?.delete((this.constructor as typeof BaseComponent).DATA_KEY)
77
+
78
+ // Drop the element's entry once it holds no components at all.
79
+ if (instances?.size === 0) {
80
+ componentRegistry.delete(this._element)
81
+ }
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Dispatch a namespaced custom event that bubbles — so applications can
87
+ * listen once on `document` — and can optionally carry a payload or be
88
+ * canceled. Returns the event so callers can check `defaultPrevented`.
89
+ *
90
+ * @param element The element to dispatch the event on.
91
+ * @param name The namespaced event name, e.g. `collapse.lte.push-menu`.
92
+ * @param options `cancelable` opts the event into preventDefault(); `detail`
93
+ * is the payload handed to listeners.
94
+ * @returns The dispatched event, after listeners have run.
95
+ */
96
+ const dispatchCustomEvent = <T = undefined>(
97
+ element: Element,
98
+ name: string,
99
+ options: { cancelable?: boolean; detail?: T } = {}
100
+ ): CustomEvent<T | undefined> => {
101
+ const event = new CustomEvent<T | undefined>(name, {
102
+ bubbles: true,
103
+ cancelable: options.cancelable ?? false,
104
+ detail: options.detail
105
+ })
106
+
107
+ element.dispatchEvent(event)
108
+ return event
109
+ }
110
+
111
+ export { BaseComponent, dispatchCustomEvent }
@@ -6,8 +6,8 @@
6
6
  * --------------------------------------------
7
7
  */
8
8
 
9
+ import { BaseComponent, dispatchCustomEvent } from './base-component'
9
10
  import {
10
- onDOMContentLoaded,
11
11
  slideUp,
12
12
  slideDown
13
13
  } from './util/index'
@@ -17,11 +17,18 @@ import {
17
17
  * ====================================================
18
18
  */
19
19
 
20
- const DATA_KEY = 'lte.card-widget'
21
- const EVENT_KEY = `.${DATA_KEY}`
20
+ const NAME = 'card-widget'
21
+ const EVENT_KEY = `.lte.${NAME}`
22
+
23
+ // Cancelable "before" events, dispatched on the card when an action starts.
24
+ const EVENT_COLLAPSE = `collapse${EVENT_KEY}`
25
+ const EVENT_EXPAND = `expand${EVENT_KEY}`
26
+ const EVENT_REMOVE = `remove${EVENT_KEY}`
27
+
28
+ // "After" events, dispatched on the card when the action has completed.
22
29
  const EVENT_COLLAPSED = `collapsed${EVENT_KEY}`
23
30
  const EVENT_EXPANDED = `expanded${EVENT_KEY}`
24
- const EVENT_REMOVE = `remove${EVENT_KEY}`
31
+ const EVENT_REMOVED = `removed${EVENT_KEY}`
25
32
  const EVENT_MAXIMIZED = `maximized${EVENT_KEY}`
26
33
  const EVENT_MINIMIZED = `minimized${EVENT_KEY}`
27
34
 
@@ -53,14 +60,24 @@ const Default: Config = {
53
60
  maximizeTrigger: SELECTOR_DATA_MAXIMIZE
54
61
  }
55
62
 
56
- class CardWidget {
57
- _element: HTMLElement
63
+ class CardWidget extends BaseComponent {
64
+ static get NAME(): string {
65
+ return NAME
66
+ }
67
+
68
+ static getInstance(element: Element | null | undefined): CardWidget | null {
69
+ return this._getInstance(element) as CardWidget | null
70
+ }
71
+
72
+ static getOrCreateInstance(element: HTMLElement, config: Partial<Config> = {}): CardWidget {
73
+ return this.getInstance(element) ?? new this(element, config)
74
+ }
75
+
58
76
  _parent: HTMLElement | undefined
59
- _clone: HTMLElement | undefined
60
77
  _config: Config
61
78
 
62
- constructor(element: HTMLElement, config: Config) {
63
- this._element = element
79
+ constructor(element: HTMLElement, config: Partial<Config> = {}) {
80
+ super(element)
64
81
  this._parent = element.closest(SELECTOR_CARD) as HTMLElement | undefined
65
82
 
66
83
  if (element.classList.contains(CLASS_NAME_CARD)) {
@@ -71,76 +88,87 @@ class CardWidget {
71
88
  }
72
89
 
73
90
  collapse() {
74
- const event = new Event(EVENT_COLLAPSED)
91
+ if (!this._parent) {
92
+ return
93
+ }
75
94
 
76
- if (this._parent) {
77
- this._parent.classList.add(CLASS_NAME_COLLAPSING)
78
- this._parent.classList.remove(CLASS_NAME_EXPANDING)
95
+ if (dispatchCustomEvent(this._parent, EVENT_COLLAPSE, { cancelable: true }).defaultPrevented) {
96
+ return
97
+ }
79
98
 
80
- // Only target direct children to avoid affecting nested cards
81
- const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`)
99
+ this._parent.classList.add(CLASS_NAME_COLLAPSING)
100
+ this._parent.classList.remove(CLASS_NAME_EXPANDING)
82
101
 
83
- elm.forEach(el => {
84
- if (el instanceof HTMLElement) {
85
- slideUp(el, this._config.animationSpeed)
86
- }
87
- })
102
+ // Only target direct children to avoid affecting nested cards
103
+ const elm = this._parent.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`)
88
104
 
89
- setTimeout(() => {
90
- // Guard against a stale timer: if expand() ran while this collapse
91
- // was still animating, the card must not end up marked collapsed.
92
- if (this._parent?.classList.contains(CLASS_NAME_COLLAPSING)) {
93
- this._parent.classList.add(CLASS_NAME_COLLAPSED)
94
- this._parent.classList.remove(CLASS_NAME_COLLAPSING)
95
- }
96
- }, this._config.animationSpeed)
97
- }
105
+ elm.forEach(el => {
106
+ if (el instanceof HTMLElement) {
107
+ slideUp(el, this._config.animationSpeed)
108
+ }
109
+ })
98
110
 
99
- this._element?.dispatchEvent(event)
111
+ setTimeout(() => {
112
+ // Guard against a stale timer: if expand() ran while this collapse
113
+ // was still animating, the card must not end up marked collapsed.
114
+ if (this._parent?.classList.contains(CLASS_NAME_COLLAPSING)) {
115
+ this._parent.classList.add(CLASS_NAME_COLLAPSED)
116
+ this._parent.classList.remove(CLASS_NAME_COLLAPSING)
117
+ dispatchCustomEvent(this._parent, EVENT_COLLAPSED)
118
+ }
119
+ }, this._config.animationSpeed)
100
120
  }
101
121
 
102
122
  expand() {
103
- const event = new Event(EVENT_EXPANDED)
123
+ if (!this._parent) {
124
+ return
125
+ }
104
126
 
105
- if (this._parent) {
106
- this._parent.classList.add(CLASS_NAME_EXPANDING)
107
- this._parent.classList.remove(CLASS_NAME_COLLAPSING, CLASS_NAME_COLLAPSED)
127
+ if (dispatchCustomEvent(this._parent, EVENT_EXPAND, { cancelable: true }).defaultPrevented) {
128
+ return
129
+ }
108
130
 
109
- // Only target direct children to avoid affecting nested cards
110
- const elm = this._parent?.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`)
131
+ this._parent.classList.add(CLASS_NAME_EXPANDING)
132
+ this._parent.classList.remove(CLASS_NAME_COLLAPSING, CLASS_NAME_COLLAPSED)
111
133
 
112
- elm.forEach(el => {
113
- if (el instanceof HTMLElement) {
114
- slideDown(el, this._config.animationSpeed)
115
- }
116
- })
134
+ // Only target direct children to avoid affecting nested cards
135
+ const elm = this._parent.querySelectorAll(`:scope > ${SELECTOR_CARD_BODY}, :scope > ${SELECTOR_CARD_FOOTER}`)
117
136
 
118
- setTimeout(() => {
119
- // Same stale-timer guard as collapse(), in the other direction.
120
- if (this._parent?.classList.contains(CLASS_NAME_EXPANDING)) {
121
- this._parent.classList.remove(CLASS_NAME_EXPANDING)
122
- }
123
- }, this._config.animationSpeed)
124
- }
137
+ elm.forEach(el => {
138
+ if (el instanceof HTMLElement) {
139
+ slideDown(el, this._config.animationSpeed)
140
+ }
141
+ })
125
142
 
126
- this._element?.dispatchEvent(event)
143
+ setTimeout(() => {
144
+ // Same stale-timer guard as collapse(), in the other direction.
145
+ if (this._parent?.classList.contains(CLASS_NAME_EXPANDING)) {
146
+ this._parent.classList.remove(CLASS_NAME_EXPANDING)
147
+ dispatchCustomEvent(this._parent, EVENT_EXPANDED)
148
+ }
149
+ }, this._config.animationSpeed)
127
150
  }
128
151
 
129
152
  remove() {
130
- const event = new Event(EVENT_REMOVE)
131
-
132
- if (this._parent) {
133
- const parent = this._parent
134
- slideUp(parent, this._config.animationSpeed)
153
+ if (!this._parent) {
154
+ return
155
+ }
135
156
 
136
- // Actually remove the card once the animation finishes — hiding it
137
- // would keep its form fields submitting and its ids in the document.
138
- setTimeout(() => {
139
- parent.remove()
140
- }, this._config.animationSpeed)
157
+ if (dispatchCustomEvent(this._parent, EVENT_REMOVE, { cancelable: true }).defaultPrevented) {
158
+ return
141
159
  }
142
160
 
143
- this._element?.dispatchEvent(event)
161
+ const parent = this._parent
162
+ slideUp(parent, this._config.animationSpeed)
163
+
164
+ // Actually remove the card once the animation finishes — hiding it
165
+ // would keep its form fields submitting and its ids in the document.
166
+ // `removed` is dispatched just before detachment so it can still bubble.
167
+ setTimeout(() => {
168
+ dispatchCustomEvent(parent, EVENT_REMOVED)
169
+ parent.remove()
170
+ this.dispose()
171
+ }, this._config.animationSpeed)
144
172
  }
145
173
 
146
174
  toggle() {
@@ -155,8 +183,6 @@ class CardWidget {
155
183
  }
156
184
 
157
185
  maximize() {
158
- const event = new Event(EVENT_MAXIMIZED)
159
-
160
186
  if (this._parent) {
161
187
  this._parent.style.height = `${this._parent.offsetHeight}px`
162
188
  this._parent.style.width = `${this._parent.offsetWidth}px`
@@ -175,16 +201,14 @@ class CardWidget {
175
201
  if (this._parent.classList.contains(CLASS_NAME_COLLAPSED)) {
176
202
  this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED)
177
203
  }
204
+
205
+ dispatchCustomEvent(this._parent, EVENT_MAXIMIZED)
178
206
  }
179
207
  }, 150)
180
208
  }
181
-
182
- this._element?.dispatchEvent(event)
183
209
  }
184
210
 
185
211
  minimize() {
186
- const event = new Event(EVENT_MINIMIZED)
187
-
188
212
  if (this._parent) {
189
213
  this._parent.style.height = 'auto'
190
214
  this._parent.style.width = 'auto'
@@ -204,6 +228,8 @@ class CardWidget {
204
228
  this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED)
205
229
  }
206
230
 
231
+ dispatchCustomEvent(this._parent, EVENT_MINIMIZED)
232
+
207
233
  // Drop the inline sizing/transition once the restore transition has
208
234
  // finished so the card doesn't carry stale styles forever.
209
235
  setTimeout(() => {
@@ -214,8 +240,6 @@ class CardWidget {
214
240
  }
215
241
  }, 10)
216
242
  }
217
-
218
- this._element?.dispatchEvent(event)
219
243
  }
220
244
 
221
245
  toggleMaximize() {
@@ -232,44 +256,44 @@ class CardWidget {
232
256
  *
233
257
  * Data Api implementation
234
258
  * ====================================================
259
+ * A single delegated listener on `document` handles all card toggles — so
260
+ * cards inserted later (AJAX, Turbo Frames) work without re-initialisation,
261
+ * and the listener itself survives Turbo's <body> swaps.
235
262
  */
236
263
 
237
- onDOMContentLoaded(() => {
238
- const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE)
239
-
240
- // Use currentTarget (the [data-lte-toggle] button itself), not target:
241
- // clicking the <i> icon inside the button would otherwise make the widget's
242
- // events fire on the icon instead of the button.
243
- collapseBtn.forEach(btn => {
244
- btn.addEventListener('click', event => {
245
- event.preventDefault()
246
- const target = event.currentTarget as HTMLElement
247
- const data = new CardWidget(target, Default)
248
- data.toggle()
249
- })
250
- })
264
+ document.addEventListener('click', event => {
265
+ const target = event.target
251
266
 
252
- const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE)
267
+ if (!(target instanceof Element)) {
268
+ return
269
+ }
253
270
 
254
- removeBtn.forEach(btn => {
255
- btn.addEventListener('click', event => {
256
- event.preventDefault()
257
- const target = event.currentTarget as HTMLElement
258
- const data = new CardWidget(target, Default)
259
- data.remove()
260
- })
261
- })
271
+ const collapseTrigger = target.closest(SELECTOR_DATA_COLLAPSE)
272
+ const removeTrigger = target.closest(SELECTOR_DATA_REMOVE)
273
+ const maximizeTrigger = target.closest(SELECTOR_DATA_MAXIMIZE)
274
+ const trigger = collapseTrigger ?? removeTrigger ?? maximizeTrigger
262
275
 
263
- const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE)
276
+ if (!trigger) {
277
+ return
278
+ }
264
279
 
265
- maxBtn.forEach(btn => {
266
- btn.addEventListener('click', event => {
267
- event.preventDefault()
268
- const target = event.currentTarget as HTMLElement
269
- const data = new CardWidget(target, Default)
270
- data.toggleMaximize()
271
- })
272
- })
280
+ event.preventDefault()
281
+
282
+ const card = trigger.closest(SELECTOR_CARD) as HTMLElement | null
283
+
284
+ if (!card) {
285
+ return
286
+ }
287
+
288
+ const widget = CardWidget.getOrCreateInstance(card)
289
+
290
+ if (collapseTrigger) {
291
+ widget.toggle()
292
+ } else if (removeTrigger) {
293
+ widget.remove()
294
+ } else {
295
+ widget.toggleMaximize()
296
+ }
273
297
  })
274
298
 
275
299
  export default CardWidget