@perspective-ai/sdk-react 1.3.0 → 1.4.0-pr-25-20260306102715
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/index.cjs +224 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +225 -108
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/hooks/useAutoOpen.test.ts +110 -13
- package/src/hooks/useAutoOpen.ts +63 -4
- package/src/hooks/useFloatBubble.ts +8 -4
- package/src/hooks/usePopup.test.ts +224 -55
- package/src/hooks/usePopup.ts +93 -62
- package/src/hooks/useSlider.test.ts +274 -41
- package/src/hooks/useSlider.ts +93 -62
- package/src/hooks/useStableValue.test.ts +108 -0
- package/src/hooks/useStableValue.ts +34 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderHook } from "@testing-library/react";
|
|
3
|
+
import { useStableValue } from "./useStableValue";
|
|
4
|
+
|
|
5
|
+
describe("useStableValue", () => {
|
|
6
|
+
it("returns the same reference for deeply equal objects", () => {
|
|
7
|
+
const { result, rerender } = renderHook(
|
|
8
|
+
({ value }) => useStableValue(value),
|
|
9
|
+
{ initialProps: { value: { a: 1, b: "test" } } }
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const first = result.current;
|
|
13
|
+
|
|
14
|
+
rerender({ value: { a: 1, b: "test" } });
|
|
15
|
+
|
|
16
|
+
expect(result.current).toBe(first);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("returns new reference when value changes", () => {
|
|
20
|
+
const { result, rerender } = renderHook(
|
|
21
|
+
({ value }) => useStableValue(value),
|
|
22
|
+
{ initialProps: { value: { a: 1 } } }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const first = result.current;
|
|
26
|
+
|
|
27
|
+
rerender({ value: { a: 2 } });
|
|
28
|
+
|
|
29
|
+
expect(result.current).not.toBe(first);
|
|
30
|
+
expect(result.current).toEqual({ a: 2 });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("ignores object key order differences", () => {
|
|
34
|
+
const { result, rerender } = renderHook(
|
|
35
|
+
({ value }) => useStableValue(value),
|
|
36
|
+
{
|
|
37
|
+
initialProps: {
|
|
38
|
+
value: {
|
|
39
|
+
dark: { secondary: "#222", primary: "#111" },
|
|
40
|
+
light: { secondary: "#eee", primary: "#fff" },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const first = result.current;
|
|
47
|
+
|
|
48
|
+
rerender({
|
|
49
|
+
value: {
|
|
50
|
+
light: { primary: "#fff", secondary: "#eee" },
|
|
51
|
+
dark: { primary: "#111", secondary: "#222" },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(result.current).toBe(first);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("handles undefined values", () => {
|
|
59
|
+
const { result, rerender } = renderHook(
|
|
60
|
+
({ value }) => useStableValue(value),
|
|
61
|
+
{
|
|
62
|
+
initialProps: {
|
|
63
|
+
value: undefined as Record<string, unknown> | undefined,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(result.current).toBeUndefined();
|
|
69
|
+
|
|
70
|
+
rerender({ value: undefined });
|
|
71
|
+
|
|
72
|
+
expect(result.current).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("handles primitive values", () => {
|
|
76
|
+
const { result, rerender } = renderHook(
|
|
77
|
+
({ value }) => useStableValue(value),
|
|
78
|
+
{ initialProps: { value: "hello" } }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const first = result.current;
|
|
82
|
+
|
|
83
|
+
rerender({ value: "hello" });
|
|
84
|
+
expect(result.current).toBe(first);
|
|
85
|
+
|
|
86
|
+
rerender({ value: "world" });
|
|
87
|
+
expect(result.current).toBe("world");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("handles arrays with nested objects", () => {
|
|
91
|
+
const { result, rerender } = renderHook(
|
|
92
|
+
({ value }) => useStableValue(value),
|
|
93
|
+
{
|
|
94
|
+
initialProps: {
|
|
95
|
+
value: [{ b: 2, a: 1 }, { items: ["x", "y"] }],
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const first = result.current;
|
|
101
|
+
|
|
102
|
+
rerender({
|
|
103
|
+
value: [{ a: 1, b: 2 }, { items: ["x", "y"] }],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result.current).toBe(first);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
|
|
3
|
+
function sortObjectKeys(value: unknown): unknown {
|
|
4
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
5
|
+
return Object.keys(value as Record<string, unknown>)
|
|
6
|
+
.sort()
|
|
7
|
+
.reduce<Record<string, unknown>>((result, key) => {
|
|
8
|
+
result[key] = (value as Record<string, unknown>)[key];
|
|
9
|
+
return result;
|
|
10
|
+
}, {});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns a referentially stable version of a value.
|
|
18
|
+
* Uses JSON serialization to detect deep equality changes.
|
|
19
|
+
* Useful for object/array deps in useEffect that are recreated each render.
|
|
20
|
+
*/
|
|
21
|
+
export function useStableValue<T>(value: T): T {
|
|
22
|
+
const ref = useRef(value);
|
|
23
|
+
const serialized = JSON.stringify(value, (_key, currentValue) =>
|
|
24
|
+
sortObjectKeys(currentValue)
|
|
25
|
+
);
|
|
26
|
+
const prevSerialized = useRef(serialized);
|
|
27
|
+
|
|
28
|
+
if (prevSerialized.current !== serialized) {
|
|
29
|
+
prevSerialized.current = serialized;
|
|
30
|
+
ref.current = value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return ref.current;
|
|
34
|
+
}
|