@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.
@@ -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
+ };
@@ -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,7 @@
1
+ // src/js/index.ts
2
+ export * from "./element";
3
+ export * from "./event";
4
+ export * from "./number";
5
+ export * from "./string";
6
+ export * from "./timer";
7
+ export * from "./value";
@@ -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
+ };