@oscarpalmer/atoms 0.6.0 → 0.7.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/css/flex.css +30 -30
- package/dist/js/atoms.js +104 -25
- package/package.json +7 -10
- package/src/css/flex.scss +2 -2
- package/src/js/element.ts +58 -0
- package/src/js/event.ts +57 -0
- package/src/js/index.ts +3 -0
- package/src/js/number.ts +56 -0
- package/src/js/string.ts +6 -6
- package/src/js/value.ts +2 -37
- package/types/element.d.ts +11 -0
- package/types/event.d.ts +13 -0
- package/types/index.d.ts +3 -0
- package/types/number.d.ts +9 -0
package/dist/css/flex.css
CHANGED
|
@@ -45,104 +45,104 @@
|
|
|
45
45
|
gap: 0;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
.ac-c {
|
|
48
|
+
.flex-ac-c {
|
|
49
49
|
align-content: center;
|
|
50
50
|
}
|
|
51
|
-
.ac-fe {
|
|
51
|
+
.flex-ac-fe {
|
|
52
52
|
align-content: flex-end;
|
|
53
53
|
}
|
|
54
|
-
.ac-fs {
|
|
54
|
+
.flex-ac-fs {
|
|
55
55
|
align-content: flex-start;
|
|
56
56
|
}
|
|
57
|
-
.ac-s {
|
|
57
|
+
.flex-ac-s {
|
|
58
58
|
align-content: stretch;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
.ai-c {
|
|
61
|
+
.flex-ai-c {
|
|
62
62
|
align-items: center;
|
|
63
63
|
}
|
|
64
|
-
.ai-fe {
|
|
64
|
+
.flex-ai-fe {
|
|
65
65
|
align-items: flex-end;
|
|
66
66
|
}
|
|
67
|
-
.ai-fs {
|
|
67
|
+
.flex-ai-fs {
|
|
68
68
|
align-items: flex-start;
|
|
69
69
|
}
|
|
70
|
-
.ai-s {
|
|
70
|
+
.flex-ai-s {
|
|
71
71
|
align-items: stretch;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
.as-c {
|
|
74
|
+
.flex-as-c {
|
|
75
75
|
align-self: center;
|
|
76
76
|
}
|
|
77
|
-
.as-fe {
|
|
77
|
+
.flex-as-fe {
|
|
78
78
|
align-self: flex-end;
|
|
79
79
|
}
|
|
80
|
-
.as-fs {
|
|
80
|
+
.flex-as-fs {
|
|
81
81
|
align-self: flex-start;
|
|
82
82
|
}
|
|
83
|
-
.as-s {
|
|
83
|
+
.flex-as-s {
|
|
84
84
|
align-self: stretch;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
.ac-sa {
|
|
87
|
+
.flex-ac-sa {
|
|
88
88
|
align-content: space-around;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
.ac-sb {
|
|
91
|
+
.flex-ac-sb {
|
|
92
92
|
align-content: space-between;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
.ac-se {
|
|
95
|
+
.flex-ac-se {
|
|
96
96
|
align-content: space-evenly;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
.jc-c {
|
|
99
|
+
.flex-jc-c {
|
|
100
100
|
justify-content: center;
|
|
101
101
|
}
|
|
102
|
-
.jc-fe {
|
|
102
|
+
.flex-jc-fe {
|
|
103
103
|
justify-content: flex-end;
|
|
104
104
|
}
|
|
105
|
-
.jc-fs {
|
|
105
|
+
.flex-jc-fs {
|
|
106
106
|
justify-content: flex-start;
|
|
107
107
|
}
|
|
108
|
-
.jc-s {
|
|
108
|
+
.flex-jc-s {
|
|
109
109
|
justify-content: stretch;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
.ji-c {
|
|
112
|
+
.flex-ji-c {
|
|
113
113
|
justify-items: center;
|
|
114
114
|
}
|
|
115
|
-
.ji-fe {
|
|
115
|
+
.flex-ji-fe {
|
|
116
116
|
justify-items: flex-end;
|
|
117
117
|
}
|
|
118
|
-
.ji-fs {
|
|
118
|
+
.flex-ji-fs {
|
|
119
119
|
justify-items: flex-start;
|
|
120
120
|
}
|
|
121
|
-
.ji-s {
|
|
121
|
+
.flex-ji-s {
|
|
122
122
|
justify-items: stretch;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
.js-c {
|
|
125
|
+
.flex-js-c {
|
|
126
126
|
justify-self: center;
|
|
127
127
|
}
|
|
128
|
-
.js-fe {
|
|
128
|
+
.flex-js-fe {
|
|
129
129
|
justify-self: flex-end;
|
|
130
130
|
}
|
|
131
|
-
.js-fs {
|
|
131
|
+
.flex-js-fs {
|
|
132
132
|
justify-self: flex-start;
|
|
133
133
|
}
|
|
134
|
-
.js-s {
|
|
134
|
+
.flex-js-s {
|
|
135
135
|
justify-self: stretch;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
.jc-sa {
|
|
138
|
+
.flex-jc-sa {
|
|
139
139
|
justify-content: space-around;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
.jc-sb {
|
|
142
|
+
.flex-jc-sb {
|
|
143
143
|
justify-content: space-between;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
.jc-se {
|
|
146
|
+
.flex-jc-se {
|
|
147
147
|
justify-content: space-evenly;
|
|
148
148
|
}
|
package/dist/js/atoms.js
CHANGED
|
@@ -1,12 +1,105 @@
|
|
|
1
|
+
// src/js/element.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(all) {
|
|
22
|
+
const elements = Array.from(document.querySelectorAll(":hover")).filter((element) => {
|
|
23
|
+
const style = window.getComputedStyle(element);
|
|
24
|
+
return element.tagName !== "HEAD" && (typeof all === "boolean" && all ? true : style.pointerEvents !== "none" && style.visibility !== "hidden");
|
|
25
|
+
});
|
|
26
|
+
return elements[elements.length - 1];
|
|
27
|
+
}
|
|
28
|
+
// src/js/event.ts
|
|
29
|
+
function getPosition(event) {
|
|
30
|
+
let x;
|
|
31
|
+
let y;
|
|
32
|
+
if (event instanceof MouseEvent) {
|
|
33
|
+
x = event.clientX;
|
|
34
|
+
y = event.clientY;
|
|
35
|
+
} else if (event instanceof TouchEvent) {
|
|
36
|
+
x = event.touches[0]?.clientX;
|
|
37
|
+
y = event.touches[0]?.clientY;
|
|
38
|
+
}
|
|
39
|
+
return typeof x === "number" && typeof y === "number" ? { x, y } : undefined;
|
|
40
|
+
}
|
|
41
|
+
var supportsTouch = (() => {
|
|
42
|
+
let value = false;
|
|
43
|
+
try {
|
|
44
|
+
if ("matchMedia" in window) {
|
|
45
|
+
const media = matchMedia("(pointer: coarse)");
|
|
46
|
+
if (typeof media?.matches === "boolean") {
|
|
47
|
+
value = media.matches;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!value) {
|
|
51
|
+
value = "ontouchstart" in window || navigator.maxTouchPoints > 0 || (navigator.msMaxTouchPoints ?? 0) > 0;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
value = false;
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
})();
|
|
58
|
+
// src/js/number.ts
|
|
59
|
+
function clampNumber(value, min, max) {
|
|
60
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
61
|
+
}
|
|
62
|
+
function getNumber(value) {
|
|
63
|
+
if (typeof value === "number") {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === "symbol") {
|
|
67
|
+
return NaN;
|
|
68
|
+
}
|
|
69
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
70
|
+
if (typeof parsed === "object") {
|
|
71
|
+
parsed = parsed?.toString() ?? parsed;
|
|
72
|
+
}
|
|
73
|
+
if (typeof parsed !== "string") {
|
|
74
|
+
return parsed == null ? NaN : typeof parsed === "number" ? parsed : +parsed;
|
|
75
|
+
}
|
|
76
|
+
if (zeroPattern.test(parsed)) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
const trimmed = parsed.trim();
|
|
80
|
+
if (trimmed.length === 0) {
|
|
81
|
+
return NaN;
|
|
82
|
+
}
|
|
83
|
+
const isBinary = binaryPattern.test(trimmed);
|
|
84
|
+
if (isBinary || octalPattern.test(trimmed)) {
|
|
85
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
86
|
+
}
|
|
87
|
+
return +(hexadecimalPattern.test(trimmed) ? trimmed : trimmed.replace(separatorPattern, ""));
|
|
88
|
+
}
|
|
89
|
+
var binaryPattern = /^0b[01]+$/i;
|
|
90
|
+
var hexadecimalPattern = /^0x[0-9a-f]+$/i;
|
|
91
|
+
var octalPattern = /^0o[0-7]+$/i;
|
|
92
|
+
var separatorPattern = /_/g;
|
|
93
|
+
var zeroPattern = /^\s*0+\s*$/;
|
|
1
94
|
// src/js/string.ts
|
|
2
95
|
function createUuid() {
|
|
3
96
|
return uuidTemplate.replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
|
|
4
97
|
}
|
|
5
98
|
function getString(value) {
|
|
6
|
-
return typeof value === "string" ? value : String(value);
|
|
99
|
+
return typeof value === "string" ? value : typeof value?.toString === "function" ? value.toString() : String(value);
|
|
7
100
|
}
|
|
8
101
|
function isNullableOrWhitespace(value) {
|
|
9
|
-
return value
|
|
102
|
+
return value == null || getString(value).trim().length === 0;
|
|
10
103
|
}
|
|
11
104
|
var uuidTemplate = "10000000-1000-4000-8000-100000000000";
|
|
12
105
|
// src/js/value.ts
|
|
@@ -17,9 +110,6 @@ var _getValue = function(data, key) {
|
|
|
17
110
|
if (data instanceof Map) {
|
|
18
111
|
return data.get(key);
|
|
19
112
|
}
|
|
20
|
-
if (data instanceof Set) {
|
|
21
|
-
return Array.from(data)[key];
|
|
22
|
-
}
|
|
23
113
|
return data[key];
|
|
24
114
|
};
|
|
25
115
|
var _setValue = function(data, key, value) {
|
|
@@ -28,27 +118,10 @@ var _setValue = function(data, key, value) {
|
|
|
28
118
|
}
|
|
29
119
|
if (data instanceof Map) {
|
|
30
120
|
data.set(key, value);
|
|
31
|
-
} else if (data instanceof Set) {
|
|
32
|
-
_setValueInSet(data, key, value);
|
|
33
121
|
} else {
|
|
34
122
|
data[key] = value;
|
|
35
123
|
}
|
|
36
124
|
};
|
|
37
|
-
var _setValueInSet = function(data, key, value) {
|
|
38
|
-
const index = numberExpression.test(key) ? Number.parseInt(key, 10) : -1;
|
|
39
|
-
if (index === -1 || index >= data.size) {
|
|
40
|
-
data.add(value);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const array = Array.from(data);
|
|
44
|
-
array.splice(index, 1, value);
|
|
45
|
-
data.clear();
|
|
46
|
-
const { length } = array;
|
|
47
|
-
let position = Number(length);
|
|
48
|
-
while (position--) {
|
|
49
|
-
data.add(array[length - position - 1]);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
125
|
function getValue(data, key) {
|
|
53
126
|
if (typeof data !== "object" || data === null || isNullableOrWhitespace(key)) {
|
|
54
127
|
return;
|
|
@@ -58,7 +131,7 @@ function getValue(data, key) {
|
|
|
58
131
|
let value = data;
|
|
59
132
|
while (position--) {
|
|
60
133
|
value = _getValue(value, parts[position]);
|
|
61
|
-
if (value
|
|
134
|
+
if (value == null) {
|
|
62
135
|
break;
|
|
63
136
|
}
|
|
64
137
|
}
|
|
@@ -68,7 +141,7 @@ function isArrayOrObject(value) {
|
|
|
68
141
|
return constructors.has(value?.constructor?.name);
|
|
69
142
|
}
|
|
70
143
|
function isNullable(value) {
|
|
71
|
-
return value
|
|
144
|
+
return value == null;
|
|
72
145
|
}
|
|
73
146
|
function isObject(value) {
|
|
74
147
|
return value?.constructor?.name === objectConstructor;
|
|
@@ -100,6 +173,7 @@ var objectConstructor = "Object";
|
|
|
100
173
|
var constructors = new Set(["Array", objectConstructor]);
|
|
101
174
|
var numberExpression = /^\d+$/;
|
|
102
175
|
export {
|
|
176
|
+
supportsTouch,
|
|
103
177
|
setValue,
|
|
104
178
|
isObject,
|
|
105
179
|
isNullableOrWhitespace,
|
|
@@ -107,5 +181,10 @@ export {
|
|
|
107
181
|
isArrayOrObject,
|
|
108
182
|
getValue,
|
|
109
183
|
getString,
|
|
110
|
-
|
|
184
|
+
getPosition,
|
|
185
|
+
getNumber,
|
|
186
|
+
getElementUnderPointer,
|
|
187
|
+
findParentElement,
|
|
188
|
+
createUuid,
|
|
189
|
+
clampNumber
|
|
111
190
|
};
|
package/package.json
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
},
|
|
6
6
|
"description": "Sweet little atomic goodies…",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"@biomejs/biome": "^1.
|
|
8
|
+
"@biomejs/biome": "^1.5",
|
|
9
|
+
"@happy-dom/global-registrator": "^13.3",
|
|
9
10
|
"bun": "^1.0",
|
|
10
|
-
"sass": "^1.
|
|
11
|
+
"sass": "^1.70",
|
|
11
12
|
"typescript": "^5.3"
|
|
12
13
|
},
|
|
13
14
|
"exports": {
|
|
@@ -17,11 +18,7 @@
|
|
|
17
18
|
},
|
|
18
19
|
"./package.json": "./package.json"
|
|
19
20
|
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"src",
|
|
23
|
-
"types"
|
|
24
|
-
],
|
|
21
|
+
"files": ["dist", "src", "types"],
|
|
25
22
|
"keywords": [],
|
|
26
23
|
"license": "MIT",
|
|
27
24
|
"main": "./dist/js/atoms.js",
|
|
@@ -35,12 +32,12 @@
|
|
|
35
32
|
"build": "bun run build:css && bun run build:js && bun run types",
|
|
36
33
|
"build:css": "bunx sass ./src/css:./dist/css --no-source-map",
|
|
37
34
|
"build:js": "bunx bun build ./src/js/index.ts --outfile ./dist/js/atoms.js",
|
|
38
|
-
"test": "bun test --coverage",
|
|
35
|
+
"test": "bun test --preload ./test/_preload.ts --coverage",
|
|
39
36
|
"types": "bunx tsc -p ./tsconfig.json",
|
|
40
37
|
"watch:css": "bunx sass ./src/css:./dist/css --no-source-map --watch",
|
|
41
38
|
"watch:js": "bun build ./src/js/index.ts --outfile ./dist/js/atoms.js --watch"
|
|
42
39
|
},
|
|
43
40
|
"type": "module",
|
|
44
41
|
"types": "./types/index.d.ts",
|
|
45
|
-
"version": "0.
|
|
46
|
-
}
|
|
42
|
+
"version": "0.7.0"
|
|
43
|
+
}
|
package/src/css/flex.scss
CHANGED
|
@@ -61,7 +61,7 @@ $types: (a: 'align', j: 'justify');
|
|
|
61
61
|
|
|
62
62
|
@each $typeKey, $typeValue in $types {
|
|
63
63
|
@each $alignmentOriginKey, $alignmentOriginValue in $alignmentOrigins {
|
|
64
|
-
|
|
64
|
+
.flex-#{$typeKey}#{$alignmentOriginKey} {
|
|
65
65
|
@each $alignmentValueKey, $alignmentValueValue in $alignmentValues {
|
|
66
66
|
&-#{$alignmentValueKey} {
|
|
67
67
|
#{$typeValue}-#{$alignmentOriginValue}: #{$alignmentValueValue}
|
|
@@ -71,7 +71,7 @@ $types: (a: 'align', j: 'justify');
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
@each $justificationValueKey, $justificationValueValue in $justificationValues {
|
|
74
|
-
|
|
74
|
+
.flex-#{$typeKey}c-#{$justificationValueKey} {
|
|
75
75
|
#{$typeValue}-content: #{$justificationValueValue}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* - Find the parent element that matches the selector
|
|
3
|
+
* - Matches may be found by a query string or a callback
|
|
4
|
+
*/
|
|
5
|
+
export function findParentElement(
|
|
6
|
+
origin: Element,
|
|
7
|
+
selector: string | ((element: Element) => boolean),
|
|
8
|
+
): Element | undefined {
|
|
9
|
+
if (origin == null || selector == null) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function matches(element: Element): boolean {
|
|
14
|
+
return typeof selector === 'string'
|
|
15
|
+
? element.matches?.(selector) ?? false
|
|
16
|
+
: typeof selector === 'function'
|
|
17
|
+
? selector(element)
|
|
18
|
+
: false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (matches(origin)) {
|
|
22
|
+
return origin;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let parent: Element | null = origin.parentElement;
|
|
26
|
+
|
|
27
|
+
while (parent != null && !matches(parent)) {
|
|
28
|
+
if (parent === document.body) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parent = parent.parentElement;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return parent ?? undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* - Get the most specific element under the pointer
|
|
40
|
+
* - Ignores elements with `pointer-events: none` and `visibility: hidden`
|
|
41
|
+
* - If `all` is `true`, all elements under the pointer are returned
|
|
42
|
+
*/
|
|
43
|
+
export function getElementUnderPointer(all?: boolean): Element | undefined {
|
|
44
|
+
const elements = Array.from(document.querySelectorAll(':hover')).filter(
|
|
45
|
+
element => {
|
|
46
|
+
const style = window.getComputedStyle(element);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
element.tagName !== 'HEAD' &&
|
|
50
|
+
(typeof all === 'boolean' && all
|
|
51
|
+
? true
|
|
52
|
+
: style.pointerEvents !== 'none' && style.visibility !== 'hidden')
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return elements[elements.length - 1];
|
|
58
|
+
}
|
package/src/js/event.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
type NavigatorWithMsMaxTouchPoints = Navigator & {
|
|
2
|
+
msMaxTouchPoints: number;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
type Position = {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Does the browser support touch events?
|
|
12
|
+
*/
|
|
13
|
+
export const supportsTouch = (() => {
|
|
14
|
+
let value = false;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
if ('matchMedia' in window) {
|
|
18
|
+
const media = matchMedia('(pointer: coarse)');
|
|
19
|
+
|
|
20
|
+
if (typeof media?.matches === 'boolean') {
|
|
21
|
+
value = media.matches;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!value) {
|
|
26
|
+
value =
|
|
27
|
+
'ontouchstart' in window ||
|
|
28
|
+
navigator.maxTouchPoints > 0 ||
|
|
29
|
+
((navigator as NavigatorWithMsMaxTouchPoints).msMaxTouchPoints ?? 0) >
|
|
30
|
+
0;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
value = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return value;
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get the X- and Y-coordinates from a pointer event
|
|
41
|
+
*/
|
|
42
|
+
export function getPosition(
|
|
43
|
+
event: MouseEvent | TouchEvent,
|
|
44
|
+
): Position | undefined {
|
|
45
|
+
let x: number | undefined;
|
|
46
|
+
let y: number | undefined;
|
|
47
|
+
|
|
48
|
+
if (event instanceof MouseEvent) {
|
|
49
|
+
x = event.clientX;
|
|
50
|
+
y = event.clientY;
|
|
51
|
+
} else if (event instanceof TouchEvent) {
|
|
52
|
+
x = event.touches[0]?.clientX;
|
|
53
|
+
y = event.touches[0]?.clientY;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return typeof x === 'number' && typeof y === 'number' ? {x, y} : undefined;
|
|
57
|
+
}
|
package/src/js/index.ts
CHANGED
package/src/js/number.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const binaryPattern = /^0b[01]+$/i;
|
|
2
|
+
const hexadecimalPattern = /^0x[0-9a-f]+$/i;
|
|
3
|
+
const octalPattern = /^0o[0-7]+$/i;
|
|
4
|
+
const separatorPattern = /_/g;
|
|
5
|
+
const zeroPattern = /^\s*0+\s*$/;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Clamps a number between a minimum and maximum value
|
|
9
|
+
*/
|
|
10
|
+
export function clampNumber(value: number, min: number, max: number): number {
|
|
11
|
+
return Math.min(Math.max(getNumber(value), getNumber(min)), getNumber(max));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* - Gets the number value from an unknown value
|
|
16
|
+
* - Returns `NaN` if the value is `undefined`, `null`, or cannot be parsed
|
|
17
|
+
*/
|
|
18
|
+
export function getNumber(value: unknown): number {
|
|
19
|
+
if (typeof value === 'number') {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (typeof value === 'symbol') {
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let parsed = value?.valueOf?.() ?? value;
|
|
28
|
+
|
|
29
|
+
if (typeof parsed === 'object') {
|
|
30
|
+
parsed = parsed?.toString() ?? parsed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof parsed !== 'string') {
|
|
34
|
+
return parsed == null ? NaN : typeof parsed === 'number' ? parsed : +parsed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (zeroPattern.test(parsed)) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const trimmed = parsed.trim();
|
|
42
|
+
|
|
43
|
+
if (trimmed.length === 0) {
|
|
44
|
+
return NaN;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const isBinary = binaryPattern.test(trimmed);
|
|
48
|
+
|
|
49
|
+
if (isBinary || octalPattern.test(trimmed)) {
|
|
50
|
+
return parseInt(trimmed.slice(2), isBinary ? 2 : 8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return +(hexadecimalPattern.test(trimmed)
|
|
54
|
+
? trimmed
|
|
55
|
+
: trimmed.replace(separatorPattern, ''));
|
|
56
|
+
}
|
package/src/js/string.ts
CHANGED
|
@@ -16,7 +16,11 @@ export function createUuid(): string {
|
|
|
16
16
|
* Get the string value from any value
|
|
17
17
|
*/
|
|
18
18
|
export function getString(value: unknown): string {
|
|
19
|
-
return typeof value === 'string'
|
|
19
|
+
return typeof value === 'string'
|
|
20
|
+
? value
|
|
21
|
+
: typeof value?.toString === 'function'
|
|
22
|
+
? value.toString()
|
|
23
|
+
: String(value);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
/**
|
|
@@ -25,9 +29,5 @@ export function getString(value: unknown): string {
|
|
|
25
29
|
export function isNullableOrWhitespace(
|
|
26
30
|
value: unknown,
|
|
27
31
|
): value is undefined | null | '' {
|
|
28
|
-
return (
|
|
29
|
-
value === undefined ||
|
|
30
|
-
value === null ||
|
|
31
|
-
getString(value).trim().length === 0
|
|
32
|
-
);
|
|
32
|
+
return value == null || getString(value).trim().length === 0;
|
|
33
33
|
}
|
package/src/js/value.ts
CHANGED
|
@@ -22,10 +22,6 @@ function _getValue(data: ValueObject, key: string): unknown {
|
|
|
22
22
|
return data.get(key as never);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
if (data instanceof Set) {
|
|
26
|
-
return Array.from(data)[key as never];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
25
|
return data[key as never];
|
|
30
26
|
}
|
|
31
27
|
|
|
@@ -39,42 +35,11 @@ function _setValue(data: ValueObject, key: string, value: unknown): void {
|
|
|
39
35
|
|
|
40
36
|
if (data instanceof Map) {
|
|
41
37
|
data.set(key as never, value);
|
|
42
|
-
} else if (data instanceof Set) {
|
|
43
|
-
_setValueInSet(data, key, value);
|
|
44
38
|
} else {
|
|
45
39
|
data[key as never] = value as never;
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
42
|
|
|
49
|
-
/**
|
|
50
|
-
* - Internal function to set a value in a `Set`
|
|
51
|
-
* - If key is not a valid index or if it is greater than or equal to the length of the set, we simply append it to the set
|
|
52
|
-
* - If the index is less than the size of the set, we convert the set to an array, splice the value into the array, and then convert the array back to a set
|
|
53
|
-
*/
|
|
54
|
-
function _setValueInSet(data: Set<unknown>, key: string, value: unknown): void {
|
|
55
|
-
const index = numberExpression.test(key) ? Number.parseInt(key, 10) : -1;
|
|
56
|
-
|
|
57
|
-
if (index === -1 || index >= data.size) {
|
|
58
|
-
data.add(value);
|
|
59
|
-
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const array = Array.from(data);
|
|
64
|
-
|
|
65
|
-
array.splice(index, 1, value);
|
|
66
|
-
|
|
67
|
-
data.clear();
|
|
68
|
-
|
|
69
|
-
const {length} = array;
|
|
70
|
-
|
|
71
|
-
let position = Number(length);
|
|
72
|
-
|
|
73
|
-
while (position--) {
|
|
74
|
-
data.add(array[length - position - 1]);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
43
|
/**
|
|
79
44
|
* - Get the value from an object using a key path
|
|
80
45
|
* - You can retrieve a nested value by using dot notation, e.g., `foo.bar.baz`
|
|
@@ -97,7 +62,7 @@ export function getValue(data: ValueObject, key: Key): unknown {
|
|
|
97
62
|
while (position--) {
|
|
98
63
|
value = _getValue(value, parts[position]) as ValueObject;
|
|
99
64
|
|
|
100
|
-
if (value
|
|
65
|
+
if (value == null) {
|
|
101
66
|
break;
|
|
102
67
|
}
|
|
103
68
|
}
|
|
@@ -116,7 +81,7 @@ export function isArrayOrObject(value: unknown): value is ArrayOrObject {
|
|
|
116
81
|
* Is the value undefined or null?
|
|
117
82
|
*/
|
|
118
83
|
export function isNullable(value: unknown): value is undefined | null {
|
|
119
|
-
return value
|
|
84
|
+
return value == null;
|
|
120
85
|
}
|
|
121
86
|
|
|
122
87
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* - Find the parent element that matches the selector
|
|
3
|
+
* - Matches may be found by a query string or a callback
|
|
4
|
+
*/
|
|
5
|
+
export declare function findParentElement(origin: Element, selector: string | ((element: Element) => boolean)): Element | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* - Get the most specific element under the pointer
|
|
8
|
+
* - Ignores elements with `pointer-events: none` and `visibility: hidden`
|
|
9
|
+
* - If `all` is `true`, all elements under the pointer are returned
|
|
10
|
+
*/
|
|
11
|
+
export declare function getElementUnderPointer(all?: boolean): Element | undefined;
|
package/types/event.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type Position = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Does the browser support touch events?
|
|
7
|
+
*/
|
|
8
|
+
export declare const supportsTouch: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Get the X- and Y-coordinates from a pointer event
|
|
11
|
+
*/
|
|
12
|
+
export declare function getPosition(event: MouseEvent | TouchEvent): Position | undefined;
|
|
13
|
+
export {};
|
package/types/index.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clamps a number between a minimum and maximum value
|
|
3
|
+
*/
|
|
4
|
+
export declare function clampNumber(value: number, min: number, max: number): number;
|
|
5
|
+
/**
|
|
6
|
+
* - Gets the number value from an unknown value
|
|
7
|
+
* - Returns `NaN` if the value is `undefined`, `null`, or cannot be parsed
|
|
8
|
+
*/
|
|
9
|
+
export declare function getNumber(value: unknown): number;
|