@stasho/ds 0.16.0 → 0.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stasho/ds",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "stasho design system",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,6 +28,18 @@ const VARIANT_BG_CLASS: Record<AlertVariant, string> = {
28
28
  success: "alert-bg-success",
29
29
  };
30
30
 
31
+ // Mirrors the root's `duration-200` exit transition; the fallback timer below
32
+ // resolves a dismissal whose `transitionend` never arrives.
33
+ const EXIT_DURATION_MS = 200;
34
+ const EXIT_FALLBACK_MS = EXIT_DURATION_MS + 50;
35
+
36
+ function prefersReducedMotion(): boolean {
37
+ return (
38
+ typeof window !== "undefined" &&
39
+ window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true
40
+ );
41
+ }
42
+
31
43
  const alertVariants = cva(
32
44
  [
33
45
  "relative overflow-hidden rounded-sm border",
@@ -99,6 +111,8 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(
99
111
  const [isDismissing, setIsDismissing] = useState(false);
100
112
  const [progressActive, setProgressActive] = useState(false);
101
113
  const timerRef = useRef<ReturnType<typeof setTimeout>>(null);
114
+ const fallbackRef = useRef<ReturnType<typeof setTimeout>>(null);
115
+ const dismissedRef = useRef(false);
102
116
  const resolvedVariant: AlertVariant = variant ?? "warning";
103
117
 
104
118
  const label = VARIANT_LABELS[resolvedVariant];
@@ -110,24 +124,38 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(
110
124
  setProgressActive(true);
111
125
  });
112
126
 
113
- timerRef.current = setTimeout(() => {
114
- setIsDismissing(true);
115
- }, dismissAfter);
127
+ timerRef.current = setTimeout(startDismiss, dismissAfter);
116
128
 
117
129
  return () => {
118
130
  if (timerRef.current) clearTimeout(timerRef.current);
119
131
  };
120
132
  }, [dismissAfter, onDismiss]);
121
133
 
122
- function handleDismiss() {
134
+ useEffect(() => {
135
+ return () => {
136
+ if (fallbackRef.current) clearTimeout(fallbackRef.current);
137
+ };
138
+ }, []);
139
+
140
+ function finishDismiss() {
141
+ if (dismissedRef.current) return;
142
+ dismissedRef.current = true;
143
+ if (fallbackRef.current) clearTimeout(fallbackRef.current);
144
+ onDismiss?.();
145
+ }
146
+
147
+ function startDismiss() {
123
148
  if (timerRef.current) clearTimeout(timerRef.current);
124
149
  setIsDismissing(true);
150
+ if (prefersReducedMotion()) {
151
+ finishDismiss();
152
+ return;
153
+ }
154
+ fallbackRef.current = setTimeout(finishDismiss, EXIT_FALLBACK_MS);
125
155
  }
126
156
 
127
157
  function handleTransitionEnd() {
128
- if (isDismissing && onDismiss) {
129
- onDismiss();
130
- }
158
+ if (isDismissing) finishDismiss();
131
159
  }
132
160
 
133
161
  return (
@@ -168,7 +196,7 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(
168
196
  {onDismiss ? (
169
197
  <button
170
198
  type="button"
171
- onClick={handleDismiss}
199
+ onClick={startDismiss}
172
200
  className={cn(
173
201
  "absolute top-2 right-2 cursor-pointer",
174
202
  labelVariants({ variant }),