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.
Files changed (107) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +44 -18
  3. package/dist/UI/general.html +311 -92
  4. package/dist/UI/icons.html +311 -92
  5. package/dist/UI/timeline.html +311 -92
  6. package/dist/css/adminlte.css +2814 -1138
  7. package/dist/css/adminlte.css.map +1 -1
  8. package/dist/css/adminlte.min.css +2 -2
  9. package/dist/css/adminlte.min.css.map +1 -1
  10. package/dist/css/adminlte.rtl.css +2814 -1138
  11. package/dist/css/adminlte.rtl.css.map +1 -1
  12. package/dist/css/adminlte.rtl.min.css +2 -2
  13. package/dist/css/adminlte.rtl.min.css.map +1 -1
  14. package/dist/docs/browser-support.html +291 -385
  15. package/dist/docs/color-mode.html +283 -381
  16. package/dist/docs/components/main-header.html +269 -363
  17. package/dist/docs/components/main-sidebar.html +284 -378
  18. package/dist/docs/customization.html +1056 -0
  19. package/dist/docs/deployment.html +1121 -0
  20. package/dist/docs/faq.html +887 -397
  21. package/dist/docs/getting-started.html +967 -0
  22. package/dist/docs/how-to-contribute.html +365 -450
  23. package/dist/docs/index.html +16 -0
  24. package/dist/docs/integrations.html +1207 -0
  25. package/dist/docs/introduction.html +483 -434
  26. package/dist/docs/javascript/accessibility.html +925 -0
  27. package/dist/docs/javascript/card-widget.html +995 -0
  28. package/dist/docs/javascript/direct-chat.html +872 -0
  29. package/dist/docs/javascript/fullscreen.html +881 -0
  30. package/dist/docs/javascript/layout.html +884 -0
  31. package/dist/docs/javascript/plugins-overview.html +1120 -0
  32. package/dist/docs/javascript/pushmenu.html +522 -377
  33. package/dist/docs/javascript/treeview.html +498 -390
  34. package/dist/docs/layout-blueprint.html +1063 -0
  35. package/dist/docs/layout.html +316 -402
  36. package/dist/docs/license.html +282 -375
  37. package/dist/docs/migration.html +1125 -0
  38. package/dist/docs/recipes.html +1161 -0
  39. package/dist/docs/rtl.html +933 -0
  40. package/dist/examples/lockscreen.html +66 -2
  41. package/dist/examples/login-v2.html +65 -1
  42. package/dist/examples/login.html +65 -1
  43. package/dist/examples/register-v2.html +65 -1
  44. package/dist/examples/register.html +65 -1
  45. package/dist/forms/elements.html +1381 -0
  46. package/dist/forms/{general.html → layout.html} +382 -452
  47. package/dist/forms/validation.html +1251 -0
  48. package/dist/forms/wizard.html +1386 -0
  49. package/dist/generate/theme.html +135 -5
  50. package/dist/index.html +311 -92
  51. package/dist/index2.html +314 -92
  52. package/dist/index3.html +311 -92
  53. package/dist/js/adminlte.js +135 -314
  54. package/dist/js/adminlte.js.map +1 -1
  55. package/dist/js/adminlte.min.js +3 -3
  56. package/dist/js/adminlte.min.js.map +1 -1
  57. package/dist/layout/collapsed-sidebar-without-hover.html +1120 -0
  58. package/dist/layout/collapsed-sidebar.html +311 -92
  59. package/dist/layout/fixed-complete.html +311 -92
  60. package/dist/layout/fixed-footer.html +311 -92
  61. package/dist/layout/fixed-header.html +311 -92
  62. package/dist/layout/fixed-sidebar.html +311 -92
  63. package/dist/layout/layout-custom-area.html +311 -92
  64. package/dist/layout/layout-rtl.html +311 -92
  65. package/dist/layout/logo-switch.html +135 -5
  66. package/dist/layout/sidebar-mini.html +311 -92
  67. package/dist/layout/unfixed-sidebar.html +311 -92
  68. package/dist/mailbox/compose.html +1127 -0
  69. package/dist/mailbox/inbox.html +1477 -0
  70. package/dist/mailbox/read.html +1178 -0
  71. package/dist/pages/404.html +213 -0
  72. package/dist/pages/500.html +201 -0
  73. package/dist/pages/calendar.html +1242 -0
  74. package/dist/pages/chat.html +1403 -0
  75. package/dist/pages/faq.html +1359 -0
  76. package/dist/pages/file-manager.html +1811 -0
  77. package/dist/pages/invoice.html +1175 -0
  78. package/dist/pages/kanban.html +1415 -0
  79. package/dist/pages/maintenance.html +204 -0
  80. package/dist/pages/pricing.html +1255 -0
  81. package/dist/pages/profile.html +1358 -0
  82. package/dist/pages/projects.html +1620 -0
  83. package/dist/pages/settings.html +1363 -0
  84. package/dist/tables/data.html +1295 -0
  85. package/dist/tables/simple.html +311 -92
  86. package/dist/widgets/cards.html +311 -92
  87. package/dist/widgets/info-box.html +311 -92
  88. package/dist/widgets/small-box.html +311 -92
  89. package/netlify.toml +16 -0
  90. package/package.json +32 -26
  91. package/src/scss/_accessibility.scss +44 -0
  92. package/src/scss/_app-footer.scss +9 -0
  93. package/src/scss/_app-header.scss +27 -0
  94. package/src/scss/_app-sidebar.scss +73 -74
  95. package/src/scss/_bootstrap-variables.scss +12 -5
  96. package/src/scss/_docs.scss +816 -0
  97. package/src/scss/_table.scss +4 -4
  98. package/src/scss/adminlte.scss +4 -1
  99. package/src/scss/pages/_login_and_register.scss +1 -1
  100. package/src/ts/accessibility.ts +9 -7
  101. package/src/ts/adminlte.ts +2 -14
  102. package/src/ts/fullscreen.ts +8 -4
  103. package/src/ts/layout.ts +34 -16
  104. package/src/ts/push-menu.ts +257 -104
  105. package/src/ts/util/index.ts +10 -0
  106. package/.claude/settings.local.json +0 -33
  107. package/CLAUDE.md +0 -79
@@ -1,9 +1,9 @@
1
1
  /**
2
- * --------------------------------------------
2
+ * ----------------------------------------------------------------------------
3
3
  * @file AdminLTE push-menu.ts
4
4
  * @description Push menu for AdminLTE.
5
5
  * @license MIT
6
- * --------------------------------------------
6
+ * ----------------------------------------------------------------------------
7
7
  */
8
8
 
9
9
  import {
@@ -11,34 +11,44 @@ import {
11
11
  } from './util/index'
12
12
 
13
13
  /**
14
- * ------------------------------------------------------------------------
14
+ * ----------------------------------------------------------------------------
15
15
  * Constants
16
- * ------------------------------------------------------------------------
16
+ * ----------------------------------------------------------------------------
17
17
  */
18
18
 
19
19
  const DATA_KEY = 'lte.push-menu'
20
20
  const EVENT_KEY = `.${DATA_KEY}`
21
-
22
21
  const EVENT_OPEN = `open${EVENT_KEY}`
23
22
  const EVENT_COLLAPSE = `collapse${EVENT_KEY}`
24
23
 
25
24
  const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini'
26
- const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse'
27
- const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open'
28
25
  const CLASS_NAME_SIDEBAR_EXPAND = 'sidebar-expand'
29
26
  const CLASS_NAME_SIDEBAR_OVERLAY = 'sidebar-overlay'
30
- const CLASS_NAME_MENU_OPEN = 'menu-open'
27
+
28
+ // Classes used to explicitly indicate the sidebar state.
29
+ // - sidebar-collapse: Indicates the sidebar is explicitly collapsed.
30
+ // - sidebar-open: Indicates the sidebar is explicitly open on mobile sizes.
31
+ const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse'
32
+ const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open'
31
33
 
32
34
  const SELECTOR_APP_SIDEBAR = '.app-sidebar'
33
- const SELECTOR_SIDEBAR_MENU = '.sidebar-menu'
34
- const SELECTOR_NAV_ITEM = '.nav-item'
35
- const SELECTOR_NAV_TREEVIEW = '.nav-treeview'
36
35
  const SELECTOR_APP_WRAPPER = '.app-wrapper'
37
36
  const SELECTOR_SIDEBAR_EXPAND = `[class*="${CLASS_NAME_SIDEBAR_EXPAND}"]`
38
37
  const SELECTOR_SIDEBAR_TOGGLE = '[data-lte-toggle="sidebar"]'
39
38
 
40
39
  const STORAGE_KEY_SIDEBAR_STATE = 'lte.sidebar.state'
41
40
 
41
+ /**
42
+ * ----------------------------------------------------------------------------
43
+ * Configuration Object Interface
44
+ * - sidebarBreakpoint: The screen width (in pixels) below which the sidebar
45
+ * is considered to be on a "mobile" size and should be collapsed by default
46
+ * unless explicitly opened.
47
+ * - enablePersistence: Whether to save the sidebar state (collapsed/open) in
48
+ * localStorage and restore it on page load.
49
+ * ----------------------------------------------------------------------------
50
+ */
51
+
42
52
  type Config = {
43
53
  sidebarBreakpoint: number;
44
54
  enablePersistence: boolean;
@@ -46,12 +56,13 @@ type Config = {
46
56
 
47
57
  const Defaults: Config = {
48
58
  sidebarBreakpoint: 992,
49
- enablePersistence: true
59
+ enablePersistence: false
50
60
  }
51
61
 
52
62
  /**
63
+ * ----------------------------------------------------------------------------
53
64
  * Class Definition
54
- * ====================================================
65
+ * ----------------------------------------------------------------------------
55
66
  */
56
67
 
57
68
  class PushMenu {
@@ -63,120 +74,191 @@ class PushMenu {
63
74
  this._config = { ...Defaults, ...config }
64
75
  }
65
76
 
66
- menusClose() {
67
- const navTreeview = document.querySelectorAll<HTMLElement>(SELECTOR_NAV_TREEVIEW)
77
+ /**
78
+ * Check if the sidebar is collapsed.
79
+ *
80
+ * @returns True if the sidebar is collapsed, false otherwise.
81
+ */
82
+ isCollapsed(): boolean {
83
+ return document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)
84
+ }
68
85
 
69
- navTreeview.forEach(navTree => {
70
- navTree.style.removeProperty('display')
71
- navTree.style.removeProperty('height')
72
- })
86
+ /**
87
+ * Check if the sidebar is explicitly open on mobile sizes.
88
+ *
89
+ * @returns True if the sidebar is explicitly open, false otherwise.
90
+ */
91
+ isExplicitlyOpen(): boolean {
92
+ return document.body.classList.contains(CLASS_NAME_SIDEBAR_OPEN)
93
+ }
73
94
 
74
- const navSidebar = document.querySelector(SELECTOR_SIDEBAR_MENU)
75
- const navItem = navSidebar?.querySelectorAll(SELECTOR_NAV_ITEM)
95
+ /**
96
+ * Check if the sidebar is in mini mode.
97
+ *
98
+ * @returns True if the sidebar is in mini mode, false otherwise.
99
+ */
100
+ isMiniMode(): boolean {
101
+ return document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI)
102
+ }
76
103
 
77
- if (navItem) {
78
- navItem.forEach(navI => {
79
- navI.classList.remove(CLASS_NAME_MENU_OPEN)
80
- })
81
- }
104
+ /**
105
+ * Check if the current screen size is considered "mobile" based on the
106
+ * sidebarBreakpoint config value.
107
+ *
108
+ * @returns True if the screen size is mobile, false otherwise.
109
+ */
110
+ isMobileSize(): boolean {
111
+ return globalThis.innerWidth <= this._config.sidebarBreakpoint
82
112
  }
83
113
 
84
- expand() {
85
- const event = new Event(EVENT_OPEN)
114
+ /**
115
+ * Expand the sidebar menu.
116
+ */
117
+ expand(): void {
118
+ // Remove the sidebar-collapse class. Only on mobile, add the sidebar-open
119
+ // class to indicate the sidebar is explicitly open.
86
120
 
87
121
  document.body.classList.remove(CLASS_NAME_SIDEBAR_COLLAPSE)
88
- document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN)
89
122
 
90
- this._element.dispatchEvent(event)
123
+ if (this.isMobileSize()) {
124
+ document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN)
125
+ }
126
+
127
+ // Dispatch the expand event.
128
+
129
+ this._element.dispatchEvent(new Event(EVENT_OPEN))
91
130
  }
92
131
 
93
- collapse() {
94
- const event = new Event(EVENT_COLLAPSE)
132
+ /**
133
+ * Collapse the sidebar menu.
134
+ */
135
+ collapse(): void {
136
+ // Remove the sidebar-open class (if present), and add the sidebar-collapse
137
+ // class.
95
138
 
96
139
  document.body.classList.remove(CLASS_NAME_SIDEBAR_OPEN)
97
140
  document.body.classList.add(CLASS_NAME_SIDEBAR_COLLAPSE)
98
141
 
99
- this._element.dispatchEvent(event)
142
+ // Dispatch the collapse event.
143
+
144
+ this._element.dispatchEvent(new Event(EVENT_COLLAPSE))
100
145
  }
101
146
 
102
- addSidebarBreakPoint() {
103
- const sidebarExpandList = document.querySelector(SELECTOR_SIDEBAR_EXPAND)?.classList ?? []
104
- const sidebarExpand = Array.from(sidebarExpandList).find(className => className.startsWith(CLASS_NAME_SIDEBAR_EXPAND)) ?? ''
105
- const sidebar = document.getElementsByClassName(sidebarExpand)[0]
106
- const sidebarContent = globalThis.getComputedStyle(sidebar, '::before').getPropertyValue('content')
107
- this._config = { ...this._config, sidebarBreakpoint: Number(sidebarContent.replace(/[^\d.-]/g, '')) }
108
-
109
- // FIXED: Don't auto-collapse on mobile if sidebar is currently open
110
- // This prevents resize events (triggered by scrolling) from closing the sidebar
111
- const isCurrentlyOpen = document.body.classList.contains(CLASS_NAME_SIDEBAR_OPEN)
112
-
113
- if (window.innerWidth <= this._config.sidebarBreakpoint) {
114
- // Only collapse if not currently open (prevents scroll-triggered closes)
115
- if (!isCurrentlyOpen) {
116
- this.collapse()
117
- }
118
- } else {
119
- if (!document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI)) {
120
- this.expand()
121
- }
147
+ /**
148
+ * Toggle the sidebar menu state.
149
+ */
150
+ toggle(): void {
151
+ // Toggle the sidebar state.
122
152
 
123
- if (document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI) && document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
124
- this.collapse()
125
- }
126
- }
127
- }
153
+ const isCollapsed = this.isCollapsed()
128
154
 
129
- toggle() {
130
- if (document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
155
+ if (isCollapsed) {
131
156
  this.expand()
132
157
  } else {
133
158
  this.collapse()
134
159
  }
135
160
 
136
- this.saveSidebarState()
161
+ // If persistence is enabled, save the new state to localStorage.
162
+
163
+ if (this._config.enablePersistence) {
164
+ this.saveSidebarState(
165
+ isCollapsed ? CLASS_NAME_SIDEBAR_OPEN : CLASS_NAME_SIDEBAR_COLLAPSE
166
+ )
167
+ }
137
168
  }
138
169
 
139
170
  /**
140
- * Save sidebar state to localStorage
171
+ * Read the CSS breakpoint of the sidebar from the DOM and update the
172
+ * sidebarBreakpoint config. This breakpoint is defined using a CSS ::before
173
+ * pseudo element on the sidebar-expand element when @media queries are used
174
+ * to change the sidebar behavior based on screen size.
141
175
  */
142
- saveSidebarState(): void {
143
- if (!this._config.enablePersistence) {
176
+ setupSidebarBreakPoint(): void {
177
+ // Find the sidebar-expand element in the DOM.
178
+
179
+ const sidebarExpand = document.querySelector(SELECTOR_SIDEBAR_EXPAND)
180
+
181
+ if (!sidebarExpand) {
182
+ return
183
+ }
184
+
185
+ // Read the content property of the ::before pseudo element to get the
186
+ // breakpoint value.
187
+
188
+ const content = globalThis.getComputedStyle(sidebarExpand, '::before')
189
+ .getPropertyValue('content')
190
+
191
+ // Update the config.sidebarBreakpoint value by extracting the numeric
192
+ // value from the content string.
193
+
194
+ if (!content || content === 'none') {
144
195
  return
145
196
  }
146
197
 
147
- // Check for SSR environment
148
- if (globalThis.window === undefined || globalThis.localStorage === undefined) {
198
+ const breakpointValue = Number(content.replace(/[^\d.-]/g, ''))
199
+
200
+ if (Number.isNaN(breakpointValue)) {
149
201
  return
150
202
  }
151
203
 
204
+ this._config = { ...this._config, sidebarBreakpoint: breakpointValue }
205
+ }
206
+
207
+ /**
208
+ * Update the sidebar state based on the current screen size and the
209
+ * sidebarBreakpoint config value.
210
+ */
211
+ updateStateByResponsiveLogic(): void {
212
+ if (this.isMobileSize()) {
213
+ // On mobile sizes, keep the sidebar collapsed unless explicitly opened
214
+ // by the user to prevent unintended collapse on scroll or resize events.
215
+
216
+ if (!this.isExplicitlyOpen()) {
217
+ this.collapse()
218
+ }
219
+ } else {
220
+ // On big screen sizes, keep the sidebar expanded unless in mini mode and
221
+ // explicitly collapsed.
222
+
223
+ if (!(this.isMiniMode() && this.isCollapsed())) {
224
+ this.expand()
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Save sidebar state to localStorage.
231
+ *
232
+ * @param state The state to save ('sidebar-open' or 'sidebar-collapse').
233
+ */
234
+ saveSidebarState(state: string): void {
235
+ // Check for localStorage availability (not a SSR environment).
236
+
237
+ if (globalThis.localStorage === undefined) {
238
+ return
239
+ }
240
+
241
+ // Save the sidebar state to local storage.
242
+
152
243
  try {
153
- const state = document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE) ?
154
- CLASS_NAME_SIDEBAR_COLLAPSE :
155
- CLASS_NAME_SIDEBAR_OPEN
156
244
  localStorage.setItem(STORAGE_KEY_SIDEBAR_STATE, state)
157
245
  } catch {
158
- // localStorage may be unavailable (private browsing, quota exceeded, etc.)
246
+ // The localStorage may be unavailable (private browsing, quota exceeded,
247
+ // etc.). Fail silently in these cases.
159
248
  }
160
249
  }
161
250
 
162
251
  /**
163
- * Load sidebar state from localStorage
164
- * Only applies on desktop; mobile always starts collapsed
252
+ * Load sidebar state from localStorage.
165
253
  */
166
254
  loadSidebarState(): void {
167
- if (!this._config.enablePersistence) {
168
- return
169
- }
255
+ // Check for localStorage availability (not a SSR environment).
170
256
 
171
- // Check for SSR environment
172
- if (globalThis.window === undefined || globalThis.localStorage === undefined) {
257
+ if (globalThis.localStorage === undefined) {
173
258
  return
174
259
  }
175
260
 
176
- // Don't restore state on mobile - let responsive behavior handle it
177
- if (globalThis.innerWidth <= this._config.sidebarBreakpoint) {
178
- return
179
- }
261
+ // Load the sidebar state from local storage.
180
262
 
181
263
  try {
182
264
  const storedState = localStorage.getItem(STORAGE_KEY_SIDEBAR_STATE)
@@ -185,44 +267,118 @@ class PushMenu {
185
267
  this.collapse()
186
268
  } else if (storedState === CLASS_NAME_SIDEBAR_OPEN) {
187
269
  this.expand()
270
+ } else {
271
+ // If null (never saved), let the responsive logic apply.
272
+ this.updateStateByResponsiveLogic()
188
273
  }
189
- // If null (never saved), let default behavior apply
190
274
  } catch {
191
- // localStorage may be unavailable
275
+ // The localStorage may be unavailable. Let the responsive logic apply.
276
+ this.updateStateByResponsiveLogic()
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Clear sidebar state from localStorage.
282
+ */
283
+ clearSidebarState(): void {
284
+ // Check for localStorage availability (not a SSR environment).
285
+
286
+ if (globalThis.localStorage === undefined) {
287
+ return
288
+ }
289
+
290
+ // Clear the sidebar state from local storage.
291
+
292
+ try {
293
+ localStorage.removeItem(STORAGE_KEY_SIDEBAR_STATE)
294
+ } catch {
295
+ // The localStorage may be unavailable. Fail silently in these cases.
192
296
  }
193
297
  }
194
298
 
195
- init() {
196
- this.addSidebarBreakPoint()
197
- this.loadSidebarState()
299
+ /**
300
+ * Initialize the push menu plugin and setup the initial sidebar state.
301
+ */
302
+ init(): void {
303
+ // Read and setup the sidebar breakpoint from the DOM. This breakpoint is
304
+ // used to determine when the sidebar should be considered "mobile" vs
305
+ // "desktop".
306
+
307
+ this.setupSidebarBreakPoint()
308
+
309
+ // When persistence is not enabled, clear any saved state, just for safety.
310
+
311
+ if (!this._config.enablePersistence) {
312
+ this.clearSidebarState()
313
+ }
314
+
315
+ // When persistence is enabled and screen size is above the breakpoint, load
316
+ // the saved sidebar state from local storage. Otherwise, use responsive
317
+ // logic to set the initial state. On low screen sizes, the sidebar should
318
+ // always be collapsed by default unless explicitly opened.
319
+
320
+ if (this._config.enablePersistence && !this.isMobileSize()) {
321
+ this.loadSidebarState()
322
+ } else {
323
+ this.updateStateByResponsiveLogic()
324
+ }
198
325
  }
199
326
  }
200
327
 
201
328
  /**
202
- * ------------------------------------------------------------------------
329
+ * ----------------------------------------------------------------------------
203
330
  * Data Api implementation
204
- * ------------------------------------------------------------------------
331
+ * ----------------------------------------------------------------------------
205
332
  */
206
333
 
207
334
  onDOMContentLoaded(() => {
335
+ // Find the sidebar element in the DOM.
336
+
208
337
  const sidebar = document?.querySelector(SELECTOR_APP_SIDEBAR) as HTMLElement | undefined
209
338
 
210
- if (sidebar) {
211
- const data = new PushMenu(sidebar, Defaults)
212
- data.init()
339
+ if (!sidebar) {
340
+ return
341
+ }
213
342
 
214
- window.addEventListener('resize', () => {
215
- data.init()
216
- })
343
+ // Read config from data attributes on the sidebar element, falling back to
344
+ // Defaults if not present.
345
+
346
+ const sidebarBreakpointAttr = sidebar.dataset.sidebarBreakpoint
347
+ const enablePersistenceAttr = sidebar.dataset.enablePersistence
348
+
349
+ const config: Config = {
350
+ sidebarBreakpoint: sidebarBreakpointAttr === undefined ?
351
+ Defaults.sidebarBreakpoint :
352
+ Number(sidebarBreakpointAttr),
353
+ enablePersistence: enablePersistenceAttr === undefined ?
354
+ Defaults.enablePersistence :
355
+ enablePersistenceAttr === 'true'
217
356
  }
218
357
 
358
+ // Initialize the PushMenu plugin (a unique instance).
359
+
360
+ const pushMenu = new PushMenu(sidebar, config)
361
+ pushMenu.init()
362
+
363
+ // Update the sidebar state on window resize events.
364
+
365
+ window.addEventListener('resize', () => {
366
+ pushMenu.setupSidebarBreakPoint()
367
+ pushMenu.updateStateByResponsiveLogic()
368
+ })
369
+
370
+ // Create the sidebar overlay element and append it to the app wrapper.
371
+
219
372
  const sidebarOverlay = document.createElement('div')
220
373
  sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY
221
374
  document.querySelector(SELECTOR_APP_WRAPPER)?.append(sidebarOverlay)
222
375
 
376
+ // Handle touch events on overlay (area outside sidebar), usually we want to
377
+ // close the sidebar when the user taps outside the sidebar on mobile
378
+ // devices.
379
+
223
380
  let overlayTouchMoved = false
224
381
 
225
- // Handle touch events on overlay (area outside sidebar)
226
382
  sidebarOverlay.addEventListener('touchstart', () => {
227
383
  overlayTouchMoved = false
228
384
  }, { passive: true })
@@ -234,21 +390,19 @@ onDOMContentLoaded(() => {
234
390
  sidebarOverlay.addEventListener('touchend', event => {
235
391
  if (!overlayTouchMoved) {
236
392
  event.preventDefault()
237
- const target = event.currentTarget as HTMLElement
238
- const data = new PushMenu(target, Defaults)
239
- data.collapse()
393
+ pushMenu.collapse()
240
394
  }
395
+
241
396
  overlayTouchMoved = false
242
397
  }, { passive: false })
243
398
 
244
-
245
399
  sidebarOverlay.addEventListener('click', event => {
246
400
  event.preventDefault()
247
- const target = event.currentTarget as HTMLElement
248
- const data = new PushMenu(target, Defaults)
249
- data.collapse()
401
+ pushMenu.collapse()
250
402
  })
251
403
 
404
+ // Handle click events on sidebar toggle buttons.
405
+
252
406
  const fullBtn = document.querySelectorAll(SELECTOR_SIDEBAR_TOGGLE)
253
407
 
254
408
  fullBtn.forEach(btn => {
@@ -263,8 +417,7 @@ onDOMContentLoaded(() => {
263
417
 
264
418
  if (button) {
265
419
  event?.preventDefault()
266
- const data = new PushMenu(button, Defaults)
267
- data.toggle()
420
+ pushMenu.toggle()
268
421
  }
269
422
  })
270
423
  })
@@ -48,6 +48,11 @@ const safePropertyAccess = (obj: Record<string, unknown>, property: string): unk
48
48
 
49
49
  /* SLIDE UP */
50
50
  const slideUp = (target: HTMLElement, duration = 500) => {
51
+ if (duration <= 1) {
52
+ target.style.display = 'none'
53
+ return
54
+ }
55
+
51
56
  target.style.transitionProperty = 'height, margin, padding'
52
57
  target.style.transitionDuration = `${duration}ms`
53
58
  target.style.boxSizing = 'border-box'
@@ -85,6 +90,11 @@ const slideDown = (target: HTMLElement, duration = 500) => {
85
90
  }
86
91
 
87
92
  target.style.display = display
93
+
94
+ if (duration <= 1) {
95
+ return
96
+ }
97
+
88
98
  const height = target.offsetHeight
89
99
  target.style.overflow = 'hidden'
90
100
  target.style.height = '0'
@@ -1,33 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm audit:*)",
5
- "Bash(npm run build:*)",
6
- "Bash(npm ls:*)",
7
- "Bash(gh pr list:*)",
8
- "Bash(npm outdated:*)",
9
- "Bash(npm install:*)",
10
- "Bash(gh pr view:*)",
11
- "Bash(gh pr diff:*)",
12
- "Bash(rm:*)",
13
- "Bash(gh api:*)",
14
- "Bash(gh issue list:*)",
15
- "Bash(gh issue view:*)",
16
- "Bash(npm run css:*)",
17
- "Bash(npm run js:*)",
18
- "Bash(npm run js-lint:*)",
19
- "Bash(npm run css-lint:*)",
20
- "Bash(npm run lint:*)",
21
- "Bash(npm whoami:*)",
22
- "Bash(npm run production:*)",
23
- "Bash(git add:*)",
24
- "Bash(git commit:*)",
25
- "Bash(npm view:*)",
26
- "Bash(cat:*)",
27
- "Bash(npm pack:*)",
28
- "Bash(npm publish:*)"
29
- ],
30
- "deny": [],
31
- "ask": []
32
- }
33
- }
package/CLAUDE.md DELETED
@@ -1,79 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- AdminLTE is a Bootstrap 5 admin dashboard template (v4.0.0-rc5). It provides responsive UI components for building admin panels and control dashboards.
8
-
9
- ## Build Commands
10
-
11
- ```bash
12
- npm install # Install dependencies
13
- npm start # Start dev server with file watching (http://localhost:3000)
14
- npm run build # Build all assets (CSS, JS, docs)
15
- npm run production # Full production build with linting and bundlewatch
16
- npm run lint # Run all linters (JS, CSS, docs, lockfile)
17
- ```
18
-
19
- ### Individual Build Tasks
20
-
21
- ```bash
22
- npm run css # Build CSS only (compile, prefix, rtl, minify)
23
- npm run js # Build JavaScript only (compile, minify)
24
- npm run css-lint # Lint SCSS files
25
- npm run js-lint # Lint TypeScript/JavaScript files
26
- ```
27
-
28
- ## Architecture
29
-
30
- ### Source Structure
31
-
32
- - **src/ts/** - TypeScript source files compiled via Rollup to `dist/js/adminlte.js`
33
- - **src/scss/** - SCSS source files compiled via Sass to `dist/css/adminlte.css`
34
- - **src/html/** - Astro-based documentation/demo pages
35
- - **src/config/** - Build tool configurations (rollup, postcss, astro, assets)
36
- - **src/assets/** - Static images copied to dist
37
-
38
- ### JavaScript Components (src/ts/)
39
-
40
- Entry point: `adminlte.ts` exports all components:
41
- - `Layout` - Core layout management and hold-transition behavior
42
- - `PushMenu` - Sidebar toggle functionality
43
- - `Treeview` - Hierarchical menu navigation
44
- - `CardWidget` - Card collapse/expand/remove actions
45
- - `DirectChat` - Chat widget toggle
46
- - `FullScreen` - Fullscreen toggle functionality
47
- - `initAccessibility` - WCAG 2.1 AA accessibility features
48
-
49
- ### SCSS Structure (src/scss/)
50
-
51
- Entry point: `adminlte.scss` imports Bootstrap then AdminLTE components:
52
- - `_bootstrap-variables.scss` - Bootstrap variable overrides
53
- - `_variables.scss` / `_variables-dark.scss` - AdminLTE theme variables
54
- - `parts/` - Organized component groups (core, components, extra-components, pages, miscellaneous)
55
- - Individual component files (`_cards.scss`, `_app-sidebar.scss`, etc.)
56
-
57
- ### Documentation (src/html/)
58
-
59
- Built with Astro framework:
60
- - `pages/` - Demo pages and documentation
61
- - `components/` - Reusable Astro components and MDX content
62
- - Output goes to `dist/html/` then flattened to `dist/`
63
-
64
- ### Build Output (dist/)
65
-
66
- Production assets:
67
- - `dist/js/adminlte.min.js` - Compiled and minified JavaScript (UMD format)
68
- - `dist/css/adminlte.min.css` - Compiled and minified CSS
69
- - `dist/css/*.rtl.css` - RTL (right-to-left) variants
70
- - `dist/assets/` - Static assets
71
-
72
- ## Key Technologies
73
-
74
- - Bootstrap 5.3.x - CSS framework base
75
- - TypeScript - JavaScript source language
76
- - Sass - CSS preprocessor
77
- - Rollup - JavaScript bundler
78
- - Astro - Documentation site generator
79
- - PostCSS with Autoprefixer and RTLCSS - CSS post-processing