@multiplatform.one/forms 7.49.0 → 7.49.2

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.
Files changed (44) hide show
  1. package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +13 -2
  2. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +14 -5
  3. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  4. package/dist/cjs/shared/focusManagement.cjs +13 -11
  5. package/dist/cjs/shared/focusManagement.native.js +15 -15
  6. package/dist/cjs/shared/focusManagement.native.js.map +1 -1
  7. package/dist/cjs/shared/restoreRetries.cjs +96 -0
  8. package/dist/cjs/shared/restoreRetries.native.js +101 -0
  9. package/dist/cjs/shared/restoreRetries.native.js.map +1 -0
  10. package/dist/esm/FloatingPanel/useFloatingPanel.mjs +13 -2
  11. package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  12. package/dist/esm/FloatingPanel/useFloatingPanel.native.js +14 -5
  13. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  14. package/dist/esm/shared/focusManagement.mjs +13 -11
  15. package/dist/esm/shared/focusManagement.mjs.map +1 -1
  16. package/dist/esm/shared/focusManagement.native.js +15 -15
  17. package/dist/esm/shared/focusManagement.native.js.map +1 -1
  18. package/dist/esm/shared/restoreRetries.mjs +71 -0
  19. package/dist/esm/shared/restoreRetries.mjs.map +1 -0
  20. package/dist/esm/shared/restoreRetries.native.js +73 -0
  21. package/dist/esm/shared/restoreRetries.native.js.map +1 -0
  22. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +13 -2
  23. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  24. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +14 -5
  25. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  26. package/dist/jsx/shared/focusManagement.mjs +13 -11
  27. package/dist/jsx/shared/focusManagement.mjs.map +1 -1
  28. package/dist/jsx/shared/focusManagement.native.js +15 -15
  29. package/dist/jsx/shared/focusManagement.native.js.map +1 -1
  30. package/dist/jsx/shared/restoreRetries.mjs +71 -0
  31. package/dist/jsx/shared/restoreRetries.mjs.map +1 -0
  32. package/dist/jsx/shared/restoreRetries.native.js +101 -0
  33. package/dist/jsx/shared/restoreRetries.native.js.map +1 -0
  34. package/package.json +8 -8
  35. package/src/FloatingPanel/useFloatingPanel.frames.spec.tsx +79 -0
  36. package/src/FloatingPanel/useFloatingPanel.ts +14 -2
  37. package/src/shared/animationFrames.spec.ts +47 -0
  38. package/src/shared/focusManagement.spec.tsx +52 -3
  39. package/src/shared/focusManagement.ts +20 -25
  40. package/src/shared/restoreRetries.ts +87 -0
  41. package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
  42. package/types/shared/focusManagement.d.ts.map +1 -1
  43. package/types/shared/restoreRetries.d.ts +14 -0
  44. package/types/shared/restoreRetries.d.ts.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/forms",
3
- "version": "7.49.0",
3
+ "version": "7.49.2",
4
4
  "description": "Cross-platform form components with URL state management",
5
5
  "keywords": [
6
6
  "cross-platform",
@@ -57,10 +57,10 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "@floating-ui/react": "^0.27.20",
60
- "@multiplatform.one/platform": "^7.49.0",
61
- "@multiplatform.one/router": "^7.49.0",
62
- "@multiplatform.one/table-primitives": "^7.49.0",
63
- "@multiplatform.one/theme": "^7.49.0",
60
+ "@multiplatform.one/platform": "^7.49.2",
61
+ "@multiplatform.one/router": "^7.49.2",
62
+ "@multiplatform.one/table-primitives": "^7.49.2",
63
+ "@multiplatform.one/theme": "^7.49.2",
64
64
  "@phosphor-icons/react": "^2.1.10",
65
65
  "@tamagui/get-font-sized": "2.7.6",
66
66
  "@tamagui/get-token": "2.7.6",
@@ -79,9 +79,9 @@
79
79
  "tamagui": "2.7.6"
80
80
  },
81
81
  "devDependencies": {
82
- "@multiplatform.one/config": "7.49.0",
83
- "@multiplatform.one/storybook": "7.49.0",
84
- "@multiplatform.one/test-utils": "7.49.0",
82
+ "@multiplatform.one/config": "7.49.2",
83
+ "@multiplatform.one/storybook": "7.49.2",
84
+ "@multiplatform.one/test-utils": "7.49.2",
85
85
  "@tamagui/build": "2.7.6",
86
86
  "@tamagui/config": "2.7.6",
87
87
  "@tamagui/vite-plugin": "2.7.6",
@@ -0,0 +1,79 @@
1
+ /**
2
+ * FZ-7904: the open effect's two frames (setVisible(true)) and the scroll
3
+ * lock's frame (window.scrollTo) used to be requested and never cancelled, so
4
+ * a close or unmount inside them still showed the panel or scrolled the page,
5
+ * and after teardown they threw into a deleted window.
6
+ */
7
+ import { holdAnimationFrames } from "@multiplatform.one/test-utils/frames";
8
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
9
+ import { act, cleanup } from "@testing-library/react";
10
+ import { afterEach, describe, expect, it, vi } from "vitest";
11
+ import { useFloatingPanel } from "./useFloatingPanel";
12
+
13
+ afterEach(() => {
14
+ cleanup();
15
+ vi.restoreAllMocks();
16
+ vi.unstubAllGlobals();
17
+ });
18
+
19
+ function Harness({ open }: { open: boolean }) {
20
+ const panel = useFloatingPanel({ open, onOpenChange: () => {}, useAutoUpdate: false });
21
+ return (
22
+ <>
23
+ <div ref={panel.setReferenceRef} />
24
+ {panel.mounted && (
25
+ <div
26
+ data-testid="panel"
27
+ data-visible={String(panel.visible)}
28
+ ref={panel.refs.setFloating}
29
+ />
30
+ )}
31
+ </>
32
+ );
33
+ }
34
+
35
+ function openPanel() {
36
+ const frames = holdAnimationFrames();
37
+ const result = renderWithProviders(<Harness open={false} />);
38
+ result.rerender(<Harness open />);
39
+ expect(result.getByTestId("panel")).toBeTruthy();
40
+ return { ...result, frames };
41
+ }
42
+
43
+ function flushFrames(frames: { flush: () => void }) {
44
+ act(() => {
45
+ frames.flush();
46
+ frames.flush();
47
+ });
48
+ }
49
+
50
+ describe("useFloatingPanel frames (FZ-7904)", () => {
51
+ it("shows the panel two frames after it opens", () => {
52
+ const h = openPanel();
53
+ flushFrames(h.frames);
54
+ expect(h.getByTestId("panel").dataset.visible).toBe("true");
55
+ });
56
+
57
+ it("a close inside the open frames never shows the panel", () => {
58
+ const h = openPanel();
59
+ h.rerender(<Harness open={false} />);
60
+ flushFrames(h.frames);
61
+ expect(h.getByTestId("panel").dataset.visible).toBe("false");
62
+ });
63
+
64
+ it("a close cancels the scroll lock's frame", () => {
65
+ const scrollTo = vi.spyOn(window, "scrollTo").mockImplementation(() => {});
66
+ const h = openPanel();
67
+ h.rerender(<Harness open={false} />);
68
+ scrollTo.mockClear();
69
+ flushFrames(h.frames);
70
+ expect(scrollTo).not.toHaveBeenCalled();
71
+ });
72
+
73
+ it("an unmount leaves no frame behind", () => {
74
+ const h = openPanel();
75
+ expect(h.frames.pending).toBeGreaterThan(0);
76
+ h.unmount();
77
+ expect(h.frames.pending).toBe(0);
78
+ });
79
+ });
@@ -336,7 +336,18 @@ export function useFloatingPanel({
336
336
  if (open) {
337
337
  if (needsSetupRef.current) setupForOpen();
338
338
  needsSetupRef.current = true;
339
- requestAnimationFrame(() => requestAnimationFrame(() => setVisible(true)));
339
+ let inner = 0;
340
+ const outer = requestAnimationFrame(() => {
341
+ // The outer frame can outlive the document, and with it the global.
342
+ if (typeof requestAnimationFrame === "function") {
343
+ inner = requestAnimationFrame(() => setVisible(true));
344
+ }
345
+ });
346
+ return () => {
347
+ if (typeof cancelAnimationFrame !== "function") return;
348
+ cancelAnimationFrame(outer);
349
+ cancelAnimationFrame(inner);
350
+ };
340
351
  } else {
341
352
  setVisible(false);
342
353
  setArrowUp(false);
@@ -473,12 +484,13 @@ export function useFloatingPanel({
473
484
  };
474
485
 
475
486
  window.scrollTo(0, scrollY);
476
- requestAnimationFrame(() => window.scrollTo(0, scrollY));
487
+ const restoreScroll = requestAnimationFrame(() => window.scrollTo(0, scrollY));
477
488
 
478
489
  document.addEventListener("wheel", preventScroll, { passive: false, capture: true });
479
490
  document.addEventListener("keydown", preventKeyScroll, { capture: true });
480
491
  document.addEventListener("touchmove", preventTouchScroll, { passive: false, capture: true });
481
492
  return () => {
493
+ if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(restoreScroll);
482
494
  document.removeEventListener("wheel", preventScroll, { capture: true });
483
495
  document.removeEventListener("keydown", preventKeyScroll, { capture: true });
484
496
  document.removeEventListener("touchmove", preventTouchScroll, { capture: true });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @multiplatform.one/test-utils frames (FZ-7904). test-utils has no runner of
3
+ * its own; forms loads its setup, so the frames are pinned here.
4
+ */
5
+ import { createAnimationFrames } from "@multiplatform.one/test-utils/frames";
6
+ import { afterEach, describe, expect, it, vi } from "vitest";
7
+
8
+ afterEach(() => {
9
+ vi.useRealTimers();
10
+ });
11
+
12
+ describe("test-utils animation frames", () => {
13
+ it("runs a frame on the next timer turn with a timestamp", () => {
14
+ vi.useFakeTimers();
15
+ const frames = createAnimationFrames();
16
+ const callback = vi.fn();
17
+ frames.request(callback);
18
+ expect(callback).not.toHaveBeenCalled();
19
+ vi.advanceTimersByTime(0);
20
+ expect(callback).toHaveBeenCalledTimes(1);
21
+ expect(typeof callback.mock.calls[0][0]).toBe("number");
22
+ });
23
+
24
+ it("cancels a frame by its handle", () => {
25
+ vi.useFakeTimers();
26
+ const frames = createAnimationFrames();
27
+ const callback = vi.fn();
28
+ frames.cancel(frames.request(callback));
29
+ vi.advanceTimersByTime(10);
30
+ expect(callback).not.toHaveBeenCalled();
31
+ expect(vi.getTimerCount()).toBe(0);
32
+ });
33
+
34
+ it("close cancels pending frames and refuses new ones", () => {
35
+ vi.useFakeTimers();
36
+ const frames = createAnimationFrames();
37
+ const pending = vi.fn();
38
+ frames.request(pending);
39
+ frames.close();
40
+ const late = vi.fn();
41
+ frames.request(late);
42
+ expect(vi.getTimerCount()).toBe(0);
43
+ vi.advanceTimersByTime(10);
44
+ expect(pending).not.toHaveBeenCalled();
45
+ expect(late).not.toHaveBeenCalled();
46
+ });
47
+ });
@@ -1,20 +1,23 @@
1
- import type { ReactNode } from "react";
1
+ import { StrictMode, type ReactNode } from "react";
2
2
  import { Preset, type Animation } from "@multiplatform.one/theme";
3
3
  import { TestProviders } from "@multiplatform.one/test-utils";
4
+ import { holdAnimationFrames } from "@multiplatform.one/test-utils/frames";
4
5
  import { act, cleanup, renderHook } from "@testing-library/react";
5
6
  import { afterEach, describe, expect, it, vi } from "vitest";
6
7
  import { useReturnFocusOnClose, useSheetModalFocus } from "./focusManagement";
7
8
 
8
9
  afterEach(() => {
9
10
  cleanup();
11
+ vi.unstubAllGlobals();
10
12
  vi.useRealTimers();
11
13
  document.body.replaceChildren();
12
14
  });
13
15
 
14
16
  for (const mode of ["floating", "sheet"] as const) {
15
17
  describe(`${mode} close focus`, () => {
16
- function setup() {
18
+ function setup({ holdFrames = false, strict = false } = {}) {
17
19
  vi.useFakeTimers();
20
+ const frames = holdFrames ? holdAnimationFrames() : null;
18
21
  const trigger = document.createElement("button");
19
22
  const frame = document.createElement("div");
20
23
  const item = document.createElement("button");
@@ -25,11 +28,19 @@ for (const mode of ["floating", "sheet"] as const) {
25
28
  const skipReturnFocusRef = { current: false };
26
29
  const frameRef = { current: frame };
27
30
  let animation: Animation = "quick";
28
- const Wrapper = ({ children }: { children: ReactNode }) => (
31
+ const Providers = ({ children }: { children: ReactNode }) => (
29
32
  <TestProviders>
30
33
  <Preset overrides={{ animation }}>{children}</Preset>
31
34
  </TestProviders>
32
35
  );
36
+ const Wrapper = ({ children }: { children: ReactNode }) =>
37
+ strict ? (
38
+ <StrictMode>
39
+ <Providers>{children}</Providers>
40
+ </StrictMode>
41
+ ) : (
42
+ <Providers>{children}</Providers>
43
+ );
33
44
  const hook = renderHook(
34
45
  ({ open }) => {
35
46
  if (mode === "sheet") useSheetModalFocus(open, frameRef, skipReturnFocusRef);
@@ -39,6 +50,7 @@ for (const mode of ["floating", "sheet"] as const) {
39
50
  );
40
51
  return {
41
52
  ...hook,
53
+ frames,
42
54
  trigger,
43
55
  item,
44
56
  outside,
@@ -111,5 +123,42 @@ for (const mode of ["floating", "sheet"] as const) {
111
123
  await act(() => vi.advanceTimersByTimeAsync(400));
112
124
  expect(document.activeElement).not.toBe(h.trigger);
113
125
  });
126
+
127
+ // FZ-7904: the close retries used to drop their frame and timer ids.
128
+ it("cancels a close's retries when the overlay reopens", () => {
129
+ const h = setup({ holdFrames: true });
130
+ const frames = h.frames!.pending;
131
+ const timers = vi.getTimerCount();
132
+ h.rerender({ open: false });
133
+ h.rerender({ open: true });
134
+ expect(h.frames!.pending).toBe(frames);
135
+ expect(vi.getTimerCount()).toBe(timers);
136
+ });
137
+
138
+ it("leaves no frame or timer behind once it unmounts, and still restores", async () => {
139
+ const h = setup({ holdFrames: true });
140
+ h.trigger.blur();
141
+ h.rerender({ open: false });
142
+ h.unmount();
143
+ expect(h.frames!.pending).toBe(0);
144
+ expect(vi.getTimerCount()).toBe(0);
145
+ await Promise.resolve();
146
+ expect(document.activeElement).toBe(h.trigger);
147
+ });
148
+
149
+ it("a StrictMode remount on open does not pull focus back out", async () => {
150
+ const h = setup({ strict: true });
151
+ h.item.focus();
152
+ await Promise.resolve();
153
+ await act(() => vi.advanceTimersByTimeAsync(400));
154
+ expect(document.activeElement).toBe(h.item);
155
+ });
156
+
157
+ it("does not throw when its frame outlives the requestAnimationFrame global", () => {
158
+ const h = setup({ holdFrames: true });
159
+ h.rerender({ open: false });
160
+ vi.stubGlobal("requestAnimationFrame", undefined);
161
+ expect(() => h.frames!.flush()).not.toThrow();
162
+ });
114
163
  });
115
164
  }
@@ -37,6 +37,7 @@
37
37
  import { useEffect, useRef, type RefObject } from "react";
38
38
  import { isWeb, useDialogContext } from "tamagui";
39
39
  import { wasKeyboardFocus, useResolvedKnobs, motionHideDelayMs } from "@multiplatform.one/theme";
40
+ import { useRestoreRetries } from "./restoreRetries";
40
41
 
41
42
  const FOCUSABLE =
42
43
  'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
@@ -67,15 +68,14 @@ function useCaptureOnOpen(open: boolean) {
67
68
  return { prevRef, generationRef };
68
69
  }
69
70
 
70
- function restoreLater(
71
+ function focusRestore(
71
72
  prevRef: RefObject<HTMLElement | null>,
72
73
  generationRef: RefObject<number>,
73
- delayMs: number,
74
74
  insideRef?: RefObject<HTMLElement | null>,
75
75
  skipReturnFocusRef?: RefObject<boolean>,
76
76
  ) {
77
77
  const generation = generationRef.current;
78
- const restore = () => {
78
+ return () => {
79
79
  // Fires from the unmount path, so the document can be gone by now (a test
80
80
  // environment torn down inside the retry window). `isConnected` stays
81
81
  // stale-true on a detached node, so it does not cover this.
@@ -90,10 +90,6 @@ function restoreLater(
90
90
  } as FocusOptions);
91
91
  }
92
92
  };
93
- // Exit animations unmount / park the overlay a few frames later; retry
94
- // after the typical exit window. All calls are idempotent.
95
- requestAnimationFrame(() => requestAnimationFrame(restore));
96
- setTimeout(restore, delayMs);
97
93
  }
98
94
 
99
95
  /**
@@ -111,17 +107,18 @@ export function useReturnFocusOnClose(
111
107
  const { knobProps } = useResolvedKnobs();
112
108
  const restoreDelayMsRef = useRef(0);
113
109
  restoreDelayMsRef.current = motionHideDelayMs(knobProps.motion.scrim.transition);
110
+ const retries = useRestoreRetries();
114
111
  useEffect(() => {
115
112
  if (!canUseDom() || !open) return;
113
+ retries.cancel();
114
+ // Exit animations unmount / park the overlay a few frames later; retry
115
+ // after the typical exit window. All calls are idempotent.
116
116
  return () =>
117
- restoreLater(
118
- prevRef,
119
- generationRef,
117
+ retries.schedule(
118
+ focusRestore(prevRef, generationRef, insideRef, skipReturnFocusRef),
120
119
  restoreDelayMsRef.current,
121
- insideRef,
122
- skipReturnFocusRef,
123
120
  );
124
- }, [open, prevRef, generationRef, skipReturnFocusRef, insideRef]);
121
+ }, [open, prevRef, generationRef, skipReturnFocusRef, insideRef, retries]);
125
122
  }
126
123
 
127
124
  /**
@@ -139,19 +136,17 @@ export function useSheetModalFocus(
139
136
  const { knobProps } = useResolvedKnobs();
140
137
  const restoreDelayMsRef = useRef(0);
141
138
  restoreDelayMsRef.current = motionHideDelayMs(knobProps.motion.scrim.transition);
139
+ const retries = useRestoreRetries();
142
140
  useEffect(() => {
143
141
  if (!canUseDom() || !open) return;
142
+ retries.cancel();
143
+ const restoreLater = () =>
144
+ retries.schedule(
145
+ focusRestore(prevRef, generationRef, frameRef, skipReturnFocusRef),
146
+ restoreDelayMsRef.current,
147
+ );
144
148
  const frame = frameRef.current;
145
- if (!frame || typeof frame.addEventListener !== "function") {
146
- return () =>
147
- restoreLater(
148
- prevRef,
149
- generationRef,
150
- restoreDelayMsRef.current,
151
- frameRef,
152
- skipReturnFocusRef,
153
- );
154
- }
149
+ if (!frame || typeof frame.addEventListener !== "function") return restoreLater;
155
150
 
156
151
  // Focus moves in once the sheet has mounted its frame (spring start).
157
152
  const focusIn = () => {
@@ -193,9 +188,9 @@ export function useSheetModalFocus(
193
188
  cancelAnimationFrame(raf);
194
189
  cancelAnimationFrame(innerRaf);
195
190
  frame.removeEventListener("keydown", onKeyDown);
196
- restoreLater(prevRef, generationRef, restoreDelayMsRef.current, frameRef, skipReturnFocusRef);
191
+ restoreLater();
197
192
  };
198
- }, [open, frameRef, prevRef, generationRef, skipReturnFocusRef]);
193
+ }, [open, frameRef, prevRef, generationRef, skipReturnFocusRef, retries]);
199
194
  }
200
195
 
201
196
  const DIALOG_SHOW_FOCUS_EVENT = "mp.dialogShowFocus";
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The retries an overlay runs to return focus after it closes: after two
3
+ * frames, then after the exit window (FZ-7904). The handles are kept, so a
4
+ * new schedule or a reopen cancels the old retries instead of stacking them,
5
+ * and an unmount leaves no frame or timer behind: the restore it would have
6
+ * run happens once, in a microtask, unless the component mounted again
7
+ * (StrictMode's simulated unmount).
8
+ */
9
+
10
+ import { useEffect, useRef } from "react";
11
+
12
+ export interface RestoreRetries {
13
+ schedule: (restore: () => void, delayMs: number) => void;
14
+ cancel: () => void;
15
+ }
16
+
17
+ const hasFrames = () => typeof requestAnimationFrame === "function";
18
+
19
+ export function useRestoreRetries(): RestoreRetries {
20
+ const mountedRef = useRef(false);
21
+ const retriesRef = useRef<(RestoreRetries & { unmount: () => void }) | null>(null);
22
+ if (!retriesRef.current) {
23
+ let outer = 0;
24
+ let inner = 0;
25
+ let timer: ReturnType<typeof setTimeout> | undefined;
26
+ let pending: (() => void) | null = null;
27
+ const restoreOnceUnmounted = (restore: () => void) => {
28
+ queueMicrotask(() => {
29
+ if (!mountedRef.current) restore();
30
+ });
31
+ };
32
+ const cancel = () => {
33
+ if (typeof cancelAnimationFrame === "function") {
34
+ if (outer) cancelAnimationFrame(outer);
35
+ if (inner) cancelAnimationFrame(inner);
36
+ }
37
+ if (timer !== undefined) clearTimeout(timer);
38
+ outer = 0;
39
+ inner = 0;
40
+ timer = undefined;
41
+ pending = null;
42
+ };
43
+ retriesRef.current = {
44
+ cancel,
45
+ schedule(restore, delayMs) {
46
+ cancel();
47
+ if (!mountedRef.current) {
48
+ restoreOnceUnmounted(restore);
49
+ return;
50
+ }
51
+ pending = restore;
52
+ if (hasFrames()) {
53
+ outer = requestAnimationFrame(() => {
54
+ outer = 0;
55
+ // The outer frame can outlive the document (a test window torn
56
+ // down between the two frames), and with it the global.
57
+ if (!hasFrames()) return;
58
+ inner = requestAnimationFrame(() => {
59
+ inner = 0;
60
+ if (timer === undefined) pending = null;
61
+ restore();
62
+ });
63
+ });
64
+ }
65
+ timer = setTimeout(() => {
66
+ timer = undefined;
67
+ if (!outer && !inner) pending = null;
68
+ restore();
69
+ }, delayMs);
70
+ },
71
+ unmount() {
72
+ const restore = pending;
73
+ cancel();
74
+ if (restore) restoreOnceUnmounted(restore);
75
+ },
76
+ };
77
+ }
78
+ const retries = retriesRef.current;
79
+ useEffect(() => {
80
+ mountedRef.current = true;
81
+ return () => {
82
+ mountedRef.current = false;
83
+ retries.unmount();
84
+ };
85
+ }, [retries]);
86
+ return retries;
87
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"useFloatingPanel.d.ts","sourceRoot":"","sources":["../../src/FloatingPanel/useFloatingPanel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFloatingPanel.d.ts","sourceRoot":"","sources":["../../src/FloatingPanel/useFloatingPanel.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"focusManagement.d.ts","sourceRoot":"","sources":["../../src/shared/focusManagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA8D1D;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,EACb,kBAAkB,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,EACvC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,QAiB1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACvC,kBAAkB,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,QA+DxC;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,QAWzE;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,EAC9B,eAAe,GAChB,EAAE;IACD,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACzC,GAAG,IAAI,CAGP"}
1
+ {"version":3,"file":"focusManagement.d.ts","sourceRoot":"","sources":["../../src/shared/focusManagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA0D1D;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,EACb,kBAAkB,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,EACvC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,QAkB1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACvC,kBAAkB,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,QA6DxC;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,QAWzE;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,EAC9B,eAAe,GAChB,EAAE;IACD,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACzC,GAAG,IAAI,CAGP"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The retries an overlay runs to return focus after it closes: after two
3
+ * frames, then after the exit window (FZ-7904). The handles are kept, so a
4
+ * new schedule or a reopen cancels the old retries instead of stacking them,
5
+ * and an unmount leaves no frame or timer behind: the restore it would have
6
+ * run happens once, in a microtask, unless the component mounted again
7
+ * (StrictMode's simulated unmount).
8
+ */
9
+ export interface RestoreRetries {
10
+ schedule: (restore: () => void, delayMs: number) => void;
11
+ cancel: () => void;
12
+ }
13
+ export declare function useRestoreRetries(): RestoreRetries;
14
+ //# sourceMappingURL=restoreRetries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restoreRetries.d.ts","sourceRoot":"","sources":["../../src/shared/restoreRetries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAID,wBAAgB,iBAAiB,IAAI,cAAc,CAoElD"}