@oscarpalmer/atoms 0.9.1 → 0.11.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/dist/js/element/focusable.mjs +152 -0
- package/dist/js/element/index.js +60 -0
- package/dist/js/element/index.mjs +60 -0
- package/dist/js/event.js +16 -0
- package/dist/js/event.mjs +16 -0
- package/dist/js/{atoms.js → index.js} +199 -82
- package/dist/js/index.mjs +7 -0
- package/dist/js/number.js +35 -0
- package/dist/js/number.mjs +35 -0
- package/dist/js/string.js +15 -0
- package/dist/js/string.mjs +15 -0
- package/dist/js/timer.js +96 -0
- package/dist/js/timer.mjs +96 -0
- package/dist/js/touch.mjs +22 -0
- package/dist/js/value.js +84 -0
- package/dist/js/value.mjs +74 -0
- package/package.json +60 -15
- package/src/js/element/index.ts +42 -0
- package/src/js/event.ts +1 -1
- package/src/js/index.ts +1 -0
- package/src/js/timer.ts +207 -0
- package/types/element/index.d.ts +7 -0
- package/types/event.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/timer.d.ts +51 -0
- /package/dist/js/{focusable.js → element/focusable.js} +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// src/js/element/focusable.ts
|
|
2
|
+
var _getItem = function(type, element) {
|
|
3
|
+
return {
|
|
4
|
+
element,
|
|
5
|
+
tabIndex: type === "focusable" ? -1 : _getTabIndex(element)
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
var _getTabIndex = function(element) {
|
|
9
|
+
if (element.tabIndex < 0 && (audioDetailsVideoPattern.test(element.tagName) || _isEditable(element)) && !_hasTabIndex(element)) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
return element.tabIndex;
|
|
13
|
+
};
|
|
14
|
+
var _getValidElements = function(type, parent, filters) {
|
|
15
|
+
const items = Array.from(parent.querySelectorAll(selector)).map((element) => _getItem(type, element)).filter((item) => !filters.some((filter) => filter(item)));
|
|
16
|
+
if (type === "focusable") {
|
|
17
|
+
return items.map((item) => item.element);
|
|
18
|
+
}
|
|
19
|
+
const indiced = [];
|
|
20
|
+
const zeroed = [];
|
|
21
|
+
const { length } = items;
|
|
22
|
+
let position = Number(length);
|
|
23
|
+
while (position--) {
|
|
24
|
+
const index = length - position - 1;
|
|
25
|
+
const item = items[index];
|
|
26
|
+
if (item.tabIndex === 0) {
|
|
27
|
+
zeroed.push(item.element);
|
|
28
|
+
} else {
|
|
29
|
+
indiced[item.tabIndex] = [
|
|
30
|
+
...indiced[item.tabIndex] ?? [],
|
|
31
|
+
item.element
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [...indiced.flat(), ...zeroed];
|
|
36
|
+
};
|
|
37
|
+
var _hasTabIndex = function(element) {
|
|
38
|
+
return !Number.isNaN(Number.parseInt(element.getAttribute("tabindex"), 10));
|
|
39
|
+
};
|
|
40
|
+
var _isDisabled = function(item) {
|
|
41
|
+
if (inputPattern.test(item.element.tagName) && _isDisabledFromFieldset(item.element)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return (item.element.disabled ?? false) || item.element.getAttribute("aria-disabled") === "true";
|
|
45
|
+
};
|
|
46
|
+
var _isDisabledFromFieldset = function(element) {
|
|
47
|
+
let parent = element.parentElement;
|
|
48
|
+
while (parent !== null) {
|
|
49
|
+
if (parent instanceof HTMLFieldSetElement && parent.disabled) {
|
|
50
|
+
const children = Array.from(parent.children);
|
|
51
|
+
for (const child of children) {
|
|
52
|
+
if (child instanceof HTMLLegendElement) {
|
|
53
|
+
return parent.matches("fieldset[disabled] *") ? true : !child.contains(element);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
parent = parent.parentElement;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
var _isEditable = function(element) {
|
|
63
|
+
return booleanPattern.test(element.getAttribute("contenteditable"));
|
|
64
|
+
};
|
|
65
|
+
var _isHidden = function(item) {
|
|
66
|
+
if ((item.element.hidden ?? false) || item.element instanceof HTMLInputElement && item.element.type === "hidden") {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
const isDirectSummary = item.element.matches("details > summary:first-of-type");
|
|
70
|
+
const nodeUnderDetails = isDirectSummary ? item.element.parentElement : item.element;
|
|
71
|
+
if (nodeUnderDetails?.matches("details:not([open]) *") ?? false) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
const style = getComputedStyle(item.element);
|
|
75
|
+
if (style.display === "none" || style.visibility === "hidden") {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
const { height, width } = item.element.getBoundingClientRect();
|
|
79
|
+
return height === 0 && width === 0;
|
|
80
|
+
};
|
|
81
|
+
var _isInert = function(item) {
|
|
82
|
+
return (item.element.inert ?? false) || booleanPattern.test(item.element.getAttribute("inert")) || item.element.parentElement !== null && _isInert({
|
|
83
|
+
element: item.element.parentElement,
|
|
84
|
+
tabIndex: -1
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
var _isNotTabbable = function(item) {
|
|
88
|
+
return (item.tabIndex ?? -1) < 0;
|
|
89
|
+
};
|
|
90
|
+
var _isNotTabbableRadio = function(item) {
|
|
91
|
+
if (!(item.element instanceof HTMLInputElement) || item.element.type !== "radio" || !item.element.name || item.element.checked) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const parent = item.element.form ?? item.element.getRootNode?.() ?? item.element.ownerDocument;
|
|
95
|
+
const realName = CSS?.escape?.(item.element.name) ?? item.element.name;
|
|
96
|
+
const radios = Array.from(parent.querySelectorAll(`input[type="radio"][name="${realName}"]`));
|
|
97
|
+
const checked = radios.find((radio) => radio.checked);
|
|
98
|
+
return checked !== undefined && checked !== item.element;
|
|
99
|
+
};
|
|
100
|
+
var _isSummarised = function(item) {
|
|
101
|
+
return item.element instanceof HTMLDetailsElement && Array.from(item.element.children).some((child) => summaryPattern.test(child.tagName));
|
|
102
|
+
};
|
|
103
|
+
var _isValidElement = function(element, filters) {
|
|
104
|
+
const item = _getItem("focusable", element);
|
|
105
|
+
return !filters.some((filter) => filter(item));
|
|
106
|
+
};
|
|
107
|
+
function getFocusableElements(parent) {
|
|
108
|
+
return _getValidElements("focusable", parent, focusableFilters);
|
|
109
|
+
}
|
|
110
|
+
function getTabbableElements(parent) {
|
|
111
|
+
return _getValidElements("tabbable", parent, tabbableFilters);
|
|
112
|
+
}
|
|
113
|
+
function isFocusableElement(element) {
|
|
114
|
+
return _isValidElement(element, focusableFilters);
|
|
115
|
+
}
|
|
116
|
+
function isTabbableElement(element) {
|
|
117
|
+
return _isValidElement(element, tabbableFilters);
|
|
118
|
+
}
|
|
119
|
+
var audioDetailsVideoPattern = /^(audio|details|video)$/i;
|
|
120
|
+
var booleanPattern = /^(|true)$/i;
|
|
121
|
+
var focusableFilters = [
|
|
122
|
+
_isDisabled,
|
|
123
|
+
_isInert,
|
|
124
|
+
_isHidden,
|
|
125
|
+
_isSummarised
|
|
126
|
+
];
|
|
127
|
+
var inputPattern = /^(button|input|select|textarea)$/i;
|
|
128
|
+
var selector = [
|
|
129
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
130
|
+
"[tabindex]:not(slot)",
|
|
131
|
+
"a[href]",
|
|
132
|
+
"audio[controls]",
|
|
133
|
+
"button",
|
|
134
|
+
"details",
|
|
135
|
+
"details > summary:first-of-type",
|
|
136
|
+
"input",
|
|
137
|
+
"select",
|
|
138
|
+
"textarea",
|
|
139
|
+
"video[controls]"
|
|
140
|
+
].map((selector2) => `${selector2}:not([inert])`).join(",");
|
|
141
|
+
var summaryPattern = /^summary$/i;
|
|
142
|
+
var tabbableFilters = [
|
|
143
|
+
_isNotTabbable,
|
|
144
|
+
_isNotTabbableRadio,
|
|
145
|
+
...focusableFilters
|
|
146
|
+
];
|
|
147
|
+
export {
|
|
148
|
+
isTabbableElement,
|
|
149
|
+
isFocusableElement,
|
|
150
|
+
getTabbableElements,
|
|
151
|
+
getFocusableElements
|
|
152
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/js/element/index.ts
|
|
2
|
+
function findElements(selector, context) {
|
|
3
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
4
|
+
const elements = [];
|
|
5
|
+
if (typeof selector === "string") {
|
|
6
|
+
for (const context2 of contexts) {
|
|
7
|
+
elements.push(...Array.from(context2.querySelectorAll(selector) ?? []));
|
|
8
|
+
}
|
|
9
|
+
return elements;
|
|
10
|
+
}
|
|
11
|
+
const nodes = Array.isArray(selector) || selector instanceof NodeList ? selector : [selector];
|
|
12
|
+
for (const node of nodes) {
|
|
13
|
+
if (node instanceof Element && contexts.some((context2) => context2.contains(node))) {
|
|
14
|
+
elements.push(node);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return elements;
|
|
18
|
+
}
|
|
19
|
+
function findParentElement(origin, selector) {
|
|
20
|
+
if (origin == null || selector == null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
function matches(element) {
|
|
24
|
+
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
25
|
+
}
|
|
26
|
+
if (matches(origin)) {
|
|
27
|
+
return origin;
|
|
28
|
+
}
|
|
29
|
+
let parent = origin.parentElement;
|
|
30
|
+
while (parent != null && !matches(parent)) {
|
|
31
|
+
if (parent === document.body) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
parent = parent.parentElement;
|
|
35
|
+
}
|
|
36
|
+
return parent ?? undefined;
|
|
37
|
+
}
|
|
38
|
+
function getElementUnderPointer(skipIgnore) {
|
|
39
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
40
|
+
if (/^head$/i.test(element.tagName)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const style = getComputedStyle(element);
|
|
44
|
+
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
45
|
+
});
|
|
46
|
+
return elements[elements.length - 1];
|
|
47
|
+
}
|
|
48
|
+
function getTextDirection(element) {
|
|
49
|
+
const direction = element.getAttribute("dir");
|
|
50
|
+
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
51
|
+
return direction.toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
getTextDirection,
|
|
57
|
+
getElementUnderPointer,
|
|
58
|
+
findParentElement,
|
|
59
|
+
findElements
|
|
60
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/js/element/index.ts
|
|
2
|
+
function findElements(selector, context) {
|
|
3
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
4
|
+
const elements = [];
|
|
5
|
+
if (typeof selector === "string") {
|
|
6
|
+
for (const context2 of contexts) {
|
|
7
|
+
elements.push(...Array.from(context2.querySelectorAll(selector) ?? []));
|
|
8
|
+
}
|
|
9
|
+
return elements;
|
|
10
|
+
}
|
|
11
|
+
const nodes = Array.isArray(selector) || selector instanceof NodeList ? selector : [selector];
|
|
12
|
+
for (const node of nodes) {
|
|
13
|
+
if (node instanceof Element && contexts.some((context2) => context2.contains(node))) {
|
|
14
|
+
elements.push(node);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return elements;
|
|
18
|
+
}
|
|
19
|
+
function findParentElement(origin, selector) {
|
|
20
|
+
if (origin == null || selector == null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
function matches(element) {
|
|
24
|
+
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
25
|
+
}
|
|
26
|
+
if (matches(origin)) {
|
|
27
|
+
return origin;
|
|
28
|
+
}
|
|
29
|
+
let parent = origin.parentElement;
|
|
30
|
+
while (parent != null && !matches(parent)) {
|
|
31
|
+
if (parent === document.body) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
parent = parent.parentElement;
|
|
35
|
+
}
|
|
36
|
+
return parent ?? undefined;
|
|
37
|
+
}
|
|
38
|
+
function getElementUnderPointer(skipIgnore) {
|
|
39
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
40
|
+
if (/^head$/i.test(element.tagName)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const style = getComputedStyle(element);
|
|
44
|
+
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
45
|
+
});
|
|
46
|
+
return elements[elements.length - 1];
|
|
47
|
+
}
|
|
48
|
+
function getTextDirection(element) {
|
|
49
|
+
const direction = element.getAttribute("dir");
|
|
50
|
+
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
51
|
+
return direction.toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
getTextDirection,
|
|
57
|
+
getElementUnderPointer,
|
|
58
|
+
findParentElement,
|
|
59
|
+
findElements
|
|
60
|
+
};
|
package/dist/js/event.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/js/event.ts
|
|
2
|
+
function getEventPosition(event) {
|
|
3
|
+
let x;
|
|
4
|
+
let y;
|
|
5
|
+
if (event instanceof MouseEvent) {
|
|
6
|
+
x = event.clientX;
|
|
7
|
+
y = event.clientY;
|
|
8
|
+
} else if (event instanceof TouchEvent) {
|
|
9
|
+
x = event.touches[0]?.clientX;
|
|
10
|
+
y = event.touches[0]?.clientY;
|
|
11
|
+
}
|
|
12
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getEventPosition
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/js/event.ts
|
|
2
|
+
function getEventPosition(event) {
|
|
3
|
+
let x;
|
|
4
|
+
let y;
|
|
5
|
+
if (event instanceof MouseEvent) {
|
|
6
|
+
x = event.clientX;
|
|
7
|
+
y = event.clientY;
|
|
8
|
+
} else if (event instanceof TouchEvent) {
|
|
9
|
+
x = event.touches[0]?.clientX;
|
|
10
|
+
y = event.touches[0]?.clientY;
|
|
11
|
+
}
|
|
12
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getEventPosition
|
|
16
|
+
};
|
|
@@ -1,84 +1,3 @@
|
|
|
1
|
-
// src/js/element/index.ts
|
|
2
|
-
function findParentElement(origin, selector) {
|
|
3
|
-
if (origin == null || selector == null) {
|
|
4
|
-
return;
|
|
5
|
-
}
|
|
6
|
-
function matches(element) {
|
|
7
|
-
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
8
|
-
}
|
|
9
|
-
if (matches(origin)) {
|
|
10
|
-
return origin;
|
|
11
|
-
}
|
|
12
|
-
let parent = origin.parentElement;
|
|
13
|
-
while (parent != null && !matches(parent)) {
|
|
14
|
-
if (parent === document.body) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
parent = parent.parentElement;
|
|
18
|
-
}
|
|
19
|
-
return parent ?? undefined;
|
|
20
|
-
}
|
|
21
|
-
function getElementUnderPointer(skipIgnore) {
|
|
22
|
-
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
23
|
-
if (/^head$/i.test(element.tagName)) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const style = getComputedStyle(element);
|
|
27
|
-
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
28
|
-
});
|
|
29
|
-
return elements[elements.length - 1];
|
|
30
|
-
}
|
|
31
|
-
function getTextDirection(element) {
|
|
32
|
-
const direction = element.getAttribute("dir");
|
|
33
|
-
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
34
|
-
return direction.toLowerCase();
|
|
35
|
-
}
|
|
36
|
-
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
37
|
-
}
|
|
38
|
-
// src/js/event.ts
|
|
39
|
-
function getPosition(event) {
|
|
40
|
-
let x;
|
|
41
|
-
let y;
|
|
42
|
-
if (event instanceof MouseEvent) {
|
|
43
|
-
x = event.clientX;
|
|
44
|
-
y = event.clientY;
|
|
45
|
-
} else if (event instanceof TouchEvent) {
|
|
46
|
-
x = event.touches[0]?.clientX;
|
|
47
|
-
y = event.touches[0]?.clientY;
|
|
48
|
-
}
|
|
49
|
-
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
50
|
-
}
|
|
51
|
-
// src/js/number.ts
|
|
52
|
-
function clampNumber(value, min, max) {
|
|
53
|
-
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
54
|
-
}
|
|
55
|
-
function getNumber(value) {
|
|
56
|
-
if (typeof value === "number") {
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
if (typeof value === "symbol") {
|
|
60
|
-
return NaN;
|
|
61
|
-
}
|
|
62
|
-
let parsed = value?.valueOf?.() ?? value;
|
|
63
|
-
if (typeof parsed === "object") {
|
|
64
|
-
parsed = parsed?.toString() ?? parsed;
|
|
65
|
-
}
|
|
66
|
-
if (typeof parsed !== "string") {
|
|
67
|
-
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
68
|
-
}
|
|
69
|
-
if (/^\s*0+\s*$/.test(parsed)) {
|
|
70
|
-
return 0;
|
|
71
|
-
}
|
|
72
|
-
const trimmed = parsed.trim();
|
|
73
|
-
if (trimmed.length === 0) {
|
|
74
|
-
return NaN;
|
|
75
|
-
}
|
|
76
|
-
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
77
|
-
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
78
|
-
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
79
|
-
}
|
|
80
|
-
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
81
|
-
}
|
|
82
1
|
// src/js/string.ts
|
|
83
2
|
function createUuid() {
|
|
84
3
|
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
|
|
@@ -89,6 +8,7 @@ function getString(value) {
|
|
|
89
8
|
function isNullableOrWhitespace(value) {
|
|
90
9
|
return value == null || getString(value).trim().length === 0;
|
|
91
10
|
}
|
|
11
|
+
|
|
92
12
|
// src/js/value.ts
|
|
93
13
|
var _getValue = function(data, key) {
|
|
94
14
|
if (typeof data !== "object" || data === null || /^(__proto__|constructor|prototype)$/i.test(key)) {
|
|
@@ -155,8 +75,204 @@ function setValue(data, key, value) {
|
|
|
155
75
|
}
|
|
156
76
|
return data;
|
|
157
77
|
}
|
|
78
|
+
|
|
79
|
+
// src/js/number.ts
|
|
80
|
+
function clampNumber(value, min, max) {
|
|
81
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
82
|
+
}
|
|
83
|
+
function getNumber(value) {
|
|
84
|
+
if (typeof value === "number") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "symbol") {
|
|
88
|
+
return NaN;
|
|
89
|
+
}
|
|
90
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
91
|
+
if (typeof parsed === "object") {
|
|
92
|
+
parsed = parsed?.toString() ?? parsed;
|
|
93
|
+
}
|
|
94
|
+
if (typeof parsed !== "string") {
|
|
95
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
96
|
+
}
|
|
97
|
+
if (/^\s*0+\s*$/.test(parsed)) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
const trimmed = parsed.trim();
|
|
101
|
+
if (trimmed.length === 0) {
|
|
102
|
+
return NaN;
|
|
103
|
+
}
|
|
104
|
+
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
105
|
+
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
106
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
107
|
+
}
|
|
108
|
+
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/js/timer.ts
|
|
112
|
+
function repeat(callback, options) {
|
|
113
|
+
const count = typeof options?.count === "number" ? options.count : Infinity;
|
|
114
|
+
return timer(callback, { ...{ count }, ...options ?? {} }).start();
|
|
115
|
+
}
|
|
116
|
+
var timer = function(callback, config) {
|
|
117
|
+
const options = {
|
|
118
|
+
afterCallback: typeof config.afterCallback === "function" ? config.afterCallback : undefined,
|
|
119
|
+
callback,
|
|
120
|
+
count: typeof config.count === "number" && config.count >= 1 ? config.count : 1,
|
|
121
|
+
interval: typeof config.interval === "number" && config.interval >= 0 ? config.interval : 0
|
|
122
|
+
};
|
|
123
|
+
const state = {
|
|
124
|
+
active: false
|
|
125
|
+
};
|
|
126
|
+
const timer2 = Object.create(null);
|
|
127
|
+
Object.defineProperties(timer2, {
|
|
128
|
+
active: {
|
|
129
|
+
get() {
|
|
130
|
+
return state.active;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
restart: {
|
|
134
|
+
value() {
|
|
135
|
+
return work("restart", timer2, state, options);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
start: {
|
|
139
|
+
value() {
|
|
140
|
+
return work("start", timer2, state, options);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
stop: {
|
|
144
|
+
value() {
|
|
145
|
+
return work("stop", timer2, state, options);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return timer2;
|
|
150
|
+
};
|
|
151
|
+
function wait(callback, time) {
|
|
152
|
+
return timer(callback, {
|
|
153
|
+
count: 1,
|
|
154
|
+
interval: time
|
|
155
|
+
}).start();
|
|
156
|
+
}
|
|
157
|
+
var work = function(type, timer2, state, options) {
|
|
158
|
+
if (type === "start" && timer2.active || type === "stop" && !timer2.active) {
|
|
159
|
+
return timer2;
|
|
160
|
+
}
|
|
161
|
+
const { afterCallback, callback, count, interval } = options;
|
|
162
|
+
if (typeof state.frame === "number") {
|
|
163
|
+
cancelAnimationFrame(state.frame);
|
|
164
|
+
afterCallback?.(false);
|
|
165
|
+
}
|
|
166
|
+
if (type === "stop") {
|
|
167
|
+
state.active = false;
|
|
168
|
+
state.frame = undefined;
|
|
169
|
+
return timer2;
|
|
170
|
+
}
|
|
171
|
+
state.active = true;
|
|
172
|
+
const isRepeated = count > 0;
|
|
173
|
+
const milliseconds = 16.666666666666668;
|
|
174
|
+
let index = 0;
|
|
175
|
+
let start;
|
|
176
|
+
function step(timestamp) {
|
|
177
|
+
if (!state.active) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
start ??= timestamp;
|
|
181
|
+
const elapsed = timestamp - start;
|
|
182
|
+
const maximum = elapsed + milliseconds;
|
|
183
|
+
const minimum = elapsed - milliseconds;
|
|
184
|
+
if (minimum < interval && interval < maximum) {
|
|
185
|
+
if (state.active) {
|
|
186
|
+
callback(isRepeated ? index : undefined);
|
|
187
|
+
}
|
|
188
|
+
index += 1;
|
|
189
|
+
if (index < count) {
|
|
190
|
+
start = undefined;
|
|
191
|
+
} else {
|
|
192
|
+
state.active = false;
|
|
193
|
+
state.frame = undefined;
|
|
194
|
+
afterCallback?.(true);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
state.frame = requestAnimationFrame(step);
|
|
199
|
+
}
|
|
200
|
+
state.frame = requestAnimationFrame(step);
|
|
201
|
+
return timer2;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/js/element/index.ts
|
|
205
|
+
function findElements(selector, context) {
|
|
206
|
+
const contexts = context === undefined ? [document] : findElements(context);
|
|
207
|
+
const elements = [];
|
|
208
|
+
if (typeof selector === "string") {
|
|
209
|
+
for (const context2 of contexts) {
|
|
210
|
+
elements.push(...Array.from(context2.querySelectorAll(selector) ?? []));
|
|
211
|
+
}
|
|
212
|
+
return elements;
|
|
213
|
+
}
|
|
214
|
+
const nodes = Array.isArray(selector) || selector instanceof NodeList ? selector : [selector];
|
|
215
|
+
for (const node of nodes) {
|
|
216
|
+
if (node instanceof Element && contexts.some((context2) => context2.contains(node))) {
|
|
217
|
+
elements.push(node);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return elements;
|
|
221
|
+
}
|
|
222
|
+
function findParentElement(origin, selector) {
|
|
223
|
+
if (origin == null || selector == null) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
function matches(element) {
|
|
227
|
+
return typeof selector === "string" ? element.matches?.(selector) ?? false : typeof selector === "function" ? selector(element) : false;
|
|
228
|
+
}
|
|
229
|
+
if (matches(origin)) {
|
|
230
|
+
return origin;
|
|
231
|
+
}
|
|
232
|
+
let parent = origin.parentElement;
|
|
233
|
+
while (parent != null && !matches(parent)) {
|
|
234
|
+
if (parent === document.body) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
parent = parent.parentElement;
|
|
238
|
+
}
|
|
239
|
+
return parent ?? undefined;
|
|
240
|
+
}
|
|
241
|
+
function getElementUnderPointer(skipIgnore) {
|
|
242
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
243
|
+
if (/^head$/i.test(element.tagName)) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
const style = getComputedStyle(element);
|
|
247
|
+
return typeof skipIgnore === "boolean" && skipIgnore || style.pointerEvents !== "none" && style.visibility !== "hidden";
|
|
248
|
+
});
|
|
249
|
+
return elements[elements.length - 1];
|
|
250
|
+
}
|
|
251
|
+
function getTextDirection(element) {
|
|
252
|
+
const direction = element.getAttribute("dir");
|
|
253
|
+
if (direction !== null && /^(ltr|rtl)$/i.test(direction)) {
|
|
254
|
+
return direction.toLowerCase();
|
|
255
|
+
}
|
|
256
|
+
return getComputedStyle?.(element)?.direction === "rtl" ? "rtl" : "ltr";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/js/event.ts
|
|
260
|
+
function getEventPosition(event) {
|
|
261
|
+
let x;
|
|
262
|
+
let y;
|
|
263
|
+
if (event instanceof MouseEvent) {
|
|
264
|
+
x = event.clientX;
|
|
265
|
+
y = event.clientY;
|
|
266
|
+
} else if (event instanceof TouchEvent) {
|
|
267
|
+
x = event.touches[0]?.clientX;
|
|
268
|
+
y = event.touches[0]?.clientY;
|
|
269
|
+
}
|
|
270
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
271
|
+
}
|
|
158
272
|
export {
|
|
273
|
+
wait,
|
|
159
274
|
setValue,
|
|
275
|
+
repeat,
|
|
160
276
|
isObject,
|
|
161
277
|
isNullableOrWhitespace,
|
|
162
278
|
isNullable,
|
|
@@ -164,10 +280,11 @@ export {
|
|
|
164
280
|
getValue,
|
|
165
281
|
getTextDirection,
|
|
166
282
|
getString,
|
|
167
|
-
getPosition,
|
|
168
283
|
getNumber,
|
|
284
|
+
getEventPosition,
|
|
169
285
|
getElementUnderPointer,
|
|
170
286
|
findParentElement,
|
|
287
|
+
findElements,
|
|
171
288
|
createUuid,
|
|
172
289
|
clampNumber
|
|
173
290
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/js/number.ts
|
|
2
|
+
function clampNumber(value, min, max) {
|
|
3
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
4
|
+
}
|
|
5
|
+
function getNumber(value) {
|
|
6
|
+
if (typeof value === "number") {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === "symbol") {
|
|
10
|
+
return NaN;
|
|
11
|
+
}
|
|
12
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
13
|
+
if (typeof parsed === "object") {
|
|
14
|
+
parsed = parsed?.toString() ?? parsed;
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed !== "string") {
|
|
17
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
18
|
+
}
|
|
19
|
+
if (/^\s*0+\s*$/.test(parsed)) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
const trimmed = parsed.trim();
|
|
23
|
+
if (trimmed.length === 0) {
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
27
|
+
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
28
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
29
|
+
}
|
|
30
|
+
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
getNumber,
|
|
34
|
+
clampNumber
|
|
35
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/js/number.ts
|
|
2
|
+
function clampNumber(value, min, max) {
|
|
3
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
4
|
+
}
|
|
5
|
+
function getNumber(value) {
|
|
6
|
+
if (typeof value === "number") {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === "symbol") {
|
|
10
|
+
return NaN;
|
|
11
|
+
}
|
|
12
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
13
|
+
if (typeof parsed === "object") {
|
|
14
|
+
parsed = parsed?.toString() ?? parsed;
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed !== "string") {
|
|
17
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
18
|
+
}
|
|
19
|
+
if (/^\s*0+\s*$/.test(parsed)) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
const trimmed = parsed.trim();
|
|
23
|
+
if (trimmed.length === 0) {
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
const isBinary = /^0b[01]+$/i.test(trimmed);
|
|
27
|
+
if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
|
|
28
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
29
|
+
}
|
|
30
|
+
return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
getNumber,
|
|
34
|
+
clampNumber
|
|
35
|
+
};
|