@cdx-ui/primitives 0.3.12 → 0.3.14

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.
@@ -9,11 +9,18 @@ var _react = require("react");
9
9
  var _reactNative = require("react-native");
10
10
  function focusDomNode(node) {
11
11
  if (_reactNative.Platform.OS !== 'web') {
12
- return;
12
+ return undefined;
13
13
  }
14
- requestAnimationFrame(() => {
14
+ const focusedAtSchedule = document.activeElement;
15
+ const frame = requestAnimationFrame(() => {
16
+ // Focus moved elsewhere (e.g. to an outside element that dismisses the menu)
17
+ // before this frame; taking it back would strand focus on a closing menu.
18
+ if (document.activeElement !== focusedAtSchedule) {
19
+ return;
20
+ }
15
21
  node?.focus?.();
16
22
  });
23
+ return () => cancelAnimationFrame(frame);
17
24
  }
18
25
 
19
26
  /**
@@ -35,15 +42,14 @@ function useMenuOverlayOpenFocus({
35
42
  const enabled = getItems().filter(([, entry]) => !entry.disabled);
36
43
  if (intent === 'menu' || enabled.length === 0) {
37
44
  setFocusedItemId(undefined);
38
- focusDomNode(overlay.contentRef.current);
39
- return;
45
+ return focusDomNode(overlay.contentRef.current);
40
46
  }
41
47
  const target = intent === 'last' ? enabled.at(-1) : enabled[0];
42
48
  if (!target) {
43
49
  return;
44
50
  }
45
51
  setFocusedItemId(target[0]);
46
- focusDomNode(target[1].node);
52
+ return focusDomNode(target[1].node);
47
53
  }, [open, getItems, overlay, setFocusedItemId]);
48
54
  }
49
55
  function useMenuOverlayCloseFocus(overlay) {
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_reactNative","focusDomNode","node","Platform","OS","requestAnimationFrame","focus","useMenuOverlayOpenFocus","overlay","getItems","setFocusedItemId","open","useEffect","intent","focusOnOpenRef","current","enabled","filter","entry","disabled","length","undefined","contentRef","target","at","useMenuOverlayCloseFocus","closeReasonRef","triggerRef","prevOpenRef","useRef","shouldRestoreFocus"],"sourceRoot":"../../../src","sources":["menu/useMenuOverlayLifecycle.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAUA,SAASE,YAAYA,CAACC,IAA+C,EAAE;EACrE,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;IACzB;EACF;EACAC,qBAAqB,CAAC,MAAM;IAC1BH,IAAI,EAAEI,KAAK,GAAG,CAAC;EACjB,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASC,uBAAuBA,CAAC;EACtCC,OAAO;EACPC,QAAQ;EACRC;AAC8B,CAAC,EAAE;EACjC,MAAMC,IAAI,GAAGH,OAAO,EAAEG,IAAI,IAAI,KAAK;EAEnC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,CAACJ,OAAO,IAAI,CAACG,IAAI,EAAE;MACrB;IACF;IAEA,MAAME,MAAM,GAAGL,OAAO,CAACM,cAAc,CAACC,OAAO;IAC7C,MAAMC,OAAO,GAAGP,QAAQ,CAAC,CAAC,CAACQ,MAAM,CAAC,CAAC,GAAGC,KAAK,CAAC,KAAK,CAACA,KAAK,CAACC,QAAQ,CAAC;IAEjE,IAAIN,MAAM,KAAK,MAAM,IAAIG,OAAO,CAACI,MAAM,KAAK,CAAC,EAAE;MAC7CV,gBAAgB,CAACW,SAAS,CAAC;MAC3BpB,YAAY,CAACO,OAAO,CAACc,UAAU,CAACP,OAAwC,CAAC;MACzE;IACF;IAEA,MAAMQ,MAAM,GAAGV,MAAM,KAAK,MAAM,GAAGG,OAAO,CAACQ,EAAE,CAAC,CAAC,CAAC,CAAC,GAAGR,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAACO,MAAM,EAAE;MACX;IACF;IAEAb,gBAAgB,CAACa,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3BtB,YAAY,CAACsB,MAAM,CAAC,CAAC,CAAC,CAACrB,IAAI,CAAC;EAC9B,CAAC,EAAE,CAACS,IAAI,EAAEF,QAAQ,EAAED,OAAO,EAAEE,gBAAgB,CAAC,CAAC;AACjD;AAEO,SAASe,wBAAwBA,CAACjB,OAAgC,EAAE;EACzE,MAAM;IAAEkB,cAAc;IAAEf,IAAI;IAAEgB;EAAW,CAAC,GAAGnB,OAAO;EACpD,MAAMoB,WAAW,GAAG,IAAAC,aAAM,EAAClB,IAAI,CAAC;EAEhC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAID,IAAI,EAAE;MACRe,cAAc,CAACX,OAAO,GAAG,cAAc;IACzC,CAAC,MAAM,IAAIa,WAAW,CAACb,OAAO,IAAIZ,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACvD,MAAM0B,kBAAkB,GACtBJ,cAAc,CAACX,OAAO,KAAK,QAAQ,IACnCW,cAAc,CAACX,OAAO,KAAK,cAAc,IACzCW,cAAc,CAACX,OAAO,KAAK,QAAQ;MAErC,IAAIe,kBAAkB,EAAE;QACrBH,UAAU,CAACZ,OAAO,EAAoCT,KAAK,GAAG,CAAC;MAClE;IACF;IAEAsB,WAAW,CAACb,OAAO,GAAGJ,IAAI;EAC5B,CAAC,EAAE,CAACe,cAAc,EAAEf,IAAI,EAAEgB,UAAU,CAAC,CAAC;AACxC","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_reactNative","focusDomNode","node","Platform","OS","undefined","focusedAtSchedule","document","activeElement","frame","requestAnimationFrame","focus","cancelAnimationFrame","useMenuOverlayOpenFocus","overlay","getItems","setFocusedItemId","open","useEffect","intent","focusOnOpenRef","current","enabled","filter","entry","disabled","length","contentRef","target","at","useMenuOverlayCloseFocus","closeReasonRef","triggerRef","prevOpenRef","useRef","shouldRestoreFocus"],"sourceRoot":"../../../src","sources":["menu/useMenuOverlayLifecycle.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAUA,SAASE,YAAYA,CAACC,IAA+C,EAAE;EACrE,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;IACzB,OAAOC,SAAS;EAClB;EACA,MAAMC,iBAAiB,GAAGC,QAAQ,CAACC,aAAa;EAChD,MAAMC,KAAK,GAAGC,qBAAqB,CAAC,MAAM;IACxC;IACA;IACA,IAAIH,QAAQ,CAACC,aAAa,KAAKF,iBAAiB,EAAE;MAChD;IACF;IACAJ,IAAI,EAAES,KAAK,GAAG,CAAC;EACjB,CAAC,CAAC;EACF,OAAO,MAAMC,oBAAoB,CAACH,KAAK,CAAC;AAC1C;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASI,uBAAuBA,CAAC;EACtCC,OAAO;EACPC,QAAQ;EACRC;AAC8B,CAAC,EAAE;EACjC,MAAMC,IAAI,GAAGH,OAAO,EAAEG,IAAI,IAAI,KAAK;EAEnC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,CAACJ,OAAO,IAAI,CAACG,IAAI,EAAE;MACrB;IACF;IAEA,MAAME,MAAM,GAAGL,OAAO,CAACM,cAAc,CAACC,OAAO;IAC7C,MAAMC,OAAO,GAAGP,QAAQ,CAAC,CAAC,CAACQ,MAAM,CAAC,CAAC,GAAGC,KAAK,CAAC,KAAK,CAACA,KAAK,CAACC,QAAQ,CAAC;IAEjE,IAAIN,MAAM,KAAK,MAAM,IAAIG,OAAO,CAACI,MAAM,KAAK,CAAC,EAAE;MAC7CV,gBAAgB,CAACX,SAAS,CAAC;MAC3B,OAAOJ,YAAY,CAACa,OAAO,CAACa,UAAU,CAACN,OAAwC,CAAC;IAClF;IAEA,MAAMO,MAAM,GAAGT,MAAM,KAAK,MAAM,GAAGG,OAAO,CAACO,EAAE,CAAC,CAAC,CAAC,CAAC,GAAGP,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAACM,MAAM,EAAE;MACX;IACF;IAEAZ,gBAAgB,CAACY,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO3B,YAAY,CAAC2B,MAAM,CAAC,CAAC,CAAC,CAAC1B,IAAI,CAAC;EACrC,CAAC,EAAE,CAACe,IAAI,EAAEF,QAAQ,EAAED,OAAO,EAAEE,gBAAgB,CAAC,CAAC;AACjD;AAEO,SAASc,wBAAwBA,CAAChB,OAAgC,EAAE;EACzE,MAAM;IAAEiB,cAAc;IAAEd,IAAI;IAAEe;EAAW,CAAC,GAAGlB,OAAO;EACpD,MAAMmB,WAAW,GAAG,IAAAC,aAAM,EAACjB,IAAI,CAAC;EAEhC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAID,IAAI,EAAE;MACRc,cAAc,CAACV,OAAO,GAAG,cAAc;IACzC,CAAC,MAAM,IAAIY,WAAW,CAACZ,OAAO,IAAIlB,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACvD,MAAM+B,kBAAkB,GACtBJ,cAAc,CAACV,OAAO,KAAK,QAAQ,IACnCU,cAAc,CAACV,OAAO,KAAK,cAAc,IACzCU,cAAc,CAACV,OAAO,KAAK,QAAQ;MAErC,IAAIc,kBAAkB,EAAE;QACrBH,UAAU,CAACX,OAAO,EAAoCV,KAAK,GAAG,CAAC;MAClE;IACF;IAEAsB,WAAW,CAACZ,OAAO,GAAGJ,IAAI;EAC5B,CAAC,EAAE,CAACc,cAAc,EAAEd,IAAI,EAAEe,UAAU,CAAC,CAAC;AACxC","ignoreList":[]}
@@ -19,7 +19,7 @@ var _dataAttributes = require("../utils/dataAttributes");
19
19
  * platform-aware `dataAttrs` object so the styled layer can emit interaction-state
20
20
  * `data-*` attributes (Standard 1) without re-implementing the toggle machinery.
21
21
  *
22
- * Standards mapping (per `.cursor/plans/primitive_design_standards_0d0ca4b1.plan.md`):
22
+ * Standards mapping:
23
23
  * - Standard 3 #1 — controlled state via `useToggleState`.
24
24
  * - Standard 3 #2 — `role="switch"` semantics (consumer wires to host).
25
25
  *
@@ -4,11 +4,18 @@ import { useEffect, useRef } from 'react';
4
4
  import { Platform } from 'react-native';
5
5
  function focusDomNode(node) {
6
6
  if (Platform.OS !== 'web') {
7
- return;
7
+ return undefined;
8
8
  }
9
- requestAnimationFrame(() => {
9
+ const focusedAtSchedule = document.activeElement;
10
+ const frame = requestAnimationFrame(() => {
11
+ // Focus moved elsewhere (e.g. to an outside element that dismisses the menu)
12
+ // before this frame; taking it back would strand focus on a closing menu.
13
+ if (document.activeElement !== focusedAtSchedule) {
14
+ return;
15
+ }
10
16
  node?.focus?.();
11
17
  });
18
+ return () => cancelAnimationFrame(frame);
12
19
  }
13
20
 
14
21
  /**
@@ -30,15 +37,14 @@ export function useMenuOverlayOpenFocus({
30
37
  const enabled = getItems().filter(([, entry]) => !entry.disabled);
31
38
  if (intent === 'menu' || enabled.length === 0) {
32
39
  setFocusedItemId(undefined);
33
- focusDomNode(overlay.contentRef.current);
34
- return;
40
+ return focusDomNode(overlay.contentRef.current);
35
41
  }
36
42
  const target = intent === 'last' ? enabled.at(-1) : enabled[0];
37
43
  if (!target) {
38
44
  return;
39
45
  }
40
46
  setFocusedItemId(target[0]);
41
- focusDomNode(target[1].node);
47
+ return focusDomNode(target[1].node);
42
48
  }, [open, getItems, overlay, setFocusedItemId]);
43
49
  }
44
50
  export function useMenuOverlayCloseFocus(overlay) {
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","Platform","focusDomNode","node","OS","requestAnimationFrame","focus","useMenuOverlayOpenFocus","overlay","getItems","setFocusedItemId","open","intent","focusOnOpenRef","current","enabled","filter","entry","disabled","length","undefined","contentRef","target","at","useMenuOverlayCloseFocus","closeReasonRef","triggerRef","prevOpenRef","shouldRestoreFocus"],"sourceRoot":"../../../src","sources":["menu/useMenuOverlayLifecycle.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;AACzC,SAASC,QAAQ,QAAQ,cAAc;AAUvC,SAASC,YAAYA,CAACC,IAA+C,EAAE;EACrE,IAAIF,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;IACzB;EACF;EACAC,qBAAqB,CAAC,MAAM;IAC1BF,IAAI,EAAEG,KAAK,GAAG,CAAC;EACjB,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CAAC;EACtCC,OAAO;EACPC,QAAQ;EACRC;AAC8B,CAAC,EAAE;EACjC,MAAMC,IAAI,GAAGH,OAAO,EAAEG,IAAI,IAAI,KAAK;EAEnCZ,SAAS,CAAC,MAAM;IACd,IAAI,CAACS,OAAO,IAAI,CAACG,IAAI,EAAE;MACrB;IACF;IAEA,MAAMC,MAAM,GAAGJ,OAAO,CAACK,cAAc,CAACC,OAAO;IAC7C,MAAMC,OAAO,GAAGN,QAAQ,CAAC,CAAC,CAACO,MAAM,CAAC,CAAC,GAAGC,KAAK,CAAC,KAAK,CAACA,KAAK,CAACC,QAAQ,CAAC;IAEjE,IAAIN,MAAM,KAAK,MAAM,IAAIG,OAAO,CAACI,MAAM,KAAK,CAAC,EAAE;MAC7CT,gBAAgB,CAACU,SAAS,CAAC;MAC3BlB,YAAY,CAACM,OAAO,CAACa,UAAU,CAACP,OAAwC,CAAC;MACzE;IACF;IAEA,MAAMQ,MAAM,GAAGV,MAAM,KAAK,MAAM,GAAGG,OAAO,CAACQ,EAAE,CAAC,CAAC,CAAC,CAAC,GAAGR,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAACO,MAAM,EAAE;MACX;IACF;IAEAZ,gBAAgB,CAACY,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3BpB,YAAY,CAACoB,MAAM,CAAC,CAAC,CAAC,CAACnB,IAAI,CAAC;EAC9B,CAAC,EAAE,CAACQ,IAAI,EAAEF,QAAQ,EAAED,OAAO,EAAEE,gBAAgB,CAAC,CAAC;AACjD;AAEA,OAAO,SAASc,wBAAwBA,CAAChB,OAAgC,EAAE;EACzE,MAAM;IAAEiB,cAAc;IAAEd,IAAI;IAAEe;EAAW,CAAC,GAAGlB,OAAO;EACpD,MAAMmB,WAAW,GAAG3B,MAAM,CAACW,IAAI,CAAC;EAEhCZ,SAAS,CAAC,MAAM;IACd,IAAIY,IAAI,EAAE;MACRc,cAAc,CAACX,OAAO,GAAG,cAAc;IACzC,CAAC,MAAM,IAAIa,WAAW,CAACb,OAAO,IAAIb,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;MACvD,MAAMwB,kBAAkB,GACtBH,cAAc,CAACX,OAAO,KAAK,QAAQ,IACnCW,cAAc,CAACX,OAAO,KAAK,cAAc,IACzCW,cAAc,CAACX,OAAO,KAAK,QAAQ;MAErC,IAAIc,kBAAkB,EAAE;QACrBF,UAAU,CAACZ,OAAO,EAAoCR,KAAK,GAAG,CAAC;MAClE;IACF;IAEAqB,WAAW,CAACb,OAAO,GAAGH,IAAI;EAC5B,CAAC,EAAE,CAACc,cAAc,EAAEd,IAAI,EAAEe,UAAU,CAAC,CAAC;AACxC","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","useRef","Platform","focusDomNode","node","OS","undefined","focusedAtSchedule","document","activeElement","frame","requestAnimationFrame","focus","cancelAnimationFrame","useMenuOverlayOpenFocus","overlay","getItems","setFocusedItemId","open","intent","focusOnOpenRef","current","enabled","filter","entry","disabled","length","contentRef","target","at","useMenuOverlayCloseFocus","closeReasonRef","triggerRef","prevOpenRef","shouldRestoreFocus"],"sourceRoot":"../../../src","sources":["menu/useMenuOverlayLifecycle.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;AACzC,SAASC,QAAQ,QAAQ,cAAc;AAUvC,SAASC,YAAYA,CAACC,IAA+C,EAAE;EACrE,IAAIF,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;IACzB,OAAOC,SAAS;EAClB;EACA,MAAMC,iBAAiB,GAAGC,QAAQ,CAACC,aAAa;EAChD,MAAMC,KAAK,GAAGC,qBAAqB,CAAC,MAAM;IACxC;IACA;IACA,IAAIH,QAAQ,CAACC,aAAa,KAAKF,iBAAiB,EAAE;MAChD;IACF;IACAH,IAAI,EAAEQ,KAAK,GAAG,CAAC;EACjB,CAAC,CAAC;EACF,OAAO,MAAMC,oBAAoB,CAACH,KAAK,CAAC;AAC1C;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,uBAAuBA,CAAC;EACtCC,OAAO;EACPC,QAAQ;EACRC;AAC8B,CAAC,EAAE;EACjC,MAAMC,IAAI,GAAGH,OAAO,EAAEG,IAAI,IAAI,KAAK;EAEnClB,SAAS,CAAC,MAAM;IACd,IAAI,CAACe,OAAO,IAAI,CAACG,IAAI,EAAE;MACrB;IACF;IAEA,MAAMC,MAAM,GAAGJ,OAAO,CAACK,cAAc,CAACC,OAAO;IAC7C,MAAMC,OAAO,GAAGN,QAAQ,CAAC,CAAC,CAACO,MAAM,CAAC,CAAC,GAAGC,KAAK,CAAC,KAAK,CAACA,KAAK,CAACC,QAAQ,CAAC;IAEjE,IAAIN,MAAM,KAAK,MAAM,IAAIG,OAAO,CAACI,MAAM,KAAK,CAAC,EAAE;MAC7CT,gBAAgB,CAACX,SAAS,CAAC;MAC3B,OAAOH,YAAY,CAACY,OAAO,CAACY,UAAU,CAACN,OAAwC,CAAC;IAClF;IAEA,MAAMO,MAAM,GAAGT,MAAM,KAAK,MAAM,GAAGG,OAAO,CAACO,EAAE,CAAC,CAAC,CAAC,CAAC,GAAGP,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAACM,MAAM,EAAE;MACX;IACF;IAEAX,gBAAgB,CAACW,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAOzB,YAAY,CAACyB,MAAM,CAAC,CAAC,CAAC,CAACxB,IAAI,CAAC;EACrC,CAAC,EAAE,CAACc,IAAI,EAAEF,QAAQ,EAAED,OAAO,EAAEE,gBAAgB,CAAC,CAAC;AACjD;AAEA,OAAO,SAASa,wBAAwBA,CAACf,OAAgC,EAAE;EACzE,MAAM;IAAEgB,cAAc;IAAEb,IAAI;IAAEc;EAAW,CAAC,GAAGjB,OAAO;EACpD,MAAMkB,WAAW,GAAGhC,MAAM,CAACiB,IAAI,CAAC;EAEhClB,SAAS,CAAC,MAAM;IACd,IAAIkB,IAAI,EAAE;MACRa,cAAc,CAACV,OAAO,GAAG,cAAc;IACzC,CAAC,MAAM,IAAIY,WAAW,CAACZ,OAAO,IAAInB,QAAQ,CAACG,EAAE,KAAK,KAAK,EAAE;MACvD,MAAM6B,kBAAkB,GACtBH,cAAc,CAACV,OAAO,KAAK,QAAQ,IACnCU,cAAc,CAACV,OAAO,KAAK,cAAc,IACzCU,cAAc,CAACV,OAAO,KAAK,QAAQ;MAErC,IAAIa,kBAAkB,EAAE;QACrBF,UAAU,CAACX,OAAO,EAAoCT,KAAK,GAAG,CAAC;MAClE;IACF;IAEAqB,WAAW,CAACZ,OAAO,GAAGH,IAAI;EAC5B,CAAC,EAAE,CAACa,cAAc,EAAEb,IAAI,EAAEc,UAAU,CAAC,CAAC;AACxC","ignoreList":[]}
@@ -15,7 +15,7 @@ import { dataAttributes } from '../utils/dataAttributes';
15
15
  * platform-aware `dataAttrs` object so the styled layer can emit interaction-state
16
16
  * `data-*` attributes (Standard 1) without re-implementing the toggle machinery.
17
17
  *
18
- * Standards mapping (per `.cursor/plans/primitive_design_standards_0d0ca4b1.plan.md`):
18
+ * Standards mapping:
19
19
  * - Standard 3 #1 — controlled state via `useToggleState`.
20
20
  * - Standard 3 #2 — `role="switch"` semantics (consumer wires to host).
21
21
  *
@@ -1 +1 @@
1
- {"version":3,"file":"useMenuOverlayLifecycle.d.ts","sourceRoot":"","sources":["../../../src/menu/useMenuOverlayLifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAAC;IAC/C,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACxD;AAWD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,QAAQ,EACR,gBAAgB,GACjB,EAAE,8BAA8B,QAyBhC;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,QAoBxE"}
1
+ {"version":3,"file":"useMenuOverlayLifecycle.d.ts","sourceRoot":"","sources":["../../../src/menu/useMenuOverlayLifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAAC;IAC/C,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACxD;AAkBD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,QAAQ,EACR,gBAAgB,GACjB,EAAE,8BAA8B,QAwBhC;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,QAoBxE"}
@@ -8,7 +8,7 @@ import type { ISwitchProps, IUseSwitchReturn } from './types';
8
8
  * platform-aware `dataAttrs` object so the styled layer can emit interaction-state
9
9
  * `data-*` attributes (Standard 1) without re-implementing the toggle machinery.
10
10
  *
11
- * Standards mapping (per `.cursor/plans/primitive_design_standards_0d0ca4b1.plan.md`):
11
+ * Standards mapping:
12
12
  * - Standard 3 #1 — controlled state via `useToggleState`.
13
13
  * - Standard 3 #2 — `role="switch"` semantics (consumer wires to host).
14
14
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdx-ui/primitives",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -69,8 +69,8 @@
69
69
  "@react-stately/disclosure": "3.1.1",
70
70
  "@react-stately/radio": "3.12.0",
71
71
  "@react-stately/toggle": "3.9.4",
72
- "@cdx-ui/formatters": "0.3.12",
73
- "@cdx-ui/utils": "0.3.12"
72
+ "@cdx-ui/utils": "0.3.14",
73
+ "@cdx-ui/formatters": "0.3.14"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/react": "19.2.14",
@@ -11,11 +11,18 @@ export interface UseMenuOverlayOpenFocusOptions {
11
11
 
12
12
  function focusDomNode(node: { focus?: () => void } | null | undefined) {
13
13
  if (Platform.OS !== 'web') {
14
- return;
14
+ return undefined;
15
15
  }
16
- requestAnimationFrame(() => {
16
+ const focusedAtSchedule = document.activeElement;
17
+ const frame = requestAnimationFrame(() => {
18
+ // Focus moved elsewhere (e.g. to an outside element that dismisses the menu)
19
+ // before this frame; taking it back would strand focus on a closing menu.
20
+ if (document.activeElement !== focusedAtSchedule) {
21
+ return;
22
+ }
17
23
  node?.focus?.();
18
24
  });
25
+ return () => cancelAnimationFrame(frame);
19
26
  }
20
27
 
21
28
  /**
@@ -40,8 +47,7 @@ export function useMenuOverlayOpenFocus({
40
47
 
41
48
  if (intent === 'menu' || enabled.length === 0) {
42
49
  setFocusedItemId(undefined);
43
- focusDomNode(overlay.contentRef.current as { focus?: () => void } | null);
44
- return;
50
+ return focusDomNode(overlay.contentRef.current as { focus?: () => void } | null);
45
51
  }
46
52
 
47
53
  const target = intent === 'last' ? enabled.at(-1) : enabled[0];
@@ -50,7 +56,7 @@ export function useMenuOverlayOpenFocus({
50
56
  }
51
57
 
52
58
  setFocusedItemId(target[0]);
53
- focusDomNode(target[1].node);
59
+ return focusDomNode(target[1].node);
54
60
  }, [open, getItems, overlay, setFocusedItemId]);
55
61
  }
56
62
 
@@ -15,7 +15,7 @@ import type { ISwitchProps, IUseSwitchReturn } from './types';
15
15
  * platform-aware `dataAttrs` object so the styled layer can emit interaction-state
16
16
  * `data-*` attributes (Standard 1) without re-implementing the toggle machinery.
17
17
  *
18
- * Standards mapping (per `.cursor/plans/primitive_design_standards_0d0ca4b1.plan.md`):
18
+ * Standards mapping:
19
19
  * - Standard 3 #1 — controlled state via `useToggleState`.
20
20
  * - Standard 3 #2 — `role="switch"` semantics (consumer wires to host).
21
21
  *