@tangle-network/ui 11.38.0 → 11.38.1
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/CHANGELOG.md +6 -0
- package/dist/lib/surface-motion.js +11 -2
- package/package.json +1 -1
- package/src/lib/surface-motion.tsx +15 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.38.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c049dc4: Bound shared surface dismissal by its scoped role deadline so missing CSS animation events cannot strand Radix cleanup or focus return. Preserve retained motion values on reopen.
|
|
8
|
+
|
|
3
9
|
## 11.38.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -4,9 +4,10 @@ import * as React from "react";
|
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
import { animate, motionValue, styleEffect } from "motion";
|
|
6
6
|
//#region src/lib/surface-motion.tsx
|
|
7
|
-
/** Radix owns state
|
|
7
|
+
/** Radix owns state and focus; visual retirement cannot wait for a CSS event. */
|
|
8
8
|
const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-state": state, ...props }, forwardedRef) => {
|
|
9
9
|
const element = React.useRef(null);
|
|
10
|
+
const [retired, setRetired] = React.useState(false);
|
|
10
11
|
const values = React.useRef(null);
|
|
11
12
|
const controls = React.useRef([]);
|
|
12
13
|
const ref = React.useCallback((node) => {
|
|
@@ -24,7 +25,10 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
24
25
|
if (!node) return;
|
|
25
26
|
const preference = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : void 0;
|
|
26
27
|
let cancelRender;
|
|
28
|
+
let retirement;
|
|
27
29
|
const run = () => {
|
|
30
|
+
clearTimeout(retirement);
|
|
31
|
+
setRetired(false);
|
|
28
32
|
const closed = state === "closed";
|
|
29
33
|
const bounds = node.getBoundingClientRect();
|
|
30
34
|
const hidden = {
|
|
@@ -46,6 +50,10 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
46
50
|
node.style.opacity = String(value.opacity);
|
|
47
51
|
node.style.transform = `translateX(${value.x}px) translateY(${value.y}px) scale(${value.scale})`;
|
|
48
52
|
};
|
|
53
|
+
if (closed) {
|
|
54
|
+
if (preference?.matches || seconds <= 0 || style.animationName === "none") setRetired(true);
|
|
55
|
+
else retirement = setTimeout(() => setRetired(true), seconds * 1e3);
|
|
56
|
+
}
|
|
49
57
|
if (preference?.matches || seconds <= 0 || style.animationName === "none") {
|
|
50
58
|
controls.current.forEach((control) => control.stop());
|
|
51
59
|
if (values.current) for (const key of [
|
|
@@ -84,6 +92,7 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
84
92
|
run();
|
|
85
93
|
preference?.addEventListener("change", run);
|
|
86
94
|
return () => {
|
|
95
|
+
clearTimeout(retirement);
|
|
87
96
|
preference?.removeEventListener("change", run);
|
|
88
97
|
cancelRender?.();
|
|
89
98
|
};
|
|
@@ -94,7 +103,7 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
94
103
|
props.style?.animation
|
|
95
104
|
]);
|
|
96
105
|
React.useEffect(() => () => controls.current.forEach((control) => control.stop()), []);
|
|
97
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
106
|
+
return state === "closed" && retired ? null : /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
98
107
|
...props,
|
|
99
108
|
"data-state": state,
|
|
100
109
|
ref
|
package/package.json
CHANGED
|
@@ -12,10 +12,11 @@ type SurfaceProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
12
12
|
"data-state"?: string;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
/** Radix owns state
|
|
15
|
+
/** Radix owns state and focus; visual retirement cannot wait for a CSS event. */
|
|
16
16
|
export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
17
17
|
({ kind, side = "right", asChild, "data-state": state, ...props }, forwardedRef) => {
|
|
18
18
|
const element = React.useRef<HTMLDivElement | null>(null);
|
|
19
|
+
const [retired, setRetired] = React.useState(false);
|
|
19
20
|
const values = React.useRef<Record<"opacity" | "scale" | "x" | "y", MotionValue<number>> | null>(null);
|
|
20
21
|
const controls = React.useRef<Array<ReturnType<typeof animate>>>([]);
|
|
21
22
|
const ref = React.useCallback<React.RefCallback<HTMLDivElement>>((node) => {
|
|
@@ -31,7 +32,10 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
31
32
|
if (!node) return;
|
|
32
33
|
const preference = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : undefined;
|
|
33
34
|
let cancelRender: (() => void) | undefined;
|
|
35
|
+
let retirement: ReturnType<typeof setTimeout> | undefined;
|
|
34
36
|
const run = () => {
|
|
37
|
+
clearTimeout(retirement);
|
|
38
|
+
setRetired(false);
|
|
35
39
|
const closed = state === "closed";
|
|
36
40
|
const bounds = node.getBoundingClientRect();
|
|
37
41
|
const hidden = {
|
|
@@ -49,6 +53,14 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
49
53
|
node.style.opacity = String(value.opacity);
|
|
50
54
|
node.style.transform = `translateX(${value.x}px) translateY(${value.y}px) scale(${value.scale})`;
|
|
51
55
|
};
|
|
56
|
+
// Radix may retain an exit until animationend. Retire this DOM node at
|
|
57
|
+
// the scoped deadline even when keyframes/events are missing or paused;
|
|
58
|
+
// its null ref lets Radix release presence, focus and modal resources.
|
|
59
|
+
// Reopen cancels retirement and keeps the same node and Motion values.
|
|
60
|
+
if (closed) {
|
|
61
|
+
if (preference?.matches || seconds <= 0 || style.animationName === "none") setRetired(true);
|
|
62
|
+
else retirement = setTimeout(() => setRetired(true), seconds * 1000);
|
|
63
|
+
}
|
|
52
64
|
// Missing styles and reduced motion settle synchronously. No semantic
|
|
53
65
|
// transition waits for a JavaScript completion callback.
|
|
54
66
|
if (preference?.matches || seconds <= 0 || style.animationName === "none") {
|
|
@@ -73,13 +85,13 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
73
85
|
};
|
|
74
86
|
run();
|
|
75
87
|
preference?.addEventListener("change", run);
|
|
76
|
-
return () => { preference?.removeEventListener("change", run); cancelRender?.(); };
|
|
88
|
+
return () => { clearTimeout(retirement); preference?.removeEventListener("change", run); cancelRender?.(); };
|
|
77
89
|
// Radix Popper temporarily suppresses animation until placement resolves.
|
|
78
90
|
// Start the same retained values when that inline override clears.
|
|
79
91
|
}, [state, kind, side, props.style?.animation]);
|
|
80
92
|
React.useEffect(() => () => controls.current.forEach(control => control.stop()), []);
|
|
81
93
|
const Component = asChild ? Slot : "div";
|
|
82
|
-
return <Component {...props} data-state={state} ref={ref} />;
|
|
94
|
+
return state === "closed" && retired ? null : <Component {...props} data-state={state} ref={ref} />;
|
|
83
95
|
},
|
|
84
96
|
);
|
|
85
97
|
SurfaceMotion.displayName = "SurfaceMotion";
|