@synnaxlabs/x 0.55.0 → 0.56.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 +10 -13
- package/dist/src/array/nullable.d.ts +1 -1
- package/dist/src/array/nullable.d.ts.map +1 -1
- package/dist/src/caseconv/caseconv.d.ts.map +1 -1
- package/dist/src/compare/compare.d.ts +14 -0
- package/dist/src/compare/compare.d.ts.map +1 -1
- package/dist/src/debounce/debounce.d.ts +7 -2
- package/dist/src/debounce/debounce.d.ts.map +1 -1
- package/dist/src/destructor/destructor.d.ts +1 -0
- package/dist/src/destructor/destructor.d.ts.map +1 -1
- package/dist/src/errors/errors.d.ts +6 -10
- package/dist/src/errors/errors.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/notation/external.d.ts +3 -0
- package/dist/src/notation/external.d.ts.map +1 -0
- package/dist/src/notation/index.d.ts +1 -1
- package/dist/src/notation/notation.d.ts +5 -9
- package/dist/src/notation/notation.d.ts.map +1 -1
- package/dist/src/notation/types.gen.d.ts +9 -0
- package/dist/src/notation/types.gen.d.ts.map +1 -0
- package/dist/src/primitive/primitive.d.ts +16 -0
- package/dist/src/primitive/primitive.d.ts.map +1 -1
- package/dist/src/record/record.d.ts +8 -1
- package/dist/src/record/record.d.ts.map +1 -1
- package/dist/src/require/index.d.ts +2 -0
- package/dist/src/require/index.d.ts.map +1 -0
- package/dist/src/require/require.d.ts +2 -0
- package/dist/src/require/require.d.ts.map +1 -0
- package/dist/src/spatial/base.d.ts +1 -103
- package/dist/src/spatial/base.d.ts.map +1 -1
- package/dist/src/spatial/bounds/bounds.d.ts +3 -3
- package/dist/src/spatial/bounds/bounds.d.ts.map +1 -1
- package/dist/src/spatial/box/box.d.ts +7 -13
- package/dist/src/spatial/box/box.d.ts.map +1 -1
- package/dist/src/spatial/direction/direction.d.ts +17 -16
- package/dist/src/spatial/direction/direction.d.ts.map +1 -1
- package/dist/src/spatial/external.d.ts +1 -2
- package/dist/src/spatial/external.d.ts.map +1 -1
- package/dist/src/spatial/location/location.d.ts +28 -28
- package/dist/src/spatial/location/location.d.ts.map +1 -1
- package/dist/src/spatial/scale/scale.d.ts +2 -2
- package/dist/src/spatial/scale/scale.d.ts.map +1 -1
- package/dist/src/spatial/sticky/sticky.d.ts +15 -15
- package/dist/src/spatial/sticky/sticky.d.ts.map +1 -1
- package/dist/src/spatial/types.gen.d.ts +179 -2
- package/dist/src/spatial/types.gen.d.ts.map +1 -1
- package/dist/src/spatial/xy/xy.d.ts +4 -4
- package/dist/src/spatial/xy/xy.d.ts.map +1 -1
- package/dist/src/status/status.d.ts +11 -0
- package/dist/src/status/status.d.ts.map +1 -1
- package/dist/src/telem/external.d.ts +1 -0
- package/dist/src/telem/external.d.ts.map +1 -1
- package/dist/src/telem/series.d.ts +5 -2
- package/dist/src/telem/series.d.ts.map +1 -1
- package/dist/src/telem/telem.d.ts +42 -34
- package/dist/src/telem/telem.d.ts.map +1 -1
- package/dist/src/telem/types.gen.d.ts +19 -0
- package/dist/src/telem/types.gen.d.ts.map +1 -0
- package/dist/src/text/external.d.ts +3 -0
- package/dist/src/text/external.d.ts.map +1 -0
- package/dist/src/text/index.d.ts +2 -0
- package/dist/src/text/index.d.ts.map +1 -0
- package/dist/src/text/types.d.ts +21 -0
- package/dist/src/text/types.d.ts.map +1 -0
- package/dist/src/text/types.gen.d.ts +13 -0
- package/dist/src/text/types.gen.d.ts.map +1 -0
- package/dist/src/throttle/index.d.ts +2 -0
- package/dist/src/throttle/index.d.ts.map +1 -0
- package/dist/src/throttle/throttle.d.ts +3 -0
- package/dist/src/throttle/throttle.d.ts.map +1 -0
- package/dist/src/throttle/throttle.spec.d.ts +2 -0
- package/dist/src/throttle/throttle.spec.d.ts.map +1 -0
- package/dist/src/zod/parse.d.ts.map +1 -1
- package/dist/x.cjs +9 -9
- package/dist/x.js +1469 -1346
- package/package.json +11 -11
- package/src/array/nullable.ts +1 -4
- package/src/caseconv/caseconv.spec.ts +71 -0
- package/src/caseconv/caseconv.ts +15 -2
- package/src/compare/compare.spec.ts +115 -0
- package/src/compare/compare.ts +29 -0
- package/src/debounce/debounce.spec.ts +258 -24
- package/src/debounce/debounce.ts +49 -30
- package/src/deep/copy.spec.ts +13 -0
- package/src/deep/difference.ts +1 -1
- package/src/destructor/destructor.ts +2 -0
- package/src/errors/errors.spec.ts +30 -0
- package/src/errors/errors.ts +29 -17
- package/src/index.ts +4 -1
- package/src/notation/external.ts +11 -0
- package/src/notation/index.ts +1 -1
- package/src/notation/notation.spec.ts +260 -2
- package/src/notation/notation.ts +25 -7
- package/src/notation/types.gen.ts +16 -0
- package/src/primitive/primitive.spec.ts +58 -5
- package/src/primitive/primitive.ts +22 -0
- package/src/record/record.spec.ts +26 -0
- package/src/record/record.ts +20 -5
- package/src/require/index.ts +10 -0
- package/src/require/require.ts +10 -0
- package/src/spatial/base.ts +1 -93
- package/src/spatial/bounds/bounds.ts +10 -10
- package/src/spatial/box/box.ts +5 -5
- package/src/spatial/direction/direction.ts +16 -17
- package/src/spatial/external.ts +1 -2
- package/src/spatial/location/location.ts +19 -17
- package/src/spatial/scale/scale.ts +2 -2
- package/src/spatial/sticky/sticky.spec.ts +2 -2
- package/src/spatial/sticky/sticky.ts +6 -13
- package/src/spatial/types.gen.ts +140 -0
- package/src/spatial/xy/xy.ts +7 -7
- package/src/status/status.spec.ts +65 -0
- package/src/status/status.ts +20 -0
- package/src/telem/external.ts +8 -0
- package/src/telem/series.spec.ts +183 -0
- package/src/telem/series.ts +54 -16
- package/src/telem/telem.spec.ts +128 -9
- package/src/telem/telem.ts +91 -79
- package/src/telem/types.gen.ts +28 -0
- package/src/text/external.ts +11 -0
- package/src/text/index.ts +10 -0
- package/src/text/types.gen.ts +16 -0
- package/src/text/types.ts +37 -0
- package/src/{worker → throttle}/index.ts +1 -1
- package/src/throttle/throttle.spec.ts +147 -0
- package/src/throttle/throttle.ts +44 -0
- package/src/zod/parse.ts +2 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/src/spatial/spatial.d.ts +0 -3
- package/dist/src/spatial/spatial.d.ts.map +0 -1
- package/dist/src/worker/index.d.ts +0 -2
- package/dist/src/worker/index.d.ts.map +0 -1
- package/dist/src/worker/worker.d.ts +0 -33
- package/dist/src/worker/worker.d.ts.map +0 -1
- package/dist/src/worker/worker.spec.d.ts +0 -2
- package/dist/src/worker/worker.spec.d.ts.map +0 -1
- package/src/spatial/spatial.ts +0 -44
- package/src/worker/worker.spec.ts +0 -41
- package/src/worker/worker.ts +0 -86
|
@@ -54,13 +54,13 @@ describe("primitive", () => {
|
|
|
54
54
|
|
|
55
55
|
describe("isStringer", () => {
|
|
56
56
|
it("should return true for a stringer", () => {
|
|
57
|
-
expect(primitive.isStringer(new ExampleStringer("cat"))).
|
|
57
|
+
expect(primitive.isStringer(new ExampleStringer("cat"))).toBe(true);
|
|
58
58
|
});
|
|
59
59
|
it("should return false for a non-stringer", () => {
|
|
60
|
-
expect(primitive.isStringer(0)).
|
|
60
|
+
expect(primitive.isStringer(0)).toBe(false);
|
|
61
61
|
});
|
|
62
62
|
it("should return false for null", () => {
|
|
63
|
-
expect(primitive.isStringer(null)).
|
|
63
|
+
expect(primitive.isStringer(null)).toBe(false);
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -178,12 +178,65 @@ describe("primitive", () => {
|
|
|
178
178
|
|
|
179
179
|
describe("isCrudeValueExtension", () => {
|
|
180
180
|
it("should return true for a CrudeValueExtension", () => {
|
|
181
|
-
expect(primitive.isCrudeValueExtension({ value: 12n })).
|
|
181
|
+
expect(primitive.isCrudeValueExtension({ value: 12n })).toBe(true);
|
|
182
182
|
});
|
|
183
183
|
it("should return false for a non-CrudeValueExtension", () => {
|
|
184
|
-
expect(primitive.isCrudeValueExtension(12n)).
|
|
184
|
+
expect(primitive.isCrudeValueExtension(12n)).toBe(false);
|
|
185
185
|
});
|
|
186
186
|
});
|
|
187
187
|
});
|
|
188
188
|
});
|
|
189
|
+
|
|
190
|
+
describe("isHashable", () => {
|
|
191
|
+
class HashableThing implements primitive.Hashable {
|
|
192
|
+
constructor(private readonly v: string) {}
|
|
193
|
+
hash(): string {
|
|
194
|
+
return this.v;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
it("returns true for an object with a hash() method", () => {
|
|
199
|
+
expect(primitive.isHashable(new HashableThing("x"))).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("returns true for a plain object literal with a hash function", () => {
|
|
203
|
+
expect(primitive.isHashable({ hash: () => "x" })).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("narrows the type for downstream calls", () => {
|
|
207
|
+
const v: unknown = new HashableThing("abc");
|
|
208
|
+
if (primitive.isHashable(v)) expect(v.hash()).toEqual("abc");
|
|
209
|
+
else throw new Error("expected isHashable to narrow");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("returns false for null", () => {
|
|
213
|
+
expect(primitive.isHashable(null)).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("returns false for undefined", () => {
|
|
217
|
+
expect(primitive.isHashable(undefined)).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("returns false for primitives", () => {
|
|
221
|
+
expect(primitive.isHashable("x")).toBe(false);
|
|
222
|
+
expect(primitive.isHashable(42)).toBe(false);
|
|
223
|
+
expect(primitive.isHashable(42n)).toBe(false);
|
|
224
|
+
expect(primitive.isHashable(true)).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("returns false for plain objects without a hash function", () => {
|
|
228
|
+
expect(primitive.isHashable({})).toBe(false);
|
|
229
|
+
expect(primitive.isHashable({ key: "x" })).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("returns false when hash is not a function", () => {
|
|
233
|
+
expect(primitive.isHashable({ hash: "not a function" })).toBe(false);
|
|
234
|
+
expect(primitive.isHashable({ hash: 42 })).toBe(false);
|
|
235
|
+
expect(primitive.isHashable({ hash: null })).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("returns false for arrays", () => {
|
|
239
|
+
expect(primitive.isHashable([1, 2, 3])).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
189
242
|
});
|
|
@@ -61,6 +61,28 @@ export interface Stringer {
|
|
|
61
61
|
export const isStringer = (value: unknown): boolean =>
|
|
62
62
|
value != null && typeof value === "object" && "toString" in value;
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Hashable is a duck-typed protocol for class instances that can produce a stable,
|
|
66
|
+
* canonical string representation of themselves. Implementers commit to: (1) the
|
|
67
|
+
* returned string uniquely identifies the value's logical content, and (2) two
|
|
68
|
+
* instances representing the same logical value return equal strings.
|
|
69
|
+
*
|
|
70
|
+
* Used by cache-key derivation paths (e.g. flux queryCache) so non-primitive query
|
|
71
|
+
* fields hash to a stable identifier rather than relying on enumerable own-property
|
|
72
|
+
* iteration, which is fragile for class instances.
|
|
73
|
+
*/
|
|
74
|
+
export interface Hashable {
|
|
75
|
+
/** @returns a stable, canonical string representation of the value. */
|
|
76
|
+
hash: () => string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @returns true if the value implements primitive.Hashable, otherwise returns false. */
|
|
80
|
+
export const isHashable = (value: unknown): value is Hashable =>
|
|
81
|
+
value != null &&
|
|
82
|
+
typeof value === "object" &&
|
|
83
|
+
"hash" in value &&
|
|
84
|
+
typeof value.hash === "function";
|
|
85
|
+
|
|
64
86
|
/**
|
|
65
87
|
* Type representing zero values for each primitive type
|
|
66
88
|
*/
|
|
@@ -353,6 +353,32 @@ describe("record", () => {
|
|
|
353
353
|
const obj = { nested: { key: "value" }, arr: [1, 2, 3] };
|
|
354
354
|
expect(record.nullishToEmpty().parse(obj)).toEqual(obj);
|
|
355
355
|
});
|
|
356
|
+
|
|
357
|
+
describe("with typed key and value schemas", () => {
|
|
358
|
+
const schema = record.nullishToEmpty(z.string(), z.number());
|
|
359
|
+
|
|
360
|
+
it("should coerce null to empty object", () => {
|
|
361
|
+
expect(schema.parse(null)).toEqual({});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("should coerce undefined to empty object", () => {
|
|
365
|
+
expect(schema.parse(undefined)).toEqual({});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should pass through populated record matching the value schema", () => {
|
|
369
|
+
expect(schema.parse({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should reject values that don't match the value schema", () => {
|
|
373
|
+
expect(() => schema.parse({ a: "not a number" })).toThrow();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("should preserve the inferred typed shape", () => {
|
|
377
|
+
const parsed = schema.parse({ a: 1 });
|
|
378
|
+
const value: number = parsed.a;
|
|
379
|
+
expect(value).toBe(1);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
356
382
|
});
|
|
357
383
|
|
|
358
384
|
describe("omit", () => {
|
package/src/record/record.ts
CHANGED
|
@@ -141,6 +141,14 @@ export const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> =>
|
|
|
141
141
|
return result;
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
+
export interface NullishToEmpty {
|
|
145
|
+
(): z.ZodType<Unknown>;
|
|
146
|
+
<K extends z.ZodType<Key>, V extends z.ZodType>(
|
|
147
|
+
key: K,
|
|
148
|
+
value: V,
|
|
149
|
+
): z.ZodType<Record<z.infer<K>, z.infer<V>>>;
|
|
150
|
+
}
|
|
151
|
+
|
|
144
152
|
/**
|
|
145
153
|
* For required JSON/record fields: coerces null/undefined to empty object {}.
|
|
146
154
|
* Use when the record must always be present and iterable.
|
|
@@ -149,9 +157,16 @@ export const omit = <T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> =>
|
|
|
149
157
|
* - undefined → {}
|
|
150
158
|
* - {} → {}
|
|
151
159
|
* - {data} → {data}
|
|
160
|
+
*
|
|
161
|
+
* Without arguments the value type is unknown. Pass key and value zod schemas
|
|
162
|
+
* to type the resulting record (e.g. for `map<string, Color>` fields).
|
|
152
163
|
*/
|
|
153
|
-
export const nullishToEmpty = (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
export const nullishToEmpty = ((key?: z.ZodType, value?: z.ZodType) => {
|
|
165
|
+
if (key === undefined || value === undefined)
|
|
166
|
+
return z
|
|
167
|
+
.union([z.null().transform<Unknown>(() => ({})), unknownZ()])
|
|
168
|
+
.default(() => ({}));
|
|
169
|
+
return z
|
|
170
|
+
.union([z.null().transform(() => ({})), z.record(key as z.ZodType<Key>, value)])
|
|
171
|
+
.default(() => ({}));
|
|
172
|
+
}) as NullishToEmpty;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2026 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export * as require from "@/require/require";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2026 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export type Require<T, K extends keyof T> = Pick<Required<T>, K> & Omit<T, K>;
|
package/src/spatial/base.ts
CHANGED
|
@@ -9,99 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
export * from "@/spatial/types.gen";
|
|
13
13
|
|
|
14
14
|
export const numberCouple = z.tuple([z.number(), z.number()]);
|
|
15
15
|
export type NumberCouple<T extends number | bigint = number> = [T, T];
|
|
16
|
-
|
|
17
|
-
// Direction
|
|
18
|
-
|
|
19
|
-
export const DIRECTIONS = ["x", "y"] as const;
|
|
20
|
-
export const directionZ = z.enum(DIRECTIONS);
|
|
21
|
-
export type Direction = z.infer<typeof directionZ>;
|
|
22
|
-
|
|
23
|
-
// Location
|
|
24
|
-
|
|
25
|
-
export const OUTER_LOCATIONS = ["top", "right", "bottom", "left"] as const;
|
|
26
|
-
export const outerLocationZ = z.enum(OUTER_LOCATIONS);
|
|
27
|
-
export type OuterLocation = z.infer<typeof outerLocationZ>;
|
|
28
|
-
|
|
29
|
-
export const X_LOCATIONS = ["left", "right"] as const;
|
|
30
|
-
export const xLocationZ = z.enum(X_LOCATIONS);
|
|
31
|
-
export type XLocation = z.infer<typeof xLocationZ>;
|
|
32
|
-
|
|
33
|
-
export const Y_LOCATIONS = ["top", "bottom"] as const;
|
|
34
|
-
export const yLocationZ = z.enum(Y_LOCATIONS);
|
|
35
|
-
export type YLocation = z.infer<typeof yLocationZ>;
|
|
36
|
-
|
|
37
|
-
export const CENTER_LOCATIONS = ["center"] as const;
|
|
38
|
-
export const centerLocationZ = z.enum(CENTER_LOCATIONS);
|
|
39
|
-
export type CenterLocation = z.infer<typeof centerLocationZ>;
|
|
40
|
-
|
|
41
|
-
export const LOCATIONS = ["top", "right", "bottom", "left", "center"] as const;
|
|
42
|
-
export const locationZ = z.enum(LOCATIONS);
|
|
43
|
-
export type Location = z.infer<typeof locationZ>;
|
|
44
|
-
|
|
45
|
-
// Alignment
|
|
46
|
-
|
|
47
|
-
export const ALIGNMENTS = ["start", "center", "end"] as const;
|
|
48
|
-
export const alignmentZ = z.enum(ALIGNMENTS);
|
|
49
|
-
export type Alignment = z.infer<typeof alignmentZ>;
|
|
50
|
-
|
|
51
|
-
// Order
|
|
52
|
-
|
|
53
|
-
export const ORDERS = ["first", "last"] as const;
|
|
54
|
-
export const orderZ = z.enum(ORDERS);
|
|
55
|
-
export type Order = z.infer<typeof orderZ>;
|
|
56
|
-
|
|
57
|
-
// XY
|
|
58
|
-
|
|
59
|
-
export const clientXyZ = z.object({ clientX: z.number(), clientY: z.number() });
|
|
60
|
-
export type ClientXY = z.infer<typeof clientXyZ>;
|
|
61
|
-
|
|
62
|
-
// Dimensions
|
|
63
|
-
|
|
64
|
-
export const dimensionsZ = z.object({ width: z.number(), height: z.number() });
|
|
65
|
-
export type Dimensions = z.infer<typeof dimensionsZ>;
|
|
66
|
-
|
|
67
|
-
export const signedDimensionsZ = z.object({
|
|
68
|
-
signedWidth: z.number(),
|
|
69
|
-
signedHeight: z.number(),
|
|
70
|
-
});
|
|
71
|
-
export type SignedDimensions = z.infer<typeof signedDimensionsZ>;
|
|
72
|
-
|
|
73
|
-
export type Dimension = "width" | "height";
|
|
74
|
-
export type SignedDimension = "signedWidth" | "signedHeight";
|
|
75
|
-
|
|
76
|
-
// Bounds
|
|
77
|
-
|
|
78
|
-
export const boundsZ = z.object({ lower: z.number(), upper: z.number() });
|
|
79
|
-
|
|
80
|
-
// Generic bounds interface (supports bigint)
|
|
81
|
-
export interface Bounds<T extends number | bigint = number> {
|
|
82
|
-
lower: T;
|
|
83
|
-
upper: T;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export type CrudeBounds<T extends number | bigint = number> =
|
|
87
|
-
| Bounds<T>
|
|
88
|
-
| NumberCouple<T>;
|
|
89
|
-
|
|
90
|
-
// Derived/complex types
|
|
91
|
-
|
|
92
|
-
export const crudeDirection = z.enum([
|
|
93
|
-
"x",
|
|
94
|
-
"y",
|
|
95
|
-
...OUTER_LOCATIONS,
|
|
96
|
-
...CENTER_LOCATIONS,
|
|
97
|
-
]);
|
|
98
|
-
export type CrudeDirection = z.infer<typeof crudeDirection>;
|
|
99
|
-
export type CrudeXDirection = "x" | "left" | "right";
|
|
100
|
-
export type CrudeYDirection = "y" | "top" | "bottom";
|
|
101
|
-
export type AngularDirection = "clockwise" | "counterclockwise";
|
|
102
|
-
export const crudeLocation = z.union([
|
|
103
|
-
directionZ,
|
|
104
|
-
z.enum([...OUTER_LOCATIONS, ...CENTER_LOCATIONS]),
|
|
105
|
-
z.instanceof(String),
|
|
106
|
-
]);
|
|
107
|
-
export type CrudeLocation = z.infer<typeof crudeLocation>;
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import { abs, add, equal as mathEqual, min as mathMin, sub } from "@/math/math";
|
|
11
11
|
import { type numeric } from "@/numeric";
|
|
12
|
-
import { type Bounds, boundsZ, type
|
|
12
|
+
import { type Bounds, boundsZ, type NumberCouple } from "@/spatial/base";
|
|
13
13
|
|
|
14
14
|
export { type Bounds, boundsZ };
|
|
15
15
|
|
|
16
|
-
export type Crude<T extends numeric.Value = number> =
|
|
16
|
+
export type Crude<T extends numeric.Value = number> = Bounds<T> | NumberCouple<T>;
|
|
17
17
|
|
|
18
18
|
/** Options for the `construct` function. */
|
|
19
19
|
interface ConstructOptions {
|
|
@@ -209,7 +209,7 @@ export const clamp = <T extends numeric.Value>(bounds: Crude<T>, target: T): T =
|
|
|
209
209
|
*/
|
|
210
210
|
export const contains = <T extends numeric.Value>(
|
|
211
211
|
bounds: Crude<T>,
|
|
212
|
-
target: T |
|
|
212
|
+
target: T | Crude<T>,
|
|
213
213
|
): boolean => {
|
|
214
214
|
const _bounds = construct(bounds);
|
|
215
215
|
if (typeof target === "number" || typeof target === "bigint")
|
|
@@ -594,7 +594,7 @@ export const traverse = <T extends numeric.Value = number>(
|
|
|
594
594
|
if (dir === 0) return start;
|
|
595
595
|
|
|
596
596
|
let remainingDist = dist;
|
|
597
|
-
let currentPosition = start
|
|
597
|
+
let currentPosition = start;
|
|
598
598
|
|
|
599
599
|
while (mathEqual(remainingDist, 0) === false) {
|
|
600
600
|
// Find the bound we're currently in or adjacent to
|
|
@@ -607,15 +607,15 @@ export const traverse = <T extends numeric.Value = number>(
|
|
|
607
607
|
const b = _bounds[index];
|
|
608
608
|
let distanceInBound: T;
|
|
609
609
|
if (dir > 0) distanceInBound = sub(b.upper, currentPosition);
|
|
610
|
-
else distanceInBound = sub(currentPosition, b.lower)
|
|
610
|
+
else distanceInBound = sub(currentPosition, b.lower);
|
|
611
611
|
|
|
612
612
|
if (distanceInBound > (0 as T)) {
|
|
613
613
|
const moveDist = mathMin(abs(remainingDist), distanceInBound);
|
|
614
|
-
currentPosition = add(currentPosition, dir > 0 ? moveDist : -moveDist)
|
|
614
|
+
currentPosition = add(currentPosition, dir > 0 ? moveDist : -moveDist);
|
|
615
615
|
remainingDist = sub<T>(remainingDist, dir > 0 ? moveDist : -moveDist);
|
|
616
616
|
|
|
617
617
|
// If we've exhausted the distance, return the current position
|
|
618
|
-
if (mathEqual(remainingDist, 0)) return currentPosition
|
|
618
|
+
if (mathEqual(remainingDist, 0)) return currentPosition;
|
|
619
619
|
continue;
|
|
620
620
|
}
|
|
621
621
|
}
|
|
@@ -626,17 +626,17 @@ export const traverse = <T extends numeric.Value = number>(
|
|
|
626
626
|
const nextBounds = _bounds.filter((b) => b.lower > currentPosition);
|
|
627
627
|
if (nextBounds.length > 0) currentPosition = nextBounds[0].lower;
|
|
628
628
|
// No more bounds in this direction
|
|
629
|
-
else return currentPosition
|
|
629
|
+
else return currentPosition;
|
|
630
630
|
} else {
|
|
631
631
|
// Move to the previous bound's upper value
|
|
632
632
|
const prevBounds = _bounds.filter((b) => b.upper < currentPosition);
|
|
633
633
|
if (prevBounds.length > 0)
|
|
634
634
|
currentPosition = prevBounds[prevBounds.length - 1].upper;
|
|
635
635
|
// No more bounds in this direction
|
|
636
|
-
else return currentPosition
|
|
636
|
+
else return currentPosition;
|
|
637
637
|
}
|
|
638
638
|
}
|
|
639
|
-
return currentPosition
|
|
639
|
+
return currentPosition;
|
|
640
640
|
};
|
|
641
641
|
|
|
642
642
|
/**
|
package/src/spatial/box/box.ts
CHANGED
|
@@ -32,7 +32,7 @@ export const domRect = z.object({
|
|
|
32
32
|
export const box = z.object({
|
|
33
33
|
one: xy.xyZ,
|
|
34
34
|
two: xy.xyZ,
|
|
35
|
-
root: location.
|
|
35
|
+
root: location.cornerZ,
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
export type Box = z.infer<typeof box>;
|
|
@@ -50,7 +50,7 @@ export const ZERO = { one: xy.ZERO, two: xy.ZERO, root: location.TOP_LEFT };
|
|
|
50
50
|
*/
|
|
51
51
|
export const DECIMAL = { one: xy.ZERO, two: xy.ONE, root: location.BOTTOM_LEFT };
|
|
52
52
|
|
|
53
|
-
export const copy = (b: Box, root?: location.
|
|
53
|
+
export const copy = (b: Box, root?: location.Corner): Box => ({
|
|
54
54
|
one: b.one,
|
|
55
55
|
two: b.two,
|
|
56
56
|
root: root ?? b.root,
|
|
@@ -71,7 +71,7 @@ export const construct = (
|
|
|
71
71
|
second?: number | xy.XY | dimensions.Dimensions | dimensions.Signed,
|
|
72
72
|
width: number = 0,
|
|
73
73
|
height: number = 0,
|
|
74
|
-
coordinateRoot?: location.
|
|
74
|
+
coordinateRoot?: location.Corner,
|
|
75
75
|
): Box => {
|
|
76
76
|
const b: Box = {
|
|
77
77
|
one: { ...xy.ZERO },
|
|
@@ -339,7 +339,7 @@ export const yBounds = (b: Crude): bounds.Bounds => {
|
|
|
339
339
|
return { lower: b_.one.y, upper: b_.two.y };
|
|
340
340
|
};
|
|
341
341
|
|
|
342
|
-
export const reRoot = (b: Box, corner: location.
|
|
342
|
+
export const reRoot = (b: Box, corner: location.Corner): Box => copy(b, corner);
|
|
343
343
|
|
|
344
344
|
export const edgePoints = (b: Crude, loc: location.Location): [xy.XY, xy.XY] => {
|
|
345
345
|
const b_ = construct(b);
|
|
@@ -461,7 +461,7 @@ export const constructWithAlternateRoot = (
|
|
|
461
461
|
width: number,
|
|
462
462
|
height: number,
|
|
463
463
|
currRoot: location.XY,
|
|
464
|
-
newRoot: location.
|
|
464
|
+
newRoot: location.Corner,
|
|
465
465
|
): Box => {
|
|
466
466
|
const first = { x, y };
|
|
467
467
|
const second = { x: x + width, y: y + height };
|
|
@@ -7,30 +7,29 @@
|
|
|
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 z from "zod";
|
|
11
|
+
|
|
10
12
|
import {
|
|
11
13
|
type AngularDirection,
|
|
12
|
-
|
|
13
|
-
crudeDirection,
|
|
14
|
-
type CrudeXDirection,
|
|
15
|
-
type CrudeYDirection,
|
|
14
|
+
CENTER_LOCATIONS,
|
|
16
15
|
type Dimension,
|
|
17
16
|
type Direction,
|
|
18
17
|
DIRECTIONS,
|
|
19
18
|
directionZ,
|
|
20
19
|
type Location,
|
|
20
|
+
OUTER_LOCATIONS,
|
|
21
21
|
type SignedDimension,
|
|
22
22
|
Y_LOCATIONS,
|
|
23
23
|
type YLocation,
|
|
24
|
-
} from "@/spatial/
|
|
24
|
+
} from "@/spatial/types.gen";
|
|
25
25
|
|
|
26
26
|
export { type Direction, DIRECTIONS, directionZ };
|
|
27
27
|
|
|
28
|
-
export const crude = crudeDirection;
|
|
29
|
-
|
|
30
|
-
export type Crude = CrudeDirection;
|
|
31
|
-
export type CrudeX = CrudeXDirection;
|
|
32
|
-
export type CrudeY = CrudeYDirection;
|
|
33
28
|
export type Angular = AngularDirection;
|
|
29
|
+
export const crudeZ = z.enum(["x", "y", ...OUTER_LOCATIONS, ...CENTER_LOCATIONS]);
|
|
30
|
+
export type Crude = z.infer<typeof crudeZ>;
|
|
31
|
+
export type CrudeX = "x" | "left" | "right";
|
|
32
|
+
export type CrudeY = "y" | "top" | "bottom";
|
|
34
33
|
|
|
35
34
|
export const construct = (c: Crude): Direction => {
|
|
36
35
|
if (DIRECTIONS.includes(c as Direction)) return c as Direction;
|
|
@@ -38,24 +37,24 @@ export const construct = (c: Crude): Direction => {
|
|
|
38
37
|
return "x";
|
|
39
38
|
};
|
|
40
39
|
|
|
41
|
-
export const swap = (direction:
|
|
40
|
+
export const swap = (direction: Crude): Direction =>
|
|
42
41
|
construct(direction) === "x" ? "y" : "x";
|
|
43
42
|
|
|
44
|
-
export const dimension = (direction:
|
|
43
|
+
export const dimension = (direction: Crude): Dimension =>
|
|
45
44
|
construct(direction) === "x" ? "width" : "height";
|
|
46
45
|
|
|
47
|
-
export const location = (direction:
|
|
46
|
+
export const location = (direction: Crude): Location =>
|
|
48
47
|
construct(direction) === "x" ? "left" : "top";
|
|
49
48
|
|
|
50
|
-
export const isDirection = (c: unknown): c is Direction =>
|
|
49
|
+
export const isDirection = (c: unknown): c is Direction => crudeZ.safeParse(c).success;
|
|
51
50
|
|
|
52
|
-
export const signedDimension = (direction:
|
|
51
|
+
export const signedDimension = (direction: Crude): SignedDimension =>
|
|
53
52
|
construct(direction) === "x" ? "signedWidth" : "signedHeight";
|
|
54
53
|
|
|
55
|
-
export const isX = (direction:
|
|
54
|
+
export const isX = (direction: Crude): direction is CrudeX => {
|
|
56
55
|
if (direction === "center") return false;
|
|
57
56
|
return construct(direction) === "x";
|
|
58
57
|
};
|
|
59
58
|
|
|
60
|
-
export const isY = (direction:
|
|
59
|
+
export const isY = (direction: Crude): direction is CrudeY =>
|
|
61
60
|
construct(direction) === "y";
|
package/src/spatial/external.ts
CHANGED
|
@@ -13,7 +13,6 @@ export * from "@/spatial/dimensions";
|
|
|
13
13
|
export * from "@/spatial/direction";
|
|
14
14
|
export * from "@/spatial/location";
|
|
15
15
|
export * from "@/spatial/scale";
|
|
16
|
-
export * as spatial from "@/spatial/spatial";
|
|
17
16
|
export * from "@/spatial/sticky";
|
|
18
|
-
export * from "@/spatial/types.gen";
|
|
17
|
+
export * as spatial from "@/spatial/types.gen";
|
|
19
18
|
export * from "@/spatial/xy";
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
CENTER_LOCATIONS,
|
|
16
16
|
type CenterLocation,
|
|
17
17
|
centerLocationZ,
|
|
18
|
-
type
|
|
19
|
-
|
|
18
|
+
type CornerLocation,
|
|
19
|
+
cornerLocationZ,
|
|
20
20
|
type Direction,
|
|
21
21
|
DIRECTIONS,
|
|
22
|
+
directionZ,
|
|
22
23
|
type Location,
|
|
23
24
|
LOCATIONS,
|
|
24
25
|
locationZ,
|
|
@@ -43,15 +44,18 @@ export {
|
|
|
43
44
|
Y_LOCATIONS,
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
export const
|
|
47
|
-
export const
|
|
48
|
-
export const
|
|
47
|
+
export const xZ = xLocationZ;
|
|
48
|
+
export const yZ = yLocationZ;
|
|
49
|
+
export const centerZ = centerLocationZ;
|
|
49
50
|
export const outerZ = outerLocationZ;
|
|
51
|
+
export const cornerZ = cornerLocationZ;
|
|
50
52
|
|
|
51
53
|
export type X = XLocation;
|
|
52
54
|
export type Y = YLocation;
|
|
53
55
|
export type Outer = OuterLocation;
|
|
54
56
|
export type Center = CenterLocation;
|
|
57
|
+
export type Corner = CornerLocation;
|
|
58
|
+
export type CornerString = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
55
59
|
|
|
56
60
|
const SWAPPED: Record<Location, Location> = {
|
|
57
61
|
top: "bottom",
|
|
@@ -67,10 +71,12 @@ const ROTATIONS: Record<Outer, Record<AngularDirection, Outer>> = {
|
|
|
67
71
|
bottom: { clockwise: "right", counterclockwise: "left" },
|
|
68
72
|
left: { clockwise: "bottom", counterclockwise: "top" },
|
|
69
73
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
export const crudeZ = z.union([
|
|
75
|
+
directionZ,
|
|
76
|
+
z.enum([...OUTER_LOCATIONS, ...CENTER_LOCATIONS]),
|
|
77
|
+
z.instanceof(String),
|
|
78
|
+
]);
|
|
79
|
+
export type Crude = z.infer<typeof crudeZ>;
|
|
74
80
|
|
|
75
81
|
export const construct = (cl: Crude): Location => {
|
|
76
82
|
if (cl instanceof String) return cl as Location;
|
|
@@ -93,16 +99,12 @@ export const xy = z.object({
|
|
|
93
99
|
x: xLocationZ.or(centerLocationZ),
|
|
94
100
|
y: yLocationZ.or(centerLocationZ),
|
|
95
101
|
});
|
|
96
|
-
export const corner = z.object({ x: xLocationZ, y: yLocationZ });
|
|
97
|
-
|
|
98
102
|
export type XY = z.infer<typeof xy>;
|
|
99
|
-
export type CornerXY = z.infer<typeof corner>;
|
|
100
|
-
export type CornerXYString = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
101
103
|
|
|
102
|
-
export const TOP_LEFT:
|
|
103
|
-
export const TOP_RIGHT:
|
|
104
|
-
export const BOTTOM_LEFT:
|
|
105
|
-
export const BOTTOM_RIGHT:
|
|
104
|
+
export const TOP_LEFT: Corner = Object.freeze({ x: "left", y: "top" });
|
|
105
|
+
export const TOP_RIGHT: Corner = Object.freeze({ x: "right", y: "top" });
|
|
106
|
+
export const BOTTOM_LEFT: Corner = Object.freeze({ x: "left", y: "bottom" });
|
|
107
|
+
export const BOTTOM_RIGHT: Corner = Object.freeze({ x: "right", y: "bottom" });
|
|
106
108
|
export const CENTER: XY = Object.freeze({ x: "center", y: "center" });
|
|
107
109
|
export const TOP_CENTER: XY = Object.freeze({ x: "center", y: "top" });
|
|
108
110
|
export const BOTTOM_CENTER: XY = Object.freeze({ x: "center", y: "bottom" });
|
|
@@ -417,12 +417,12 @@ export class Scale<T extends numeric.Value = number> {
|
|
|
417
417
|
export class XY {
|
|
418
418
|
x: Scale<number>;
|
|
419
419
|
y: Scale<number>;
|
|
420
|
-
currRoot: location.
|
|
420
|
+
currRoot: location.Corner | null;
|
|
421
421
|
|
|
422
422
|
constructor(
|
|
423
423
|
x: Scale<number> = new Scale<number>(),
|
|
424
424
|
y: Scale<number> = new Scale<number>(),
|
|
425
|
-
root: location.
|
|
425
|
+
root: location.Corner | null = null,
|
|
426
426
|
) {
|
|
427
427
|
this.x = x;
|
|
428
428
|
this.y = y;
|
|
@@ -134,7 +134,7 @@ describe("sticky", () => {
|
|
|
134
134
|
];
|
|
135
135
|
SPECS.forEach(({ value, valid }, i) => {
|
|
136
136
|
test(`xy schema ${i}`, () => {
|
|
137
|
-
const result = sticky.
|
|
137
|
+
const result = sticky.xyZ.safeParse(value);
|
|
138
138
|
expect(result.success).toBe(valid);
|
|
139
139
|
});
|
|
140
140
|
});
|
|
@@ -196,7 +196,7 @@ describe("sticky", () => {
|
|
|
196
196
|
];
|
|
197
197
|
SPECS.forEach(({ value, valid }, i) => {
|
|
198
198
|
test(`completeXY schema ${i}`, () => {
|
|
199
|
-
const result = sticky.
|
|
199
|
+
const result = sticky.completeXYZ.safeParse(value);
|
|
200
200
|
expect(result.success).toBe(valid);
|
|
201
201
|
});
|
|
202
202
|
});
|
|
@@ -7,25 +7,18 @@
|
|
|
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 z from "zod";
|
|
10
|
+
import type z from "zod";
|
|
11
11
|
|
|
12
12
|
import { box } from "@/spatial/box";
|
|
13
13
|
import { location } from "@/spatial/location";
|
|
14
|
+
import { type StickyXY, stickyXYZ } from "@/spatial/types.gen";
|
|
14
15
|
import { xy as base } from "@/spatial/xy";
|
|
15
16
|
|
|
16
|
-
export const
|
|
17
|
-
|
|
18
|
-
units: z.object({
|
|
19
|
-
x: z.enum(["px", "decimal"]),
|
|
20
|
-
y: z.enum(["px", "decimal"]),
|
|
21
|
-
}),
|
|
22
|
-
});
|
|
17
|
+
export const xyZ = stickyXYZ;
|
|
18
|
+
export type XY = StickyXY;
|
|
23
19
|
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
export const xy = completeXY.partial({ root: true, units: true });
|
|
27
|
-
|
|
28
|
-
export interface XY extends z.infer<typeof xy> {}
|
|
20
|
+
export const completeXYZ = stickyXYZ.required({ root: true, units: true });
|
|
21
|
+
export type CompleteXY = z.infer<typeof completeXYZ>;
|
|
29
22
|
|
|
30
23
|
interface ToCSSReturn extends Partial<Record<location.Outer, string>> {}
|
|
31
24
|
|