@astryxdesign/core 0.4.1 → 0.4.2-canary.01592f7

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 (217) 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/Chat/ChatTokenizedText.js +1 -1
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +5 -0
  17. package/dist/CommandPalette/CommandPaletteFooter.d.ts.map +1 -1
  18. package/dist/CommandPalette/CommandPaletteFooter.js +5 -3
  19. package/dist/ComplexSelector/ComplexSelector.d.ts +38 -4
  20. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  21. package/dist/ComplexSelector/ComplexSelector.js +96 -35
  22. package/dist/ComplexSelector/index.d.ts +2 -2
  23. package/dist/ComplexSelector/index.d.ts.map +1 -1
  24. package/dist/ComplexSelector/index.js +1 -1
  25. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  26. package/dist/DateTimeInput/DateTimeInput.js +2 -2
  27. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  28. package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -15
  29. package/dist/HoverCard/HoverCard.d.ts +1 -1
  30. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  31. package/dist/HoverCard/HoverCard.js +5 -13
  32. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  33. package/dist/HoverCard/useHoverCard.js +6 -5
  34. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  35. package/dist/InputGroup/groupStyles.js +7 -2
  36. package/dist/Layer/layerHost.d.ts +24 -0
  37. package/dist/Layer/layerHost.d.ts.map +1 -0
  38. package/dist/Layer/layerHost.js +79 -0
  39. package/dist/Layer/useLayer.d.ts +14 -6
  40. package/dist/Layer/useLayer.d.ts.map +1 -1
  41. package/dist/Layer/useLayer.js +158 -30
  42. package/dist/Markdown/parser.d.ts.map +1 -1
  43. package/dist/Markdown/parser.js +55 -12
  44. package/dist/NavItem/navItemStyles.stylex.d.ts +17 -5
  45. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  46. package/dist/NavItem/navItemStyles.stylex.js +11 -5
  47. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  48. package/dist/RadioList/RadioListItem.js +5 -0
  49. package/dist/SideNav/SideNav.d.ts +7 -9
  50. package/dist/SideNav/SideNav.d.ts.map +1 -1
  51. package/dist/SideNav/SideNav.js +32 -5
  52. package/dist/SideNav/SideNavCollapseButton.d.ts +25 -9
  53. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  54. package/dist/SideNav/SideNavCollapseButton.js +39 -15
  55. package/dist/SideNav/SideNavCollapseContext.d.ts +21 -0
  56. package/dist/SideNav/SideNavCollapseContext.d.ts.map +1 -1
  57. package/dist/SideNav/SideNavCollapseContext.js +16 -2
  58. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  59. package/dist/SideNav/SideNavHeading.js +89 -32
  60. package/dist/SideNav/SideNavItem.d.ts +7 -2
  61. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  62. package/dist/SideNav/SideNavItem.js +119 -75
  63. package/dist/SideNav/SideNavSection.d.ts.map +1 -1
  64. package/dist/SideNav/SideNavSection.js +7 -16
  65. package/dist/SideNav/index.d.ts +1 -1
  66. package/dist/SideNav/index.d.ts.map +1 -1
  67. package/dist/Slider/Slider.d.ts.map +1 -1
  68. package/dist/Slider/Slider.js +56 -15
  69. package/dist/Switch/Switch.d.ts.map +1 -1
  70. package/dist/Switch/Switch.js +5 -0
  71. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  72. package/dist/Thumbnail/Thumbnail.js +5 -0
  73. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  74. package/dist/TopNav/TopNavHeading.js +14 -6
  75. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  76. package/dist/TopNav/TopNavMegaMenu.js +25 -93
  77. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  78. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  79. package/dist/TopNav/TopNavMenu.js +10 -5
  80. package/dist/astryx.css +40 -8
  81. package/dist/astryx.umd.js +50 -50
  82. package/dist/astryx.umd.js.map +4 -4
  83. package/dist/hooks/containerReveal.stylex.d.ts +71 -4
  84. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  85. package/dist/hooks/containerReveal.stylex.js +57 -5
  86. package/dist/hooks/index.d.ts +2 -2
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/index.js +1 -1
  89. package/dist/hooks/useContainerReveal.d.ts +60 -3
  90. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  91. package/dist/hooks/useContainerReveal.js +27 -5
  92. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  93. package/dist/hooks/useFocusTrap.js +16 -2
  94. package/dist/hooks/useMenuHover.d.ts +50 -5
  95. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  96. package/dist/hooks/useMenuHover.js +171 -51
  97. package/dist/theme/defineTheme.d.ts +16 -5
  98. package/dist/theme/defineTheme.d.ts.map +1 -1
  99. package/dist/theme/defineTheme.js +44 -48
  100. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  101. package/dist/theme/derivedVarRegistry.js +7 -0
  102. package/dist/theme/expandColorScale.d.ts.map +1 -1
  103. package/dist/theme/expandColorScale.js +1 -0
  104. package/dist/theme/expandMotionScale.d.ts +1 -0
  105. package/dist/theme/expandMotionScale.d.ts.map +1 -1
  106. package/dist/theme/expandMotionScale.js +1 -0
  107. package/dist/theme/expandRadiusScale.d.ts +1 -0
  108. package/dist/theme/expandRadiusScale.d.ts.map +1 -1
  109. package/dist/theme/expandRadiusScale.js +1 -0
  110. package/dist/theme/expandTypeScale.d.ts +1 -0
  111. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  112. package/dist/theme/expandTypeScale.js +1 -0
  113. package/dist/theme/mergeComponents.d.ts +20 -0
  114. package/dist/theme/mergeComponents.d.ts.map +1 -0
  115. package/dist/theme/mergeComponents.js +56 -0
  116. package/dist/theme/onMediaTokens.d.ts +6 -1
  117. package/dist/theme/onMediaTokens.d.ts.map +1 -1
  118. package/dist/theme/onMediaTokens.js +11 -3
  119. package/dist/theme/tokens.stylex.d.ts +3 -1
  120. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  121. package/dist/theme/tokens.stylex.js +3 -1
  122. package/locales/en.json +84 -0
  123. package/locales/pseudo.json +63 -0
  124. package/package.json +3 -3
  125. package/src/Avatar/Avatar.doc.mjs +5 -2
  126. package/src/Avatar/Avatar.test.tsx +51 -0
  127. package/src/Avatar/Avatar.tsx +30 -20
  128. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  129. package/src/AvatarGroup/AvatarGroup.test.tsx +14 -0
  130. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -2
  131. package/src/Button/Button.tsx +5 -3
  132. package/src/ButtonGroup/ButtonGroup.test.tsx +7 -4
  133. package/src/Card/Card.doc.mjs +2 -0
  134. package/src/Chat/Chat.doc.mjs +4 -1
  135. package/src/Chat/ChatMessage.doc.mjs +3 -3
  136. package/src/Chat/ChatMessage.tsx +7 -0
  137. package/src/Chat/ChatMessageBubble.doc.mjs +7 -0
  138. package/src/Chat/ChatMessageBubble.test.tsx +95 -6
  139. package/src/Chat/ChatMessageBubble.tsx +25 -0
  140. package/src/Chat/ChatTokenizedText.tsx +1 -1
  141. package/src/CheckboxInput/CheckboxInput.tsx +20 -0
  142. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  143. package/src/CommandPalette/CommandPaletteFooter.tsx +6 -3
  144. package/src/ComplexSelector/ComplexSelector.doc.mjs +55 -6
  145. package/src/ComplexSelector/ComplexSelector.test.tsx +197 -6
  146. package/src/ComplexSelector/ComplexSelector.tsx +153 -32
  147. package/src/ComplexSelector/index.ts +3 -1
  148. package/src/DateTimeInput/DateTimeInput.tsx +4 -2
  149. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +72 -1
  150. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +24 -20
  151. package/src/HoverCard/HoverCard.doc.mjs +3 -3
  152. package/src/HoverCard/HoverCard.test.tsx +283 -55
  153. package/src/HoverCard/HoverCard.tsx +5 -13
  154. package/src/HoverCard/useHoverCard.tsx +8 -11
  155. package/src/InputGroup/groupStyles.ts +7 -8
  156. package/src/Item/Item.doc.mjs +4 -0
  157. package/src/Layer/layerHost.test.ts +99 -0
  158. package/src/Layer/layerHost.ts +141 -0
  159. package/src/Layer/useLayer.doc.mjs +14 -4
  160. package/src/Layer/useLayer.test.tsx +332 -7
  161. package/src/Layer/useLayer.tsx +235 -36
  162. package/src/Markdown/parser.test.ts +53 -0
  163. package/src/Markdown/parser.ts +53 -12
  164. package/src/MobileNav/MobileNav.doc.mjs +7 -0
  165. package/src/NavItem/navItemStyles.stylex.ts +31 -5
  166. package/src/RadioList/RadioListItem.tsx +20 -0
  167. package/src/SideNav/SideNav.doc.mjs +13 -4
  168. package/src/SideNav/SideNav.test.tsx +858 -2
  169. package/src/SideNav/SideNav.tsx +37 -16
  170. package/src/SideNav/SideNavCollapseButton.doc.mjs +28 -6
  171. package/src/SideNav/SideNavCollapseButton.tsx +67 -15
  172. package/src/SideNav/SideNavCollapseContext.ts +29 -9
  173. package/src/SideNav/SideNavHeading.tsx +53 -25
  174. package/src/SideNav/SideNavItem.doc.mjs +13 -0
  175. package/src/SideNav/SideNavItem.tsx +97 -84
  176. package/src/SideNav/SideNavSection.tsx +6 -20
  177. package/src/SideNav/index.ts +2 -0
  178. package/src/Slider/Slider.test.tsx +69 -33
  179. package/src/Slider/Slider.tsx +65 -13
  180. package/src/Switch/Switch.tsx +20 -0
  181. package/src/TabList/TabList.doc.mjs +3 -0
  182. package/src/Text/Text.doc.mjs +1 -1
  183. package/src/Thumbnail/Thumbnail.doc.mjs +3 -0
  184. package/src/Thumbnail/Thumbnail.tsx +10 -0
  185. package/src/Timestamp/Timestamp.test.tsx +40 -22
  186. package/src/Toolbar/Toolbar.test.tsx +14 -14
  187. package/src/TopNav/TopNav.test.tsx +86 -1
  188. package/src/TopNav/TopNavHeading.tsx +21 -14
  189. package/src/TopNav/TopNavMegaMenu.test.tsx +43 -0
  190. package/src/TopNav/TopNavMegaMenu.tsx +29 -105
  191. package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
  192. package/src/TopNav/TopNavMenu.test.tsx +41 -0
  193. package/src/TopNav/TopNavMenu.tsx +14 -4
  194. package/src/TreeList/TreeList.doc.mjs +1 -0
  195. package/src/hooks/containerReveal.stylex.ts +140 -9
  196. package/src/hooks/index.ts +6 -1
  197. package/src/hooks/useContainerReveal.doc.mjs +11 -5
  198. package/src/hooks/useContainerReveal.test.tsx +70 -3
  199. package/src/hooks/useContainerReveal.ts +86 -7
  200. package/src/hooks/useFocusTrap.test.tsx +16 -0
  201. package/src/hooks/useFocusTrap.ts +20 -2
  202. package/src/hooks/useMenuHover.test.tsx +364 -0
  203. package/src/hooks/useMenuHover.ts +243 -47
  204. package/src/theme/defineTheme.test.ts +127 -0
  205. package/src/theme/defineTheme.ts +57 -51
  206. package/src/theme/derivedVarRegistry.test.ts +78 -12
  207. package/src/theme/derivedVarRegistry.ts +4 -0
  208. package/src/theme/expandColorScale.ts +1 -0
  209. package/src/theme/expandMotionScale.ts +1 -0
  210. package/src/theme/expandRadiusScale.ts +1 -0
  211. package/src/theme/expandTypeScale.ts +1 -0
  212. package/src/theme/mergeComponents.ts +59 -0
  213. package/src/theme/onMediaTokens.ts +9 -2
  214. package/src/theme/themingTargets.test.ts +152 -26
  215. package/src/theme/tokens.stylex.ts +3 -1
  216. package/src/theme/tokens.test.ts +12 -0
  217. package/src/theme/useTheme.test.tsx +18 -0
@@ -32,6 +32,7 @@ import { MobileNav } from "../MobileNav/MobileNav.js";
32
32
  import { useResizable } from "../Resizable/useResizable.js";
33
33
  import { ResizeHandle } from "../Resizable/ResizeHandle.js";
34
34
  import { themeProps } from "../utils/themeProps.js";
35
+ import { SizeProvider } from "../SizeContext/SizeContext.js";
35
36
  import { useTranslator } from "../i18n/index.js";
36
37
 
37
38
  // =============================================================================
@@ -75,6 +76,13 @@ const styles = {
75
76
  }
76
77
  };
77
78
 
79
+ /**
80
+ * Cascaded to the icon rows through `SizeContext` so the built-in collapse
81
+ * button and the consumer's `footerIcons` come out one height. An explicit
82
+ * `size` on a child still wins.
83
+ */
84
+ const FOOTER_ICON_SIZE = 'sm';
85
+
78
86
  // =============================================================================
79
87
  // Types
80
88
  // =============================================================================
@@ -160,6 +168,9 @@ export function SideNav({
160
168
  });
161
169
  const toggle = useCallback(() => {
162
170
  const next = !collapsed;
171
+
172
+ // Deprecated `handleRef` path only: an out-of-tree button reads this
173
+ // snapshot while rendering, which can happen before SideNav re-renders.
163
174
  collapseStateRef.current = {
164
175
  ...collapseStateRef.current,
165
176
  isCollapsed: next
@@ -204,7 +215,10 @@ export function SideNav({
204
215
  ...{
205
216
  className: "x78zum5 x6s0dn4 xzye2dw xvc5jky"
206
217
  },
207
- children: footerIcons
218
+ children: /*#__PURE__*/_jsx(SizeProvider, {
219
+ value: FOOTER_ICON_SIZE,
220
+ children: footerIcons
221
+ })
208
222
  })]
209
223
  });
210
224
  }
@@ -217,6 +231,10 @@ export function SideNav({
217
231
  return /*#__PURE__*/_jsxs(MobileNav, {
218
232
  header: header,
219
233
  "data-testid": testId,
234
+ xstyle: xstyle,
235
+ className: className,
236
+ style: style,
237
+ ...props,
220
238
  children: [topContent, children, hasDrawerFooter && /*#__PURE__*/_jsxs("div", {
221
239
  ...{
222
240
  className: "x78zum5 xdt5ytf xr1yuqi x1txdalj x1xye8es"
@@ -225,7 +243,10 @@ export function SideNav({
225
243
  ...{
226
244
  className: "x78zum5 x6s0dn4 xzye2dw"
227
245
  },
228
- children: footerIcons
246
+ children: /*#__PURE__*/_jsx(SizeProvider, {
247
+ value: FOOTER_ICON_SIZE,
248
+ children: footerIcons
249
+ })
229
250
  })]
230
251
  })]
231
252
  });
@@ -245,7 +266,10 @@ export function SideNav({
245
266
  ...{
246
267
  className: "x78zum5 x6s0dn4 xzye2dw"
247
268
  },
248
- children: footerIcons
269
+ children: /*#__PURE__*/_jsx(SizeProvider, {
270
+ value: FOOTER_ICON_SIZE,
271
+ children: footerIcons
272
+ })
249
273
  })]
250
274
  })]
251
275
  });
@@ -323,7 +347,7 @@ export function SideNav({
323
347
  className: "x78zum5 xdt5ytf x2lah0s xr1yuqi x7wzq59 x1ey2m1c xgcd1z6 x1txdalj xf314gf x1wesfrj xexx8yu x6s0dn4"
324
348
  }
325
349
  }[!!collapsed << 0],
326
- children: [footer, /*#__PURE__*/_jsxs("div", {
350
+ children: [footer, /*#__PURE__*/_jsx("div", {
327
351
  ...{
328
352
  0: {
329
353
  className: "x78zum5 x6s0dn4 xzye2dw"
@@ -332,7 +356,10 @@ export function SideNav({
332
356
  className: "x78zum5 x6s0dn4 xzye2dw x3ieub6"
333
357
  }
334
358
  }[!!collapsed << 0],
335
- children: [showCollapseButton && /*#__PURE__*/_jsx(SideNavCollapseButton, {}), footerIcons]
359
+ children: /*#__PURE__*/_jsxs(SizeProvider, {
360
+ value: FOOTER_ICON_SIZE,
361
+ children: [showCollapseButton && /*#__PURE__*/_jsx(SideNavCollapseButton, {}), footerIcons]
362
+ })
336
363
  })]
337
364
  })]
338
365
  });
@@ -5,7 +5,7 @@
5
5
  * @position Composable toggle button for sidenav collapse
6
6
  *
7
7
  * Place inside SideNav (reads context automatically) or outside
8
- * (pass handleRef to connect). Customizable via label/children.
8
+ * (pass the same controlled `collapsible` config both get).
9
9
  *
10
10
  * SYNC: When modified, update:
11
11
  * - /packages/core/src/SideNav/SideNav.doc.mjs
@@ -14,12 +14,20 @@
14
14
  */
15
15
  import React, { type ReactNode } from 'react';
16
16
  import type { BaseProps } from '../BaseProps';
17
- import { type SideNavImperativeCollapseHandle } from './SideNavCollapseContext';
17
+ import { type SideNavControlledCollapsible, type SideNavImperativeCollapseHandle } from './SideNavCollapseContext';
18
+ import type { ElementSize } from '../SizeContext/SizeContext';
18
19
  export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement> {
19
20
  ref?: React.Ref<HTMLButtonElement>;
20
21
  /**
21
- * Imperative handle from SideNav. Only needed when the button is rendered
22
- * outside the sidenav, where collapse context is unavailable.
22
+ * The same controlled `collapsible` config given to SideNav
23
+ * (`{isCollapsed, onCollapsedChange}`). Needed only when the button is
24
+ * rendered outside the sidenav, where collapse context cannot reach it.
25
+ */
26
+ collapsible?: SideNavControlledCollapsible;
27
+ /**
28
+ * Imperative handle from SideNav.
29
+ *
30
+ * @deprecated Pass `collapsible` instead.
23
31
  */
24
32
  handleRef?: React.RefObject<SideNavImperativeCollapseHandle | null>;
25
33
  /**
@@ -27,6 +35,12 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
27
35
  * with the chevron icon. When omitted, renders as an icon-only button.
28
36
  */
29
37
  label?: string;
38
+ /**
39
+ * Button size. Defaults to the size its container cascades — `sm` in a
40
+ * SideNav footer — or `md` outside one. Set it for placements with no row
41
+ * to inherit from, e.g. a button placed in a `TopNav`.
42
+ */
43
+ size?: ElementSize;
30
44
  /**
31
45
  * Custom button content. Overrides the default chevron icon and label.
32
46
  */
@@ -37,7 +51,8 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
37
51
  *
38
52
  * Place anywhere inside SideNav (header, topContent, footer, footerIcons)
39
53
  * and it reads collapse state from context automatically. For placement
40
- * outside the sidenav (e.g. in TopNav or content area), pass handleRef.
54
+ * outside the sidenav (e.g. in TopNav or content area), hold the state and
55
+ * hand the same `collapsible` config to both.
41
56
  *
42
57
  * @example
43
58
  * ```
@@ -48,12 +63,13 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
48
63
  *
49
64
  * @example
50
65
  * ```
51
- * const ref = useRef(null);
52
- * <TopNav endContent={<SideNavCollapseButton handleRef={ref} />} />
53
- * <SideNav handleRef={ref} collapsible>...</SideNav>
66
+ * const [isCollapsed, setIsCollapsed] = useState(false);
67
+ * const collapsible = {isCollapsed, onCollapsedChange: setIsCollapsed};
68
+ * <TopNav endContent={<SideNavCollapseButton collapsible={collapsible} />} />
69
+ * <SideNav collapsible={{...collapsible, hasButton: false}}>...</SideNav>
54
70
  * ```
55
71
  */
56
- export declare function SideNavCollapseButton({ ref, handleRef, label, children, onClick: onClickProp, ...props }: SideNavCollapseButtonProps): React.JSX.Element | null;
72
+ export declare function SideNavCollapseButton({ ref, collapsible, handleRef, label, size, children, onClick: onClickProp, ...props }: SideNavCollapseButtonProps): React.JSX.Element | null;
57
73
  export declare namespace SideNavCollapseButton {
58
74
  var displayName: string;
59
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SideNavCollapseButton.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavCollapseButton.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAK5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,EAGL,KAAK,+BAA+B,EACrC,MAAM,0BAA0B,CAAC;AAyBlC,MAAM,WAAW,0BAA2B,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC9E,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAAC;IAEpE;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,GAAG,EACH,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAO,EAAE,WAAW,EACpB,GAAG,KAAK,EACT,EAAE,0BAA0B,4BA8C5B;yBArDe,qBAAqB"}
1
+ {"version":3,"file":"SideNavCollapseButton.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavCollapseButton.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAAc,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAKzD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,EAGL,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACrC,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,4BAA4B,CAAC;AAkC5D,MAAM,WAAW,0BAA2B,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC9E,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;;;OAIG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAC;IAE3C;;;;OAIG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAAC;IAEpE;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,GAAG,EACH,WAAW,EACX,SAAS,EACT,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,OAAO,EAAE,WAAW,EACpB,GAAG,KAAK,EACT,EAAE,0BAA0B,4BAiD5B;yBA1De,qBAAqB"}
@@ -9,14 +9,14 @@
9
9
  * @position Composable toggle button for sidenav collapse
10
10
  *
11
11
  * Place inside SideNav (reads context automatically) or outside
12
- * (pass handleRef to connect). Customizable via label/children.
12
+ * (pass the same controlled `collapsible` config both get).
13
13
  *
14
14
  * SYNC: When modified, update:
15
15
  * - /packages/core/src/SideNav/SideNav.doc.mjs
16
16
  * - /packages/core/src/SideNav/index.ts
17
17
  * - /packages/cli/assets/templates/blocks/components/SideNav/ (showcase blocks)
18
18
  */
19
- import React from 'react';
19
+ import React, { useCallback } from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import "../theme/tokens.stylex.js";
22
22
  import { durationVars, easeVars } from "../theme/tokens.stylex.js";
@@ -26,17 +26,23 @@ import { composeEventHandlers, rtlStyles } from "../utils/index.js";
26
26
  import { useSideNavCollapse } from "./SideNavCollapseContext.js";
27
27
  import { useAppShellMobile } from "../AppShell/AppShellMobileContext.js";
28
28
  import { useTranslator } from "../i18n/index.js";
29
-
29
+ import { jsx as _jsx } from "react/jsx-runtime";
30
30
  // =============================================================================
31
31
  // Styles
32
32
  // =============================================================================
33
- import { jsx as _jsx } from "react/jsx-runtime";
33
+
34
34
  const styles = {
35
+ chevronMirror: {
36
+ k1xSpc: "x78zum5",
37
+ kGNEyG: "x6s0dn4",
38
+ kjj79g: "xl56j7k",
39
+ $$css: true
40
+ },
35
41
  chevron: {
36
42
  k1xSpc: "x3nfvp2",
37
43
  kGNEyG: "x6s0dn4",
38
44
  k1ekBW: "x11xpdln",
39
- kIyJzY: "xuedmi6",
45
+ kIyJzY: "xuedmi6 x12w9bfk",
40
46
  kAMwcw: "xlr8y92",
41
47
  $$css: true
42
48
  },
@@ -59,7 +65,8 @@ const styles = {
59
65
  *
60
66
  * Place anywhere inside SideNav (header, topContent, footer, footerIcons)
61
67
  * and it reads collapse state from context automatically. For placement
62
- * outside the sidenav (e.g. in TopNav or content area), pass handleRef.
68
+ * outside the sidenav (e.g. in TopNav or content area), hold the state and
69
+ * hand the same `collapsible` config to both.
63
70
  *
64
71
  * @example
65
72
  * ```
@@ -70,15 +77,18 @@ const styles = {
70
77
  *
71
78
  * @example
72
79
  * ```
73
- * const ref = useRef(null);
74
- * <TopNav endContent={<SideNavCollapseButton handleRef={ref} />} />
75
- * <SideNav handleRef={ref} collapsible>...</SideNav>
80
+ * const [isCollapsed, setIsCollapsed] = useState(false);
81
+ * const collapsible = {isCollapsed, onCollapsedChange: setIsCollapsed};
82
+ * <TopNav endContent={<SideNavCollapseButton collapsible={collapsible} />} />
83
+ * <SideNav collapsible={{...collapsible, hasButton: false}}>...</SideNav>
76
84
  * ```
77
85
  */
78
86
  export function SideNavCollapseButton({
79
87
  ref,
88
+ collapsible,
80
89
  handleRef,
81
90
  label,
91
+ size,
82
92
  children,
83
93
  onClick: onClickProp,
84
94
  ...props
@@ -88,7 +98,7 @@ export function SideNavCollapseButton({
88
98
  isCollapsed,
89
99
  toggle,
90
100
  isCollapsible
91
- } = useSideNavCollapseState(handleRef);
101
+ } = useSideNavCollapseState(collapsible, handleRef);
92
102
  const {
93
103
  isMobile
94
104
  } = useAppShellMobile();
@@ -102,6 +112,7 @@ export function SideNavCollapseButton({
102
112
  ref: ref,
103
113
  label: label ?? (isCollapsed ? t('@astryx.sideNavCollapseButton.expandSidebar') : t('@astryx.sideNavCollapseButton.collapseSidebar')),
104
114
  variant: "ghost",
115
+ size: size,
105
116
  ...props,
106
117
  onClick: composeEventHandlers(onClickProp, toggle),
107
118
  icon: children ??
@@ -111,7 +122,7 @@ export function SideNavCollapseButton({
111
122
  // element one would overwrite the other and the chevron would stop
112
123
  // mirroring under RTL. See utils/rtlStyles.ts.
113
124
  _jsx("span", {
114
- ...stylex.props(rtlStyles.mirror),
125
+ ...stylex.props(styles.chevronMirror, rtlStyles.mirror),
115
126
  children: /*#__PURE__*/_jsx(Icon, {
116
127
  icon: "chevronLeft",
117
128
  size: "sm",
@@ -123,17 +134,30 @@ export function SideNavCollapseButton({
123
134
  });
124
135
  }
125
136
  SideNavCollapseButton.displayName = 'SideNavCollapseButton';
126
- function useSideNavCollapseState(handleRef) {
137
+ function useSideNavCollapseState(collapsible, handleRef) {
127
138
  const contextCollapseState = useSideNavCollapse();
139
+ const {
140
+ isCollapsed,
141
+ onCollapsedChange
142
+ } = collapsible ?? {};
143
+ const toggleCollapsible = useCallback(() => onCollapsedChange?.(!isCollapsed), [isCollapsed, onCollapsedChange]);
144
+ const toggleHandle = useCallback(() => {
145
+ handleRef?.current?.getCollapseState()?.toggle();
146
+ }, [handleRef]);
147
+ if (collapsible != null) {
148
+ return {
149
+ isCollapsed: isCollapsed ?? false,
150
+ toggle: toggleCollapsible,
151
+ isCollapsible: true
152
+ };
153
+ }
128
154
  if (handleRef == null) {
129
155
  return contextCollapseState;
130
156
  }
131
157
  const externalCollapseState = handleRef.current?.getCollapseState() ?? null;
132
158
  return {
133
159
  isCollapsed: externalCollapseState?.isCollapsed ?? false,
134
- toggle: () => {
135
- handleRef.current?.getCollapseState()?.toggle();
136
- },
160
+ toggle: toggleHandle,
137
161
  isCollapsible: externalCollapseState?.isCollapsible ?? true
138
162
  };
139
163
  }
@@ -6,6 +6,27 @@ export interface SideNavCollapseState {
6
6
  /** Whether collapse is enabled */
7
7
  isCollapsible: boolean;
8
8
  }
9
+ /** Object form of SideNav's `collapsible` prop. */
10
+ export interface SideNavCollapsibleConfig {
11
+ defaultIsCollapsed?: boolean;
12
+ isCollapsed?: boolean;
13
+ onCollapsedChange?: (isCollapsed: boolean) => void;
14
+ hasButton?: boolean;
15
+ buttonLabel?: string;
16
+ }
17
+ /**
18
+ * The controlled form: the consumer holds the state, so it can be handed to
19
+ * both SideNav and a SideNavCollapseButton rendered outside it.
20
+ */
21
+ export interface SideNavControlledCollapsible extends SideNavCollapsibleConfig {
22
+ isCollapsed: boolean;
23
+ onCollapsedChange: (isCollapsed: boolean) => void;
24
+ }
25
+ /**
26
+ * @deprecated Pass the same controlled `collapsible` config to SideNav and to
27
+ * the out-of-tree SideNavCollapseButton instead. The state then reaches the
28
+ * button through props rather than through a ref.
29
+ */
9
30
  export interface SideNavImperativeCollapseHandle {
10
31
  getCollapseState: () => SideNavCollapseState | null;
11
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SideNavCollapseContext.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavCollapseContext.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,kCAAkC;IAClC,aAAa,EAAE,OAAO,CAAC;CACxB;AAGD,MAAM,WAAW,+BAA+B;IAC9C,gBAAgB,EAAE,MAAM,oBAAoB,GAAG,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,sBAAsB,+CAMlC,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,oBAAoB,CAEzD"}
1
+ {"version":3,"file":"SideNavCollapseContext.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavCollapseContext.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,kCAAkC;IAClC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA6B,SAAQ,wBAAwB;IAC5E,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,gBAAgB,EAAE,MAAM,oBAAoB,GAAG,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,sBAAsB,+CAIjC,CAAC;AAGH;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,oBAAoB,CAEzD"}
@@ -10,10 +10,24 @@
10
10
  *
11
11
  * Provides collapse state to SideNavCollapseButton and other
12
12
  * sidenav children. Set by SideNav when isCollapsible is true.
13
- * Also provides a small imperative handle for ref-based collapse buttons
14
- * rendered outside the SideNav tree.
13
+ * A button rendered outside the SideNav tree is out of context's reach and
14
+ * takes the controlled `collapsible` config as a prop instead.
15
15
  */
16
16
  import { createContext, use } from 'react';
17
+
18
+ /** Object form of SideNav's `collapsible` prop. */
19
+
20
+ /**
21
+ * The controlled form: the consumer holds the state, so it can be handed to
22
+ * both SideNav and a SideNavCollapseButton rendered outside it.
23
+ */
24
+
25
+ /**
26
+ * @deprecated Pass the same controlled `collapsible` config to SideNav and to
27
+ * the out-of-tree SideNavCollapseButton instead. The state then reaches the
28
+ * button through props rather than through a ref.
29
+ */
30
+
17
31
  export const SideNavCollapseContext = /*#__PURE__*/createContext({
18
32
  isCollapsed: false,
19
33
  toggle: () => {},
@@ -1 +1 @@
1
- {"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAmBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiN5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACpE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,mBAAmB,sCA6drB;yBA9ee,cAAc"}
1
+ {"version":3,"file":"SideNavHeading.d.ts","sourceRoot":"","sources":["../../src/SideNav/SideNavHeading.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAoBtD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiN5C,MAAM,WAAW,mBAAoB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACpE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,EAC7B,EAAE,EACF,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,mBAAmB,sCAwfrB;yBAzgBe,cAAc"}
@@ -30,6 +30,7 @@ import { Link } from "../Link/index.js";
30
30
  import { Icon } from "../Icon/index.js";
31
31
  import { Tooltip } from "../Tooltip/index.js";
32
32
  import { navItemStyles } from "../NavItem/navItemStyles.stylex.js";
33
+ import { focusOutlineProps } from "../utils/focusOutline.stylex.js";
33
34
  import { useSideNavCollapse } from "./SideNavCollapseContext.js";
34
35
  import { useLinkComponent } from "../Link/useLinkComponent.js";
35
36
  import { mergeProps, mergeRefs } from "../utils/index.js";
@@ -62,6 +63,19 @@ const styles = {
62
63
  kg3NbH: "xnjsko4",
63
64
  $$css: true
64
65
  },
66
+ interactive: {
67
+ kkrTdU: "x1ypdohk",
68
+ kaIpWk: "xh6dtrn",
69
+ kMzoRj: "xc342km",
70
+ ksu8eU: "xng3xce",
71
+ kWkggS: "xjbqb8w",
72
+ kMv6JI: "xjb2p0i",
73
+ kGuDYH: "x1qlqyl8",
74
+ k63SB2: "x1sodnla",
75
+ k9WMMc: "x1yc453h",
76
+ kHE3J0: "xe9uy6x",
77
+ $$css: true
78
+ },
65
79
  menuTrigger: {
66
80
  kkrTdU: "x1ypdohk",
67
81
  kaIpWk: "xh6dtrn",
@@ -74,6 +88,32 @@ const styles = {
74
88
  k9WMMc: "x1yc453h",
75
89
  $$css: true
76
90
  },
91
+ icon: {
92
+ kmuXW: "x2lah0s",
93
+ k1xSpc: "x78zum5",
94
+ kGNEyG: "x6s0dn4",
95
+ kjj79g: "xl56j7k",
96
+ $$css: true
97
+ },
98
+ heading: {
99
+ kGuDYH: "x18juvz8",
100
+ k63SB2: "x2mo6ok",
101
+ kLWn49: "xf74fhv",
102
+ kMwMTN: "x1tgivj0",
103
+ kybGjl: "x1hl2dhg",
104
+ kVQacm: "xb3r6kr",
105
+ kg5iWk: "xlyipyv",
106
+ khDVqt: "xuxw1ft",
107
+ $$css: true
108
+ },
109
+ headingLink: {
110
+ kybGjl: "x1hl2dhg",
111
+ kMwMTN: "x1heor9g",
112
+ kVQacm: "xb3r6kr",
113
+ kg5iWk: "xlyipyv",
114
+ khDVqt: "xuxw1ft",
115
+ $$css: true
116
+ },
77
117
  chevron: {
78
118
  kmuXW: "x2lah0s",
79
119
  k1xSpc: "x78zum5",
@@ -85,6 +125,26 @@ const styles = {
85
125
  kGuDYH: "x1qlqyl8",
86
126
  $$css: true
87
127
  },
128
+ popoverHeading: {
129
+ k1xSpc: "x78zum5",
130
+ kGNEyG: "x6s0dn4",
131
+ kOIVth: "x1txdalj",
132
+ kzqmXN: "xh8yej3",
133
+ kWkggS: "xjbqb8w",
134
+ kMv6JI: "xjb2p0i",
135
+ kGuDYH: "x1qlqyl8",
136
+ kMwMTN: "x1heor9g",
137
+ k9WMMc: "x1yc453h",
138
+ kAzted: "xkoleio",
139
+ kZCmMZ: "x12gdq22 x1betex2",
140
+ kwRFfy: "x1djylfy",
141
+ k8WAf4: "xt970qd",
142
+ keoZOQ: "xcsaf9d",
143
+ k1K539: "x1p37lm5",
144
+ kUOVxO: "xhrcg97",
145
+ kkrTdU: "x1ypdohk",
146
+ $$css: true
147
+ },
88
148
  popoverChevron: {
89
149
  kmuXW: "x2lah0s",
90
150
  k1xSpc: "x78zum5",
@@ -187,14 +247,12 @@ export function SideNavHeading({
187
247
  role: 'none',
188
248
  hasCloseButton: false
189
249
  });
190
- const closeMenuCtx = useMemo(() => ({
191
- closeMenu: popover.hide
192
- }), [popover.hide]);
193
250
  const {
194
251
  triggerProps,
195
252
  contentProps,
196
253
  menuRef,
197
- setTriggerEl
254
+ setTriggerEl,
255
+ close: closeMenu
198
256
  } = useMenuHover({
199
257
  show: popover.show,
200
258
  hide: popover.hide,
@@ -202,7 +260,14 @@ export function SideNavHeading({
202
260
  isEnabled: !!menu,
203
261
  showDelay: 0
204
262
  });
205
- const setRef = mergeRefs(rootRef, setTriggerEl, ref, menu ? popover.triggerRef : undefined);
263
+ const closeMenuCtx = useMemo(() => ({
264
+ closeMenu
265
+ }), [closeMenu]);
266
+
267
+ // setTriggerEl belongs on the chevron button, not this root: it is the
268
+ // focus-restore target and a <div> cannot take focus. triggerRef stays here
269
+ // because the panel anchors to the whole heading.
270
+ const setRef = mergeRefs(rootRef, ref, menu ? popover.triggerRef : undefined);
206
271
 
207
272
  // In collapsed mode: hide if no icon, show icon-only if has icon
208
273
  if (isCollapsed && !icon) {
@@ -215,7 +280,9 @@ export function SideNavHeading({
215
280
  },
216
281
  children: icon
217
282
  });
218
- const collapsedSetRef = mergeRefs(collapsedItemRef, ref, menu ? popover.triggerRef : undefined);
283
+ const collapsedSetRef = mergeRefs(collapsedItemRef, ref,
284
+ // Collapsed, this button is the trigger, so it takes both roles.
285
+ menu ? popover.triggerRef : undefined, menu ? setTriggerEl : undefined);
219
286
  let collapsedElement;
220
287
  if (headingHref) {
221
288
  collapsedElement = /*#__PURE__*/_jsx(LinkComponent, {
@@ -223,7 +290,7 @@ export function SideNavHeading({
223
290
  href: headingHref,
224
291
  "aria-label": heading,
225
292
  "data-testid": testId,
226
- ...mergeProps(themeProps('side-nav-heading'), stylex.props(navItemStyles.item, styles.rootCollapsed, xstyle), className, style),
293
+ ...mergeProps(themeProps('side-nav-heading'), focusOutlineProps.focusVisible(navItemStyles.item, styles.rootCollapsed, xstyle), className, style),
227
294
  children: collapsedIcon
228
295
  });
229
296
  } else if (menu) {
@@ -235,7 +302,7 @@ export function SideNavHeading({
235
302
  "data-testid": testId,
236
303
  ...popover.triggerProps,
237
304
  ...triggerProps,
238
- ...mergeProps(themeProps('side-nav-heading'), stylex.props(navItemStyles.item, styles.rootCollapsed, styles.menuTrigger, xstyle), className, style),
305
+ ...mergeProps(themeProps('side-nav-heading'), focusOutlineProps.focusVisible(navItemStyles.item, styles.rootCollapsed, styles.menuTrigger, xstyle), className, style),
239
306
  children: collapsedIcon
240
307
  }), popover.render(/*#__PURE__*/_jsxs("div", {
241
308
  ref: menuRef,
@@ -245,10 +312,9 @@ export function SideNavHeading({
245
312
  ...contentProps,
246
313
  children: [/*#__PURE__*/_jsxs("button", {
247
314
  type: "button",
248
- ...{
249
- className: "x78zum5 x6s0dn4 x1txdalj xh8yej3 xjbqb8w xjb2p0i x1qlqyl8 x1heor9g x1yc453h xkoleio x12gdq22 x1betex2 x1djylfy xt970qd xcsaf9d x1p37lm5 xhrcg97 x1ypdohk"
250
- },
251
- onClick: triggerProps.onClick,
315
+ ...focusOutlineProps.focusVisible(styles.popoverHeading),
316
+ // A close affordance, not the trigger: dismiss only.
317
+ onClick: closeMenu,
252
318
  children: [icon && /*#__PURE__*/_jsx("span", {
253
319
  ...{
254
320
  className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
@@ -350,9 +416,7 @@ export function SideNavHeading({
350
416
  },
351
417
  children: [hasAnyHref && headingHref && menu ? /*#__PURE__*/_jsx(LinkComponent, {
352
418
  href: headingHref,
353
- ...{
354
- className: "x18juvz8 x2mo6ok xf74fhv x1hl2dhg x1heor9g xb3r6kr xlyipyv xuxw1ft"
355
- },
419
+ ...focusOutlineProps.focusVisible(styles.heading, styles.headingLink),
356
420
  children: heading
357
421
  }) : /*#__PURE__*/_jsx("span", {
358
422
  ...{
@@ -389,10 +453,9 @@ export function SideNavHeading({
389
453
  // sizing, with flipped chevron inline after the title. Always static (no links).
390
454
  const popoverHeadingContent = /*#__PURE__*/_jsxs("button", {
391
455
  type: "button",
392
- ...{
393
- className: "x78zum5 x6s0dn4 x1txdalj xh8yej3 xjbqb8w xjb2p0i x1qlqyl8 x1heor9g x1yc453h xkoleio x12gdq22 x1betex2 x1djylfy xt970qd xcsaf9d x1p37lm5 xhrcg97 x1ypdohk"
394
- },
395
- onClick: triggerProps.onClick,
456
+ ...focusOutlineProps.focusVisible(styles.popoverHeading),
457
+ // A close affordance, not the trigger: dismiss only.
458
+ onClick: closeMenu,
396
459
  children: [icon && /*#__PURE__*/_jsx("span", {
397
460
  ...{
398
461
  className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
@@ -412,7 +475,7 @@ export function SideNavHeading({
412
475
  ref: ref,
413
476
  href: headingHref,
414
477
  "data-testid": testId,
415
- ...mergeProps(themeProps('side-nav-heading'), stylex.props(styles.root, styles.menuTrigger, xstyle), className, style),
478
+ ...mergeProps(themeProps('side-nav-heading'), focusOutlineProps.focusVisible(styles.root, styles.menuTrigger, xstyle), className, style),
416
479
  ...props,
417
480
  children: [icon && /*#__PURE__*/_jsx("span", {
418
481
  ...{
@@ -437,6 +500,7 @@ export function SideNavHeading({
437
500
  },
438
501
  children: icon
439
502
  }), renderTextContent(/*#__PURE__*/_jsx("button", {
503
+ ref: setTriggerEl,
440
504
  type: "button",
441
505
  "aria-label": t('@astryx.sideNav.heading.openMenu'),
442
506
  onClick: e => {
@@ -444,9 +508,7 @@ export function SideNavHeading({
444
508
  triggerProps.onClick();
445
509
  },
446
510
  ...popover.triggerProps,
447
- ...{
448
- className: "x2lah0s x78zum5 x6s0dn4 xl56j7k xy8csz5 ximsjs8 xv9yike x1ypdohk xh6dtrn xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x1sodnla x1yc453h xe9uy6x"
449
- },
511
+ ...focusOutlineProps.focusVisible(styles.chevron, styles.interactive),
450
512
  children: /*#__PURE__*/_jsx(Icon, {
451
513
  icon: "chevronDown",
452
514
  size: "sm",
@@ -486,9 +548,7 @@ export function SideNavHeading({
486
548
  children: [icon && (headingHref ? /*#__PURE__*/_jsx(LinkComponent, {
487
549
  href: headingHref,
488
550
  "aria-label": heading,
489
- ...{
490
- className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
491
- },
551
+ ...focusOutlineProps.focusVisible(styles.icon),
492
552
  children: icon
493
553
  }) : /*#__PURE__*/_jsx("span", {
494
554
  ...{
@@ -496,6 +556,7 @@ export function SideNavHeading({
496
556
  },
497
557
  children: icon
498
558
  })), renderTextContent(showChevron ? /*#__PURE__*/_jsx("button", {
559
+ ref: setTriggerEl,
499
560
  type: "button",
500
561
  "aria-label": t('@astryx.sideNav.heading.openMenu'),
501
562
  onClick: e => {
@@ -503,9 +564,7 @@ export function SideNavHeading({
503
564
  triggerProps.onClick();
504
565
  },
505
566
  ...popover.triggerProps,
506
- ...{
507
- className: "x2lah0s x78zum5 x6s0dn4 xl56j7k xy8csz5 ximsjs8 xv9yike x1ypdohk xh6dtrn xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x1sodnla x1yc453h xe9uy6x"
508
- },
567
+ ...focusOutlineProps.focusVisible(styles.chevron, styles.interactive),
509
568
  children: /*#__PURE__*/_jsx(Icon, {
510
569
  icon: "chevronDown",
511
570
  size: "sm",
@@ -542,9 +601,7 @@ export function SideNavHeading({
542
601
  children: [icon && (headingHref ? /*#__PURE__*/_jsx(LinkComponent, {
543
602
  href: headingHref,
544
603
  "aria-label": heading,
545
- ...{
546
- className: "x2lah0s x78zum5 x6s0dn4 xl56j7k"
547
- },
604
+ ...focusOutlineProps.focusVisible(styles.icon),
548
605
  children: icon
549
606
  }) : /*#__PURE__*/_jsx("span", {
550
607
  ...{
@@ -1,11 +1,16 @@
1
1
  /**
2
2
  * @file SideNavItem.tsx
3
- * @input Uses React, ReactNode, StyleX, Icon, IconType
3
+ * @input Uses React, ReactNode, StyleX, Icon, IconType, useMenuHover
4
4
  * @output Exports SideNavItem component and SideNavItemProps
5
5
  * @position Core implementation; used inside SideNav children
6
6
  *
7
7
  * Navigation item with icon, selected state, and nesting.
8
8
  *
9
+ * Collapsed items with children open their submenu flyout through
10
+ * `useMenuHover`, the shared hover-intent hook (same one `SideNavHeading` and
11
+ * `TopNavMenu` use). Hover is a progressive enhancement over the popover's
12
+ * click behavior and is inert on coarse pointers.
13
+ *
9
14
  * SYNC: When modified, update these files to stay in sync:
10
15
  * - /packages/core/src/SideNav/SideNav.doc.mjs
11
16
  * - /packages/core/src/SideNav/SideNav.test.tsx
@@ -107,7 +112,7 @@ export interface SideNavItemProps extends BaseProps<HTMLElement> {
107
112
  * </SideNavItem>
108
113
  * ```
109
114
  */
110
- export declare function SideNavItem({ as, label, icon, selectedIcon, isSelected, isDisabled, href, onClick, endContent, children, collapsible: itemCollapsible, size, 'data-testid': testId, ref, xstyle, }: SideNavItemProps): import("react").JSX.Element | null;
115
+ export declare function SideNavItem({ as, label, icon, selectedIcon, isSelected, isDisabled, href, onClick, endContent, children, collapsible: itemCollapsible, size, 'data-testid': testId, ref, xstyle, ...rest }: SideNavItemProps): import("react").JSX.Element | null;
111
116
  export declare namespace SideNavItem {
112
117
  var displayName: string;
113
118
  }