@synnaxlabs/x 0.44.3 → 0.45.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/.turbo/turbo-build.log +37 -41
- package/dist/{base-DFq0vvGn.js → base-DRybODwJ.js} +16 -12
- package/dist/base-KIBsp6TI.cjs +1 -0
- package/dist/binary.cjs +1 -1
- package/dist/binary.js +1 -1
- package/dist/{bounds-CRK04jp7.cjs → bounds-C2TKFgVk.cjs} +1 -1
- package/dist/{bounds-8OC_obRs.js → bounds-DeUXrllt.js} +11 -11
- package/dist/bounds.cjs +1 -1
- package/dist/bounds.js +1 -1
- package/dist/{box-CO_2_DGG.js → box-BYuq-Gjx.js} +67 -73
- package/dist/box-Blu-4d1n.cjs +1 -0
- package/dist/box.cjs +1 -1
- package/dist/box.js +1 -1
- package/dist/caseconv.cjs +1 -1
- package/dist/caseconv.js +1 -1
- package/dist/compare.cjs +1 -1
- package/dist/compare.js +1 -1
- package/dist/{dimensions-D2QGoNXO.cjs → dimensions-Cg5Owbwn.cjs} +1 -1
- package/dist/{dimensions-CRgergMS.js → dimensions-DC0uLPwn.js} +1 -1
- package/dist/dimensions.cjs +1 -1
- package/dist/dimensions.js +1 -1
- package/dist/direction-C_b4tfRN.js +19 -0
- package/dist/direction-DqQB9M37.cjs +1 -0
- package/dist/direction.cjs +1 -1
- package/dist/direction.js +1 -1
- package/dist/{index-B5l_quQn.js → index-BHXRDFYj.js} +1 -1
- package/dist/index-Bxlv0uf_.js +57 -0
- package/dist/{index-CBMHFqs4.js → index-C452Pas0.js} +25 -22
- package/dist/{index-BQe8OIgm.cjs → index-CwGPVvbl.cjs} +1 -1
- package/dist/index-uDxeM-cl.cjs +1 -0
- package/dist/index-xaxa1hoa.cjs +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +452 -392
- package/dist/location-BPoXwOni.cjs +1 -0
- package/dist/{location-Ar5y2DX2.js → location-CVxysrHI.js} +40 -44
- package/dist/location.cjs +1 -1
- package/dist/location.js +1 -1
- package/dist/record-BwjIgrpU.cjs +1 -0
- package/dist/record-tSFQKmdG.js +19 -0
- package/dist/record.cjs +1 -1
- package/dist/record.js +1 -1
- package/dist/{scale-C3fEtXxW.cjs → scale-BHs716im.cjs} +1 -1
- package/dist/{scale-Db1Gunj0.js → scale-DjxC6ep2.js} +4 -4
- package/dist/scale.cjs +1 -1
- package/dist/scale.js +1 -1
- package/dist/series-C6ZwNf8i.cjs +6 -0
- package/dist/{series-Cl3Vh_u-.js → series-W5Aafjeu.js} +303 -270
- package/dist/{spatial-BSWPzMkK.js → spatial-DnsaOypA.js} +1 -1
- package/dist/{spatial-DGpZ2sO3.cjs → spatial-DrxzaD5U.cjs} +1 -1
- package/dist/spatial.cjs +1 -1
- package/dist/spatial.js +14 -16
- package/dist/src/caseconv/caseconv.d.ts +9 -0
- package/dist/src/caseconv/caseconv.d.ts.map +1 -1
- package/dist/src/color/color.d.ts +7 -0
- package/dist/src/color/color.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/primitive/primitive.d.ts +4 -0
- package/dist/src/primitive/primitive.d.ts.map +1 -1
- package/dist/src/record/record.d.ts +18 -0
- package/dist/src/record/record.d.ts.map +1 -1
- package/dist/src/spatial/base.d.ts +12 -0
- package/dist/src/spatial/base.d.ts.map +1 -1
- package/dist/src/spatial/direction/direction.d.ts +5 -1
- package/dist/src/spatial/direction/direction.d.ts.map +1 -1
- package/dist/src/spatial/external.d.ts +0 -1
- package/dist/src/spatial/external.d.ts.map +1 -1
- package/dist/src/spatial/location/location.d.ts +2 -2
- package/dist/src/spatial/location/location.d.ts.map +1 -1
- package/dist/src/spatial/xy/xy.d.ts +3 -0
- package/dist/src/spatial/xy/xy.d.ts.map +1 -1
- package/dist/src/status/status.d.ts +2 -1
- package/dist/src/status/status.d.ts.map +1 -1
- package/dist/src/telem/series.d.ts +1 -0
- package/dist/src/telem/series.d.ts.map +1 -1
- package/dist/src/telem/telem.d.ts +23 -6
- package/dist/src/telem/telem.d.ts.map +1 -1
- package/dist/src/undefined.d.ts +2 -0
- package/dist/src/undefined.d.ts.map +1 -0
- package/dist/telem.cjs +1 -1
- package/dist/telem.js +1 -1
- package/dist/xy-DWwtHmgn.cjs +1 -0
- package/dist/{xy-C_-hb3Q2.js → xy-DYPw8-8C.js} +41 -31
- package/dist/xy.cjs +1 -1
- package/dist/xy.js +1 -1
- package/package.json +2 -7
- package/src/caseconv/caseconv.spec.ts +30 -0
- package/src/caseconv/caseconv.ts +46 -0
- package/src/color/color.spec.ts +40 -0
- package/src/color/color.ts +72 -0
- package/src/index.ts +1 -0
- package/src/primitive/primitive.spec.ts +9 -0
- package/src/primitive/primitive.ts +9 -0
- package/src/record/record.spec.ts +31 -0
- package/src/record/record.ts +23 -0
- package/src/spatial/base.ts +4 -0
- package/src/spatial/direction/direction.spec.ts +35 -1
- package/src/spatial/direction/direction.ts +12 -0
- package/src/spatial/external.ts +0 -1
- package/src/spatial/location/location.ts +2 -0
- package/src/spatial/xy/xy.spec.ts +124 -7
- package/src/spatial/xy/xy.ts +15 -2
- package/src/status/status.spec.ts +52 -0
- package/src/status/status.ts +19 -6
- package/src/telem/series.ts +2 -0
- package/src/telem/telem.spec.ts +352 -0
- package/src/telem/telem.ts +85 -18
- package/src/{spatial/position/index.ts → undefined.ts} +5 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.config.ts +0 -1
- package/dist/base-BAM2mqCy.cjs +0 -1
- package/dist/box-Cxki783Y.cjs +0 -1
- package/dist/direction-386XDm2w.cjs +0 -1
- package/dist/direction-8etxfKaR.js +0 -17
- package/dist/index-D2xcvEO5.js +0 -46
- package/dist/index-DOJlZHqJ.cjs +0 -1
- package/dist/index-DdhM_E4k.cjs +0 -1
- package/dist/location-DZi8ftXp.cjs +0 -1
- package/dist/position-DSy2hONH.cjs +0 -1
- package/dist/position-PQ6op54I.js +0 -85
- package/dist/position.cjs +0 -1
- package/dist/position.js +0 -4
- package/dist/record-CAcQ5PNX.js +0 -14
- package/dist/record-YvCh7bzB.cjs +0 -1
- package/dist/series-BcF7A8Je.cjs +0 -6
- package/dist/src/spatial/position/index.d.ts +0 -2
- package/dist/src/spatial/position/index.d.ts.map +0 -1
- package/dist/src/spatial/position/position.d.ts +0 -20
- package/dist/src/spatial/position/position.d.ts.map +0 -1
- package/dist/src/spatial/position/position.spec.d.ts +0 -2
- package/dist/src/spatial/position/position.spec.d.ts.map +0 -1
- package/dist/xy-CUE3QDNn.cjs +0 -1
- package/src/spatial/position/position.spec.ts +0 -211
- package/src/spatial/position/position.ts +0 -157
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { z as
|
|
2
|
-
import { x as p, n as
|
|
3
|
-
const
|
|
4
|
-
|
|
1
|
+
import { z as b } from "zod";
|
|
2
|
+
import { x as p, n as v, d as I, s as A, j as M } from "./base-DRybODwJ.js";
|
|
3
|
+
const E = b.union([
|
|
4
|
+
b.number(),
|
|
5
5
|
p,
|
|
6
|
-
|
|
6
|
+
v,
|
|
7
7
|
I,
|
|
8
8
|
A,
|
|
9
9
|
M
|
|
@@ -13,10 +13,10 @@ const X = g.union([
|
|
|
13
13
|
return t === "x" ? { x: n, y: 0 } : { x: 0, y: n };
|
|
14
14
|
}
|
|
15
15
|
return typeof t == "number" ? { x: t, y: n ?? t } : Array.isArray(t) ? { x: t[0], y: t[1] } : "signedWidth" in t ? { x: t.signedWidth, y: t.signedHeight } : "clientX" in t ? { x: t.clientX, y: t.clientY } : "width" in t ? { x: t.width, y: t.height } : { x: t.x, y: t.y };
|
|
16
|
-
}, d = Object.freeze({ x: 0, y: 0 }),
|
|
16
|
+
}, d = Object.freeze({ x: 0, y: 0 }), T = Object.freeze({ x: 1, y: 1 }), X = Object.freeze({ x: 1 / 0, y: 1 / 0 }), Y = Object.freeze({ x: NaN, y: NaN }), O = (t, n, e = 0) => {
|
|
17
17
|
const r = s(t), o = s(n);
|
|
18
18
|
return e === 0 ? r.x === o.x && r.y === o.y : Math.abs(r.x - o.x) <= e && Math.abs(r.y - o.y) <= e;
|
|
19
|
-
},
|
|
19
|
+
}, Z = (t) => O(t, d), h = (t, n, e) => {
|
|
20
20
|
const r = s(t), o = s(n, e);
|
|
21
21
|
return { x: r.x * o.x, y: r.y * o.y };
|
|
22
22
|
}, P = (t, n) => {
|
|
@@ -25,10 +25,20 @@ const X = g.union([
|
|
|
25
25
|
}, _ = (t, n) => {
|
|
26
26
|
const e = s(t);
|
|
27
27
|
return { x: e.x, y: e.y + n };
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
}, j = (t, n, e, ...r) => {
|
|
29
|
+
if (typeof n == "string") {
|
|
30
|
+
if (typeof e != "number") throw new Error("The value must be a number.");
|
|
31
|
+
return n === "x" ? P(t, e) : _(t, e);
|
|
32
|
+
}
|
|
33
|
+
if (typeof n == "object" && "x" in n && typeof n.x == "string") {
|
|
34
|
+
const o = s(e), y = s(t);
|
|
35
|
+
return n.x === "left" ? o.x = -o.x : n.x === "center" && (o.x = 0), n.y === "top" ? o.y = -o.y : n.y === "center" && (o.y = 0), { x: y.x + o.x, y: y.y + o.y };
|
|
36
|
+
}
|
|
37
|
+
return [t, n, e ?? d, ...r].reduce((o, y) => {
|
|
38
|
+
const c = s(y);
|
|
39
|
+
return { x: o.x + c.x, y: o.y + c.y };
|
|
40
|
+
}, d);
|
|
41
|
+
}, q = (t, n, e) => {
|
|
32
42
|
const r = s(t);
|
|
33
43
|
return n === "x" ? { x: e, y: r.y } : { x: r.x, y: e };
|
|
34
44
|
}, H = (t, n) => {
|
|
@@ -58,12 +68,12 @@ const X = g.union([
|
|
|
58
68
|
}, m = (t) => {
|
|
59
69
|
const n = s(t), e = Math.hypot(n.x, n.y);
|
|
60
70
|
return e === 0 ? { x: 0, y: 0 } : { x: -n.y / e, y: n.x / e };
|
|
61
|
-
},
|
|
71
|
+
}, w = (t) => {
|
|
62
72
|
const n = s(t), e = Math.hypot(n.x, n.y);
|
|
63
73
|
return e === 0 ? { x: 0, y: 0 } : { x: n.x / e, y: n.y / e };
|
|
64
|
-
},
|
|
65
|
-
const n = t.reduce((e, r) =>
|
|
66
|
-
return
|
|
74
|
+
}, z = (...t) => {
|
|
75
|
+
const n = t.reduce((e, r) => j(e, r), d);
|
|
76
|
+
return h(n, 1 / t.length);
|
|
67
77
|
}, $ = (t, n) => {
|
|
68
78
|
const e = [];
|
|
69
79
|
for (let r = 0; r < t.length; r++) {
|
|
@@ -78,12 +88,12 @@ const X = g.union([
|
|
|
78
88
|
} else {
|
|
79
89
|
const x = t[r - 1], u = t[r + 1], a = f(o, x), l = f(u, o);
|
|
80
90
|
y = m(a), c = m(l);
|
|
81
|
-
const
|
|
91
|
+
const F = Math.acos(
|
|
82
92
|
(a.x * l.x + a.y * l.y) / (Math.hypot(a.x, a.y) * Math.hypot(l.x, l.y))
|
|
83
|
-
),
|
|
84
|
-
|
|
93
|
+
), g = Math.sin(F / 2);
|
|
94
|
+
g === 0 ? i = n : i = n / g, N = w(z(y, c));
|
|
85
95
|
}
|
|
86
|
-
e.push(
|
|
96
|
+
e.push(h(N, i));
|
|
87
97
|
}
|
|
88
98
|
return e;
|
|
89
99
|
}, k = (t) => {
|
|
@@ -94,30 +104,30 @@ const X = g.union([
|
|
|
94
104
|
return { x: Math.round(n.x), y: Math.round(n.y) };
|
|
95
105
|
}, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
96
106
|
__proto__: null,
|
|
97
|
-
INFINITY:
|
|
98
|
-
NAN:
|
|
99
|
-
ONE:
|
|
107
|
+
INFINITY: X,
|
|
108
|
+
NAN: Y,
|
|
109
|
+
ONE: T,
|
|
100
110
|
ZERO: d,
|
|
101
|
-
average:
|
|
111
|
+
average: z,
|
|
102
112
|
calculateMiters: $,
|
|
103
113
|
clientXY: M,
|
|
104
114
|
construct: s,
|
|
105
115
|
couple: D,
|
|
106
|
-
crudeZ:
|
|
116
|
+
crudeZ: E,
|
|
107
117
|
css: L,
|
|
108
118
|
distance: H,
|
|
109
119
|
equals: O,
|
|
110
120
|
isFinite: C,
|
|
111
121
|
isNan: W,
|
|
112
|
-
isZero:
|
|
122
|
+
isZero: Z,
|
|
113
123
|
normal: m,
|
|
114
|
-
normalize:
|
|
124
|
+
normalize: w,
|
|
115
125
|
round: B,
|
|
116
|
-
scale:
|
|
126
|
+
scale: h,
|
|
117
127
|
set: q,
|
|
118
128
|
sub: f,
|
|
119
129
|
swap: k,
|
|
120
|
-
translate:
|
|
130
|
+
translate: j,
|
|
121
131
|
translateX: P,
|
|
122
132
|
translateY: _,
|
|
123
133
|
translation: S,
|
|
@@ -125,13 +135,13 @@ const X = g.union([
|
|
|
125
135
|
xy: p
|
|
126
136
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
127
137
|
export {
|
|
128
|
-
|
|
138
|
+
T as O,
|
|
129
139
|
d as Z,
|
|
130
140
|
R as a,
|
|
131
|
-
|
|
141
|
+
E as b,
|
|
132
142
|
s as c,
|
|
133
143
|
O as e,
|
|
134
144
|
B as r,
|
|
135
|
-
|
|
145
|
+
j as t,
|
|
136
146
|
K as x
|
|
137
147
|
};
|
package/dist/xy.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./xy-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./xy-DWwtHmgn.cjs");exports.xy=e.xy;
|
package/dist/xy.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synnaxlabs/x",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Common Utilities for Synnax Labs",
|
|
6
|
-
"repository": "https://github.com/synnaxlabs/synnax/tree/main/x/
|
|
6
|
+
"repository": "https://github.com/synnaxlabs/synnax/tree/main/x/ts",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"synnax",
|
|
9
9
|
"utilities",
|
|
@@ -95,11 +95,6 @@
|
|
|
95
95
|
"import": "./dist/location.js",
|
|
96
96
|
"require": "./dist/location.cjs"
|
|
97
97
|
},
|
|
98
|
-
"./position": {
|
|
99
|
-
"types": "./dist/src/spatial/position/index.d.ts",
|
|
100
|
-
"import": "./dist/position.js",
|
|
101
|
-
"require": "./dist/position.cjs"
|
|
102
|
-
},
|
|
103
98
|
"./scale": {
|
|
104
99
|
"types": "./dist/src/spatial/scale/index.d.ts",
|
|
105
100
|
"import": "./dist/scale.js",
|
|
@@ -127,4 +127,34 @@ describe("caseconv", () => {
|
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
|
+
describe("toProperNoun", () => {
|
|
131
|
+
const SPECS: [string, string][] = [
|
|
132
|
+
["fooBar", "Foo Bar"],
|
|
133
|
+
["foo_bar", "Foo Bar"],
|
|
134
|
+
["foo-bar", "Foo Bar"],
|
|
135
|
+
["FooBar", "Foo Bar"],
|
|
136
|
+
["foo_bar_baz", "Foo Bar Baz"],
|
|
137
|
+
["fooBarBaz", "Foo Bar Baz"],
|
|
138
|
+
["foo-bar-baz", "Foo Bar Baz"],
|
|
139
|
+
["XMLParser", "XML Parser"],
|
|
140
|
+
["parseXMLDocument", "Parse XML Document"],
|
|
141
|
+
["IODevice", "IO Device"],
|
|
142
|
+
["temperature_sensor", "Temperature Sensor"],
|
|
143
|
+
["pressure-gauge", "Pressure Gauge"],
|
|
144
|
+
["flowMeter", "Flow Meter"],
|
|
145
|
+
["my_custom_symbol", "My Custom Symbol"],
|
|
146
|
+
["valve-actuator-v2", "Valve Actuator V2"],
|
|
147
|
+
["PIDController", "PID Controller"],
|
|
148
|
+
["", ""],
|
|
149
|
+
["a", "A"],
|
|
150
|
+
["ABC", "ABC"],
|
|
151
|
+
["test123value", "Test123value"],
|
|
152
|
+
["test_123_value", "Test 123 Value"],
|
|
153
|
+
];
|
|
154
|
+
SPECS.forEach(([input, expected]) => {
|
|
155
|
+
it(`should convert ${input} to ${expected}`, () => {
|
|
156
|
+
expect(caseconv.toProperNoun(input)).toBe(expected);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
130
160
|
});
|
package/src/caseconv/caseconv.ts
CHANGED
|
@@ -165,3 +165,49 @@ const toKebabStr = (str: string): string =>
|
|
|
165
165
|
* @returns The converted string in kebab-case
|
|
166
166
|
*/
|
|
167
167
|
export const toKebab = createConverter(toKebabStr);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Converts a string to proper noun format.
|
|
171
|
+
* Handles snake_case, kebab-case, camelCase, and PascalCase.
|
|
172
|
+
* Capitalizes the first letter of each word.
|
|
173
|
+
*
|
|
174
|
+
* @param str - The string to convert
|
|
175
|
+
* @returns The converted string in proper noun format
|
|
176
|
+
*/
|
|
177
|
+
const toProperNounStr = (str: string): string => {
|
|
178
|
+
if (str.length === 0) return str;
|
|
179
|
+
|
|
180
|
+
// Replace underscores and hyphens with spaces
|
|
181
|
+
let result = str.replace(/[_-]/g, " ");
|
|
182
|
+
|
|
183
|
+
// Insert spaces before capital letters (for camelCase/PascalCase)
|
|
184
|
+
// but not at the start or when there are consecutive capitals
|
|
185
|
+
result = result.replace(
|
|
186
|
+
/([a-z0-9])([A-Z])/g,
|
|
187
|
+
(_, p1: string, p2: string) => `${p1} ${p2}`
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Handle consecutive capitals (e.g., "XMLParser" -> "XML Parser")
|
|
191
|
+
result = result.replace(
|
|
192
|
+
/([A-Z]+)([A-Z][a-z])/g,
|
|
193
|
+
(_, p1: string, p2: string) => `${p1} ${p2}`
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Clean up multiple spaces
|
|
197
|
+
result = result.replace(/\s+/g, " ").trim();
|
|
198
|
+
|
|
199
|
+
// Capitalize first letter of each word (proper noun format)
|
|
200
|
+
result = result.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
201
|
+
|
|
202
|
+
return result;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Converts a string to proper noun format.
|
|
207
|
+
* Handles snake_case, kebab-case, camelCase, and PascalCase.
|
|
208
|
+
* Each word is capitalized.
|
|
209
|
+
*
|
|
210
|
+
* @param str - The string to convert
|
|
211
|
+
* @returns The converted string in proper noun format
|
|
212
|
+
*/
|
|
213
|
+
export const toProperNoun = createConverter(toProperNounStr);
|
package/src/color/color.spec.ts
CHANGED
|
@@ -757,6 +757,46 @@ describe("color.Color", () => {
|
|
|
757
757
|
});
|
|
758
758
|
});
|
|
759
759
|
|
|
760
|
+
describe("fromCSS", () => {
|
|
761
|
+
test("parses hex colors", () => {
|
|
762
|
+
expect(color.fromCSS("#ff0000")).toEqual([255, 0, 0, 1]);
|
|
763
|
+
expect(color.fromCSS("#00ff00")).toEqual([0, 255, 0, 1]);
|
|
764
|
+
expect(color.fromCSS("#0000ff")).toEqual([0, 0, 255, 1]);
|
|
765
|
+
expect(color.fromCSS("#f00")).toEqual([255, 0, 0, 1]);
|
|
766
|
+
expect(color.fromCSS("#0f0")).toEqual([0, 255, 0, 1]);
|
|
767
|
+
expect(color.fromCSS("#00f")).toEqual([0, 0, 255, 1]);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
test("parses rgb/rgba colors", () => {
|
|
771
|
+
expect(color.fromCSS("rgb(255, 0, 0)")).toEqual([255, 0, 0, 1]);
|
|
772
|
+
expect(color.fromCSS("rgb(0, 255, 0)")).toEqual([0, 255, 0, 1]);
|
|
773
|
+
expect(color.fromCSS("rgba(0, 0, 255, 0.5)")).toEqual([0, 0, 255, 0.5]);
|
|
774
|
+
expect(color.fromCSS("rgba(128, 128, 128, 1)")).toEqual([128, 128, 128, 1]);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
test("parses named colors", () => {
|
|
778
|
+
expect(color.fromCSS("red")).toEqual([255, 0, 0, 1]);
|
|
779
|
+
expect(color.fromCSS("green")).toEqual([0, 128, 0, 1]);
|
|
780
|
+
expect(color.fromCSS("blue")).toEqual([0, 0, 255, 1]);
|
|
781
|
+
expect(color.fromCSS("black")).toEqual([0, 0, 0, 1]);
|
|
782
|
+
expect(color.fromCSS("white")).toEqual([255, 255, 255, 1]);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test("handles case insensitive input", () => {
|
|
786
|
+
expect(color.fromCSS("RED")).toEqual([255, 0, 0, 1]);
|
|
787
|
+
expect(color.fromCSS("Green")).toEqual([0, 128, 0, 1]);
|
|
788
|
+
expect(color.fromCSS("BLUE")).toEqual([0, 0, 255, 1]);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test("returns undefined for invalid input", () => {
|
|
792
|
+
expect(color.fromCSS("")).toBeUndefined();
|
|
793
|
+
expect(color.fromCSS("none")).toBeUndefined();
|
|
794
|
+
expect(color.fromCSS("transparent")).toBeUndefined();
|
|
795
|
+
expect(color.fromCSS("invalid")).toBeUndefined();
|
|
796
|
+
expect(color.fromCSS("#gggggg")).toBeUndefined();
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
|
|
760
800
|
describe("isZero", () => {
|
|
761
801
|
test("returns true for zero color", () => {
|
|
762
802
|
expect(color.isZero([0, 0, 0, 0])).toBe(true);
|
package/src/color/color.ts
CHANGED
|
@@ -282,6 +282,78 @@ const rgbaToHex = (n: number): string => Math.floor(n).toString(16).padStart(2,
|
|
|
282
282
|
const hexToRgba = (s: string, n: number): number => parseInt(s.slice(n, n + 2), 16);
|
|
283
283
|
const stripHash = (hex: string): string => (hex.startsWith("#") ? hex.slice(1) : hex);
|
|
284
284
|
|
|
285
|
+
const NAMED: Record<string, string> = {
|
|
286
|
+
black: "#000000",
|
|
287
|
+
white: "#ffffff",
|
|
288
|
+
red: "#ff0000",
|
|
289
|
+
green: "#008000",
|
|
290
|
+
blue: "#0000ff",
|
|
291
|
+
yellow: "#ffff00",
|
|
292
|
+
cyan: "#00ffff",
|
|
293
|
+
magenta: "#ff00ff",
|
|
294
|
+
silver: "#c0c0c0",
|
|
295
|
+
gray: "#808080",
|
|
296
|
+
grey: "#808080",
|
|
297
|
+
maroon: "#800000",
|
|
298
|
+
olive: "#808000",
|
|
299
|
+
lime: "#00ff00",
|
|
300
|
+
aqua: "#00ffff",
|
|
301
|
+
teal: "#008080",
|
|
302
|
+
navy: "#000080",
|
|
303
|
+
fuchsia: "#ff00ff",
|
|
304
|
+
purple: "#800080",
|
|
305
|
+
orange: "#ffa500",
|
|
306
|
+
brown: "#a52a2a",
|
|
307
|
+
tan: "#d2b48c",
|
|
308
|
+
gold: "#ffd700",
|
|
309
|
+
indigo: "#4b0082",
|
|
310
|
+
violet: "#ee82ee",
|
|
311
|
+
pink: "#ffc0cb",
|
|
312
|
+
coral: "#ff7f50",
|
|
313
|
+
salmon: "#fa8072",
|
|
314
|
+
khaki: "#f0e68c",
|
|
315
|
+
crimson: "#dc143c",
|
|
316
|
+
transparent: "transparent",
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Parses a CSS color string into a Color.
|
|
321
|
+
* Supports hex colors, rgb/rgba functions, and named colors.
|
|
322
|
+
* @param cssColor - The CSS color string to parse
|
|
323
|
+
* @returns The parsed color or undefined if invalid
|
|
324
|
+
*/
|
|
325
|
+
export const fromCSS = (cssColor: string): Color | undefined => {
|
|
326
|
+
if (!cssColor) return undefined;
|
|
327
|
+
const trimmed = cssColor.trim().toLowerCase();
|
|
328
|
+
if (trimmed === "transparent" || trimmed === "none") return undefined;
|
|
329
|
+
if (trimmed.startsWith("#")) {
|
|
330
|
+
if (trimmed.length === 4) {
|
|
331
|
+
const r = trimmed[1];
|
|
332
|
+
const g = trimmed[2];
|
|
333
|
+
const b = trimmed[3];
|
|
334
|
+
const expanded = `#${r}${r}${g}${g}${b}${b}`;
|
|
335
|
+
if (hexZ.safeParse(expanded).success) return fromHex(expanded);
|
|
336
|
+
}
|
|
337
|
+
if (
|
|
338
|
+
(trimmed.length === 7 || trimmed.length === 9) &&
|
|
339
|
+
hexZ.safeParse(trimmed).success
|
|
340
|
+
)
|
|
341
|
+
return fromHex(trimmed);
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
if (trimmed.startsWith("rgb")) {
|
|
345
|
+
const match = trimmed.match(
|
|
346
|
+
/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/,
|
|
347
|
+
);
|
|
348
|
+
if (match) {
|
|
349
|
+
const [, r, g, b, a] = match;
|
|
350
|
+
return [parseInt(r), parseInt(g), parseInt(b), a ? parseFloat(a) : 1];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (NAMED[trimmed]) return fromHex(NAMED[trimmed]);
|
|
354
|
+
return undefined;
|
|
355
|
+
};
|
|
356
|
+
|
|
285
357
|
/** @returns parse a color from an HSLA tuple. */
|
|
286
358
|
export const fromHSLA = (hsla: HSLA): RGBA => {
|
|
287
359
|
hsla = hslaZ.parse(hsla);
|
package/src/index.ts
CHANGED
|
@@ -124,6 +124,15 @@ describe("primitive", () => {
|
|
|
124
124
|
});
|
|
125
125
|
expect(decoded.myDog).toBeInstanceOf(MyValueExtension);
|
|
126
126
|
});
|
|
127
|
+
|
|
128
|
+
describe("isCrudeValueExtension", () => {
|
|
129
|
+
it("should return true for a CrudeValueExtension", () => {
|
|
130
|
+
expect(primitive.isCrudeValueExtension({ value: 12n })).toEqual(true);
|
|
131
|
+
});
|
|
132
|
+
it("should return false for a non-CrudeValueExtension", () => {
|
|
133
|
+
expect(primitive.isCrudeValueExtension(12n)).toEqual(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
127
136
|
});
|
|
128
137
|
});
|
|
129
138
|
});
|
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
/** Union of types that are primitive values or can be converted to primitive values */
|
|
11
11
|
export type Value = string | number | bigint | boolean | Stringer | null | undefined;
|
|
12
12
|
|
|
13
|
+
export type CrudeValueExtension<V extends NonNullable<Value>> = {
|
|
14
|
+
value: V;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const isCrudeValueExtension = <V extends NonNullable<Value>>(
|
|
18
|
+
value: unknown,
|
|
19
|
+
): value is CrudeValueExtension<V> =>
|
|
20
|
+
value != null && typeof value === "object" && "value" in value;
|
|
21
|
+
|
|
13
22
|
/**
|
|
14
23
|
* ValueExtension is a utility class that can be extended in order to implement objects
|
|
15
24
|
* that pseudo-extend a primitive value with additional functionality.
|
|
@@ -316,4 +316,35 @@ describe("record", () => {
|
|
|
316
316
|
});
|
|
317
317
|
});
|
|
318
318
|
});
|
|
319
|
+
|
|
320
|
+
describe("omit", () => {
|
|
321
|
+
type Object = { [key: string]: number };
|
|
322
|
+
it("should return the object if no keys are provided", () =>
|
|
323
|
+
expect(record.omit({ a: 1, b: 2, c: 3 })).toEqual({ a: 1, b: 2, c: 3 }));
|
|
324
|
+
|
|
325
|
+
it("should return the object for a single key", () =>
|
|
326
|
+
expect(record.omit({ a: 1, b: 2, c: 3 }, "a")).toEqual({ b: 2, c: 3 }));
|
|
327
|
+
|
|
328
|
+
it("should return the object for multiple keys", () =>
|
|
329
|
+
expect(record.omit({ a: 1, b: 2, c: 3 }, "a", "b")).toEqual({ c: 3 }));
|
|
330
|
+
|
|
331
|
+
it("should not mutate the original object", () => {
|
|
332
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
333
|
+
const result = record.omit(obj, "a", "b");
|
|
334
|
+
expect(result).toEqual({ c: 3 });
|
|
335
|
+
expect(obj).toEqual({ a: 1, b: 2, c: 3 });
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should be a no-op if the keys are not present", () => {
|
|
339
|
+
const obj: Object = { a: 1, b: 2, c: 3 };
|
|
340
|
+
const result = record.omit(obj, "d", "e");
|
|
341
|
+
expect(result).toEqual({ a: 1, b: 2, c: 3 });
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("should handle empty objects", () => {
|
|
345
|
+
const obj: Object = {};
|
|
346
|
+
const result = record.omit(obj, "a", "b", "c");
|
|
347
|
+
expect(result).toEqual({});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
319
350
|
});
|
package/src/record/record.ts
CHANGED
|
@@ -116,3 +116,26 @@ export const map = <T extends Record<Key, unknown>, U>(
|
|
|
116
116
|
*/
|
|
117
117
|
export const purgeUndefined = <T extends Record<Key, unknown>>(obj: T): T =>
|
|
118
118
|
Object.fromEntries(entries(obj).filter(([_, value]) => value !== undefined)) as T;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Removes specified keys from an object. This creates a shallow copy of the object and
|
|
122
|
+
* removes the keys instead of mutating the original object.
|
|
123
|
+
*
|
|
124
|
+
* @template T - The type of the input object
|
|
125
|
+
* @template K - The type of the keys to remove
|
|
126
|
+
* @param obj - The object to remove keys from
|
|
127
|
+
* @param keys - The keys to remove from the object
|
|
128
|
+
* @returns A new object with the specified keys removed
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const obj = { a: 1, b: 2, c: 3 };
|
|
133
|
+
* const omitted = record.omit(obj, "b", "c");
|
|
134
|
+
* // Result: { a: 1 }
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> => {
|
|
138
|
+
const result = { ...obj };
|
|
139
|
+
for (const key of keys) delete result[key];
|
|
140
|
+
return result;
|
|
141
|
+
};
|
package/src/spatial/base.ts
CHANGED
|
@@ -83,6 +83,10 @@ export type CrudeBounds<T extends number | bigint = number> =
|
|
|
83
83
|
| Bounds<T>
|
|
84
84
|
| NumberCouple<T>;
|
|
85
85
|
export const crudeDirection = z.enum([...direction.options, ...location.options]);
|
|
86
|
+
export const crudeXDirection = z.enum(["x", "left", "right"]);
|
|
87
|
+
export const crudeYDirection = z.enum(["y", "top", "bottom"]);
|
|
86
88
|
export type CrudeDirection = z.infer<typeof crudeDirection>;
|
|
89
|
+
export type CrudeXDirection = z.infer<typeof crudeXDirection>;
|
|
90
|
+
export type CrudeYDirection = z.infer<typeof crudeYDirection>;
|
|
87
91
|
export const crudeLocation = z.union([direction, location, z.instanceof(String)]);
|
|
88
92
|
export type CrudeLocation = z.infer<typeof crudeLocation>;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { describe, expect, test } from "vitest";
|
|
10
|
+
import { describe, expect, it, test } from "vitest";
|
|
11
11
|
|
|
12
12
|
import * as direction from "@/spatial/direction/direction";
|
|
13
13
|
|
|
@@ -22,4 +22,38 @@ describe("Direction", () => {
|
|
|
22
22
|
test(name, () => expect(direction.construct(arg)).toEqual("y")),
|
|
23
23
|
);
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
describe("isX", () => {
|
|
27
|
+
const TESTS: [direction.Crude, boolean][] = [
|
|
28
|
+
["x", true],
|
|
29
|
+
["y", false],
|
|
30
|
+
["left", true],
|
|
31
|
+
["right", true],
|
|
32
|
+
["top", false],
|
|
33
|
+
["bottom", false],
|
|
34
|
+
["center", false],
|
|
35
|
+
];
|
|
36
|
+
TESTS.forEach(([arg, expected]) => {
|
|
37
|
+
it(`should return ${expected} for ${arg}`, () => {
|
|
38
|
+
expect(direction.isX(arg)).toBe(expected);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("isY", () => {
|
|
44
|
+
const TESTS: [direction.Crude, boolean][] = [
|
|
45
|
+
["x", false],
|
|
46
|
+
["y", true],
|
|
47
|
+
["left", false],
|
|
48
|
+
["right", false],
|
|
49
|
+
["top", true],
|
|
50
|
+
["bottom", true],
|
|
51
|
+
["center", false],
|
|
52
|
+
];
|
|
53
|
+
TESTS.forEach(([arg, expected]) => {
|
|
54
|
+
it(`should return ${expected} for ${arg}`, () => {
|
|
55
|
+
expect(direction.isY(arg)).toBe(expected);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
25
59
|
});
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import {
|
|
11
11
|
type CrudeDirection,
|
|
12
12
|
crudeDirection,
|
|
13
|
+
type CrudeXDirection,
|
|
14
|
+
type CrudeYDirection,
|
|
13
15
|
type Dimension,
|
|
14
16
|
type Direction,
|
|
15
17
|
direction,
|
|
@@ -25,6 +27,8 @@ export { Direction, direction, DIRECTIONS };
|
|
|
25
27
|
export const crude = crudeDirection;
|
|
26
28
|
|
|
27
29
|
export type Crude = CrudeDirection;
|
|
30
|
+
export type CrudeX = CrudeXDirection;
|
|
31
|
+
export type CrudeY = CrudeYDirection;
|
|
28
32
|
|
|
29
33
|
export const construct = (c: Crude): Direction => {
|
|
30
34
|
if (DIRECTIONS.includes(c as Direction)) return c as Direction;
|
|
@@ -45,3 +49,11 @@ export const isDirection = (c: unknown): c is Direction => crude.safeParse(c).su
|
|
|
45
49
|
|
|
46
50
|
export const signedDimension = (direction: CrudeDirection): SignedDimension =>
|
|
47
51
|
construct(direction) === "x" ? "signedWidth" : "signedHeight";
|
|
52
|
+
|
|
53
|
+
export const isX = (direction: CrudeDirection): direction is CrudeXDirection => {
|
|
54
|
+
if (direction === "center") return false;
|
|
55
|
+
return construct(direction) === "x";
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const isY = (direction: CrudeDirection): direction is CrudeYDirection =>
|
|
59
|
+
construct(direction) === "y";
|
package/src/spatial/external.ts
CHANGED
|
@@ -12,7 +12,6 @@ export { box } from "@/spatial/box";
|
|
|
12
12
|
export * from "@/spatial/dimensions";
|
|
13
13
|
export * from "@/spatial/direction";
|
|
14
14
|
export * from "@/spatial/location";
|
|
15
|
-
export * from "@/spatial/position";
|
|
16
15
|
export * from "@/spatial/scale";
|
|
17
16
|
export * as spatial from "@/spatial/spatial";
|
|
18
17
|
export * from "@/spatial/xy";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
DIRECTIONS,
|
|
21
21
|
type Location,
|
|
22
22
|
location,
|
|
23
|
+
OUTER_LOCATIONS,
|
|
23
24
|
type OuterLocation,
|
|
24
25
|
outerLocation,
|
|
25
26
|
X_LOCATIONS,
|
|
@@ -35,6 +36,7 @@ export {
|
|
|
35
36
|
type Location,
|
|
36
37
|
location,
|
|
37
38
|
outerLocation as outer,
|
|
39
|
+
OUTER_LOCATIONS,
|
|
38
40
|
X_LOCATIONS,
|
|
39
41
|
Y_LOCATIONS,
|
|
40
42
|
};
|