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.
- package/CHANGELOG.md +41 -1
- package/README.md +6 -6
- package/dist/css/adminlte-docs.css +878 -0
- package/dist/css/adminlte-docs.css.map +1 -0
- package/dist/css/adminlte-docs.min.css +7 -0
- package/dist/css/adminlte-docs.min.css.map +1 -0
- package/dist/css/adminlte-docs.rtl.css +877 -0
- package/dist/css/adminlte-docs.rtl.css.map +1 -0
- package/dist/css/adminlte-docs.rtl.min.css +7 -0
- package/dist/css/adminlte-docs.rtl.min.css.map +1 -0
- package/dist/css/adminlte.css +132 -912
- package/dist/css/adminlte.css.map +1 -1
- package/dist/css/adminlte.min.css +2 -2
- package/dist/css/adminlte.min.css.map +1 -1
- package/dist/css/adminlte.rtl.css +132 -912
- package/dist/css/adminlte.rtl.css.map +1 -1
- package/dist/css/adminlte.rtl.min.css +2 -2
- package/dist/css/adminlte.rtl.min.css.map +1 -1
- package/dist/js/adminlte.esm.js +1406 -0
- package/dist/js/adminlte.esm.js.map +1 -0
- package/dist/js/adminlte.esm.min.js +7 -0
- package/dist/js/adminlte.esm.min.js.map +1 -0
- package/dist/js/adminlte.js +449 -215
- package/dist/js/adminlte.js.map +1 -1
- package/dist/js/adminlte.min.js +2 -2
- package/dist/js/adminlte.min.js.map +1 -1
- package/dist/js/types/accessibility.d.ts +46 -0
- package/dist/js/types/adminlte.d.ts +10 -0
- package/dist/js/types/base-component.d.ts +60 -0
- package/dist/js/types/card-widget.d.ts +30 -0
- package/dist/js/types/color-mode.d.ts +74 -0
- package/dist/js/types/direct-chat.d.ts +19 -0
- package/dist/js/types/fullscreen.d.ts +21 -0
- package/dist/js/types/layout.d.ts +19 -0
- package/dist/js/types/push-menu.d.ts +123 -0
- package/dist/js/types/treeview.d.ts +27 -0
- package/dist/js/types/util/index.d.ts +79 -0
- package/package.json +52 -25
- package/src/scss/_app-header.scss +3 -1
- package/src/scss/_app-main.scss +4 -0
- package/src/scss/_app-sidebar.scss +3 -1
- package/src/scss/_bootstrap-overrides.scss +60 -0
- package/src/scss/_info-box.scss +3 -1
- package/src/scss/_progress-bars.scss +3 -1
- package/src/scss/_table.scss +4 -2
- package/src/scss/adminlte-docs.scss +25 -0
- package/src/scss/adminlte.scss +5 -4
- package/src/scss/pages/_login_and_register.scss +3 -1
- package/src/ts/accessibility.ts +6 -2
- package/src/ts/adminlte.ts +7 -3
- package/src/ts/base-component.ts +111 -0
- package/src/ts/card-widget.ts +124 -100
- package/src/ts/color-mode.ts +254 -0
- package/src/ts/direct-chat.ts +38 -32
- package/src/ts/fullscreen.ts +32 -23
- package/src/ts/push-menu.ts +106 -43
- package/src/ts/treeview.ts +84 -50
- package/src/ts/util/index.ts +80 -16
- package/src/scss/_bootstrap-variables.scss +0 -1766
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------
|
|
3
|
+
* @file AdminLTE color-mode.ts
|
|
4
|
+
* @description Color mode (light/dark/auto) switcher for AdminLTE.
|
|
5
|
+
* Resolves the theme from, in order: the visitor's stored choice, the theme
|
|
6
|
+
* the page itself declared in <html data-bs-theme="…">, and finally the OS
|
|
7
|
+
* preference. Keeps [data-bs-theme-value] toggles and [data-lte-theme-icon]
|
|
8
|
+
* indicator icons in sync.
|
|
9
|
+
*
|
|
10
|
+
* Ships in the bundle so applications no longer need to copy the demo's
|
|
11
|
+
* inline script. The tiny no-flash snippet in <head> (see _head.astro)
|
|
12
|
+
* remains inline by design — it must run before first paint. That snippet
|
|
13
|
+
* flags the values it computes itself with [data-lte-theme-resolved], so a
|
|
14
|
+
* theme authored in the markup can be told apart from one it resolved.
|
|
15
|
+
*
|
|
16
|
+
* Applications with their own theming opt out entirely with
|
|
17
|
+
* <html data-lte-color-mode="off">.
|
|
18
|
+
* @license MIT
|
|
19
|
+
* --------------------------------------------
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { getLifecycleSignal, onDOMContentLoaded } from './util/index'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Constants
|
|
26
|
+
* ====================================================
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DATA_KEY = 'lte.color-mode'
|
|
30
|
+
const EVENT_KEY = `.${DATA_KEY}`
|
|
31
|
+
const EVENT_CHANGED = `changed${EVENT_KEY}`
|
|
32
|
+
|
|
33
|
+
const STORAGE_KEY = 'lte-theme'
|
|
34
|
+
|
|
35
|
+
const ATTRIBUTE_THEME = 'data-bs-theme'
|
|
36
|
+
const ATTRIBUTE_TOGGLE = 'data-bs-theme-value'
|
|
37
|
+
const ATTRIBUTE_DISABLED = 'data-lte-color-mode'
|
|
38
|
+
const ATTRIBUTE_RESOLVED = 'data-lte-theme-resolved'
|
|
39
|
+
|
|
40
|
+
const SELECTOR_TOGGLE = `[${ATTRIBUTE_TOGGLE}]`
|
|
41
|
+
const SELECTOR_ICON = '[data-lte-theme-icon]'
|
|
42
|
+
|
|
43
|
+
type Theme = 'light' | 'dark' | 'auto'
|
|
44
|
+
|
|
45
|
+
const THEMES = new Set<string>(['light', 'dark', 'auto'])
|
|
46
|
+
|
|
47
|
+
const isValidTheme = (value: string): value is Theme => THEMES.has(value)
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Applications with their own theming take over by adding
|
|
51
|
+
* `data-lte-color-mode="off"` to <html>: ColorMode then never writes
|
|
52
|
+
* `data-bs-theme` — not on load, not on a toggle click, not when the OS
|
|
53
|
+
* preference changes. This is also the escape hatch for custom Bootstrap
|
|
54
|
+
* themes, whose names ColorMode cannot resolve (#6084).
|
|
55
|
+
*
|
|
56
|
+
* Read live rather than captured, so it can be flipped at runtime.
|
|
57
|
+
*/
|
|
58
|
+
const isDisabled = (): boolean =>
|
|
59
|
+
document.documentElement.getAttribute(ATTRIBUTE_DISABLED) === 'off'
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The theme the page itself declared in <html data-bs-theme="…">, or null when
|
|
63
|
+
* it declared none.
|
|
64
|
+
*
|
|
65
|
+
* Captured once, at module evaluation, because the attribute is both an input
|
|
66
|
+
* and an output: after the first `_applyTheme()` it holds ColorMode's own
|
|
67
|
+
* write, which must not be mistaken for the page's intent on a later lifecycle
|
|
68
|
+
* pass (Turbo, `initialize()`). Reading it here runs before any of those.
|
|
69
|
+
*
|
|
70
|
+
* The pre-paint snippet in <head> writes before this module is even fetched,
|
|
71
|
+
* so it marks the values it computed itself with [data-lte-theme-resolved] —
|
|
72
|
+
* those are not authored, and are ignored.
|
|
73
|
+
*/
|
|
74
|
+
const readMarkupTheme = (): Theme | null => {
|
|
75
|
+
const { documentElement } = document
|
|
76
|
+
|
|
77
|
+
if (documentElement.hasAttribute(ATTRIBUTE_RESOLVED)) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const declared = documentElement.getAttribute(ATTRIBUTE_THEME)
|
|
82
|
+
|
|
83
|
+
return declared && isValidTheme(declared) ? declared : null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const MARKUP_THEME = readMarkupTheme()
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Class Definition
|
|
90
|
+
* ====================================================
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
class ColorMode {
|
|
94
|
+
/**
|
|
95
|
+
* Read the persisted theme choice, or null when nothing was stored or
|
|
96
|
+
* localStorage is unavailable (private mode, sandboxed iframe).
|
|
97
|
+
*/
|
|
98
|
+
getStoredTheme(): Theme | null {
|
|
99
|
+
try {
|
|
100
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
101
|
+
return stored && isValidTheme(stored) ? stored : null
|
|
102
|
+
} catch {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The theme declared in the markup, for applications that render it
|
|
109
|
+
* server-side from a cookie or a user record. Null when the page declared
|
|
110
|
+
* none, or when the value is a custom Bootstrap theme ColorMode cannot
|
|
111
|
+
* resolve — see `isDisabled` for those.
|
|
112
|
+
*/
|
|
113
|
+
getMarkupTheme(): Theme | null {
|
|
114
|
+
return MARKUP_THEME
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The user's effective choice: the stored theme, then the theme declared in
|
|
119
|
+
* the markup, falling back to the OS preference. Storage comes first because
|
|
120
|
+
* it is the visitor's own click on this device; markup is only the default
|
|
121
|
+
* the page shipped with.
|
|
122
|
+
*/
|
|
123
|
+
getPreferredTheme(): Theme {
|
|
124
|
+
const preferred = this.getStoredTheme() ?? this.getMarkupTheme()
|
|
125
|
+
if (preferred) {
|
|
126
|
+
return preferred
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return this._prefersDark() ? 'dark' : 'light'
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Resolve "auto" against the OS preference.
|
|
134
|
+
*/
|
|
135
|
+
resolveTheme(theme: Theme): 'light' | 'dark' {
|
|
136
|
+
if (theme === 'auto') {
|
|
137
|
+
return this._prefersDark() ? 'dark' : 'light'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return theme
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Apply a theme and persist the choice. Dispatches
|
|
145
|
+
* `changed.lte.color-mode` on the document with { theme, resolved }.
|
|
146
|
+
*/
|
|
147
|
+
setTheme(theme: Theme): void {
|
|
148
|
+
try {
|
|
149
|
+
localStorage.setItem(STORAGE_KEY, theme)
|
|
150
|
+
} catch {
|
|
151
|
+
// localStorage may be unavailable; the theme still applies for this page.
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this._applyTheme(theme)
|
|
155
|
+
this._showActiveTheme(theme)
|
|
156
|
+
|
|
157
|
+
document.dispatchEvent(new CustomEvent(EVENT_CHANGED, {
|
|
158
|
+
detail: { theme, resolved: this.resolveTheme(theme) }
|
|
159
|
+
}))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Apply without persisting — used on init and when the OS preference
|
|
164
|
+
* changes while in "auto" mode.
|
|
165
|
+
*/
|
|
166
|
+
_applyTheme(theme: Theme): void {
|
|
167
|
+
const resolved = this.resolveTheme(theme)
|
|
168
|
+
document.documentElement.setAttribute(ATTRIBUTE_THEME, resolved)
|
|
169
|
+
document.documentElement.style.colorScheme = resolved
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Whether the OS preference is currently dark.
|
|
174
|
+
*/
|
|
175
|
+
_prefersDark(): boolean {
|
|
176
|
+
return globalThis.matchMedia('(prefers-color-scheme: dark)').matches
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Sync the [data-bs-theme-value] toggles (active state, pressed state,
|
|
181
|
+
* check mark) and the [data-lte-theme-icon] indicator icons.
|
|
182
|
+
*/
|
|
183
|
+
_showActiveTheme(theme: Theme): void {
|
|
184
|
+
document.querySelectorAll(SELECTOR_TOGGLE).forEach(toggle => {
|
|
185
|
+
const isActive = toggle.getAttribute(ATTRIBUTE_TOGGLE) === theme
|
|
186
|
+
toggle.classList.toggle('active', isActive)
|
|
187
|
+
toggle.setAttribute('aria-pressed', String(isActive))
|
|
188
|
+
toggle.querySelector('.bi-check-lg')?.classList.toggle('d-none', !isActive)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
document.querySelectorAll(SELECTOR_ICON).forEach(icon => {
|
|
192
|
+
icon.classList.toggle('d-none', (icon as HTMLElement).dataset.lteThemeIcon !== theme)
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Apply the preferred theme and sync the UI without persisting anything.
|
|
198
|
+
*/
|
|
199
|
+
init(): void {
|
|
200
|
+
if (isDisabled()) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const theme = this.getPreferredTheme()
|
|
205
|
+
this._applyTheme(theme)
|
|
206
|
+
this._showActiveTheme(theme)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Data API implementation
|
|
212
|
+
* ====================================================
|
|
213
|
+
* Toggle clicks are delegated on `document`, so switcher buttons added after
|
|
214
|
+
* load work and the listener survives Turbo's <body> swaps. The class is
|
|
215
|
+
* stateless — everything lives in localStorage and the DOM.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
document.addEventListener('click', event => {
|
|
219
|
+
const target = event.target
|
|
220
|
+
|
|
221
|
+
if (!(target instanceof Element) || isDisabled()) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const toggle = target.closest(SELECTOR_TOGGLE)
|
|
226
|
+
const theme = toggle?.getAttribute(ATTRIBUTE_TOGGLE)
|
|
227
|
+
|
|
228
|
+
if (theme && isValidTheme(theme)) {
|
|
229
|
+
new ColorMode().setTheme(theme)
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
onDOMContentLoaded(() => {
|
|
234
|
+
const colorMode = new ColorMode()
|
|
235
|
+
colorMode.init()
|
|
236
|
+
|
|
237
|
+
// Follow the OS only while the OS *is* the effective choice: nothing stored
|
|
238
|
+
// and nothing declared in the markup, or an explicit "auto". A theme the
|
|
239
|
+
// page declared is a preference too, and outlives an OS change (#6093).
|
|
240
|
+
globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
241
|
+
if (isDisabled()) {
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const preferred = colorMode.getStoredTheme() ?? colorMode.getMarkupTheme()
|
|
246
|
+
|
|
247
|
+
if (!preferred || preferred === 'auto') {
|
|
248
|
+
colorMode._applyTheme('auto')
|
|
249
|
+
colorMode._showActiveTheme(preferred ?? 'auto')
|
|
250
|
+
}
|
|
251
|
+
}, { signal: getLifecycleSignal() })
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
export default ColorMode
|
package/src/ts/direct-chat.ts
CHANGED
|
@@ -6,17 +6,15 @@
|
|
|
6
6
|
* --------------------------------------------
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
onDOMContentLoaded
|
|
11
|
-
} from './util/index'
|
|
9
|
+
import { BaseComponent, dispatchCustomEvent } from './base-component'
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* Constants
|
|
15
13
|
* ====================================================
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
|
-
const
|
|
19
|
-
const EVENT_KEY =
|
|
16
|
+
const NAME = 'direct-chat'
|
|
17
|
+
const EVENT_KEY = `.lte.${NAME}`
|
|
20
18
|
const EVENT_EXPANDED = `expanded${EVENT_KEY}`
|
|
21
19
|
const EVENT_COLLAPSED = `collapsed${EVENT_KEY}`
|
|
22
20
|
|
|
@@ -30,25 +28,26 @@ const CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open'
|
|
|
30
28
|
* ====================================================
|
|
31
29
|
*/
|
|
32
30
|
|
|
33
|
-
class DirectChat {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
class DirectChat extends BaseComponent {
|
|
32
|
+
static get NAME(): string {
|
|
33
|
+
return NAME
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static getInstance(element: Element | null | undefined): DirectChat | null {
|
|
37
|
+
return this._getInstance(element) as DirectChat | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static getOrCreateInstance(element: HTMLElement): DirectChat {
|
|
41
|
+
return this.getInstance(element) ?? new this(element)
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
toggle(): void {
|
|
40
45
|
if (this._element.classList.contains(CLASS_NAME_DIRECT_CHAT_OPEN)) {
|
|
41
|
-
const event = new Event(EVENT_COLLAPSED)
|
|
42
|
-
|
|
43
46
|
this._element.classList.remove(CLASS_NAME_DIRECT_CHAT_OPEN)
|
|
44
|
-
|
|
45
|
-
this._element.dispatchEvent(event)
|
|
47
|
+
dispatchCustomEvent(this._element, EVENT_COLLAPSED)
|
|
46
48
|
} else {
|
|
47
|
-
const event = new Event(EVENT_EXPANDED)
|
|
48
|
-
|
|
49
49
|
this._element.classList.add(CLASS_NAME_DIRECT_CHAT_OPEN)
|
|
50
|
-
|
|
51
|
-
this._element.dispatchEvent(event)
|
|
50
|
+
dispatchCustomEvent(this._element, EVENT_EXPANDED)
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
}
|
|
@@ -57,23 +56,30 @@ class DirectChat {
|
|
|
57
56
|
*
|
|
58
57
|
* Data Api implementation
|
|
59
58
|
* ====================================================
|
|
59
|
+
* One delegated listener on `document`: chat panes inserted later work, and
|
|
60
|
+
* the listener survives Turbo's <body> swaps.
|
|
60
61
|
*/
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
document.addEventListener('click', event => {
|
|
64
|
+
const target = event.target
|
|
65
|
+
|
|
66
|
+
if (!(target instanceof Element)) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const trigger = target.closest(SELECTOR_DATA_TOGGLE)
|
|
71
|
+
|
|
72
|
+
if (!trigger) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
event.preventDefault()
|
|
77
|
+
|
|
78
|
+
const chatPane = trigger.closest(SELECTOR_DIRECT_CHAT) as HTMLElement | null
|
|
79
|
+
|
|
80
|
+
if (chatPane) {
|
|
81
|
+
DirectChat.getOrCreateInstance(chatPane).toggle()
|
|
82
|
+
}
|
|
77
83
|
})
|
|
78
84
|
|
|
79
85
|
export default DirectChat
|
package/src/ts/fullscreen.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* --------------------------------------------
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { BaseComponent, dispatchCustomEvent } from './base-component'
|
|
9
10
|
import {
|
|
10
11
|
getLifecycleSignal,
|
|
11
12
|
onDOMContentLoaded
|
|
@@ -15,8 +16,8 @@ import {
|
|
|
15
16
|
* Constants
|
|
16
17
|
* ============================================================================
|
|
17
18
|
*/
|
|
18
|
-
const
|
|
19
|
-
const EVENT_KEY =
|
|
19
|
+
const NAME = 'fullscreen'
|
|
20
|
+
const EVENT_KEY = `.lte.${NAME}`
|
|
20
21
|
const EVENT_MAXIMIZED = `maximized${EVENT_KEY}`
|
|
21
22
|
const EVENT_MINIMIZED = `minimized${EVENT_KEY}`
|
|
22
23
|
|
|
@@ -48,7 +49,7 @@ function syncFullScreenState(): void {
|
|
|
48
49
|
const eventName = isFullScreen ? EVENT_MAXIMIZED : EVENT_MINIMIZED
|
|
49
50
|
|
|
50
51
|
document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE).forEach(button => {
|
|
51
|
-
button
|
|
52
|
+
dispatchCustomEvent(button, eventName)
|
|
52
53
|
})
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -56,13 +57,17 @@ function syncFullScreenState(): void {
|
|
|
56
57
|
* Class Definition.
|
|
57
58
|
* ============================================================================
|
|
58
59
|
*/
|
|
59
|
-
class FullScreen {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
class FullScreen extends BaseComponent {
|
|
61
|
+
static get NAME(): string {
|
|
62
|
+
return NAME
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static getInstance(element: Element | null | undefined): FullScreen | null {
|
|
66
|
+
return this._getInstance(element) as FullScreen | null
|
|
67
|
+
}
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
this._config = config
|
|
69
|
+
static getOrCreateInstance(element: HTMLElement): FullScreen {
|
|
70
|
+
return this.getInstance(element) ?? new this(element)
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
inFullScreen(): void {
|
|
@@ -96,25 +101,29 @@ class FullScreen {
|
|
|
96
101
|
/**
|
|
97
102
|
* Data Api implementation
|
|
98
103
|
* ============================================================================
|
|
104
|
+
* Toggle clicks are delegated on `document`; the fullscreenchange listener is
|
|
105
|
+
* re-registered per page load with the lifecycle signal.
|
|
99
106
|
*/
|
|
100
|
-
onDOMContentLoaded(() => {
|
|
101
|
-
document.addEventListener('fullscreenchange', syncFullScreenState, { signal: getLifecycleSignal() })
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
document.addEventListener('click', event => {
|
|
109
|
+
const target = event.target
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
if (!(target instanceof Element)) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE) as HTMLElement | undefined
|
|
115
|
+
const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE) as HTMLElement | null
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
if (!button) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
event.preventDefault()
|
|
122
|
+
FullScreen.getOrCreateInstance(button).toggleFullScreen()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
onDOMContentLoaded(() => {
|
|
126
|
+
document.addEventListener('fullscreenchange', syncFullScreenState, { signal: getLifecycleSignal() })
|
|
118
127
|
})
|
|
119
128
|
|
|
120
129
|
export default FullScreen
|