@splendidlabz/utils 1.11.2 → 1.11.4
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/dist/cjs/dom/accessibility.cjs +2 -0
- package/dist/cjs/dom/actions/index.cjs +392 -7
- package/dist/cjs/dom/actions/masonry.cjs +97 -6
- package/dist/cjs/dom/actions/prefer-horizontal-scroll.cjs +58 -14
- package/dist/cjs/dom/actions/sticky.cjs +126 -6
- package/dist/cjs/dom/bounding-box.cjs +2 -0
- package/dist/cjs/dom/clipboard.cjs +2 -0
- package/dist/cjs/dom/cookie.cjs +3 -1
- package/dist/cjs/dom/css-vars.cjs +13 -3
- package/dist/cjs/dom/events.cjs +2 -0
- package/dist/cjs/dom/focusable.cjs +2 -0
- package/dist/cjs/dom/font-size.cjs +15 -3
- package/dist/cjs/dom/get-element.cjs +3 -1
- package/dist/cjs/dom/hash.cjs +2 -0
- package/dist/cjs/dom/index.cjs +1249 -47
- package/dist/cjs/dom/keyboard.cjs +2 -0
- package/dist/cjs/dom/local-store.cjs +3 -1
- package/dist/cjs/dom/media.cjs +2 -0
- package/dist/cjs/dom/observers/index.cjs +235 -9
- package/dist/cjs/dom/observers/intersection-observer.cjs +38 -6
- package/dist/cjs/dom/observers/mutation-observer.cjs +38 -6
- package/dist/cjs/dom/observers/observer.cjs +12 -2
- package/dist/cjs/dom/observers/resize-observer.cjs +38 -6
- package/dist/cjs/dom/observers/scroll-observer.cjs +62 -3
- package/dist/cjs/dom/pkce.cjs +13 -3
- package/dist/cjs/dom/query-params.cjs +3 -1
- package/dist/cjs/dom/random-string.cjs +2 -0
- package/dist/cjs/dom/sanitize.cjs +27 -3
- package/dist/cjs/dom/session-store.cjs +3 -1
- package/dist/cjs/dom/spam-prevention.cjs +2 -0
- package/dist/cjs/dom/trap-focus.cjs +131 -9
- package/dist/cjs/dom/ui/aria-current.cjs +2 -0
- package/dist/cjs/dom/ui/inconsistent-button-fix.cjs +2 -0
- package/dist/cjs/dom/ui/index.cjs +87 -9
- package/dist/cjs/dom/ui/scroll-container.cjs +2 -0
- package/dist/cjs/dom/ui/traverse-and-scramble.cjs +2 -0
- package/dist/cjs/lib/arrays/index.cjs +121 -13
- package/dist/cjs/lib/arrays/item-position.cjs +2 -0
- package/dist/cjs/lib/arrays/join.cjs +10 -2
- package/dist/cjs/lib/arrays/shuffle.cjs +2 -0
- package/dist/cjs/lib/arrays/sort.cjs +11 -3
- package/dist/cjs/lib/arrays/split-array.cjs +2 -0
- package/dist/cjs/lib/arrays/unique.cjs +2 -0
- package/dist/cjs/lib/auth/index.cjs +35 -3
- package/dist/cjs/lib/auth/route-manager.cjs +2 -0
- package/dist/cjs/lib/checks.cjs +2 -0
- package/dist/cjs/lib/date/days.cjs +3 -1
- package/dist/cjs/lib/date/index.cjs +190 -7
- package/dist/cjs/lib/date/months.cjs +3 -1
- package/dist/cjs/lib/date/time.cjs +14 -2
- package/dist/cjs/lib/form/form-data.cjs +2 -0
- package/dist/cjs/lib/form/index.cjs +104 -5
- package/dist/cjs/lib/form/sanitize.cjs +2 -0
- package/dist/cjs/lib/functions/callback.cjs +2 -0
- package/dist/cjs/lib/functions/debounce.cjs +2 -0
- package/dist/cjs/lib/functions/env.cjs +2 -0
- package/dist/cjs/lib/functions/functional.cjs +2 -0
- package/dist/cjs/lib/functions/index.cjs +124 -11
- package/dist/cjs/lib/functions/throttle.cjs +2 -0
- package/dist/cjs/lib/functions/timeout.cjs +2 -0
- package/dist/cjs/lib/hash.cjs +2 -0
- package/dist/cjs/lib/index.cjs +1160 -29
- package/dist/cjs/lib/numbers/index.cjs +17 -4
- package/dist/cjs/lib/numbers/math.cjs +2 -0
- package/dist/cjs/lib/objects/camelcase-keys.cjs +45 -4
- package/dist/cjs/lib/objects/empty.cjs +2 -0
- package/dist/cjs/lib/objects/equal.cjs +2 -0
- package/dist/cjs/lib/objects/extend.cjs +117 -6
- package/dist/cjs/lib/objects/flatten.cjs +11 -3
- package/dist/cjs/lib/objects/index.cjs +360 -27
- package/dist/cjs/lib/objects/json.cjs +2 -0
- package/dist/cjs/lib/objects/loop.cjs +2 -0
- package/dist/cjs/lib/objects/mix/mix.cjs +5 -3
- package/dist/cjs/lib/objects/nested-property.cjs +2 -0
- package/dist/cjs/lib/objects/normalize-object.cjs +78 -4
- package/dist/cjs/lib/objects/omit-empty.cjs +2 -0
- package/dist/cjs/lib/objects/size.cjs +2 -0
- package/dist/cjs/lib/objects/split.cjs +2 -0
- package/dist/cjs/lib/promises/index.cjs +51 -3
- package/dist/cjs/lib/promises/reject.cjs +19 -2
- package/dist/cjs/lib/sse.cjs +29 -4
- package/dist/cjs/lib/strings/convert-case/convert-case.cjs +2 -0
- package/dist/cjs/lib/strings/index.cjs +179 -11
- package/dist/cjs/lib/strings/markdown.cjs +3 -1
- package/dist/cjs/lib/strings/name.cjs +2 -0
- package/dist/cjs/lib/strings/pluralize.cjs +3 -1
- package/dist/cjs/lib/strings/query-string.cjs +2 -0
- package/dist/cjs/lib/style/index.cjs +31 -2
- package/dist/cjs/lib/symbols/index.cjs +22 -3
- package/dist/cjs/lib/symbols/symbols.cjs +2 -0
- package/dist/cjs/node/context.cjs +0 -0
- package/dist/cjs/node/dirname.cjs +4 -2
- package/dist/cjs/node/file-cache.cjs +45 -3
- package/dist/cjs/node/file.cjs +4 -2
- package/dist/cjs/node/hash.cjs +3 -1
- package/dist/cjs/node/index.cjs +278 -15
- package/dist/cjs/node/pkce.cjs +20 -8
- package/dist/cjs/node/random-string.cjs +3 -1
- package/dist/cjs/node/sanitize.cjs +27 -3
- package/dist/cjs/node/uuid.cjs +2 -0
- package/dist/esm/node/context.js +0 -0
- package/dist/esm/node/file-cache.js +1 -1
- package/dist/esm/node/pkce.js +3 -2
- package/dist/types/node/context.d.cts +2 -0
- package/dist/types/node/index.d.cts +1 -1
- package/dist/types/node/pkce.d.cts +7 -1
- package/package.json +5 -2
- package/CHANGELOG.md +0 -231
- package/eslint.config.js +0 -2
- package/src/dom/accessibility.js +0 -58
- package/src/dom/actions/index.js +0 -3
- package/src/dom/actions/masonry.js +0 -43
- package/src/dom/actions/prefer-horizontal-scroll.js +0 -121
- package/src/dom/actions/sticky.js +0 -85
- package/src/dom/bounding-box.js +0 -82
- package/src/dom/clipboard.js +0 -14
- package/src/dom/cookie.js +0 -32
- package/src/dom/css-vars.js +0 -19
- package/src/dom/events.js +0 -71
- package/src/dom/focusable.js +0 -165
- package/src/dom/font-size.js +0 -40
- package/src/dom/get-element.js +0 -176
- package/src/dom/hash.js +0 -9
- package/src/dom/index.js +0 -23
- package/src/dom/keyboard.js +0 -44
- package/src/dom/local-store.js +0 -66
- package/src/dom/media.js +0 -30
- package/src/dom/observers/index.js +0 -4
- package/src/dom/observers/intersection-observer.js +0 -43
- package/src/dom/observers/mutation-observer.js +0 -51
- package/src/dom/observers/observer.js +0 -18
- package/src/dom/observers/resize-observer.js +0 -66
- package/src/dom/observers/scroll-observer.js +0 -146
- package/src/dom/pkce.js +0 -34
- package/src/dom/query-params.js +0 -14
- package/src/dom/random-string.js +0 -18
- package/src/dom/random-string.test.js +0 -13
- package/src/dom/sanitize.js +0 -16
- package/src/dom/session-store.js +0 -29
- package/src/dom/spam-prevention.js +0 -40
- package/src/dom/trap-focus.js +0 -70
- package/src/dom/ui/aria-current.js +0 -20
- package/src/dom/ui/inconsistent-button-fix.js +0 -20
- package/src/dom/ui/index.js +0 -4
- package/src/dom/ui/scroll-container.js +0 -25
- package/src/dom/ui/traverse-and-scramble.js +0 -61
- package/src/lib/arrays/index.js +0 -6
- package/src/lib/arrays/item-position.js +0 -23
- package/src/lib/arrays/join.js +0 -20
- package/src/lib/arrays/shuffle.js +0 -21
- package/src/lib/arrays/sort.js +0 -88
- package/src/lib/arrays/sort.spec.js +0 -123
- package/src/lib/arrays/split-array.js +0 -15
- package/src/lib/arrays/unique.js +0 -3
- package/src/lib/auth/index.js +0 -1
- package/src/lib/auth/route-manager.js +0 -45
- package/src/lib/checks.js +0 -57
- package/src/lib/date/days.js +0 -9
- package/src/lib/date/index.js +0 -3
- package/src/lib/date/months.js +0 -74
- package/src/lib/date/time.js +0 -83
- package/src/lib/form/form-data.js +0 -73
- package/src/lib/form/index.js +0 -2
- package/src/lib/form/sanitize.js +0 -80
- package/src/lib/form/sanitize.spec.js +0 -126
- package/src/lib/functions/callback.js +0 -8
- package/src/lib/functions/debounce.js +0 -18
- package/src/lib/functions/env.js +0 -5
- package/src/lib/functions/functional.js +0 -113
- package/src/lib/functions/functional.test.js +0 -196
- package/src/lib/functions/index.js +0 -5
- package/src/lib/functions/throttle.js +0 -11
- package/src/lib/functions/timeout.js +0 -15
- package/src/lib/hash.js +0 -18
- package/src/lib/hash.spec.js +0 -163
- package/src/lib/index.js +0 -14
- package/src/lib/numbers/index.js +0 -12
- package/src/lib/numbers/math.js +0 -9
- package/src/lib/numbers/split-unit.test.js +0 -45
- package/src/lib/objects/camelcase-keys.js +0 -17
- package/src/lib/objects/camelcase-keys.test.js +0 -72
- package/src/lib/objects/empty.js +0 -10
- package/src/lib/objects/equal.js +0 -9
- package/src/lib/objects/extend.js +0 -16
- package/src/lib/objects/extend.test.js +0 -35
- package/src/lib/objects/flatten.js +0 -19
- package/src/lib/objects/index.js +0 -13
- package/src/lib/objects/json.js +0 -7
- package/src/lib/objects/loop.js +0 -11
- package/src/lib/objects/loop.test.js +0 -31
- package/src/lib/objects/mix/mix.js +0 -191
- package/src/lib/objects/mix/mix.md +0 -78
- package/src/lib/objects/mix/mix.test.js +0 -445
- package/src/lib/objects/nested-property.js +0 -41
- package/src/lib/objects/normalize-object.js +0 -10
- package/src/lib/objects/omit-empty.js +0 -23
- package/src/lib/objects/omit-empty.test.js +0 -85
- package/src/lib/objects/size.js +0 -41
- package/src/lib/objects/split.js +0 -19
- package/src/lib/promises/index.js +0 -1
- package/src/lib/promises/reject.js +0 -17
- package/src/lib/sse.js +0 -61
- package/src/lib/strings/convert-case/convert-case.js +0 -86
- package/src/lib/strings/convert-case/convert-case.md +0 -44
- package/src/lib/strings/convert-case/convert-case.test.js +0 -50
- package/src/lib/strings/index.js +0 -5
- package/src/lib/strings/markdown.js +0 -71
- package/src/lib/strings/name.js +0 -24
- package/src/lib/strings/name.test.js +0 -67
- package/src/lib/strings/pluralize.js +0 -2
- package/src/lib/strings/query-string.js +0 -18
- package/src/lib/style/index.js +0 -13
- package/src/lib/symbols/index.js +0 -1
- package/src/lib/symbols/symbols.js +0 -9
- package/src/node/dirname.js +0 -17
- package/src/node/file-cache.js +0 -184
- package/src/node/file.js +0 -13
- package/src/node/hash.js +0 -5
- package/src/node/index.js +0 -7
- package/src/node/pkce.js +0 -33
- package/src/node/random-string.js +0 -10
- package/src/node/random-string.test.js +0 -15
- package/src/node/sanitize.js +0 -13
- package/src/node/sanitize.spec.js +0 -142
- package/src/node/uuid.js +0 -5
- package/tsconfig.json +0 -23
- package/tsup.config.js +0 -38
- package/tsup.config.mjs +0 -45
package/src/dom/events.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} EventListener
|
|
3
|
-
* @property {Element} node - The DOM element to attach the event listener to
|
|
4
|
-
* @property {string} event - The event type (e.g., 'click', 'keydown')
|
|
5
|
-
* @property {EventListenerOrEventListenerObject} handler - The event handler function
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} ListenerManager
|
|
10
|
-
* @property {EventListener[]} list - Array of current event listeners
|
|
11
|
-
* @property {function(EventListener): void} add - Add a new event listener
|
|
12
|
-
* @property {function(EventListener): void} remove - Remove an event listener
|
|
13
|
-
* @property {function(): void} clear - Remove all event listeners
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {Object} CustomEventOptions
|
|
18
|
-
* @property {boolean} [bubbles=false] - Whether the event bubbles up through the DOM
|
|
19
|
-
* @property {boolean} [cancelable=false] - Whether the event can be canceled
|
|
20
|
-
* @property {boolean} [composed=false] - Whether the event will trigger listeners outside of a shadow root
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Adds multiple event listeners to their respective DOM elements
|
|
25
|
-
* @param {EventListener[]} listeners - Array of listener objects
|
|
26
|
-
*/
|
|
27
|
-
export function addListeners(listeners) {
|
|
28
|
-
listeners.forEach(({ node, event, handler }) => {
|
|
29
|
-
node.addEventListener(event, handler)
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Removes multiple event listeners from their respective DOM elements
|
|
35
|
-
* @param {EventListener[]} listeners - Array of listener objects
|
|
36
|
-
*/
|
|
37
|
-
export function removeListeners(listeners) {
|
|
38
|
-
listeners.forEach(({ node, event, handler }) => {
|
|
39
|
-
node.removeEventListener(event, handler)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Dispatches a custom event from a DOM node
|
|
45
|
-
* @param {Element} node - The DOM element to dispatch the event from
|
|
46
|
-
* @param {string} eventName - The name of the custom event
|
|
47
|
-
* @param {*} detail - The detail data to include with the event
|
|
48
|
-
* @param {CustomEventOptions} [options={}] - Additional event options
|
|
49
|
-
*/
|
|
50
|
-
export function dispatchEvent(node, eventName, detail, options = {}) {
|
|
51
|
-
node.dispatchEvent(
|
|
52
|
-
new CustomEvent(eventName, {
|
|
53
|
-
...options,
|
|
54
|
-
detail,
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Creates a new CustomEvent with updated properties from an existing event
|
|
61
|
-
* @param {Event} event - The original event object
|
|
62
|
-
* @param {Object} updates - Properties to update in the new event
|
|
63
|
-
* @return {CustomEvent} A new CustomEvent with updated properties
|
|
64
|
-
*/
|
|
65
|
-
export function updateEvent(event, updates) {
|
|
66
|
-
const { type, ...rest } = event
|
|
67
|
-
return new CustomEvent(event.type, {
|
|
68
|
-
...rest,
|
|
69
|
-
...updates,
|
|
70
|
-
})
|
|
71
|
-
}
|
package/src/dom/focusable.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} Focusables
|
|
3
|
-
* @property {number} length - Number of keyboard focusable elements
|
|
4
|
-
* @property {Array} all - All focusable elements that are visible and enabled
|
|
5
|
-
* @property {Array} keyboard - Elements that can be focused via keyboard (tabIndex > -1)
|
|
6
|
-
* @property {Array} keyboardOnly - Alias for keyboard focusable elements
|
|
7
|
-
* @property {HTMLElement|null} first - First keyboard focusable element
|
|
8
|
-
* @property {HTMLElement|null} last - Last keyboard focusable element
|
|
9
|
-
* @property {function(number): HTMLElement|null} next - Get next focusable element after the given index
|
|
10
|
-
* @property {function(number): HTMLElement|null} prev - Get previous focusable element before the given index
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Gets all focusable elements within a container and provides methods to navigate between them.
|
|
15
|
-
* This is useful for implementing keyboard navigation, focus trapping, and accessibility features.
|
|
16
|
-
*
|
|
17
|
-
* @param {HTMLElement} [container=document.body] - The container element to search within. Only elements inside this container will be included.
|
|
18
|
-
* @returns {Focusables} Object containing focusable elements and navigation methods
|
|
19
|
-
* @example
|
|
20
|
-
* // Get focusable elements in a modal
|
|
21
|
-
* const modal = document.querySelector('.modal')
|
|
22
|
-
* const focusable = getFocusableElements(modal)
|
|
23
|
-
*
|
|
24
|
-
* // Get all focusable elements in document
|
|
25
|
-
* const allFocusable = getFocusableElements()
|
|
26
|
-
*
|
|
27
|
-
* // Focus the first element when modal opens
|
|
28
|
-
* focusable.first?.focus()
|
|
29
|
-
*
|
|
30
|
-
* // Handle tab key to trap focus
|
|
31
|
-
* modal.addEventListener('keydown', e => {
|
|
32
|
-
* if (e.key === 'Tab') {
|
|
33
|
-
* e.preventDefault()
|
|
34
|
-
* const currentIndex = focusable.keyboard.indexOf(document.activeElement)
|
|
35
|
-
* const nextElement = e.shiftKey
|
|
36
|
-
* ? focusable.prev(currentIndex)
|
|
37
|
-
* : focusable.next(currentIndex)
|
|
38
|
-
* nextElement?.focus()
|
|
39
|
-
* }
|
|
40
|
-
* })
|
|
41
|
-
*/
|
|
42
|
-
export function getFocusableElements(container = document.body) {
|
|
43
|
-
return {
|
|
44
|
-
/**
|
|
45
|
-
* Gets the number of keyboard focusable elements.
|
|
46
|
-
* This is useful for checking if there are any focusable elements
|
|
47
|
-
* before attempting to focus them.
|
|
48
|
-
*
|
|
49
|
-
* @returns {number} The number of keyboard focusable elements
|
|
50
|
-
*/
|
|
51
|
-
get length() {
|
|
52
|
-
return this.keyboard.length
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Gets all focusable elements within the container that are visible and enabled.
|
|
57
|
-
* This includes elements that can be focused via mouse or programmatically,
|
|
58
|
-
* but may not be keyboard focusable.
|
|
59
|
-
*
|
|
60
|
-
* Elements are considered focusable if they:
|
|
61
|
-
* - Are not disabled
|
|
62
|
-
* - Are not hidden
|
|
63
|
-
* - Are not display:none
|
|
64
|
-
*
|
|
65
|
-
* @returns {HTMLElement[]} Array of focusable elements that are visible and enabled
|
|
66
|
-
*/
|
|
67
|
-
get all() {
|
|
68
|
-
const elements = Array.from(
|
|
69
|
-
container.querySelectorAll(
|
|
70
|
-
`a,
|
|
71
|
-
button,
|
|
72
|
-
input,
|
|
73
|
-
textarea,
|
|
74
|
-
select,
|
|
75
|
-
details,
|
|
76
|
-
iframe,
|
|
77
|
-
embed,
|
|
78
|
-
object,
|
|
79
|
-
summary,
|
|
80
|
-
dialog,
|
|
81
|
-
audio[controls],
|
|
82
|
-
video[controls],
|
|
83
|
-
[contenteditable],
|
|
84
|
-
[tabindex]
|
|
85
|
-
`,
|
|
86
|
-
),
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
// @ts-ignore - we check instanceof HTMLElement at runtime
|
|
90
|
-
return elements.filter(el => {
|
|
91
|
-
if (el.hasAttribute('disabled')) return false
|
|
92
|
-
if (el.hasAttribute('hidden')) return false
|
|
93
|
-
if (window.getComputedStyle(el).display === 'none') return false
|
|
94
|
-
return true
|
|
95
|
-
})
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Gets elements that can be focused via keyboard (tabIndex > -1).
|
|
100
|
-
* These are the elements that will be focused when using the Tab key.
|
|
101
|
-
*
|
|
102
|
-
* @returns {HTMLElement[]} Array of keyboard focusable elements
|
|
103
|
-
*/
|
|
104
|
-
get keyboard() {
|
|
105
|
-
return this.all.filter(el => el instanceof HTMLElement && el.tabIndex > -1)
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Alias for keyboard focusable elements.
|
|
110
|
-
* This is provided for semantic clarity in certain contexts.
|
|
111
|
-
*
|
|
112
|
-
* @returns {HTMLElement[]} Array of keyboard focusable elements
|
|
113
|
-
*/
|
|
114
|
-
get keyboardOnly() {
|
|
115
|
-
return this.keyboard
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Gets the first keyboard focusable element.
|
|
120
|
-
* This is useful for setting initial focus when opening a modal or dialog.
|
|
121
|
-
*
|
|
122
|
-
* @returns {HTMLElement|null} The first focusable element or null if none exist
|
|
123
|
-
*/
|
|
124
|
-
get first() {
|
|
125
|
-
return this.keyboard[0] || null
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Gets the last keyboard focusable element.
|
|
130
|
-
* This is useful for focus trapping when using Shift+Tab.
|
|
131
|
-
*
|
|
132
|
-
* @returns {HTMLElement|null} The last focusable element or null if none exist
|
|
133
|
-
*/
|
|
134
|
-
get last() {
|
|
135
|
-
return this.keyboard[this.length - 1] || null
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Gets the next focusable element after the given index.
|
|
140
|
-
* This is useful for implementing custom tab order or focus management.
|
|
141
|
-
*
|
|
142
|
-
* @param {number} index - The current element's index in the keyboard focusable array
|
|
143
|
-
* @returns {HTMLElement|null} The next focusable element or null if none exists
|
|
144
|
-
*/
|
|
145
|
-
next(index) {
|
|
146
|
-
return this.keyboard[index + 1] || null
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Gets the previous focusable element before the given index.
|
|
151
|
-
* This is useful for implementing custom tab order or focus management.
|
|
152
|
-
*
|
|
153
|
-
* @param {number} index - The current element's index in the keyboard focusable array
|
|
154
|
-
* @returns {HTMLElement|null} The previous focusable element or null if none exists
|
|
155
|
-
*/
|
|
156
|
-
prev(index) {
|
|
157
|
-
return this.keyboard[index - 1] || null
|
|
158
|
-
},
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Alias of getFocusableElements
|
|
163
|
-
export function Focusable(container = document.body) {
|
|
164
|
-
return getFocusableElements(container)
|
|
165
|
-
}
|
package/src/dom/font-size.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { splitUnit } from '../lib/numbers/index.js'
|
|
2
|
-
|
|
3
|
-
export function getUnit(value) {
|
|
4
|
-
const [, unit] = splitUnit(value)
|
|
5
|
-
return unit
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function rem(multiple = 1) {
|
|
9
|
-
const value = parseFloat(getComputedStyle(document.body)['font-size'])
|
|
10
|
-
return Math.round(value * multiple)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function em(element = document.body, multiple = 1) {
|
|
14
|
-
const value = parseFloat(getComputedStyle(element)['font-size'])
|
|
15
|
-
return Math.round(value * multiple)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function rlh(multiple = 1) {
|
|
19
|
-
const lineHeight = parseFloat(getComputedStyle(document.body)['line-height'])
|
|
20
|
-
return Math.round(lineHeight * multiple)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function lh(element = document.body, multiple = 1) {
|
|
24
|
-
const lineHeight = getComputedStyle(element)['line-height']
|
|
25
|
-
const value = parseFloat(lineHeight)
|
|
26
|
-
return Math.round(value * multiple)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function toPx(value, element = null) {
|
|
30
|
-
const [numeric, unit] = splitUnit(value)
|
|
31
|
-
let finalValue = 0
|
|
32
|
-
|
|
33
|
-
if (unit === 'rem') finalValue = rem(numeric)
|
|
34
|
-
if (unit === 'em') finalValue = em(element, numeric)
|
|
35
|
-
if (unit === 'lh') finalValue = lh(element, numeric)
|
|
36
|
-
if (unit === 'px') finalValue = numeric
|
|
37
|
-
if (!unit) finalValue = numeric // Default to px offsets
|
|
38
|
-
|
|
39
|
-
return finalValue
|
|
40
|
-
}
|
package/src/dom/get-element.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// Returns the target HTMLElement.
|
|
2
|
-
// If the selector is already an HTMLElement, it will return the selector itself.
|
|
3
|
-
// Otherwise, it will return the first element that matches the selector.
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* List of Astro-specific node names that need special handling
|
|
7
|
-
* @type {string[]}
|
|
8
|
-
*/
|
|
9
|
-
const astroNodes = ['ASTRO-SLOT', 'ASTRO-ISLAND']
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Gets the type of a DOM node
|
|
13
|
-
* @param {Node|NodeList|Array} node - The node to check
|
|
14
|
-
* @returns {'element'|'nodelist'|'array'|undefined} The type of the node
|
|
15
|
-
*/
|
|
16
|
-
export function getNodeType(node) {
|
|
17
|
-
if (node instanceof Element) return 'element'
|
|
18
|
-
if (node instanceof NodeList) return 'nodelist'
|
|
19
|
-
if (Array.isArray(node)) return 'array'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Gets an element from a selector or returns the element if it's already an Element
|
|
24
|
-
* @param {string|Element} selector - CSS selector or Element
|
|
25
|
-
* @returns {Element|undefined} The matching element or undefined if not found
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* // Get element by selector
|
|
29
|
-
* const button = getElement('.button')
|
|
30
|
-
*
|
|
31
|
-
* // Pass through Element
|
|
32
|
-
* const element = document.querySelector('.button')
|
|
33
|
-
* const sameElement = getElement(element) // Returns element directly
|
|
34
|
-
*/
|
|
35
|
-
export function getElement(selector) {
|
|
36
|
-
if (!selector) return
|
|
37
|
-
if (selector instanceof Element) return selector
|
|
38
|
-
return document.querySelector(selector)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Gets all child elements of a node, handling Astro slots
|
|
43
|
-
* @param {Element} node - The parent node
|
|
44
|
-
* @returns {Element[]} Array of child elements
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* // Get children of a div
|
|
48
|
-
* const children = getChildrenElements(div)
|
|
49
|
-
*
|
|
50
|
-
* // Works with Astro slots
|
|
51
|
-
* const slotChildren = getChildrenElements(astroSlot)
|
|
52
|
-
*/
|
|
53
|
-
export function getChildrenElements(node) {
|
|
54
|
-
let children = node.children
|
|
55
|
-
|
|
56
|
-
// Compensate for Astro Slots
|
|
57
|
-
if (children[0]?.nodeName === 'ASTRO-SLOT') children = children[0].children
|
|
58
|
-
return Array.from(children)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Gets the parent element, skipping Astro-specific nodes
|
|
63
|
-
* @param {Element} element - The element to get the parent of
|
|
64
|
-
* @returns {Element|null} The parent element, or null if no parent exists
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* // Get parent, skipping Astro slots
|
|
68
|
-
* const parent = getParentElement(element)
|
|
69
|
-
*/
|
|
70
|
-
export function getParentElement(element) {
|
|
71
|
-
const parent = element.parentElement
|
|
72
|
-
if (astroNodes.includes(parent.nodeName)) return getParentElement(parent)
|
|
73
|
-
return parent
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Gets all sibling elements of an element (excluding the element itself)
|
|
78
|
-
* @param {Element} element - The element to get siblings of
|
|
79
|
-
* @returns {Element[]} Array of sibling elements
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* // Get all siblings of an element
|
|
83
|
-
* const siblings = getSiblingElements(element)
|
|
84
|
-
*/
|
|
85
|
-
export function getSiblingElements(element) {
|
|
86
|
-
return Array.from(element.parentElement.children).filter(
|
|
87
|
-
child => child !== element
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Gets the index of an element among its siblings
|
|
93
|
-
* @param {Element} element - The element to get the index of
|
|
94
|
-
* @returns {number} The index of the element (0-based)
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* // Get position of element among siblings
|
|
98
|
-
* const index = getSelfIndex(element)
|
|
99
|
-
*/
|
|
100
|
-
export function getSelfIndex(element) {
|
|
101
|
-
return Array.from(element.parentElement.children).indexOf(element)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Checks if an element is an ancestor of another element
|
|
106
|
-
* @param {Element} ancestor - The potential ancestor element
|
|
107
|
-
* @param {Element} element - The element to check
|
|
108
|
-
* @param {Object} [options] - Options for the search
|
|
109
|
-
* @param {number} [options.searchLevels=0] - Number of levels to search up, 0 for unlimited
|
|
110
|
-
* @returns {boolean} True if ancestor is an ancestor of element
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* // Check if div is an ancestor of button
|
|
114
|
-
* const isParent = isAncestor(div, button)
|
|
115
|
-
*
|
|
116
|
-
* // Check if div is an ancestor within 2 levels
|
|
117
|
-
* const isCloseAncestor = isAncestor(div, button, { searchLevels: 2 })
|
|
118
|
-
*/
|
|
119
|
-
export function isAncestor(ancestor, element, { searchLevels = 0 } = {}) {
|
|
120
|
-
if (ancestor === element) return true
|
|
121
|
-
|
|
122
|
-
let parent = element.parentElement
|
|
123
|
-
// Infinite Search
|
|
124
|
-
if (searchLevels === 0) {
|
|
125
|
-
while (parent) {
|
|
126
|
-
if (ancestor === parent) return true
|
|
127
|
-
parent = parent.parentElement
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
// Search X levels upwards
|
|
131
|
-
for (let i = 0; i < searchLevels; i++) {
|
|
132
|
-
if (ancestor === parent) return true
|
|
133
|
-
parent = parent.parentElement
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return false
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Gets ancestor elements with their siblings, optionally limiting the search depth.
|
|
142
|
-
* Useful for finding the nearest ancestor that has siblings.
|
|
143
|
-
*
|
|
144
|
-
* @param {Element} element - The element to start searching from
|
|
145
|
-
* @param {Object} [options] - Options for the search
|
|
146
|
-
* @param {number|boolean} [options.limit=false] - Number of levels to search up, or false for unlimited
|
|
147
|
-
* @returns {Element[]|null} Array of sibling elements, or null if none found
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* // Get siblings of nearest ancestor that has siblings
|
|
151
|
-
* const ancestorSiblings = getAncestorWithSiblings(element)
|
|
152
|
-
*
|
|
153
|
-
* // Search up to 2 levels
|
|
154
|
-
* const limitedSiblings = getAncestorWithSiblings(element, { limit: 2 })
|
|
155
|
-
*/
|
|
156
|
-
export function getAncestorWithSiblings(element, { limit = false } = {}) {
|
|
157
|
-
const ancestor = element.parentElement
|
|
158
|
-
const siblings = getSiblingElements(ancestor)
|
|
159
|
-
|
|
160
|
-
// Found
|
|
161
|
-
if (siblings.length) return siblings
|
|
162
|
-
|
|
163
|
-
// No more ancestors
|
|
164
|
-
if (!ancestor) return null
|
|
165
|
-
|
|
166
|
-
// Unlimited upwards search
|
|
167
|
-
if (limit === false) return getAncestorWithSiblings(ancestor)
|
|
168
|
-
|
|
169
|
-
// Limited search
|
|
170
|
-
if (typeof limit === 'number' && limit > 0) {
|
|
171
|
-
return getAncestorWithSiblings(ancestor, { limit: limit - 1 })
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Limit reached, end search
|
|
175
|
-
if (limit === 0) return null
|
|
176
|
-
}
|
package/src/dom/hash.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export async function sha256Hash(string) {
|
|
2
|
-
const utf8 = new TextEncoder().encode(string)
|
|
3
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', utf8)
|
|
4
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
5
|
-
const hashHex = hashArray
|
|
6
|
-
.map(bytes => bytes.toString(16).padStart(2, '0'))
|
|
7
|
-
.join('')
|
|
8
|
-
return hashHex
|
|
9
|
-
}
|
package/src/dom/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export * from './accessibility.js'
|
|
2
|
-
export * from './actions/index.js'
|
|
3
|
-
export * from './bounding-box.js'
|
|
4
|
-
export * from './clipboard.js'
|
|
5
|
-
export * from './cookie.js'
|
|
6
|
-
export * from './css-vars.js'
|
|
7
|
-
export * from './events.js'
|
|
8
|
-
export * from './focusable.js'
|
|
9
|
-
export * from './font-size.js'
|
|
10
|
-
export * from './get-element.js'
|
|
11
|
-
export * from './hash.js'
|
|
12
|
-
export * from './keyboard.js'
|
|
13
|
-
export * from './local-store.js'
|
|
14
|
-
export * from './media.js'
|
|
15
|
-
export * from './observers/index.js'
|
|
16
|
-
export * from './pkce.js'
|
|
17
|
-
export * from './query-params.js'
|
|
18
|
-
export * from './random-string.js'
|
|
19
|
-
export * from './sanitize.js'
|
|
20
|
-
export * from './session-store.js'
|
|
21
|
-
export * from './spam-prevention.js'
|
|
22
|
-
export * from './trap-focus.js'
|
|
23
|
-
export * from './ui/index.js'
|
package/src/dom/keyboard.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event is Tab (without shift)
|
|
3
|
-
* @param {KeyboardEvent} event - The keyboard event object
|
|
4
|
-
* @returns {boolean}
|
|
5
|
-
*/
|
|
6
|
-
export function isTab(event) {
|
|
7
|
-
return !event.shiftKey && event.key === 'Tab'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Event is shift + Tab
|
|
12
|
-
* @param {KeyboardEvent} event - The keyboard event object
|
|
13
|
-
* @returns {boolean}
|
|
14
|
-
*/
|
|
15
|
-
export function isShiftTab(event) {
|
|
16
|
-
return event.shiftKey && event.key === 'Tab'
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Matches both Tab and Shift+Tab
|
|
21
|
-
* @param {string} key - The keyboard key to check
|
|
22
|
-
* @returns {boolean} Whether the key is Tab
|
|
23
|
-
*/
|
|
24
|
-
export function isTabKey(key) {
|
|
25
|
-
return key === 'Tab'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Event is an arrow key
|
|
30
|
-
* @param {string} key - The keyboard key to check
|
|
31
|
-
* @returns {boolean} Whether the key is an arrow key (Up, Down, Left, Right)
|
|
32
|
-
*/
|
|
33
|
-
export function isArrowKey(key) {
|
|
34
|
-
return ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Event is Escape key
|
|
39
|
-
* @param {string} key - The keyboard key to check
|
|
40
|
-
* @returns {boolean} Whether the key is Escape
|
|
41
|
-
*/
|
|
42
|
-
export function isEscapeKey(key) {
|
|
43
|
-
return key === 'Escape'
|
|
44
|
-
}
|
package/src/dom/local-store.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// ========================
|
|
2
|
-
// Local Storage
|
|
3
|
-
// ========================
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Utility object for managing localStorage operations with JSON support
|
|
7
|
-
* @namespace localStore
|
|
8
|
-
*/
|
|
9
|
-
export const localStore = {
|
|
10
|
-
/**
|
|
11
|
-
* Retrieves a value from localStorage
|
|
12
|
-
* @param {string} key - The key to retrieve from localStorage
|
|
13
|
-
* @returns {any} The stored value. Returns parsed JSON if the value was stored as JSON. Returns the original string if not JSON, or undefined if key doesn't exist
|
|
14
|
-
*/
|
|
15
|
-
get(key) {
|
|
16
|
-
const value = localStorage.getItem(key)
|
|
17
|
-
if (!value) return
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
// Returns an object if stored value is JSON
|
|
21
|
-
return JSON.parse(value)
|
|
22
|
-
} catch (error) {
|
|
23
|
-
// Returns plain string if stored value is not JSON
|
|
24
|
-
return value
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Stores a value in localStorage
|
|
30
|
-
* @param {string} key - The key to store the value under
|
|
31
|
-
* @param {any} value - The value to store. Objects will be stringified to JSON
|
|
32
|
-
*/
|
|
33
|
-
set(key, value) {
|
|
34
|
-
if (typeof value === 'string') {
|
|
35
|
-
localStorage.setItem(key, value)
|
|
36
|
-
} else {
|
|
37
|
-
localStorage.setItem(key, JSON.stringify(value))
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Stores multiple key-value pairs in localStorage
|
|
43
|
-
* @param {Object|Map} data - An object or Map containing key-value pairs to store
|
|
44
|
-
*/
|
|
45
|
-
setMultiple(data) {
|
|
46
|
-
for (const [key, value] of Object.entries(data)) {
|
|
47
|
-
this.set(key, value)
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Removes a value from localStorage
|
|
53
|
-
* @param {string} key - The key to remove from localStorage
|
|
54
|
-
*/
|
|
55
|
-
remove(key) {
|
|
56
|
-
localStorage.removeItem(key)
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Removes multiple keys from localStorage
|
|
61
|
-
* @param {string[]} keys - Array of keys to remove from localStorage
|
|
62
|
-
*/
|
|
63
|
-
removeMultiple(keys) {
|
|
64
|
-
keys.forEach(key => this.remove(key))
|
|
65
|
-
},
|
|
66
|
-
}
|
package/src/dom/media.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export async function imagesLoaded(node) {
|
|
2
|
-
const images = Array.from(node.querySelectorAll('img'))
|
|
3
|
-
const promises = images.map(img => {
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
if (img.complete) return resolve()
|
|
6
|
-
img.onload = resolve
|
|
7
|
-
img.onerror = e => {
|
|
8
|
-
// eslint-disable-next-line prefer-promise-reject-errors
|
|
9
|
-
reject('Image failed to load')
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
})
|
|
13
|
-
return Promise.all(promises)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function videosLoaded(node) {
|
|
17
|
-
const videos = Array.from(node.querySelectorAll('video'))
|
|
18
|
-
const promises = videos.map(video => {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
if (video.readyState === 4) return resolve() // HAVE_ENOUGH_DATA
|
|
21
|
-
video.onloadedmetadata = resolve
|
|
22
|
-
video.onerror = reject
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
return Promise.all(promises)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function mediaLoaded(node) {
|
|
29
|
-
return Promise.all([imagesLoaded(node), videosLoaded(node)])
|
|
30
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { dispatchEvent } from '../events.js'
|
|
2
|
-
import { useObserverMethodOnTarget } from './observer.js'
|
|
3
|
-
|
|
4
|
-
// See https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options for options
|
|
5
|
-
export function intersectionObserver(target, options = {}) {
|
|
6
|
-
const { callback, ...opts } = options
|
|
7
|
-
const observer = new IntersectionObserver(observerFn, opts)
|
|
8
|
-
|
|
9
|
-
useObserverMethodOnTarget(target, observer, 'observe')
|
|
10
|
-
|
|
11
|
-
function observerFn(entries) {
|
|
12
|
-
for (const entry of entries) {
|
|
13
|
-
if (callback) callback({ entry, entries, observer })
|
|
14
|
-
else dispatchEvent(target, 'intersect', { entry, entries, observer })
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
observer,
|
|
20
|
-
observe(target) {
|
|
21
|
-
useObserverMethodOnTarget(target, observer, 'observe', options)
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
unobserve(target) {
|
|
25
|
-
useObserverMethodOnTarget(target, observer, 'unobserve')
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
takeRecords() {
|
|
29
|
-
return observer.takeRecords()
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
disconnect() {
|
|
33
|
-
// Take records before disconnecting.
|
|
34
|
-
const records = observer.takeRecords()
|
|
35
|
-
observer.disconnect()
|
|
36
|
-
if (records.length > 0) observerFn(records)
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
destroy() {
|
|
40
|
-
observer.disconnect()
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
}
|