@astryxdesign/core 0.4.1 → 0.4.2-canary.464a445

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 (182) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/Avatar/Avatar.d.ts +4 -1
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +19 -23
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  7. package/dist/Button/Button.d.ts.map +1 -1
  8. package/dist/Button/Button.js +9 -7
  9. package/dist/Chat/ChatMessage.d.ts +7 -0
  10. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  11. package/dist/Chat/ChatMessageBubble.d.ts +13 -1
  12. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  13. package/dist/Chat/ChatMessageBubble.js +19 -1
  14. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  15. package/dist/CheckboxInput/CheckboxInput.js +5 -0
  16. package/dist/CommandPalette/CommandPaletteFooter.d.ts.map +1 -1
  17. package/dist/CommandPalette/CommandPaletteFooter.js +5 -3
  18. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  19. package/dist/DateTimeInput/DateTimeInput.js +2 -2
  20. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  21. package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -15
  22. package/dist/HoverCard/HoverCard.d.ts +1 -1
  23. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  24. package/dist/HoverCard/HoverCard.js +5 -13
  25. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  26. package/dist/HoverCard/useHoverCard.js +6 -5
  27. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  28. package/dist/InputGroup/groupStyles.js +7 -2
  29. package/dist/Layer/layerHost.d.ts +24 -0
  30. package/dist/Layer/layerHost.d.ts.map +1 -0
  31. package/dist/Layer/layerHost.js +79 -0
  32. package/dist/Layer/useLayer.d.ts +14 -6
  33. package/dist/Layer/useLayer.d.ts.map +1 -1
  34. package/dist/Layer/useLayer.js +158 -30
  35. package/dist/NavItem/navItemStyles.stylex.d.ts +17 -5
  36. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  37. package/dist/NavItem/navItemStyles.stylex.js +11 -5
  38. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  39. package/dist/RadioList/RadioListItem.js +5 -0
  40. package/dist/SideNav/SideNav.d.ts +7 -9
  41. package/dist/SideNav/SideNav.d.ts.map +1 -1
  42. package/dist/SideNav/SideNav.js +32 -5
  43. package/dist/SideNav/SideNavCollapseButton.d.ts +25 -9
  44. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  45. package/dist/SideNav/SideNavCollapseButton.js +39 -15
  46. package/dist/SideNav/SideNavCollapseContext.d.ts +21 -0
  47. package/dist/SideNav/SideNavCollapseContext.d.ts.map +1 -1
  48. package/dist/SideNav/SideNavCollapseContext.js +16 -2
  49. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  50. package/dist/SideNav/SideNavHeading.js +89 -32
  51. package/dist/SideNav/SideNavItem.d.ts +7 -2
  52. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  53. package/dist/SideNav/SideNavItem.js +119 -75
  54. package/dist/SideNav/SideNavSection.d.ts.map +1 -1
  55. package/dist/SideNav/SideNavSection.js +7 -16
  56. package/dist/SideNav/index.d.ts +1 -1
  57. package/dist/SideNav/index.d.ts.map +1 -1
  58. package/dist/Slider/Slider.d.ts.map +1 -1
  59. package/dist/Slider/Slider.js +56 -15
  60. package/dist/Switch/Switch.d.ts.map +1 -1
  61. package/dist/Switch/Switch.js +5 -0
  62. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  63. package/dist/Thumbnail/Thumbnail.js +5 -0
  64. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  65. package/dist/TopNav/TopNavHeading.js +14 -6
  66. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  67. package/dist/TopNav/TopNavMegaMenu.js +25 -93
  68. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  69. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  70. package/dist/TopNav/TopNavMenu.js +10 -5
  71. package/dist/astryx.css +13 -7
  72. package/dist/astryx.umd.js +50 -50
  73. package/dist/astryx.umd.js.map +4 -4
  74. package/dist/hooks/index.d.ts +1 -1
  75. package/dist/hooks/index.d.ts.map +1 -1
  76. package/dist/hooks/index.js +1 -1
  77. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  78. package/dist/hooks/useFocusTrap.js +16 -2
  79. package/dist/hooks/useMenuHover.d.ts +50 -5
  80. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  81. package/dist/hooks/useMenuHover.js +171 -51
  82. package/dist/theme/defineTheme.d.ts +8 -2
  83. package/dist/theme/defineTheme.d.ts.map +1 -1
  84. package/dist/theme/defineTheme.js +8 -1
  85. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  86. package/dist/theme/derivedVarRegistry.js +7 -0
  87. package/dist/theme/expandColorScale.d.ts.map +1 -1
  88. package/dist/theme/expandColorScale.js +1 -0
  89. package/dist/theme/expandMotionScale.d.ts +1 -0
  90. package/dist/theme/expandMotionScale.d.ts.map +1 -1
  91. package/dist/theme/expandMotionScale.js +1 -0
  92. package/dist/theme/expandRadiusScale.d.ts +1 -0
  93. package/dist/theme/expandRadiusScale.d.ts.map +1 -1
  94. package/dist/theme/expandRadiusScale.js +1 -0
  95. package/dist/theme/expandTypeScale.d.ts +1 -0
  96. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  97. package/dist/theme/expandTypeScale.js +1 -0
  98. package/dist/theme/tokens.stylex.d.ts +3 -1
  99. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  100. package/dist/theme/tokens.stylex.js +3 -1
  101. package/locales/en.json +84 -0
  102. package/locales/pseudo.json +63 -0
  103. package/package.json +3 -3
  104. package/src/Avatar/Avatar.doc.mjs +5 -2
  105. package/src/Avatar/Avatar.test.tsx +51 -0
  106. package/src/Avatar/Avatar.tsx +30 -20
  107. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  108. package/src/AvatarGroup/AvatarGroup.test.tsx +14 -0
  109. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -2
  110. package/src/Button/Button.tsx +5 -3
  111. package/src/ButtonGroup/ButtonGroup.test.tsx +7 -4
  112. package/src/Card/Card.doc.mjs +2 -0
  113. package/src/Chat/Chat.doc.mjs +4 -1
  114. package/src/Chat/ChatMessage.doc.mjs +3 -3
  115. package/src/Chat/ChatMessage.tsx +7 -0
  116. package/src/Chat/ChatMessageBubble.doc.mjs +7 -0
  117. package/src/Chat/ChatMessageBubble.test.tsx +95 -6
  118. package/src/Chat/ChatMessageBubble.tsx +25 -0
  119. package/src/CheckboxInput/CheckboxInput.tsx +20 -0
  120. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  121. package/src/CommandPalette/CommandPaletteFooter.tsx +6 -3
  122. package/src/DateTimeInput/DateTimeInput.tsx +4 -2
  123. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +72 -1
  124. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +24 -20
  125. package/src/HoverCard/HoverCard.doc.mjs +3 -3
  126. package/src/HoverCard/HoverCard.test.tsx +283 -55
  127. package/src/HoverCard/HoverCard.tsx +5 -13
  128. package/src/HoverCard/useHoverCard.tsx +8 -11
  129. package/src/InputGroup/groupStyles.ts +7 -8
  130. package/src/Item/Item.doc.mjs +4 -0
  131. package/src/Layer/layerHost.test.ts +99 -0
  132. package/src/Layer/layerHost.ts +141 -0
  133. package/src/Layer/useLayer.doc.mjs +14 -4
  134. package/src/Layer/useLayer.test.tsx +332 -7
  135. package/src/Layer/useLayer.tsx +235 -36
  136. package/src/MobileNav/MobileNav.doc.mjs +7 -0
  137. package/src/NavItem/navItemStyles.stylex.ts +31 -5
  138. package/src/RadioList/RadioListItem.tsx +20 -0
  139. package/src/SideNav/SideNav.doc.mjs +13 -4
  140. package/src/SideNav/SideNav.test.tsx +858 -2
  141. package/src/SideNav/SideNav.tsx +37 -16
  142. package/src/SideNav/SideNavCollapseButton.doc.mjs +28 -6
  143. package/src/SideNav/SideNavCollapseButton.tsx +67 -15
  144. package/src/SideNav/SideNavCollapseContext.ts +29 -9
  145. package/src/SideNav/SideNavHeading.tsx +53 -25
  146. package/src/SideNav/SideNavItem.doc.mjs +13 -0
  147. package/src/SideNav/SideNavItem.tsx +97 -84
  148. package/src/SideNav/SideNavSection.tsx +6 -20
  149. package/src/SideNav/index.ts +2 -0
  150. package/src/Slider/Slider.test.tsx +69 -33
  151. package/src/Slider/Slider.tsx +65 -13
  152. package/src/Switch/Switch.tsx +20 -0
  153. package/src/TabList/TabList.doc.mjs +3 -0
  154. package/src/Text/Text.doc.mjs +1 -1
  155. package/src/Thumbnail/Thumbnail.doc.mjs +3 -0
  156. package/src/Thumbnail/Thumbnail.tsx +10 -0
  157. package/src/Timestamp/Timestamp.test.tsx +40 -22
  158. package/src/Toolbar/Toolbar.test.tsx +14 -14
  159. package/src/TopNav/TopNav.test.tsx +86 -1
  160. package/src/TopNav/TopNavHeading.tsx +21 -14
  161. package/src/TopNav/TopNavMegaMenu.test.tsx +43 -0
  162. package/src/TopNav/TopNavMegaMenu.tsx +29 -105
  163. package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
  164. package/src/TopNav/TopNavMenu.test.tsx +41 -0
  165. package/src/TopNav/TopNavMenu.tsx +14 -4
  166. package/src/TreeList/TreeList.doc.mjs +1 -0
  167. package/src/hooks/index.ts +5 -1
  168. package/src/hooks/useFocusTrap.test.tsx +16 -0
  169. package/src/hooks/useFocusTrap.ts +20 -2
  170. package/src/hooks/useMenuHover.test.tsx +364 -0
  171. package/src/hooks/useMenuHover.ts +243 -47
  172. package/src/theme/defineTheme.ts +9 -0
  173. package/src/theme/derivedVarRegistry.test.ts +78 -12
  174. package/src/theme/derivedVarRegistry.ts +4 -0
  175. package/src/theme/expandColorScale.ts +1 -0
  176. package/src/theme/expandMotionScale.ts +1 -0
  177. package/src/theme/expandRadiusScale.ts +1 -0
  178. package/src/theme/expandTypeScale.ts +1 -0
  179. package/src/theme/themingTargets.test.ts +152 -26
  180. package/src/theme/tokens.stylex.ts +3 -1
  181. package/src/theme/tokens.test.ts +12 -0
  182. package/src/theme/useTheme.test.tsx +18 -0
@@ -1 +1 @@
1
- {"version":3,"file":"HoverCard.d.ts","sourceRoot":"","sources":["../../src/HoverCard/HoverCard.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAG7E,OAAO,EAAe,KAAK,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAG5C,YAAY,EAAC,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AAiB1D,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,SAAS,EACT,QAAQ,GAAG,WAAW,GAAG,OAAO,CACjC;IACC;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,SAAS,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEtC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAiBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,OAAO,EACP,SAAmB,EACnB,SAAoB,EACpB,KAAW,EACX,SAAe,EACf,YAAqB,EACrB,SAAgB,EAChB,KAAK,EACL,YAAY,EACZ,kBAA2B,EAC3B,MAAM,EACN,aAAa,EACb,MAAM,EACN,SAAS,EACT,KAAK,GACN,EAAE,cAAc,GAAG,YAAY,CAiH/B;yBAlIe,SAAS"}
1
+ {"version":3,"file":"HoverCard.d.ts","sourceRoot":"","sources":["../../src/HoverCard/HoverCard.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAG7E,OAAO,EAAe,KAAK,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAG5C,YAAY,EAAC,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AAiB1D,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,SAAS,EACT,QAAQ,GAAG,WAAW,GAAG,OAAO,CACjC;IACC;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,SAAS,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEtC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAiBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,OAAO,EACP,SAAmB,EACnB,SAAoB,EACpB,KAAW,EACX,SAAe,EACf,YAAqB,EACrB,SAAgB,EAChB,KAAK,EACL,YAAY,EACZ,kBAA2B,EAC3B,MAAM,EACN,aAAa,EACb,MAAM,EACN,SAAS,EACT,KAAK,GACN,EAAE,cAAc,GAAG,YAAY,CAyG/B;yBA1He,SAAS"}
@@ -6,7 +6,7 @@
6
6
  * @file HoverCard.tsx
7
7
  * @input Uses React, useHoverCard hook
8
8
  * @output Exports HoverCard component for hover/focus triggered layers
9
- * @position Layer component; uses inline-safe trigger wrapper and renders the floating layer inline
9
+ * @position Layer component; inline-safe trigger wrapper, floating layer hosted by useLayer
10
10
  *
11
11
  * SYNC: When modified, update these files to stay in sync:
12
12
  * - /packages/core/src/HoverCard/HoverCard.test.tsx
@@ -127,18 +127,10 @@ export function HoverCard({
127
127
  };
128
128
  }, [textOnly, hoverCard.ref, hoverCard.describedBy]);
129
129
 
130
- // Render the floating layer inline, in the same place on the server and the
131
- // client. The layer is a `popover` element opened via the Popover API, so the
132
- // browser promotes it to the top layer when shown that already escapes
133
- // ancestor clipping, stacking, and transform containing-block traps, and CSS
134
- // anchor positioning resolves the trigger reference regardless of where the
135
- // element sits in the DOM, so no portal is needed to "escape" layout.
136
- //
137
- // The layer renders as inline-safe phrasing markup (a `<span>`, see
138
- // useHoverCard), which stays put inside a `<p>` instead of being reparented
139
- // by the HTML parser. That keeps the server markup and the first client
140
- // render identical, so there is no hydration mismatch — and it preserves the
141
- // inline-safety guarantee (no block elements injected into a paragraph).
130
+ // While closed, useLayer leaves only an inert <template> marker at this JSX
131
+ // position. When the card needs to open, it uses that marker to keep the
132
+ // final layer inline when the parent is safe or portal it outside a <p>,
133
+ // link, or other ancestor that cannot contain it safely.
142
134
  const renderedHoverCard = hoverCard.renderHoverCard(content, {
143
135
  xstyle,
144
136
  className,
@@ -1 +1 @@
1
- {"version":3,"file":"useHoverCard.d.ts","sourceRoot":"","sources":["../../src/HoverCard/useHoverCard.tsx"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAgC3B;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEtC;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;OAOG;IACH,eAAe,EAAE,CACf,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,KAC5C,SAAS,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAyBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,gBAAqB,GAAG,eAAe,CAiT5E"}
1
+ {"version":3,"file":"useHoverCard.d.ts","sourceRoot":"","sources":["../../src/HoverCard/useHoverCard.tsx"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AA4B3B;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEtC;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAEzC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;OAOG;IACH,eAAe,EAAE,CACf,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,KAC5C,SAAS,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAyBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,gBAAqB,GAAG,eAAe,CAkT5E"}
@@ -88,6 +88,7 @@ export function useHoverCard(options = {}) {
88
88
  } = options;
89
89
  const layer = useLayer({
90
90
  mode: 'context',
91
+ lazyMount: true,
91
92
  onShow,
92
93
  onHide
93
94
  });
@@ -254,15 +255,15 @@ export function useHoverCard(options = {}) {
254
255
  // Consumer surface style props land on the layer container — the
255
256
  // themed surface (bg/radius/shadow) where the theme class lives — so
256
257
  // customizing the card targets the same element as the theme. The inner
257
- // span keeps `styles.content` for padding.
258
+ // div keeps `styles.content` for padding.
258
259
  xstyle: [popoverXstyle, layerAnimations[renderPlacement], props?.xstyle],
259
260
  className: props?.className ? `${themeClassName} ${props.className}` : themeClassName,
260
261
  style: props?.style,
261
- // Render the layer as inline-safe phrasing markup so HoverCard stays
262
- // valid (and hydration-stable) inside inline contexts like a `<p>`.
263
- as: 'span'
262
+ // useLayer mounts only after it has verified or corrected the parent,
263
+ // so rich HoverCard content can use block-safe markup.
264
+ as: 'div'
264
265
  };
265
- return layer.render(/*#__PURE__*/_jsx("span", {
266
+ return layer.render(/*#__PURE__*/_jsx("div", {
266
267
  ...{
267
268
  className: "x1lliihq x1vlblms xvmdzux x126nfab x1t818jl"
268
269
  },
@@ -1 +1 @@
1
- {"version":3,"file":"groupStyles.d.ts","sourceRoot":"","sources":["../../src/InputGroup/groupStyles.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAG3C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;EAmCtB,CAAC"}
1
+ {"version":3,"file":"groupStyles.d.ts","sourceRoot":"","sources":["../../src/InputGroup/groupStyles.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAQ3C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;EA6BtB,CAAC"}
@@ -10,6 +10,11 @@
10
10
  import * as stylex from '@stylexjs/stylex';
11
11
  import "../theme/tokens.stylex.js";
12
12
  import { radiusVars, borderVars } from "../theme/tokens.stylex.js";
13
+
14
+ // A grouped control may be followed by context-layer infrastructure rather
15
+ // than another control. Neither the inert marker nor the popover is a visual
16
+ // group member, so skip both when finding the trailing edge.
17
+ const IS_LAST_ITEM = ':not(:has(~ *:not([popover]):not(template)))';
13
18
  export const groupStyles = {
14
19
  inGroup: {
15
20
  kUk6DE: "x98rzlu",
@@ -18,8 +23,8 @@ export const groupStyles = {
18
23
  keTefX: "xd10s4z x1pwwqoy",
19
24
  krdFHd: "x15mokao x8eehn2",
20
25
  kVL7Gh: "xbiv7yw x1xrp5p4",
21
- kfmiAY: "x1ga7v0g x11xp8u1 xpufgd0 x1ir7nn6",
22
- kT0f0o: "x16uus16 x747jw7 xxd14kb x1vej5at",
26
+ kfmiAY: "x1ga7v0g x1tzxhge",
27
+ kT0f0o: "x16uus16 x1rmb4wm",
23
28
  kXnDq0: "x1rm9qnc",
24
29
  $$css: true
25
30
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Return the corrective portal target for a layer's intended inline parent.
3
+ *
4
+ * A null result means the inline parent and all of its ancestors are safe, so
5
+ * the layer should stay at its JSX position. Otherwise the result is the
6
+ * nearest element outside every unsafe ancestor around that position.
7
+ *
8
+ * Walking up from the actual render position (rather than the trigger, or
9
+ * portaling to `document.body`) keeps the two things a layer inherits there:
10
+ *
11
+ * - **Theme.** Theme scopes live on DOM ancestors. Staying as close as
12
+ * possible to the intended render position preserves their component rules
13
+ * and custom properties; `document.body` may sit outside a nested scope.
14
+ * - **Tab order.** Sequential focus follows DOM order, so a host near the
15
+ * trigger keeps the layer's focusables next to it. (`show()` also passes
16
+ * the trigger as the popover's invoker `source`, which pins focus order to
17
+ * the invoker in browsers that support it.)
18
+ *
19
+ * The outermost unsafe ancestor matters. A safe div may itself sit inside an
20
+ * anchor; stopping at that div would still put the layer's buttons inside the
21
+ * link. Walking the whole chain ensures the target is outside both.
22
+ */
23
+ export declare function resolveLayerPortalTarget(inlineParent: HTMLElement | null): HTMLElement | null;
24
+ //# sourceMappingURL=layerHost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layerHost.d.ts","sourceRoot":"","sources":["../../src/Layer/layerHost.ts"],"names":[],"mappings":"AAqGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,WAAW,GAAG,IAAI,GAC/B,WAAW,GAAG,IAAI,CAepB"}
@@ -0,0 +1,79 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file layerHost.ts
5
+ * @input Uses the layer's intended inline parent element
6
+ * @output Exports resolveLayerPortalTarget, the corrective portal target
7
+ * @position Layer utility; used by useLayer to place the popover in the DOM
8
+ *
9
+ * SYNC: When modified, update:
10
+ * - /packages/core/src/Layer/layerHost.test.ts
11
+ */
12
+
13
+ /**
14
+ * Ancestors a layer must not be hosted inside.
15
+ *
16
+ * Two overlapping reasons, both verified in Chrome:
17
+ *
18
+ * 1. The HTML parser owns server markup. `<p>` and the heading-ish elements
19
+ * take phrasing content only, so a block element inside one makes the
20
+ * parser close the paragraph and reparent the rest — the layer's content
21
+ * ends up in the page instead of the popover. A nested `<a>` triggers the
22
+ * same tearing through the adoption agency algorithm.
23
+ * 2. Interactive ancestors capture the layer's own interactions. A card
24
+ * hosted inside an `<a>` or `<button>` puts its links and buttons inside
25
+ * that control: clicking one navigates the wrapping link, and every
26
+ * focusable in the card joins the control's own tab stop.
27
+ *
28
+ * Inline formatting elements are on the list for a third, milder reason: a
29
+ * layer hosted in one inherits its typography (font-size, text-align), so a
30
+ * card in a 13px centered paragraph renders 13px and centered.
31
+ */
32
+ const UNSAFE_HOSTS = new Set([
33
+ // Phrasing-content-only containers
34
+ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dt', 'pre', 'legend', 'data', 'dfn', 'meter', 'output', 'progress', 'option', 'optgroup',
35
+ // Structural containers whose direct children are restricted. An inert
36
+ // <template> marker is valid script-supporting content in these positions,
37
+ // but the eventual div/span layer is not.
38
+ 'table', 'thead', 'tbody', 'tfoot', 'tr', 'colgroup', 'ul', 'ol', 'menu', 'dl', 'select', 'datalist', 'picture', 'hgroup', 'ruby', 'rt', 'rp',
39
+ // Interactive containers
40
+ 'a', 'button', 'label', 'summary',
41
+ // Inline formatting context
42
+ 'span', 'em', 'strong', 'b', 'i', 'u', 's', 'small', 'mark', 'code', 'kbd', 'samp', 'var', 'sub', 'sup', 'abbr', 'cite', 'q', 'time', 'bdi', 'bdo', 'ins', 'del']);
43
+
44
+ /**
45
+ * Return the corrective portal target for a layer's intended inline parent.
46
+ *
47
+ * A null result means the inline parent and all of its ancestors are safe, so
48
+ * the layer should stay at its JSX position. Otherwise the result is the
49
+ * nearest element outside every unsafe ancestor around that position.
50
+ *
51
+ * Walking up from the actual render position (rather than the trigger, or
52
+ * portaling to `document.body`) keeps the two things a layer inherits there:
53
+ *
54
+ * - **Theme.** Theme scopes live on DOM ancestors. Staying as close as
55
+ * possible to the intended render position preserves their component rules
56
+ * and custom properties; `document.body` may sit outside a nested scope.
57
+ * - **Tab order.** Sequential focus follows DOM order, so a host near the
58
+ * trigger keeps the layer's focusables next to it. (`show()` also passes
59
+ * the trigger as the popover's invoker `source`, which pins focus order to
60
+ * the invoker in browsers that support it.)
61
+ *
62
+ * The outermost unsafe ancestor matters. A safe div may itself sit inside an
63
+ * anchor; stopping at that div would still put the layer's buttons inside the
64
+ * link. Walking the whole chain ensures the target is outside both.
65
+ */
66
+ export function resolveLayerPortalTarget(inlineParent) {
67
+ if (!inlineParent) {
68
+ return null;
69
+ }
70
+ let outermostUnsafe = null;
71
+ let node = inlineParent;
72
+ while (node) {
73
+ if (UNSAFE_HOSTS.has(node.tagName.toLowerCase())) {
74
+ outermostUnsafe = node;
75
+ }
76
+ node = node.parentElement;
77
+ }
78
+ return outermostUnsafe?.parentElement ?? null;
79
+ }
@@ -94,12 +94,11 @@ export interface ContextRenderProps {
94
94
  /**
95
95
  * HTML tag to render the popover container as.
96
96
  *
97
- * Defaults to `'div'`. Pass `'span'` when the layer must render inline-safe
98
- * markup e.g. a `HoverCard` wrapping inline text inside a `<p>`. A `<span>`
99
- * is phrasing content, so it stays put in the DOM tree instead of being
100
- * reparented out of a paragraph by the HTML parser, which keeps server and
101
- * client markup identical. The Popover API and CSS anchor positioning work
102
- * the same on either tag.
97
+ * Defaults to `'div'`. Context layers render an inert `<template>` marker at
98
+ * the JSX position. The marker's parent is checked before the requested
99
+ * container mounts there or portals outside ancestors that cannot safely
100
+ * contain it. The marker remains available to detect a new parent if the
101
+ * render call moves. With `lazyMount`, the first check waits until `show()`.
103
102
  *
104
103
  * @default 'div'
105
104
  */
@@ -162,6 +161,15 @@ interface BaseLayerOptions {
162
161
  */
163
162
  export interface ContextLayerOptions extends BaseLayerOptions {
164
163
  mode: 'context';
164
+ /**
165
+ * Defer mounting the final layer and resolving its inline/portal position
166
+ * until `show()` is requested. Hiding unmounts it while the inert marker
167
+ * remains at the JSX position. Use this when rich content must never enter
168
+ * an unsafe ancestor, even briefly, and does not need to exist while closed.
169
+ *
170
+ * @default false
171
+ */
172
+ lazyMount?: boolean;
165
173
  }
166
174
  /**
167
175
  * Options for fixed mode (manual positioning)
@@ -1 +1 @@
1
- {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"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"}
@@ -15,10 +15,12 @@
15
15
  */
16
16
  import React, { useCallback, useEffect, useId, useRef, useState } from 'react';
17
17
  import * as stylex from '@stylexjs/stylex';
18
+ import { createPortal } from 'react-dom';
18
19
  import { addAnchorName, removeAnchorName } from "./anchorName.js";
20
+ import { resolveLayerPortalTarget } from "./layerHost.js";
19
21
  import "../theme/tokens.stylex.js";
20
- import { typographyVars } from "../theme/tokens.stylex.js";
21
- import { jsx as _jsx } from "react/jsx-runtime";
22
+ import { typeScaleVars, typographyVars } from "../theme/tokens.stylex.js";
23
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
22
24
  const _temp = {
23
25
  keoZOQ: "x1vhfslr",
24
26
  k1K539: "xlm3tn6",
@@ -38,6 +40,8 @@ const styles = {
38
40
  ksu8eU: "xng3xce",
39
41
  kVQacm: "x1rea2x4",
40
42
  kMv6JI: "x9ynric",
43
+ kGuDYH: "xjm74w1",
44
+ kLWn49: "xw6l6zx",
41
45
  kWkggS: "xjbqb8w",
42
46
  $$css: true
43
47
  },
@@ -100,17 +104,40 @@ const styles = {
100
104
  function toCssLength(value) {
101
105
  return typeof value === 'number' ? `${value}px` : value;
102
106
  }
107
+ function readPortalWritingContext(element, portalTarget) {
108
+ const view = element.ownerDocument.defaultView;
109
+ if (!view) {
110
+ return {};
111
+ }
112
+ const sourceStyle = view.getComputedStyle(element);
113
+ const targetStyle = view.getComputedStyle(portalTarget);
114
+
115
+ // Do not snapshot custom properties here. The portal target is the closest
116
+ // safe ancestor, so theme variables continue to inherit and update there.
117
+ // These two properties can be set on the unsafe chain itself and directly
118
+ // affect the logical anchor-positioning keywords used by the layer. Only
119
+ // override values the portal would actually lose; matching values should
120
+ // keep inheriting from the target so later direction changes remain live.
121
+ return {
122
+ ...(sourceStyle.direction !== targetStyle.direction && {
123
+ direction: sourceStyle.direction
124
+ }),
125
+ ...(sourceStyle.writingMode !== targetStyle.writingMode && {
126
+ writingMode: sourceStyle.writingMode
127
+ })
128
+ };
129
+ }
103
130
 
104
131
  /**
105
132
  * Map logical placement/alignment to a CSS position-area value.
106
133
  *
107
134
  * Uses the self-* logical keyword family: the inline axis resolves against
108
- * the popover's own inherited direction (the layer renders inside the
109
- * trigger's subtree, so it inherits `direction` and mirrors in RTL with no
110
- * JS). The block axis is direction-neutral but must come from the same
111
- * keyword family — mixing physical `top` with `self-inline-*` produces an
112
- * invalid position-area (computes to `none`, which pins the popover to the
113
- * viewport corner because styles.base zeroes the UA margins).
135
+ * the popover's own direction (inherited inline or preserved when portaled),
136
+ * so it mirrors in RTL without placement-specific JS. The block axis is
137
+ * direction-neutral but must come from the same keyword family — mixing
138
+ * physical `top` with `self-inline-*` produces an invalid position-area
139
+ * (computes to `none`, which pins the popover to the viewport corner because
140
+ * styles.base zeroes the UA margins).
114
141
  *
115
142
  * Note the plain logical family (`inline-start`, no `self-`) is NOT a
116
143
  * substitute: it resolves against the containing block — the page root for
@@ -182,36 +209,101 @@ export function useLayer(options) {
182
209
  onHide,
183
210
  lightDismiss = false
184
211
  } = options;
212
+ const lazyMount = mode === 'context' ? options.lazyMount ?? false : false;
185
213
  const id = useId();
186
214
  const anchorId = `--astryx-layer-${id.replace(/:/g, '')}`;
187
215
  const [isOpen, setIsOpen] = useState(false);
188
216
  const popoverRef = useRef(null);
217
+ // The DOM element on which the current logical open state was applied.
218
+ // A portal target change replaces the popover element; retaining the old
219
+ // reference lets the ref callback recognize and reopen its replacement.
220
+ const openedPopoverRef = useRef(null);
189
221
  const triggerRef = useRef(null);
222
+ // Context layers place a persistent inert marker at their real JSX position.
223
+ // Its parent tells us whether the final layer can stay inline or needs a
224
+ // corrective portal, including after the render call moves.
225
+ const sentinelRef = useRef(null);
226
+ const contextMountRef = useRef(null);
227
+ const [contextMount, setContextMount] = useState(null);
228
+ // A show() that arrives before the final layer mounts is replayed when its
229
+ // popover ref attaches.
230
+ const pendingShowRef = useRef(false);
190
231
 
191
232
  // Ref mirrors isOpen for synchronous reads inside show/hide.
192
233
  // State drives re-renders; the ref lets the imperative calls avoid
193
234
  // stale-closure reads of the previous isOpen value.
194
235
  const isOpenRef = useRef(false);
236
+ const showPopoverElement = useCallback(popover => {
237
+ // Finding infra-4: the Popover API is unsupported on Safari <17 and
238
+ // Firefox <125. On those browsers `showPopover` does not exist, so fall
239
+ // back to plain visibility instead of throwing.
240
+ if (typeof popover.showPopover === 'function') {
241
+ // The trigger is passed as the popover's invoker `source`: a layer
242
+ // hosted away from its trigger then still takes its sequential focus
243
+ // order (and its popover nesting) from the trigger rather than from its
244
+ // own DOM position. Browsers without the option ignore it.
245
+ popover.showPopover({
246
+ source: triggerRef.current ?? undefined
247
+ });
248
+ } else {
249
+ popover.style.display = 'block';
250
+ }
251
+ openedPopoverRef.current = popover;
252
+ }, []);
253
+ const isCurrentContextPopover = useCallback(popover => {
254
+ if (mode !== 'context') {
255
+ return true;
256
+ }
257
+ const mount = contextMountRef.current;
258
+ if (mount === null) {
259
+ return false;
260
+ }
261
+ const expectedParent = mount.portalTarget ?? sentinelRef.current?.parentElement ?? null;
262
+ return popover.parentElement === expectedParent;
263
+ }, [mode]);
264
+ const requestContextMount = useCallback(() => {
265
+ if (mode !== 'context') {
266
+ return;
267
+ }
268
+ const sentinel = sentinelRef.current;
269
+ const inlineParent = sentinel?.parentElement ?? null;
270
+ if (!sentinel || !inlineParent) {
271
+ return;
272
+ }
273
+ const portalTarget = resolveLayerPortalTarget(inlineParent);
274
+ const mount = {
275
+ portalTarget,
276
+ portalStyle: portalTarget ? readPortalWritingContext(sentinel, portalTarget) : {}
277
+ };
278
+ contextMountRef.current = mount;
279
+ setContextMount(mount);
280
+ }, [mode]);
281
+ const clearContextMount = useCallback(() => {
282
+ if (mode !== 'context' || !lazyMount) {
283
+ return;
284
+ }
285
+ contextMountRef.current = null;
286
+ setContextMount(null);
287
+ }, [mode, lazyMount]);
195
288
  const show = useCallback(() => {
196
- const popover = popoverRef.current;
197
- if (popover && !isOpenRef.current) {
198
- // Finding infra-4: the Popover API is unsupported on Safari <17 and
199
- // Firefox <125. On those browsers `showPopover` does not exist, so
200
- // calling it unconditionally throws a TypeError and the layer never
201
- // opens. Guard behind a feature check; when the API is missing, fall
202
- // back to plain visibility (the [popover] attribute is inert there, so
203
- // the element sits in normal flow) so the layer still becomes visible.
204
- if (typeof popover.showPopover === 'function') {
205
- popover.showPopover();
206
- } else {
207
- popover.style.display = 'block';
208
- }
289
+ // A context popover left over until React commits a previous hide must not
290
+ // be reopened. The synchronous mount ref is the source of truth.
291
+ const candidate = popoverRef.current;
292
+ const popover = candidate && isCurrentContextPopover(candidate) ? candidate : null;
293
+ if (!popover) {
294
+ pendingShowRef.current = true;
295
+ requestContextMount();
296
+ return;
297
+ }
298
+ if (!isOpenRef.current) {
299
+ showPopoverElement(popover);
209
300
  isOpenRef.current = true;
210
301
  setIsOpen(true);
211
302
  onShow?.();
212
303
  }
213
- }, [onShow]);
304
+ }, [onShow, requestContextMount, showPopoverElement, isCurrentContextPopover]);
214
305
  const hide = useCallback(() => {
306
+ pendingShowRef.current = false;
215
307
  if (isOpenRef.current) {
216
308
  const el = popoverRef.current;
217
309
  // See finding infra-4 note in `show`: mirror the same guard on hide so
@@ -223,11 +315,13 @@ export function useLayer(options) {
223
315
  el.style.display = 'none';
224
316
  }
225
317
  }
318
+ openedPopoverRef.current = null;
226
319
  isOpenRef.current = false;
227
320
  setIsOpen(false);
228
321
  onHide?.();
229
322
  }
230
- }, [onHide]);
323
+ clearContextMount();
324
+ }, [onHide, clearContextMount]);
231
325
 
232
326
  // Ref for trigger element (context mode only)
233
327
  const ref = mode === 'context' ? el => {
@@ -255,11 +349,13 @@ export function useLayer(options) {
255
349
  const handleToggle = useCallback(e => {
256
350
  const toggleEvent = e;
257
351
  if (toggleEvent.newState === 'closed' && isOpenRef.current) {
352
+ openedPopoverRef.current = null;
258
353
  isOpenRef.current = false;
259
354
  setIsOpen(false);
260
355
  onHide?.();
356
+ clearContextMount();
261
357
  }
262
- }, [onHide]);
358
+ }, [onHide, clearContextMount]);
263
359
 
264
360
  // Ref callback for popover element — sets up the `toggle` listener.
265
361
  // Tracks the element + handler currently bound so the listener is removed
@@ -283,7 +379,24 @@ export function useLayer(options) {
283
379
  const popoverRefCallback = useCallback(el => {
284
380
  popoverRef.current = el;
285
381
  bindToggleListener(el, handleToggle);
286
- }, [handleToggle, bindToggleListener]);
382
+ if (el && pendingShowRef.current) {
383
+ pendingShowRef.current = false;
384
+ show();
385
+ } else if (el && isOpenRef.current && openedPopoverRef.current !== el && isCurrentContextPopover(el)) {
386
+ // Changing a portal target remounts the popover. Preserve the logical
387
+ // open state without firing onShow again for the replacement element.
388
+ showPopoverElement(el);
389
+ }
390
+ }, [handleToggle, bindToggleListener, show, showPopoverElement, isCurrentContextPopover]);
391
+ const sentinelRefCallback = useCallback(el => {
392
+ sentinelRef.current = el;
393
+ if (el && (!lazyMount || pendingShowRef.current || isOpenRef.current)) {
394
+ // The render call may have moved while the hook stayed mounted.
395
+ // Resolve again from the newly attached marker rather than reusing a
396
+ // portal target from its previous JSX position.
397
+ requestContextMount();
398
+ }
399
+ }, [lazyMount, requestContextMount]);
287
400
 
288
401
  // Re-bind when the handler identity changes while the element stays mounted,
289
402
  // and detach on unmount.
@@ -302,6 +415,17 @@ export function useLayer(options) {
302
415
 
303
416
  // Render function for context mode
304
417
  const renderContext = useCallback((children, props) => {
418
+ // Keep the marker mounted after resolving the layer. Apart from giving
419
+ // us the real JSX parent on first show, this lets its ref report when a
420
+ // persistent hook's render call moves to a different host.
421
+ const sentinel = /*#__PURE__*/_jsx("template", {
422
+ ref: sentinelRefCallback
423
+ });
424
+ if (contextMount === null) {
425
+ return /*#__PURE__*/_jsx(_Fragment, {
426
+ children: sentinel
427
+ });
428
+ }
305
429
  const {
306
430
  placement = 'above',
307
431
  alignment = 'center',
@@ -332,10 +456,10 @@ export function useLayer(options) {
332
456
  const stylexResult = stylex.props(styles.base, offsetStyle, xstyle);
333
457
  const combinedClassName = extraClassName ? `${extraClassName} ${stylexResult.className ?? ''}` : stylexResult.className;
334
458
 
335
- // Render as the requested tag. A `span` keeps the layer phrasing content
336
- // so it is valid (and stays put on hydration) inside inline contexts like
337
- // a `<p>`; `div` remains the default for block layers.
338
- return /*#__PURE__*/_jsx(Container, {
459
+ // The marker gives us the actual JSX parent without mounting arbitrary
460
+ // children there. Safe positions preserve the existing DOM order and
461
+ // cascade; unsafe positions use the nearest corrective portal target.
462
+ const layer = /*#__PURE__*/_jsx(Container, {
339
463
  ref: popoverRefCallback,
340
464
  id: id,
341
465
  role: role,
@@ -345,13 +469,17 @@ export function useLayer(options) {
345
469
  style: {
346
470
  ...stylexResult.style,
347
471
  ...anchorStyle,
472
+ ...contextMount.portalStyle,
348
473
  ...extraStyle
349
474
  },
350
475
  onMouseEnter: onMouseEnter,
351
476
  onMouseLeave: onMouseLeave,
352
477
  children: children
353
478
  });
354
- }, [anchorId, id, lightDismiss, popoverRefCallback]);
479
+ return /*#__PURE__*/_jsxs(_Fragment, {
480
+ children: [sentinel, contextMount.portalTarget ? /*#__PURE__*/createPortal(layer, contextMount.portalTarget) : layer]
481
+ });
482
+ }, [anchorId, contextMount, id, lightDismiss, popoverRefCallback, sentinelRefCallback]);
355
483
 
356
484
  // Render function for fixed mode
357
485
  const renderFixed = useCallback((children, props) => {
@@ -9,8 +9,13 @@
9
9
  * disabled state) so all nav-like components stay in sync — especially important
10
10
  * when TopNav items render inside MobileNav drawers alongside SideNav items.
11
11
  *
12
- * Individual components layer their own overrides (e.g. collapsed mode, indentation,
13
- * focus outlines) via stylex.props composition.
12
+ * Individual components layer their own overrides (e.g. collapsed mode,
13
+ * indentation) via stylex.props composition.
14
+ *
15
+ * The focus ring is not defined here. Compose
16
+ * `focusOutlineProps.focusVisible` (utils/focusOutline.stylex.ts) at the call
17
+ * site, on whichever element actually takes focus — in a split-action row that
18
+ * is the link and the toggle, not the row that contains them.
14
19
  */
15
20
  export type NavItemSize = 'sm' | 'md' | 'lg';
16
21
  import * as stylex from '@stylexjs/stylex';
@@ -63,15 +68,22 @@ export declare const navItemStyles: Readonly<{
63
68
  }>;
64
69
  }>;
65
70
  readonly selected: Readonly<{
66
- readonly backgroundColor: stylex.StyleXClassNameFor<"backgroundColor", "light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))">;
71
+ readonly backgroundColor: stylex.StyleXClassNameFor<"backgroundColor", "light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))" | "Highlight">;
72
+ readonly color: stylex.StyleXClassNameFor<"color", "HighlightText" | null>;
67
73
  readonly fontWeight: stylex.StyleXClassNameFor<"fontWeight", "500">;
68
74
  readonly ':hover': stylex.StyleXClassNameFor<":hover", {
69
75
  readonly '@media (hover: hover)': {
70
- readonly backgroundColor: stylex.StyleXVar<"light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))">;
76
+ readonly backgroundColor: {
77
+ readonly default: stylex.StyleXVar<"light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))">;
78
+ readonly '@media (forced-colors: active)': "Highlight";
79
+ };
71
80
  };
72
81
  }>;
73
82
  readonly ':active': stylex.StyleXClassNameFor<":active", {
74
- readonly backgroundColor: stylex.StyleXVar<"light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))">;
83
+ readonly backgroundColor: {
84
+ readonly default: stylex.StyleXVar<"light-dark(rgba(5, 54, 89, 0.1), rgba(223, 226, 229, 0.2))">;
85
+ readonly '@media (forced-colors: active)': "Highlight";
86
+ };
75
87
  }>;
76
88
  }>;
77
89
  readonly disabled: Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"navItemStyles.stylex.d.ts","sourceRoot":"","sources":["../../src/NavItem/navItemStyles.stylex.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAU3C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuExB,CAAC"}
1
+ {"version":3,"file":"navItemStyles.stylex.d.ts","sourceRoot":"","sources":["../../src/NavItem/navItemStyles.stylex.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAU3C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4FxB,CAAC"}
@@ -11,8 +11,13 @@
11
11
  * disabled state) so all nav-like components stay in sync — especially important
12
12
  * when TopNav items render inside MobileNav drawers alongside SideNav items.
13
13
  *
14
- * Individual components layer their own overrides (e.g. collapsed mode, indentation,
15
- * focus outlines) via stylex.props composition.
14
+ * Individual components layer their own overrides (e.g. collapsed mode,
15
+ * indentation) via stylex.props composition.
16
+ *
17
+ * The focus ring is not defined here. Compose
18
+ * `focusOutlineProps.focusVisible` (utils/focusOutline.stylex.ts) at the call
19
+ * site, on whichever element actually takes focus — in a split-action row that
20
+ * is the link and the toggle, not the row that contains them.
16
21
  */
17
22
 
18
23
  import * as stylex from '@stylexjs/stylex';
@@ -63,10 +68,11 @@ export const navItemStyles = {
63
68
  $$css: true
64
69
  },
65
70
  selected: {
66
- kWkggS: "x17x4s8c",
71
+ kWkggS: "x17x4s8c x1jzqe4",
72
+ kMwMTN: "x1k5gbb1",
67
73
  k63SB2: "x1e4wzip",
68
- kHE3J0: "x1sua13m",
69
- kSReZ0: "xjtnuge",
74
+ kHE3J0: "x1sua13m x1jawyga",
75
+ kSReZ0: "xjtnuge x1vo6n7o",
70
76
  $$css: true
71
77
  },
72
78
  disabled: {