@womp/kookie-ui 0.4.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. package/components.css +783 -783
  2. package/dist/cjs/components/_internal/base-menu.utils.d.ts +48 -0
  3. package/dist/cjs/components/_internal/base-menu.utils.d.ts.map +1 -0
  4. package/dist/cjs/components/_internal/base-menu.utils.js +2 -0
  5. package/dist/cjs/components/_internal/base-menu.utils.js.map +7 -0
  6. package/dist/cjs/components/_internal/dropdown-menu-drill-down.d.ts +77 -59
  7. package/dist/cjs/components/_internal/dropdown-menu-drill-down.d.ts.map +1 -1
  8. package/dist/cjs/components/_internal/dropdown-menu-drill-down.js +1 -1
  9. package/dist/cjs/components/_internal/dropdown-menu-drill-down.js.map +4 -4
  10. package/dist/cjs/components/_internal/menu-context.d.ts +7 -3
  11. package/dist/cjs/components/_internal/menu-context.d.ts.map +1 -1
  12. package/dist/cjs/components/_internal/menu-context.js +1 -1
  13. package/dist/cjs/components/_internal/menu-context.js.map +4 -4
  14. package/dist/cjs/components/_internal/shell-bottom.js +1 -1
  15. package/dist/cjs/components/_internal/shell-bottom.js.map +2 -2
  16. package/dist/cjs/components/_internal/shell-inspector.js +1 -1
  17. package/dist/cjs/components/_internal/shell-inspector.js.map +2 -2
  18. package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
  19. package/dist/cjs/components/_internal/shell-sidebar.js.map +2 -2
  20. package/dist/cjs/components/accordion.js +1 -1
  21. package/dist/cjs/components/alert-dialog.js +1 -1
  22. package/dist/cjs/components/alert-dialog.js.map +2 -2
  23. package/dist/cjs/components/badge.js +1 -1
  24. package/dist/cjs/components/badge.js.map +2 -2
  25. package/dist/cjs/components/blockquote.js +1 -1
  26. package/dist/cjs/components/button.js +1 -1
  27. package/dist/cjs/components/button.js.map +2 -2
  28. package/dist/cjs/components/callout.js +1 -1
  29. package/dist/cjs/components/callout.js.map +2 -2
  30. package/dist/cjs/components/card.js +1 -1
  31. package/dist/cjs/components/card.js.map +2 -2
  32. package/dist/cjs/components/chatbar.d.ts +46 -44
  33. package/dist/cjs/components/chatbar.d.ts.map +1 -1
  34. package/dist/cjs/components/chatbar.js +2 -2
  35. package/dist/cjs/components/chatbar.js.map +3 -3
  36. package/dist/cjs/components/checkbox-cards.js +1 -1
  37. package/dist/cjs/components/checkbox-cards.js.map +2 -2
  38. package/dist/cjs/components/checkbox-group.js +1 -1
  39. package/dist/cjs/components/checkbox.js +1 -1
  40. package/dist/cjs/components/combobox.d.ts +28 -15
  41. package/dist/cjs/components/combobox.d.ts.map +1 -1
  42. package/dist/cjs/components/combobox.js +1 -1
  43. package/dist/cjs/components/combobox.js.map +4 -4
  44. package/dist/cjs/components/combobox.props.d.ts +48 -0
  45. package/dist/cjs/components/combobox.props.d.ts.map +1 -1
  46. package/dist/cjs/components/combobox.props.js +1 -1
  47. package/dist/cjs/components/combobox.props.js.map +4 -4
  48. package/dist/cjs/components/context-menu.d.ts +18 -5
  49. package/dist/cjs/components/context-menu.d.ts.map +1 -1
  50. package/dist/cjs/components/context-menu.js +1 -1
  51. package/dist/cjs/components/context-menu.js.map +4 -4
  52. package/dist/cjs/components/context-menu.props.d.ts +1 -1
  53. package/dist/cjs/components/context-menu.props.d.ts.map +1 -1
  54. package/dist/cjs/components/context-menu.props.js +1 -1
  55. package/dist/cjs/components/context-menu.props.js.map +3 -3
  56. package/dist/cjs/components/dialog.js +1 -1
  57. package/dist/cjs/components/dialog.js.map +2 -2
  58. package/dist/cjs/components/dropdown-menu.d.ts +11 -18
  59. package/dist/cjs/components/dropdown-menu.d.ts.map +1 -1
  60. package/dist/cjs/components/dropdown-menu.js +1 -1
  61. package/dist/cjs/components/dropdown-menu.js.map +4 -4
  62. package/dist/cjs/components/heading.js +1 -1
  63. package/dist/cjs/components/hover-card.js +1 -1
  64. package/dist/cjs/components/hover-card.js.map +2 -2
  65. package/dist/cjs/components/icon-button.js +1 -1
  66. package/dist/cjs/components/icon-button.js.map +2 -2
  67. package/dist/cjs/components/index.js +5 -5
  68. package/dist/cjs/components/index.js.map +4 -4
  69. package/dist/cjs/components/link.js +1 -1
  70. package/dist/cjs/components/popover.js +1 -1
  71. package/dist/cjs/components/popover.js.map +2 -2
  72. package/dist/cjs/components/radio-cards.js +1 -1
  73. package/dist/cjs/components/radio-cards.js.map +2 -2
  74. package/dist/cjs/components/radio.js +1 -1
  75. package/dist/cjs/components/radio.js.map +2 -2
  76. package/dist/cjs/components/schemas/base-button.schema.d.ts +111 -256
  77. package/dist/cjs/components/schemas/base-button.schema.d.ts.map +1 -1
  78. package/dist/cjs/components/schemas/button.schema.d.ts +136 -557
  79. package/dist/cjs/components/schemas/button.schema.d.ts.map +1 -1
  80. package/dist/cjs/components/schemas/icon-button.schema.d.ts +123 -240
  81. package/dist/cjs/components/schemas/icon-button.schema.d.ts.map +1 -1
  82. package/dist/cjs/components/schemas/index.js +1 -1
  83. package/dist/cjs/components/schemas/index.js.map +2 -2
  84. package/dist/cjs/components/schemas/shell.schema.d.ts +592 -851
  85. package/dist/cjs/components/schemas/shell.schema.d.ts.map +1 -1
  86. package/dist/cjs/components/schemas/shell.schema.js +1 -1
  87. package/dist/cjs/components/schemas/shell.schema.js.map +2 -2
  88. package/dist/cjs/components/schemas/toggle-button.schema.d.ts +138 -1026
  89. package/dist/cjs/components/schemas/toggle-button.schema.d.ts.map +1 -1
  90. package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts +124 -465
  91. package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts.map +1 -1
  92. package/dist/cjs/components/scroll-area.js +1 -1
  93. package/dist/cjs/components/segmented-control.js +1 -1
  94. package/dist/cjs/components/select.js +1 -1
  95. package/dist/cjs/components/select.js.map +2 -2
  96. package/dist/cjs/components/sheet.js +1 -1
  97. package/dist/cjs/components/sheet.js.map +2 -2
  98. package/dist/cjs/components/shell.js +1 -1
  99. package/dist/cjs/components/shell.js.map +3 -3
  100. package/dist/cjs/components/sidebar.js +1 -1
  101. package/dist/cjs/components/sidebar.js.map +4 -4
  102. package/dist/cjs/components/slider.js +1 -1
  103. package/dist/cjs/components/slot.d.ts +6 -2
  104. package/dist/cjs/components/slot.d.ts.map +1 -1
  105. package/dist/cjs/components/switch.js +1 -1
  106. package/dist/cjs/components/tab-nav.js +1 -1
  107. package/dist/cjs/components/text-field.js +2 -2
  108. package/dist/cjs/components/text.js +1 -1
  109. package/dist/cjs/components/theme-panel.js +3 -3
  110. package/dist/cjs/components/theme-panel.js.map +2 -2
  111. package/dist/cjs/components/theme.js +1 -1
  112. package/dist/cjs/components/theme.js.map +2 -2
  113. package/dist/cjs/components/theme.props.d.ts +1 -1
  114. package/dist/cjs/components/theme.props.js +1 -1
  115. package/dist/cjs/components/theme.props.js.map +2 -2
  116. package/dist/cjs/components/toggle-button.js +1 -1
  117. package/dist/cjs/components/toggle-button.js.map +2 -2
  118. package/dist/cjs/components/toggle-icon-button.js +1 -1
  119. package/dist/cjs/components/toggle-icon-button.js.map +2 -2
  120. package/dist/cjs/components/tooltip.js +1 -1
  121. package/dist/cjs/components/tooltip.js.map +2 -2
  122. package/dist/cjs/components/user-card.js +1 -1
  123. package/dist/cjs/components/user-card.js.map +2 -2
  124. package/dist/cjs/components/virtual-menu.d.ts.map +1 -1
  125. package/dist/cjs/components/virtual-menu.js +1 -1
  126. package/dist/cjs/components/virtual-menu.js.map +4 -4
  127. package/dist/cjs/helpers/extract-props.js +1 -1
  128. package/dist/cjs/helpers/extract-props.js.map +2 -2
  129. package/dist/cjs/helpers/get-margin-styles.js +1 -1
  130. package/dist/cjs/helpers/get-margin-styles.js.map +2 -2
  131. package/dist/cjs/hooks/use-breakpoint.d.ts +4 -1
  132. package/dist/cjs/hooks/use-breakpoint.d.ts.map +1 -1
  133. package/dist/cjs/hooks/use-breakpoint.js +1 -1
  134. package/dist/cjs/hooks/use-breakpoint.js.map +3 -3
  135. package/dist/cjs/index.js +5 -5
  136. package/dist/cjs/index.js.map +4 -4
  137. package/dist/cjs/props/index.js +1 -1
  138. package/dist/cjs/props/index.js.map +3 -3
  139. package/dist/esm/{chunk-P7HYVJSU.js → chunk-2TIADHPD.js} +2 -2
  140. package/dist/esm/chunk-37OGSEQR.js +2 -0
  141. package/dist/esm/chunk-37OGSEQR.js.map +7 -0
  142. package/dist/esm/{chunk-RMKXWYOV.js → chunk-3OBXZUFJ.js} +2 -2
  143. package/dist/esm/{chunk-S2HA3W3V.js → chunk-3R7XMRV5.js} +2 -2
  144. package/dist/esm/chunk-4SMJ7D4S.js +2 -0
  145. package/dist/esm/chunk-4SMJ7D4S.js.map +7 -0
  146. package/dist/esm/{chunk-S56YSRHP.js → chunk-56TS6OKQ.js} +2 -2
  147. package/dist/esm/{chunk-2FYJTSOP.js → chunk-5BYHEYO6.js} +2 -2
  148. package/dist/esm/{chunk-B24BDVG2.js → chunk-5MNBQROW.js} +2 -2
  149. package/dist/esm/{chunk-W7N7V5YD.js → chunk-7ESHXHHO.js} +2 -2
  150. package/dist/esm/{chunk-CRWYCLTE.js → chunk-ABSKRHVM.js} +2 -2
  151. package/dist/esm/chunk-ADBLURLJ.js +2 -0
  152. package/dist/esm/chunk-ADBLURLJ.js.map +7 -0
  153. package/dist/esm/{chunk-3MWWP4MW.js → chunk-BBJUB52V.js} +2 -2
  154. package/dist/esm/{chunk-PTLFMWCN.js → chunk-CQNPBTM5.js} +2 -2
  155. package/dist/esm/{chunk-GJSGKE7B.js → chunk-CTA5ODAO.js} +2 -2
  156. package/dist/esm/{chunk-GWXVETMF.js → chunk-DCAZPMOB.js} +2 -2
  157. package/dist/esm/{chunk-PLWXTEVE.js → chunk-DMJJM5IS.js} +2 -2
  158. package/dist/esm/{chunk-A2RZGGMY.js → chunk-DQAUX5ZL.js} +2 -2
  159. package/dist/esm/{chunk-6XGZPIUT.js → chunk-DX5JJYMZ.js} +2 -2
  160. package/dist/esm/{chunk-6XGZPIUT.js.map → chunk-DX5JJYMZ.js.map} +2 -2
  161. package/dist/esm/{chunk-YUP6EICB.js → chunk-DXL6V2A5.js} +2 -2
  162. package/dist/esm/{chunk-XMRIRFM7.js → chunk-FG7XM7KJ.js} +2 -2
  163. package/dist/esm/{chunk-IKSN4RNB.js → chunk-FIB3XATL.js} +3 -3
  164. package/dist/esm/chunk-FMBECDL4.js +2 -0
  165. package/dist/esm/{chunk-4XJY6FIK.js.map → chunk-FMBECDL4.js.map} +3 -3
  166. package/dist/esm/{chunk-374ZYJUP.js → chunk-HBR42MIX.js} +2 -2
  167. package/dist/esm/{chunk-2UPAYDKR.js → chunk-HIKVMKYQ.js} +2 -2
  168. package/dist/esm/{chunk-VX6ARMWP.js → chunk-HSAG7C53.js} +2 -2
  169. package/dist/esm/{chunk-OSRJRBKP.js → chunk-HW324FK3.js} +2 -2
  170. package/dist/esm/{chunk-DNHUQLXO.js → chunk-JBO5YOUM.js} +2 -2
  171. package/dist/esm/{chunk-HF5WY5YE.js → chunk-JGLAE5G7.js} +2 -2
  172. package/dist/esm/{chunk-4YFJWXL2.js → chunk-JJKQ7CGI.js} +2 -2
  173. package/dist/esm/chunk-JSW6J6RS.js +2 -0
  174. package/dist/esm/chunk-JSW6J6RS.js.map +7 -0
  175. package/dist/esm/chunk-KRDP5SQ7.js +2 -0
  176. package/dist/esm/chunk-KRDP5SQ7.js.map +7 -0
  177. package/dist/esm/{chunk-CWRNW44F.js → chunk-L6PVH3RM.js} +2 -2
  178. package/dist/esm/{chunk-MK2R6DAU.js → chunk-LAOMR7SE.js} +2 -2
  179. package/dist/esm/{chunk-FHOCUZDR.js → chunk-NIQT45PJ.js} +2 -2
  180. package/dist/esm/chunk-NKEJTTIL.js +2 -0
  181. package/dist/esm/chunk-NKEJTTIL.js.map +7 -0
  182. package/dist/esm/chunk-NVDXXGVW.js +2 -0
  183. package/dist/esm/chunk-NVDXXGVW.js.map +7 -0
  184. package/dist/esm/chunk-OS3FCYZY.js +2 -0
  185. package/dist/esm/{chunk-7QDTSBJF.js → chunk-OYF6YXH7.js} +2 -2
  186. package/dist/esm/{chunk-WFMYNZXZ.js → chunk-P5OSU4NX.js} +2 -2
  187. package/dist/esm/{chunk-QEOAXJLN.js → chunk-PTS6QYQW.js} +2 -2
  188. package/dist/esm/{chunk-CVWCKOHO.js → chunk-RLPKKCBL.js} +2 -2
  189. package/dist/esm/{chunk-LKAK5GXF.js → chunk-SPIU5JYC.js} +2 -2
  190. package/dist/esm/chunk-SZTZ2XLI.js +2 -0
  191. package/dist/esm/chunk-SZTZ2XLI.js.map +7 -0
  192. package/dist/esm/chunk-T2FZN7G3.js +2 -0
  193. package/dist/esm/chunk-T2FZN7G3.js.map +7 -0
  194. package/dist/esm/{chunk-4AQTUBVP.js → chunk-T4UHUQFJ.js} +2 -2
  195. package/dist/esm/{chunk-K6QZMNCD.js → chunk-TS623UPD.js} +2 -2
  196. package/dist/esm/{chunk-YV2KYHGR.js → chunk-UBOOUDS4.js} +2 -2
  197. package/dist/esm/{chunk-D6B7IMZU.js → chunk-UFI53QUC.js} +2 -2
  198. package/dist/esm/{chunk-AU3D6F42.js → chunk-UIRSGVJG.js} +2 -2
  199. package/dist/esm/{chunk-FXUUI6Z2.js → chunk-UUUZXUIP.js} +2 -2
  200. package/dist/esm/{chunk-25WZA7ZU.js → chunk-VA4CTVQL.js} +2 -2
  201. package/dist/esm/{chunk-TOLLPRMM.js → chunk-VNPNEJI6.js} +2 -2
  202. package/dist/esm/{chunk-6IGTK4JE.js → chunk-YMUGKYGW.js} +2 -2
  203. package/dist/esm/chunk-ZWYYPFZW.js +2 -0
  204. package/dist/esm/chunk-ZWYYPFZW.js.map +7 -0
  205. package/dist/esm/{chunk-M24UXMOQ.js → chunk-ZXZOH63L.js} +2 -2
  206. package/dist/esm/components/_internal/base-button.js +1 -1
  207. package/dist/esm/components/_internal/base-button.props.js +1 -1
  208. package/dist/esm/components/_internal/base-checkbox.props.js +1 -1
  209. package/dist/esm/components/_internal/base-menu.props.js +1 -1
  210. package/dist/esm/components/_internal/base-menu.utils.d.ts +48 -0
  211. package/dist/esm/components/_internal/base-menu.utils.d.ts.map +1 -0
  212. package/dist/esm/components/_internal/base-menu.utils.js +2 -0
  213. package/dist/esm/components/_internal/base-menu.utils.js.map +7 -0
  214. package/dist/esm/components/_internal/base-radio.props.js +1 -1
  215. package/dist/esm/components/_internal/base-tab-list.props.js +1 -1
  216. package/dist/esm/components/_internal/dropdown-menu-drill-down.d.ts +77 -59
  217. package/dist/esm/components/_internal/dropdown-menu-drill-down.d.ts.map +1 -1
  218. package/dist/esm/components/_internal/dropdown-menu-drill-down.js +1 -1
  219. package/dist/esm/components/_internal/menu-context.d.ts +7 -3
  220. package/dist/esm/components/_internal/menu-context.d.ts.map +1 -1
  221. package/dist/esm/components/_internal/menu-context.js +1 -1
  222. package/dist/esm/components/_internal/shell-bottom.js +1 -1
  223. package/dist/esm/components/_internal/shell-handles.js +1 -1
  224. package/dist/esm/components/_internal/shell-inspector.js +1 -1
  225. package/dist/esm/components/_internal/shell-prop-helpers.js +1 -1
  226. package/dist/esm/components/_internal/shell-resize.js +1 -1
  227. package/dist/esm/components/_internal/shell-sidebar.js +1 -1
  228. package/dist/esm/components/accessible-icon.js +1 -1
  229. package/dist/esm/components/accordion.js +1 -1
  230. package/dist/esm/components/accordion.props.js +1 -1
  231. package/dist/esm/components/alert-dialog.js +1 -1
  232. package/dist/esm/components/alert-dialog.props.js +1 -1
  233. package/dist/esm/components/aspect-ratio.js +1 -1
  234. package/dist/esm/components/avatar.js +1 -1
  235. package/dist/esm/components/avatar.props.js +1 -1
  236. package/dist/esm/components/badge.js +1 -1
  237. package/dist/esm/components/badge.props.js +1 -1
  238. package/dist/esm/components/blockquote.js +1 -1
  239. package/dist/esm/components/blockquote.props.js +1 -1
  240. package/dist/esm/components/box.js +1 -1
  241. package/dist/esm/components/box.props.js +1 -1
  242. package/dist/esm/components/button.js +1 -1
  243. package/dist/esm/components/button.props.js +1 -1
  244. package/dist/esm/components/callout.js +1 -1
  245. package/dist/esm/components/callout.props.js +1 -1
  246. package/dist/esm/components/card.js +1 -1
  247. package/dist/esm/components/card.props.js +1 -1
  248. package/dist/esm/components/chatbar.d.ts +46 -44
  249. package/dist/esm/components/chatbar.d.ts.map +1 -1
  250. package/dist/esm/components/chatbar.js +1 -1
  251. package/dist/esm/components/checkbox-cards.js +1 -1
  252. package/dist/esm/components/checkbox-cards.props.js +1 -1
  253. package/dist/esm/components/checkbox-group.js +1 -1
  254. package/dist/esm/components/checkbox-group.primitive.js +1 -1
  255. package/dist/esm/components/checkbox-group.props.js +1 -1
  256. package/dist/esm/components/checkbox.js +1 -1
  257. package/dist/esm/components/checkbox.props.js +1 -1
  258. package/dist/esm/components/code.js +1 -1
  259. package/dist/esm/components/code.props.js +1 -1
  260. package/dist/esm/components/combobox.d.ts +28 -15
  261. package/dist/esm/components/combobox.d.ts.map +1 -1
  262. package/dist/esm/components/combobox.js +1 -1
  263. package/dist/esm/components/combobox.props.d.ts +48 -0
  264. package/dist/esm/components/combobox.props.d.ts.map +1 -1
  265. package/dist/esm/components/combobox.props.js +1 -1
  266. package/dist/esm/components/container.js +1 -1
  267. package/dist/esm/components/container.props.js +1 -1
  268. package/dist/esm/components/context-menu.d.ts +18 -5
  269. package/dist/esm/components/context-menu.d.ts.map +1 -1
  270. package/dist/esm/components/context-menu.js +1 -1
  271. package/dist/esm/components/context-menu.props.d.ts +1 -1
  272. package/dist/esm/components/context-menu.props.d.ts.map +1 -1
  273. package/dist/esm/components/context-menu.props.js +1 -1
  274. package/dist/esm/components/data-list.js +1 -1
  275. package/dist/esm/components/data-list.props.js +1 -1
  276. package/dist/esm/components/dialog.js +1 -1
  277. package/dist/esm/components/dialog.props.js +1 -1
  278. package/dist/esm/components/dropdown-menu.d.ts +11 -18
  279. package/dist/esm/components/dropdown-menu.d.ts.map +1 -1
  280. package/dist/esm/components/dropdown-menu.js +1 -1
  281. package/dist/esm/components/dropdown-menu.props.js +1 -1
  282. package/dist/esm/components/em.js +1 -1
  283. package/dist/esm/components/em.props.js +1 -1
  284. package/dist/esm/components/flex.js +1 -1
  285. package/dist/esm/components/flex.props.js +1 -1
  286. package/dist/esm/components/grid.js +1 -1
  287. package/dist/esm/components/grid.props.js +1 -1
  288. package/dist/esm/components/heading.js +1 -1
  289. package/dist/esm/components/heading.props.js +1 -1
  290. package/dist/esm/components/hover-card.js +1 -1
  291. package/dist/esm/components/hover-card.props.js +1 -1
  292. package/dist/esm/components/icon-button.js +1 -1
  293. package/dist/esm/components/icon-button.props.js +1 -1
  294. package/dist/esm/components/icons.js +1 -1
  295. package/dist/esm/components/image.js +1 -1
  296. package/dist/esm/components/image.props.js +1 -1
  297. package/dist/esm/components/index.js +1 -1
  298. package/dist/esm/components/inset.js +1 -1
  299. package/dist/esm/components/inset.props.js +1 -1
  300. package/dist/esm/components/kbd.js +1 -1
  301. package/dist/esm/components/kbd.props.js +1 -1
  302. package/dist/esm/components/link.js +1 -1
  303. package/dist/esm/components/link.props.js +1 -1
  304. package/dist/esm/components/navbar.js +1 -1
  305. package/dist/esm/components/navbar.props.js +1 -1
  306. package/dist/esm/components/popover.js +1 -1
  307. package/dist/esm/components/popover.props.js +1 -1
  308. package/dist/esm/components/portal.js +1 -1
  309. package/dist/esm/components/progress.js +1 -1
  310. package/dist/esm/components/progress.props.js +1 -1
  311. package/dist/esm/components/quote.js +1 -1
  312. package/dist/esm/components/quote.props.js +1 -1
  313. package/dist/esm/components/radio-cards.js +1 -1
  314. package/dist/esm/components/radio-cards.props.js +1 -1
  315. package/dist/esm/components/radio-group.js +1 -1
  316. package/dist/esm/components/radio-group.props.js +1 -1
  317. package/dist/esm/components/radio.js +1 -1
  318. package/dist/esm/components/radio.props.js +1 -1
  319. package/dist/esm/components/reset.js +1 -1
  320. package/dist/esm/components/schemas/base-button.schema.d.ts +111 -256
  321. package/dist/esm/components/schemas/base-button.schema.d.ts.map +1 -1
  322. package/dist/esm/components/schemas/base-button.schema.js +1 -1
  323. package/dist/esm/components/schemas/button.schema.d.ts +136 -557
  324. package/dist/esm/components/schemas/button.schema.d.ts.map +1 -1
  325. package/dist/esm/components/schemas/button.schema.js +1 -1
  326. package/dist/esm/components/schemas/icon-button.schema.d.ts +123 -240
  327. package/dist/esm/components/schemas/icon-button.schema.d.ts.map +1 -1
  328. package/dist/esm/components/schemas/icon-button.schema.js +1 -1
  329. package/dist/esm/components/schemas/index.js +1 -1
  330. package/dist/esm/components/schemas/shell.schema.d.ts +592 -851
  331. package/dist/esm/components/schemas/shell.schema.d.ts.map +1 -1
  332. package/dist/esm/components/schemas/shell.schema.js +1 -1
  333. package/dist/esm/components/schemas/toggle-button.schema.d.ts +138 -1026
  334. package/dist/esm/components/schemas/toggle-button.schema.d.ts.map +1 -1
  335. package/dist/esm/components/schemas/toggle-button.schema.js +1 -1
  336. package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts +124 -465
  337. package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts.map +1 -1
  338. package/dist/esm/components/schemas/toggle-icon-button.schema.js +1 -1
  339. package/dist/esm/components/scroll-area.js +1 -1
  340. package/dist/esm/components/scroll-area.props.js +1 -1
  341. package/dist/esm/components/section.js +1 -1
  342. package/dist/esm/components/section.props.js +1 -1
  343. package/dist/esm/components/segmented-control.js +1 -1
  344. package/dist/esm/components/segmented-control.props.js +1 -1
  345. package/dist/esm/components/select.js +1 -1
  346. package/dist/esm/components/select.props.js +1 -1
  347. package/dist/esm/components/separator.js +1 -1
  348. package/dist/esm/components/separator.props.js +1 -1
  349. package/dist/esm/components/sheet.js +1 -1
  350. package/dist/esm/components/shell.context.js +1 -1
  351. package/dist/esm/components/shell.hooks.js +1 -1
  352. package/dist/esm/components/shell.js +1 -1
  353. package/dist/esm/components/shell.types.js +1 -1
  354. package/dist/esm/components/sidebar.js +1 -1
  355. package/dist/esm/components/sidebar.props.js +1 -1
  356. package/dist/esm/components/skeleton.js +1 -1
  357. package/dist/esm/components/skeleton.props.js +1 -1
  358. package/dist/esm/components/slider.js +1 -1
  359. package/dist/esm/components/slider.props.js +1 -1
  360. package/dist/esm/components/slot.d.ts +6 -2
  361. package/dist/esm/components/slot.d.ts.map +1 -1
  362. package/dist/esm/components/slot.js +1 -1
  363. package/dist/esm/components/spinner.js +1 -1
  364. package/dist/esm/components/spinner.props.js +1 -1
  365. package/dist/esm/components/strong.js +1 -1
  366. package/dist/esm/components/strong.props.js +1 -1
  367. package/dist/esm/components/switch.js +1 -1
  368. package/dist/esm/components/switch.props.js +1 -1
  369. package/dist/esm/components/tab-nav.js +1 -1
  370. package/dist/esm/components/tab-nav.props.js +1 -1
  371. package/dist/esm/components/table.js +1 -1
  372. package/dist/esm/components/table.props.js +1 -1
  373. package/dist/esm/components/tabs.js +1 -1
  374. package/dist/esm/components/tabs.props.js +1 -1
  375. package/dist/esm/components/text-area.js +1 -1
  376. package/dist/esm/components/text-area.props.js +1 -1
  377. package/dist/esm/components/text-field.js +1 -1
  378. package/dist/esm/components/text-field.props.js +1 -1
  379. package/dist/esm/components/text.js +1 -1
  380. package/dist/esm/components/text.props.js +1 -1
  381. package/dist/esm/components/theme-panel.js +1 -1
  382. package/dist/esm/components/theme.js +1 -1
  383. package/dist/esm/components/theme.props.d.ts +1 -1
  384. package/dist/esm/components/theme.props.js +1 -1
  385. package/dist/esm/components/toggle-button.js +1 -1
  386. package/dist/esm/components/toggle-icon-button.js +1 -1
  387. package/dist/esm/components/toolbar.js +1 -1
  388. package/dist/esm/components/tooltip.js +1 -1
  389. package/dist/esm/components/tooltip.props.js +1 -1
  390. package/dist/esm/components/user-card.js +1 -1
  391. package/dist/esm/components/user-card.props.js +1 -1
  392. package/dist/esm/components/virtual-menu.d.ts.map +1 -1
  393. package/dist/esm/components/virtual-menu.js +1 -1
  394. package/dist/esm/components/visually-hidden.js +1 -1
  395. package/dist/esm/helpers/compile-prop-defs.js +1 -1
  396. package/dist/esm/helpers/extract-margin-props.js +1 -1
  397. package/dist/esm/helpers/extract-props.js +1 -1
  398. package/dist/esm/helpers/font-config.js +1 -1
  399. package/dist/esm/helpers/get-margin-styles.js +1 -1
  400. package/dist/esm/helpers/get-matching-gray-color.js +1 -1
  401. package/dist/esm/helpers/get-responsive-styles.js +1 -1
  402. package/dist/esm/helpers/get-subtree.js +1 -1
  403. package/dist/esm/helpers/has-own-property.js +1 -1
  404. package/dist/esm/helpers/index.js +1 -1
  405. package/dist/esm/helpers/inert.js +1 -1
  406. package/dist/esm/helpers/is-responsive-object.js +1 -1
  407. package/dist/esm/helpers/map-prop-values.js +1 -1
  408. package/dist/esm/helpers/merge-styles.js +1 -1
  409. package/dist/esm/helpers/normalize-to-px.js +1 -1
  410. package/dist/esm/helpers/require-react-element.js +1 -1
  411. package/dist/esm/helpers/use-deprecation-warning.js +1 -1
  412. package/dist/esm/hooks/index.js +1 -1
  413. package/dist/esm/hooks/use-body-pointer-events-cleanup.js +1 -1
  414. package/dist/esm/hooks/use-breakpoint.d.ts +4 -1
  415. package/dist/esm/hooks/use-breakpoint.d.ts.map +1 -1
  416. package/dist/esm/hooks/use-breakpoint.js +1 -1
  417. package/dist/esm/hooks/use-live-announcer.js +1 -1
  418. package/dist/esm/hooks/use-prop-synced-state.js +1 -1
  419. package/dist/esm/hooks/use-toggle-state.js +1 -1
  420. package/dist/esm/hooks/use-tooltip-wrapper.js +1 -1
  421. package/dist/esm/index.js +1 -1
  422. package/dist/esm/props/as-child.prop.js +1 -1
  423. package/dist/esm/props/color.prop.js +1 -1
  424. package/dist/esm/props/font-family.prop.js +1 -1
  425. package/dist/esm/props/gap.props.js +1 -1
  426. package/dist/esm/props/height.props.js +1 -1
  427. package/dist/esm/props/high-contrast.prop.js +1 -1
  428. package/dist/esm/props/index.js +1 -1
  429. package/dist/esm/props/layout.props.js +1 -1
  430. package/dist/esm/props/leading-trim.prop.js +1 -1
  431. package/dist/esm/props/margin.props.js +1 -1
  432. package/dist/esm/props/padding.props.js +1 -1
  433. package/dist/esm/props/prop-def.js +1 -1
  434. package/dist/esm/props/radius.prop.js +1 -1
  435. package/dist/esm/props/shadow.props.js +1 -1
  436. package/dist/esm/props/text-align.prop.js +1 -1
  437. package/dist/esm/props/text-wrap.prop.js +1 -1
  438. package/dist/esm/props/truncate.prop.js +1 -1
  439. package/dist/esm/props/weight.prop.js +1 -1
  440. package/dist/esm/props/width.props.js +1 -1
  441. package/fonts.css +2 -0
  442. package/package.json +36 -36
  443. package/schemas/base-button.json +66 -60
  444. package/schemas/button.json +80 -74
  445. package/schemas/icon-button.json +78 -72
  446. package/schemas/index.json +390 -360
  447. package/schemas/toggle-button.json +84 -78
  448. package/schemas/toggle-icon-button.json +82 -76
  449. package/src/components/_internal/base-card.css +5 -5
  450. package/src/components/_internal/base-checkbox.css +17 -3
  451. package/src/components/_internal/base-dialog.css +4 -4
  452. package/src/components/_internal/base-menu.css +68 -143
  453. package/src/components/_internal/base-menu.utils.tsx +144 -0
  454. package/src/components/_internal/dropdown-menu-drill-down.tsx +281 -183
  455. package/src/components/_internal/menu-context.tsx +24 -15
  456. package/src/components/accordion.css +5 -5
  457. package/src/components/callout.css +5 -5
  458. package/src/components/chatbar.css +100 -32
  459. package/src/components/chatbar.tsx +739 -735
  460. package/src/components/checkbox-cards.css +30 -0
  461. package/src/components/combobox.css +62 -115
  462. package/src/components/combobox.props.tsx +14 -0
  463. package/src/components/combobox.tsx +440 -212
  464. package/src/components/context-menu.props.tsx +4 -1
  465. package/src/components/context-menu.tsx +108 -62
  466. package/src/components/dropdown-menu.css +41 -121
  467. package/src/components/dropdown-menu.tsx +430 -275
  468. package/src/components/hover-card.css +3 -3
  469. package/src/components/popover.css +4 -4
  470. package/src/components/radio-cards.css +30 -0
  471. package/src/components/schemas/shell.schema.ts +11 -18
  472. package/src/components/scroll-area.css +2 -3
  473. package/src/components/select.css +2 -2
  474. package/src/components/shell.css +1 -1
  475. package/src/components/table.css +5 -5
  476. package/src/components/theme.props.tsx +1 -1
  477. package/src/components/tooltip.css +1 -1
  478. package/src/components/virtual-menu.tsx +12 -8
  479. package/src/hooks/use-breakpoint.ts +19 -3
  480. package/src/styles/fonts.css +6 -1
  481. package/src/styles/tokens/font-family.css +13 -5
  482. package/src/styles/tokens/radius.css +47 -4
  483. package/styles.css +801 -784
  484. package/tokens/base.css +19 -2
  485. package/tokens.css +19 -2
  486. package/dist/esm/chunk-3XT5P2HO.js +0 -2
  487. package/dist/esm/chunk-4XJY6FIK.js +0 -2
  488. package/dist/esm/chunk-4Z33XHOK.js +0 -2
  489. package/dist/esm/chunk-4Z33XHOK.js.map +0 -7
  490. package/dist/esm/chunk-E2Z5IA2P.js +0 -2
  491. package/dist/esm/chunk-E2Z5IA2P.js.map +0 -7
  492. package/dist/esm/chunk-HVWEPVBH.js +0 -2
  493. package/dist/esm/chunk-HVWEPVBH.js.map +0 -7
  494. package/dist/esm/chunk-KKBP2CRH.js +0 -2
  495. package/dist/esm/chunk-KKBP2CRH.js.map +0 -7
  496. package/dist/esm/chunk-MOR3NSXQ.js +0 -2
  497. package/dist/esm/chunk-MOR3NSXQ.js.map +0 -7
  498. package/dist/esm/chunk-QCNUTT4R.js +0 -2
  499. package/dist/esm/chunk-QCNUTT4R.js.map +0 -7
  500. package/dist/esm/chunk-UGD7Z3L4.js +0 -2
  501. package/dist/esm/chunk-UGD7Z3L4.js.map +0 -7
  502. package/dist/esm/chunk-YEB3H7QN.js +0 -2
  503. package/dist/esm/chunk-YEB3H7QN.js.map +0 -7
  504. package/dist/esm/chunk-ZY6SQWHT.js +0 -2
  505. package/dist/esm/chunk-ZY6SQWHT.js.map +0 -7
  506. /package/dist/esm/{chunk-P7HYVJSU.js.map → chunk-2TIADHPD.js.map} +0 -0
  507. /package/dist/esm/{chunk-RMKXWYOV.js.map → chunk-3OBXZUFJ.js.map} +0 -0
  508. /package/dist/esm/{chunk-S2HA3W3V.js.map → chunk-3R7XMRV5.js.map} +0 -0
  509. /package/dist/esm/{chunk-S56YSRHP.js.map → chunk-56TS6OKQ.js.map} +0 -0
  510. /package/dist/esm/{chunk-2FYJTSOP.js.map → chunk-5BYHEYO6.js.map} +0 -0
  511. /package/dist/esm/{chunk-B24BDVG2.js.map → chunk-5MNBQROW.js.map} +0 -0
  512. /package/dist/esm/{chunk-W7N7V5YD.js.map → chunk-7ESHXHHO.js.map} +0 -0
  513. /package/dist/esm/{chunk-CRWYCLTE.js.map → chunk-ABSKRHVM.js.map} +0 -0
  514. /package/dist/esm/{chunk-3MWWP4MW.js.map → chunk-BBJUB52V.js.map} +0 -0
  515. /package/dist/esm/{chunk-PTLFMWCN.js.map → chunk-CQNPBTM5.js.map} +0 -0
  516. /package/dist/esm/{chunk-GJSGKE7B.js.map → chunk-CTA5ODAO.js.map} +0 -0
  517. /package/dist/esm/{chunk-GWXVETMF.js.map → chunk-DCAZPMOB.js.map} +0 -0
  518. /package/dist/esm/{chunk-PLWXTEVE.js.map → chunk-DMJJM5IS.js.map} +0 -0
  519. /package/dist/esm/{chunk-A2RZGGMY.js.map → chunk-DQAUX5ZL.js.map} +0 -0
  520. /package/dist/esm/{chunk-YUP6EICB.js.map → chunk-DXL6V2A5.js.map} +0 -0
  521. /package/dist/esm/{chunk-XMRIRFM7.js.map → chunk-FG7XM7KJ.js.map} +0 -0
  522. /package/dist/esm/{chunk-IKSN4RNB.js.map → chunk-FIB3XATL.js.map} +0 -0
  523. /package/dist/esm/{chunk-374ZYJUP.js.map → chunk-HBR42MIX.js.map} +0 -0
  524. /package/dist/esm/{chunk-2UPAYDKR.js.map → chunk-HIKVMKYQ.js.map} +0 -0
  525. /package/dist/esm/{chunk-VX6ARMWP.js.map → chunk-HSAG7C53.js.map} +0 -0
  526. /package/dist/esm/{chunk-OSRJRBKP.js.map → chunk-HW324FK3.js.map} +0 -0
  527. /package/dist/esm/{chunk-DNHUQLXO.js.map → chunk-JBO5YOUM.js.map} +0 -0
  528. /package/dist/esm/{chunk-HF5WY5YE.js.map → chunk-JGLAE5G7.js.map} +0 -0
  529. /package/dist/esm/{chunk-4YFJWXL2.js.map → chunk-JJKQ7CGI.js.map} +0 -0
  530. /package/dist/esm/{chunk-CWRNW44F.js.map → chunk-L6PVH3RM.js.map} +0 -0
  531. /package/dist/esm/{chunk-MK2R6DAU.js.map → chunk-LAOMR7SE.js.map} +0 -0
  532. /package/dist/esm/{chunk-FHOCUZDR.js.map → chunk-NIQT45PJ.js.map} +0 -0
  533. /package/dist/esm/{chunk-3XT5P2HO.js.map → chunk-OS3FCYZY.js.map} +0 -0
  534. /package/dist/esm/{chunk-7QDTSBJF.js.map → chunk-OYF6YXH7.js.map} +0 -0
  535. /package/dist/esm/{chunk-WFMYNZXZ.js.map → chunk-P5OSU4NX.js.map} +0 -0
  536. /package/dist/esm/{chunk-QEOAXJLN.js.map → chunk-PTS6QYQW.js.map} +0 -0
  537. /package/dist/esm/{chunk-CVWCKOHO.js.map → chunk-RLPKKCBL.js.map} +0 -0
  538. /package/dist/esm/{chunk-LKAK5GXF.js.map → chunk-SPIU5JYC.js.map} +0 -0
  539. /package/dist/esm/{chunk-4AQTUBVP.js.map → chunk-T4UHUQFJ.js.map} +0 -0
  540. /package/dist/esm/{chunk-K6QZMNCD.js.map → chunk-TS623UPD.js.map} +0 -0
  541. /package/dist/esm/{chunk-YV2KYHGR.js.map → chunk-UBOOUDS4.js.map} +0 -0
  542. /package/dist/esm/{chunk-D6B7IMZU.js.map → chunk-UFI53QUC.js.map} +0 -0
  543. /package/dist/esm/{chunk-AU3D6F42.js.map → chunk-UIRSGVJG.js.map} +0 -0
  544. /package/dist/esm/{chunk-FXUUI6Z2.js.map → chunk-UUUZXUIP.js.map} +0 -0
  545. /package/dist/esm/{chunk-25WZA7ZU.js.map → chunk-VA4CTVQL.js.map} +0 -0
  546. /package/dist/esm/{chunk-TOLLPRMM.js.map → chunk-VNPNEJI6.js.map} +0 -0
  547. /package/dist/esm/{chunk-6IGTK4JE.js.map → chunk-YMUGKYGW.js.map} +0 -0
  548. /package/dist/esm/{chunk-M24UXMOQ.js.map → chunk-ZXZOH63L.js.map} +0 -0
@@ -3,6 +3,7 @@
3
3
  import * as React from 'react';
4
4
  import classNames from 'classnames';
5
5
  import { DropdownMenu as DropdownMenuPrimitive, Slot } from 'radix-ui';
6
+ import { useCallbackRef } from 'radix-ui/internal';
6
7
 
7
8
  import { ScrollArea } from './scroll-area.js';
8
9
  import {
@@ -16,27 +17,60 @@ import { Theme, useThemeContext } from './theme.js';
16
17
  import { ChevronDownIcon, ThickCheckIcon, ThickChevronLeftIcon, ThickChevronRightIcon, ThickDotIcon } from './icons.js';
17
18
  import { extractProps } from '../helpers/extract-props.js';
18
19
  import {
19
- DrillDownProvider,
20
+ DrillDownLevelContext,
21
+ DrillDownRoot,
20
22
  SubContext,
21
- useDrillDownActionsOptional,
22
- useDrillDownStateOptional,
23
+ useCreateDrillDownStore,
24
+ useDrillDownDirection,
25
+ useDrillDownStore,
26
+ usePanelPosition,
27
+ useStackEntry,
23
28
  useSubContext,
24
29
  } from './_internal/dropdown-menu-drill-down.js';
25
30
  import { MenuProvider } from './_internal/menu-context.js';
26
- import type { SubmenuBehavior } from './_internal/dropdown-menu-drill-down.js';
31
+ import {
32
+ MenuShortcut,
33
+ getMenuAlignOffset,
34
+ resolveMenuMaterial,
35
+ toAriaKeyShortcuts,
36
+ useResolvedResponsiveValue,
37
+ } from './_internal/base-menu.utils.js';
27
38
  import { requireReactElement } from '../helpers/require-react-element.js';
28
- import { Kbd } from './kbd.js';
39
+ import { useDeprecatedPanelBackgroundWarning } from '../helpers/use-deprecation-warning.js';
29
40
 
41
+ import type { SubmenuBehavior } from './_internal/dropdown-menu-drill-down.js';
30
42
  import type { IconProps } from './icons.js';
31
43
  import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
32
44
  import type { GetPropDefTypes, Responsive } from '../props/prop-def.js';
33
- import { useDeprecatedPanelBackgroundWarning } from '../helpers/use-deprecation-warning.js';
45
+
46
+ /**
47
+ * Open state of the menu, mirrored from Root so Content can reset drill-down
48
+ * navigation when a force-mounted menu opens again.
49
+ */
50
+ const DropdownMenuOpenContext = React.createContext(false);
34
51
 
35
52
  interface DropdownMenuRootProps
36
53
  extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> {}
37
- const DropdownMenuRoot: React.FC<DropdownMenuRootProps> = (props) => (
38
- <DropdownMenuPrimitive.Root {...props} />
39
- );
54
+ const DropdownMenuRoot: React.FC<DropdownMenuRootProps> = (props) => {
55
+ const { open: openProp, defaultOpen = false, onOpenChange } = props;
56
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
57
+ const open = openProp ?? uncontrolledOpen;
58
+
59
+ const handleOpenChangeProp = useCallbackRef(onOpenChange);
60
+ const handleOpenChange = React.useCallback(
61
+ (next: boolean) => {
62
+ setUncontrolledOpen(next);
63
+ handleOpenChangeProp(next);
64
+ },
65
+ [handleOpenChangeProp],
66
+ );
67
+
68
+ return (
69
+ <DropdownMenuOpenContext.Provider value={open}>
70
+ <DropdownMenuPrimitive.Root {...props} onOpenChange={handleOpenChange} />
71
+ </DropdownMenuOpenContext.Provider>
72
+ );
73
+ };
40
74
  DropdownMenuRoot.displayName = 'DropdownMenu.Root';
41
75
 
42
76
  type DropdownMenuTriggerElement = React.ElementRef<typeof DropdownMenuPrimitive.Trigger>;
@@ -51,51 +85,37 @@ const DropdownMenuTrigger = React.forwardRef<DropdownMenuTriggerElement, Dropdow
51
85
  );
52
86
  DropdownMenuTrigger.displayName = 'DropdownMenu.Trigger';
53
87
 
54
- /**
55
- * Internal component that wraps root menu items and handles visibility in drill-down mode.
56
- * In drill-down mode, this hides when a submenu is active.
57
- * Uses state-only hook to subscribe only to state changes, not action changes.
58
- */
59
- function DrillDownRoot({ children }: { children: React.ReactNode }) {
60
- const drillDownState = useDrillDownStateOptional();
88
+ /** Visual props shared by Content and SubContent. */
89
+ type DropdownMenuSurfaceProps = GetPropDefTypes<typeof dropdownMenuSubContentPropDefs>;
61
90
 
62
- // In cascade mode or when no drill-down context, always show
63
- if (!drillDownState || drillDownState.behavior === 'cascade') {
64
- return <>{children}</>;
65
- }
91
+ type DropdownMenuContentContextValue = {
92
+ size?: DropdownMenuSurfaceProps['size'];
93
+ variant?: DropdownMenuSurfaceProps['variant'];
94
+ color?: DropdownMenuSurfaceProps['color'];
95
+ highContrast?: DropdownMenuSurfaceProps['highContrast'];
96
+ material?: DropdownMenuSurfaceProps['material'];
97
+ };
98
+ const DropdownMenuContentContext = React.createContext<DropdownMenuContentContextValue>({});
99
+
100
+ /** Keys that navigate back one drill-down level, by writing direction. */
101
+ const BACK_KEYS = { ltr: 'ArrowLeft', rtl: 'ArrowRight' } as const;
102
+ const FORWARD_KEYS = { ltr: 'ArrowRight', rtl: 'ArrowLeft' } as const;
66
103
 
67
- // In drill-down mode, hide root when a submenu is active
104
+ function isTextEntryTarget(target: EventTarget | null) {
68
105
  return (
69
- <div
70
- className="rt-DropdownMenuDrillDownRoot"
71
- data-drill-down-active={drillDownState.isRoot ? undefined : true}
72
- data-animation-direction={drillDownState.animationDirection ?? undefined}
73
- >
74
- {children}
75
- </div>
106
+ target instanceof HTMLElement &&
107
+ (target.isContentEditable || target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')
76
108
  );
77
109
  }
78
110
 
79
- type DropdownMenuContentOwnProps = GetPropDefTypes<typeof dropdownMenuContentPropDefs> & {
80
- /**
81
- * Controls how submenus behave.
82
- * - `cascade`: Default cascading behavior where submenus open to the side (portal-based)
83
- * - `drill-down`: Mobile-friendly behavior where submenus replace the content inline
84
- * Supports responsive values: `{ initial: 'drill-down', md: 'cascade' }`
85
- */
86
- submenuBehavior?: Responsive<SubmenuBehavior>;
87
- /**
88
- * When true, skips the ScrollArea wrapper to allow VirtualMenu to handle its own scrolling.
89
- * Use this when rendering virtualized lists inside the dropdown.
90
- */
91
- virtualized?: boolean;
92
- };
93
- type DropdownMenuContentContextValue = Omit<DropdownMenuContentOwnProps, 'submenuBehavior'>;
94
- const DropdownMenuContentContext = React.createContext<DropdownMenuContentContextValue>({});
111
+ function getDirection(element: HTMLElement | null): 'ltr' | 'rtl' {
112
+ return element?.closest('[dir]')?.getAttribute('dir') === 'rtl' ? 'rtl' : 'ltr';
113
+ }
114
+
95
115
  type DropdownMenuContentElement = React.ElementRef<typeof DropdownMenuPrimitive.Content>;
96
116
  interface DropdownMenuContentProps
97
117
  extends ComponentPropsWithout<typeof DropdownMenuPrimitive.Content, RemovedProps>,
98
- DropdownMenuContentContextValue {
118
+ DropdownMenuSurfaceProps {
99
119
  container?: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Portal>['container'];
100
120
  /**
101
121
  * Controls how submenus behave.
@@ -116,26 +136,11 @@ const DropdownMenuContent = React.forwardRef<DropdownMenuContentElement, Dropdow
116
136
 
117
137
  useDeprecatedPanelBackgroundWarning(props.panelBackground);
118
138
 
119
- // Material takes precedence over panelBackground
120
- const effectiveMaterial =
121
- props.material ?? props.panelBackground ?? themeContext.panelBackground;
122
-
123
- // Memoize theme context values to prevent unnecessary re-renders
124
- const memoizedThemeContext = React.useMemo(
125
- () => ({
126
- material: effectiveMaterial,
127
- accentColor: themeContext.accentColor,
128
- }),
129
- [effectiveMaterial, themeContext.accentColor],
130
- );
131
-
139
+ const material = resolveMenuMaterial(props.material, props.panelBackground, themeContext);
132
140
  const {
133
141
  size = dropdownMenuContentPropDefs.size.default,
134
142
  variant = dropdownMenuContentPropDefs.variant.default,
135
143
  highContrast = dropdownMenuContentPropDefs.highContrast.default,
136
- material = memoizedThemeContext.material,
137
- submenuBehavior,
138
- virtualized = false,
139
144
  } = props;
140
145
  const {
141
146
  className,
@@ -143,29 +148,50 @@ const DropdownMenuContent = React.forwardRef<DropdownMenuContentElement, Dropdow
143
148
  color,
144
149
  container,
145
150
  forceMount,
146
- material: _,
147
- panelBackground: __,
148
- submenuBehavior: ___,
149
- virtualized: ____,
151
+ material: _material,
152
+ panelBackground: _panelBackground,
153
+ submenuBehavior,
154
+ virtualized = false,
155
+ onEscapeKeyDown,
156
+ onKeyDown,
150
157
  ...contentProps
151
158
  } = extractProps(props, dropdownMenuContentPropDefs);
152
159
 
153
- // Memoize color resolution to prevent unnecessary re-renders
154
- const resolvedColor = React.useMemo(
155
- () => color || memoizedThemeContext.accentColor,
156
- [color, memoizedThemeContext.accentColor],
160
+ const resolvedColor = color || themeContext.accentColor;
161
+ const behavior = useResolvedResponsiveValue<SubmenuBehavior>(submenuBehavior, 'cascade');
162
+ const isDrillDown = behavior === 'drill-down';
163
+ const open = React.useContext(DropdownMenuOpenContext);
164
+ const drillDownStore = useCreateDrillDownStore();
165
+
166
+ // Escape goes back one drill-down level before it closes the menu.
167
+ const handleEscapeKeyDown = (event: KeyboardEvent) => {
168
+ onEscapeKeyDown?.(event);
169
+ if (event.defaultPrevented || !isDrillDown || drillDownStore.getDepth() === 0) return;
170
+ event.preventDefault();
171
+ drillDownStore.pop('keyboard');
172
+ };
173
+
174
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
175
+ onKeyDown?.(event);
176
+ if (event.defaultPrevented || !isDrillDown || drillDownStore.getDepth() === 0) return;
177
+ if (isTextEntryTarget(event.target)) return;
178
+ if (event.key === BACK_KEYS[getDirection(event.currentTarget)]) {
179
+ event.preventDefault();
180
+ drillDownStore.pop('keyboard');
181
+ }
182
+ };
183
+
184
+ const contextValue = React.useMemo(
185
+ () => ({ size, variant, color: resolvedColor, highContrast, material }),
186
+ [size, variant, resolvedColor, highContrast, material],
157
187
  );
188
+
158
189
  const contentBody = (
159
190
  <MenuProvider size={size}>
160
- <DropdownMenuContentContext.Provider
161
- value={React.useMemo(
162
- () => ({ size, variant, color: resolvedColor, highContrast, material }),
163
- [size, variant, resolvedColor, highContrast, material],
164
- )}
165
- >
166
- <DrillDownProvider submenuBehavior={submenuBehavior}>
167
- <DrillDownRoot>{children}</DrillDownRoot>
168
- </DrillDownProvider>
191
+ <DropdownMenuContentContext.Provider value={contextValue}>
192
+ <DrillDownRoot store={drillDownStore} behavior={behavior} open={open}>
193
+ {children}
194
+ </DrillDownRoot>
169
195
  </DropdownMenuContentContext.Provider>
170
196
  </MenuProvider>
171
197
  );
@@ -181,6 +207,8 @@ const DropdownMenuContent = React.forwardRef<DropdownMenuContentElement, Dropdow
181
207
  sideOffset={4}
182
208
  collisionPadding={10}
183
209
  {...contentProps}
210
+ onEscapeKeyDown={handleEscapeKeyDown}
211
+ onKeyDown={handleKeyDown}
184
212
  asChild={false}
185
213
  ref={forwardedRef}
186
214
  className={classNames(
@@ -195,7 +223,7 @@ const DropdownMenuContent = React.forwardRef<DropdownMenuContentElement, Dropdow
195
223
  {contentBody}
196
224
  </div>
197
225
  ) : (
198
- <ScrollArea type="auto">
226
+ <ScrollArea type="auto" scrollbars="vertical">
199
227
  <div className={classNames('rt-BaseMenuViewport', 'rt-DropdownMenuViewport')}>
200
228
  {contentBody}
201
229
  </div>
@@ -213,14 +241,17 @@ type DropdownMenuLabelElement = React.ElementRef<typeof DropdownMenuPrimitive.La
213
241
  interface DropdownMenuLabelProps
214
242
  extends ComponentPropsWithout<typeof DropdownMenuPrimitive.Label, RemovedProps> {}
215
243
  const DropdownMenuLabel = React.forwardRef<DropdownMenuLabelElement, DropdownMenuLabelProps>(
216
- ({ className, ...props }, forwardedRef) => (
217
- <DropdownMenuPrimitive.Label
218
- {...props}
219
- asChild={false}
220
- ref={forwardedRef}
221
- className={classNames('rt-BaseMenuLabel', 'rt-DropdownMenuLabel', className)}
222
- />
223
- ),
244
+ ({ className, ...props }, forwardedRef) => {
245
+ if (!React.useContext(DrillDownLevelContext)) return null;
246
+ return (
247
+ <DropdownMenuPrimitive.Label
248
+ {...props}
249
+ asChild={false}
250
+ ref={forwardedRef}
251
+ className={classNames('rt-BaseMenuLabel', 'rt-DropdownMenuLabel', className)}
252
+ />
253
+ );
254
+ },
224
255
  );
225
256
  DropdownMenuLabel.displayName = 'DropdownMenu.Label';
226
257
 
@@ -238,19 +269,18 @@ const DropdownMenuItem = React.forwardRef<DropdownMenuItemElement, DropdownMenuI
238
269
  shortcut,
239
270
  ...itemProps
240
271
  } = props;
272
+ // In drill-down mode, items of levels that are not on screen do not mount.
273
+ if (!React.useContext(DrillDownLevelContext)) return null;
241
274
  return (
242
275
  <DropdownMenuPrimitive.Item
243
276
  data-accent-color={color}
277
+ aria-keyshortcuts={shortcut ? toAriaKeyShortcuts(shortcut) : undefined}
244
278
  {...itemProps}
245
279
  ref={forwardedRef}
246
280
  className={classNames('rt-reset', 'rt-BaseMenuItem', 'rt-DropdownMenuItem', className)}
247
281
  >
248
282
  <Slot.Slottable>{children}</Slot.Slottable>
249
- {shortcut && (
250
- <div className="rt-BaseMenuShortcut rt-DropdownMenuShortcut">
251
- <Kbd size="1">{shortcut}</Kbd>
252
- </div>
253
- )}
283
+ {shortcut && <MenuShortcut shortcut={shortcut} className="rt-DropdownMenuShortcut" />}
254
284
  </DropdownMenuPrimitive.Item>
255
285
  );
256
286
  },
@@ -303,6 +333,7 @@ const DropdownMenuRadioItem = React.forwardRef<
303
333
  color = dropdownMenuRadioItemPropDefs.color.default,
304
334
  ...itemProps
305
335
  } = props;
336
+ if (!React.useContext(DrillDownLevelContext)) return null;
306
337
  return (
307
338
  <DropdownMenuPrimitive.RadioItem
308
339
  {...itemProps}
@@ -342,8 +373,10 @@ const DropdownMenuCheckboxItem = React.forwardRef<
342
373
  color = dropdownMenuCheckboxItemPropDefs.color.default,
343
374
  ...itemProps
344
375
  } = props;
376
+ if (!React.useContext(DrillDownLevelContext)) return null;
345
377
  return (
346
378
  <DropdownMenuPrimitive.CheckboxItem
379
+ aria-keyshortcuts={shortcut ? toAriaKeyShortcuts(shortcut) : undefined}
347
380
  {...itemProps}
348
381
  asChild={false}
349
382
  ref={forwardedRef}
@@ -358,23 +391,14 @@ const DropdownMenuCheckboxItem = React.forwardRef<
358
391
  >
359
392
  {children}
360
393
  <DropdownMenuPrimitive.ItemIndicator className="rt-BaseMenuItemIndicator rt-DropdownMenuItemIndicator">
361
- <ThickCheckIcon className="rt-BaseMenuItemIndicatorIcon rt-ContextMenuItemIndicatorIcon" />
394
+ <ThickCheckIcon className="rt-BaseMenuItemIndicatorIcon rt-DropdownMenuItemIndicatorIcon" />
362
395
  </DropdownMenuPrimitive.ItemIndicator>
363
- {shortcut && (
364
- <div className="rt-BaseMenuShortcut rt-DropdownMenuShortcut">
365
- <Kbd size="1">{shortcut}</Kbd>
366
- </div>
367
- )}
396
+ {shortcut && <MenuShortcut shortcut={shortcut} className="rt-DropdownMenuShortcut" />}
368
397
  </DropdownMenuPrimitive.CheckboxItem>
369
398
  );
370
399
  });
371
400
  DropdownMenuCheckboxItem.displayName = 'DropdownMenu.CheckboxItem';
372
401
 
373
- // Generate unique submenu IDs using React 18's useId for SSR safety
374
- function useSubId() {
375
- return React.useId();
376
- }
377
-
378
402
  interface DropdownMenuSubProps
379
403
  extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Sub> {
380
404
  /**
@@ -383,31 +407,53 @@ interface DropdownMenuSubProps
383
407
  */
384
408
  label?: React.ReactNode;
385
409
  }
386
- const DropdownMenuSub: React.FC<DropdownMenuSubProps> = ({ label = 'Back', ...props }) => {
387
- // Use state-only hook since we only need behavior check
388
- const drillDownState = useDrillDownStateOptional();
389
- const subId = useSubId();
390
-
391
- // Create context value for SubContent and SubTrigger
392
- const subContextValue = React.useMemo(
393
- () => ({ id: subId, label }),
394
- [subId, label]
395
- );
396
-
397
- // In drill-down mode, we don't use Radix's Sub component
398
- // We just provide the SubContext and render children
399
- if (drillDownState?.behavior === 'drill-down') {
400
- return (
401
- <SubContext.Provider value={subContextValue}>
402
- {props.children}
403
- </SubContext.Provider>
404
- );
405
- }
406
-
407
- // In cascade mode, use Radix's Sub component normally
410
+ const DropdownMenuSub: React.FC<DropdownMenuSubProps> = ({
411
+ label = 'Back',
412
+ open,
413
+ defaultOpen,
414
+ onOpenChange,
415
+ children,
416
+ }) => {
417
+ const drillDownStore = useDrillDownStore();
418
+ const id = React.useId();
419
+ const subContextValue = React.useMemo(() => ({ id, label }), [id, label]);
420
+ const isOpenInDrillDown = usePanelPosition(drillDownStore, id) !== 'closed';
421
+
422
+ // Drill-down: `open` and `defaultOpen` drive the stack; the stack drives
423
+ // `onOpenChange`. A controlled `open` is applied when it changes.
424
+ React.useLayoutEffect(() => {
425
+ if (!drillDownStore || open === undefined) return;
426
+ if (open && !drillDownStore.isOpen(id)) drillDownStore.push(id, null);
427
+ else if (!open && drillDownStore.isOpen(id)) drillDownStore.close(id);
428
+ }, [drillDownStore, open, id]);
429
+
430
+ const defaultOpenRef = React.useRef(defaultOpen);
431
+ React.useLayoutEffect(() => {
432
+ if (drillDownStore && defaultOpenRef.current && open === undefined) drillDownStore.push(id, null);
433
+ // Only on mount, like an uncontrolled default.
434
+ // eslint-disable-next-line react-hooks/exhaustive-deps
435
+ }, []);
436
+
437
+ const handleOpenChange = useCallbackRef(onOpenChange);
438
+ const wasOpenRef = React.useRef(isOpenInDrillDown);
439
+ React.useEffect(() => {
440
+ if (!drillDownStore || wasOpenRef.current === isOpenInDrillDown) return;
441
+ wasOpenRef.current = isOpenInDrillDown;
442
+ handleOpenChange(isOpenInDrillDown);
443
+ }, [drillDownStore, isOpenInDrillDown, handleOpenChange]);
444
+
445
+ // Radix Sub stays mounted in both behaviors so switching behavior does not
446
+ // remount the subtree. It only renders context; in drill-down its trigger
447
+ // and content are never rendered, so it reports no open changes of its own.
408
448
  return (
409
449
  <SubContext.Provider value={subContextValue}>
410
- <DropdownMenuPrimitive.Sub {...props} />
450
+ <DropdownMenuPrimitive.Sub
451
+ open={open}
452
+ defaultOpen={defaultOpen}
453
+ onOpenChange={drillDownStore ? undefined : onOpenChange}
454
+ >
455
+ {children}
456
+ </DropdownMenuPrimitive.Sub>
411
457
  </SubContext.Provider>
412
458
  );
413
459
  };
@@ -420,44 +466,38 @@ const DropdownMenuSubTrigger = React.forwardRef<
420
466
  DropdownMenuSubTriggerElement,
421
467
  DropdownMenuSubTriggerProps
422
468
  >((props, forwardedRef) => {
423
- const { className, children, onClick, ...subTriggerProps } = props;
424
- // Use split hooks: actions for stable push callback, state only for behavior check
425
- const drillDownActions = useDrillDownActionsOptional();
426
- const drillDownState = useDrillDownStateOptional();
469
+ const { className, children, onKeyDown, ...subTriggerProps } = props;
470
+ const drillDownStore = useDrillDownStore();
427
471
  const subContext = useSubContext();
428
-
429
- // Store onClick in ref to avoid recreating handlers when onClick changes
430
- const onClickRef = React.useRef(onClick);
431
- onClickRef.current = onClick;
432
-
433
- // Stable click handler - only depends on stable refs
434
- const handleClick = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
435
- e.preventDefault();
436
- if (subContext && drillDownActions) {
437
- drillDownActions.push(subContext.id);
438
- }
439
- (onClickRef.current as React.MouseEventHandler<HTMLDivElement> | undefined)?.(e);
440
- }, [subContext, drillDownActions]);
441
-
442
- // Stable keydown handler
443
- const handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
444
- if (e.key === 'Enter' || e.key === ' ') {
445
- e.preventDefault();
446
- if (subContext && drillDownActions) {
447
- drillDownActions.push(subContext.id);
472
+ const isLevelVisible = React.useContext(DrillDownLevelContext);
473
+
474
+ if (drillDownStore && subContext) {
475
+ if (!isLevelVisible) return null;
476
+
477
+ const handleSelect = (event: Event) => {
478
+ // Navigating does not close the menu.
479
+ event.preventDefault();
480
+ drillDownStore.push(subContext.id);
481
+ };
482
+
483
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
484
+ onKeyDown?.(event);
485
+ if (event.defaultPrevented || subTriggerProps.disabled) return;
486
+ if (event.key === FORWARD_KEYS[getDirection(event.currentTarget)]) {
487
+ event.preventDefault();
488
+ drillDownStore.push(subContext.id, 'keyboard');
448
489
  }
449
- }
450
- }, [subContext, drillDownActions]);
490
+ };
451
491
 
452
- // In drill-down mode, render a button that navigates to the submenu
453
- if (drillDownState?.behavior === 'drill-down' && subContext) {
454
492
  return (
455
- <div
456
- role="menuitem"
457
- tabIndex={0}
458
- ref={forwardedRef as React.Ref<HTMLDivElement>}
459
- onClick={handleClick}
493
+ <DropdownMenuPrimitive.Item
494
+ aria-haspopup="menu"
495
+ {...subTriggerProps}
496
+ data-drill-down-trigger={subContext.id}
497
+ data-drill-down-name={subTriggerProps.textValue}
498
+ onSelect={handleSelect}
460
499
  onKeyDown={handleKeyDown}
500
+ ref={forwardedRef}
461
501
  className={classNames(
462
502
  'rt-reset',
463
503
  'rt-BaseMenuItem',
@@ -470,17 +510,16 @@ const DropdownMenuSubTrigger = React.forwardRef<
470
510
  >
471
511
  {children}
472
512
  <div className="rt-BaseMenuShortcut rt-DropdownMenuShortcut">
473
- <ThickChevronRightIcon className="rt-BaseMenuSubTriggerIcon rt-DropdownMenuSubtriggerIcon" />
513
+ <ThickChevronRightIcon className="rt-BaseMenuSubTriggerIcon rt-DropdownMenuSubTriggerIcon" />
474
514
  </div>
475
- </div>
515
+ </DropdownMenuPrimitive.Item>
476
516
  );
477
517
  }
478
518
 
479
- // In cascade mode, use Radix's SubTrigger
480
519
  return (
481
520
  <DropdownMenuPrimitive.SubTrigger
482
521
  {...subTriggerProps}
483
- onClick={onClick as React.MouseEventHandler<HTMLDivElement> | undefined}
522
+ onKeyDown={onKeyDown}
484
523
  asChild={false}
485
524
  ref={forwardedRef}
486
525
  className={classNames(
@@ -493,59 +532,55 @@ const DropdownMenuSubTrigger = React.forwardRef<
493
532
  >
494
533
  {children}
495
534
  <div className="rt-BaseMenuShortcut rt-DropdownMenuShortcut">
496
- <ThickChevronRightIcon className="rt-BaseMenuSubTriggerIcon rt-DropdownMenuSubtriggerIcon" />
535
+ <ThickChevronRightIcon className="rt-BaseMenuSubTriggerIcon rt-DropdownMenuSubTriggerIcon" />
497
536
  </div>
498
537
  </DropdownMenuPrimitive.SubTrigger>
499
538
  );
500
539
  });
501
540
  DropdownMenuSubTrigger.displayName = 'DropdownMenu.SubTrigger';
502
541
 
503
- // Separator is defined here (before SubContent) because it's used in drill-down mode
504
542
  type DropdownMenuSeparatorElement = React.ElementRef<typeof DropdownMenuPrimitive.Separator>;
505
543
  interface DropdownMenuSeparatorProps
506
544
  extends ComponentPropsWithout<typeof DropdownMenuPrimitive.Separator, RemovedProps> {}
507
545
  const DropdownMenuSeparator = React.forwardRef<
508
546
  DropdownMenuSeparatorElement,
509
547
  DropdownMenuSeparatorProps
510
- >(({ className, ...props }, forwardedRef) => (
511
- <DropdownMenuPrimitive.Separator
512
- {...props}
513
- asChild={false}
514
- ref={forwardedRef}
515
- className={classNames('rt-BaseMenuSeparator', 'rt-DropdownMenuSeparator', className)}
516
- />
517
- ));
548
+ >(({ className, ...props }, forwardedRef) => {
549
+ if (!React.useContext(DrillDownLevelContext)) return null;
550
+ return (
551
+ <DropdownMenuPrimitive.Separator
552
+ {...props}
553
+ asChild={false}
554
+ ref={forwardedRef}
555
+ className={classNames('rt-BaseMenuSeparator', 'rt-DropdownMenuSeparator', className)}
556
+ />
557
+ );
558
+ });
518
559
  DropdownMenuSeparator.displayName = 'DropdownMenu.Separator';
519
560
 
520
- /**
521
- * Internal component for the drill-down back button.
522
- * Uses actions-only hook since it only needs to call pop(), never reads state.
523
- * This prevents re-renders when drill-down state changes.
524
- */
525
- function DrillDownBackItem({ label }: { label: React.ReactNode }) {
526
- // Use actions-only hook for stable pop callback - prevents re-renders on stack changes
527
- const { pop } = useDrillDownActionsOptional() ?? {};
528
-
529
- // Stable click handler
530
- const handleClick = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
531
- e.preventDefault();
532
- pop?.();
533
- }, [pop]);
534
-
535
- // Stable keydown handler
536
- const handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
537
- if (e.key === 'Enter' || e.key === ' ') {
538
- e.preventDefault();
539
- pop?.();
540
- }
541
- }, [pop]);
561
+ /** The row at the top of a drill-down panel that returns to the parent level. */
562
+ function DrillDownBackItem({ label, name }: { label: React.ReactNode; name: string }) {
563
+ const drillDownStore = useDrillDownStore();
564
+
565
+ const handleSelect = React.useCallback(
566
+ (event: Event) => {
567
+ // Navigating does not close the menu.
568
+ event.preventDefault();
569
+ drillDownStore?.pop();
570
+ },
571
+ [drillDownStore],
572
+ );
573
+
574
+ // The visible label is often the submenu title, so the accessible name says
575
+ // what the row does while still containing the visible text.
576
+ const labelText = typeof label === 'string' ? label : name;
577
+ const ariaLabel = labelText && labelText !== 'Back' ? `Back from ${labelText}` : undefined;
542
578
 
543
579
  return (
544
- <div
545
- role="menuitem"
546
- tabIndex={0}
547
- onClick={handleClick}
548
- onKeyDown={handleKeyDown}
580
+ <DropdownMenuPrimitive.Item
581
+ aria-label={ariaLabel}
582
+ textValue={labelText || 'Back'}
583
+ onSelect={handleSelect}
549
584
  className={classNames(
550
585
  'rt-reset',
551
586
  'rt-BaseMenuItem',
@@ -553,109 +588,229 @@ function DrillDownBackItem({ label }: { label: React.ReactNode }) {
553
588
  'rt-DropdownMenuDrillDownBackItem',
554
589
  )}
555
590
  >
556
- <ThickChevronLeftIcon className="rt-DropdownMenuDrillDownBackIcon" />
591
+ <ThickChevronLeftIcon className="rt-BaseMenuSubTriggerIcon rt-DropdownMenuDrillDownBackIcon" />
557
592
  <span className="rt-DropdownMenuDrillDownBackLabel">{label}</span>
558
- </div>
593
+ </DropdownMenuPrimitive.Item>
559
594
  );
560
595
  }
561
596
 
597
+ /** Radix positioning and layer props that have no meaning for an inline drill-down panel. */
598
+ type PopperOnlyProps =
599
+ | 'loop'
600
+ | 'onEscapeKeyDown'
601
+ | 'onPointerDownOutside'
602
+ | 'onFocusOutside'
603
+ | 'onInteractOutside'
604
+ | 'sideOffset'
605
+ | 'alignOffset'
606
+ | 'avoidCollisions'
607
+ | 'collisionBoundary'
608
+ | 'collisionPadding'
609
+ | 'arrowPadding'
610
+ | 'sticky'
611
+ | 'hideWhenDetached'
612
+ | 'updatePositionStrategy';
613
+
614
+ function omitPopperProps<T extends Record<string, unknown>>(props: T): Omit<T, PopperOnlyProps> {
615
+ const {
616
+ loop: _loop,
617
+ onEscapeKeyDown: _onEscapeKeyDown,
618
+ onPointerDownOutside: _onPointerDownOutside,
619
+ onFocusOutside: _onFocusOutside,
620
+ onInteractOutside: _onInteractOutside,
621
+ sideOffset: _sideOffset,
622
+ alignOffset: _alignOffset,
623
+ avoidCollisions: _avoidCollisions,
624
+ collisionBoundary: _collisionBoundary,
625
+ collisionPadding: _collisionPadding,
626
+ arrowPadding: _arrowPadding,
627
+ sticky: _sticky,
628
+ hideWhenDetached: _hideWhenDetached,
629
+ updatePositionStrategy: _updatePositionStrategy,
630
+ ...rest
631
+ } = props;
632
+ return rest;
633
+ }
634
+
562
635
  type DropdownMenuSubContentElement = React.ElementRef<typeof DropdownMenuPrimitive.SubContent>;
563
636
  interface DropdownMenuSubContentProps
564
637
  extends ComponentPropsWithout<typeof DropdownMenuPrimitive.SubContent, RemovedProps>,
565
- DropdownMenuContentContextValue {
638
+ DropdownMenuSurfaceProps {
566
639
  container?: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Portal>['container'];
640
+ /**
641
+ * When true, skips the ScrollArea wrapper so a VirtualMenu can handle its own
642
+ * scrolling. Applies to cascade submenus; a drill-down panel scrolls with the
643
+ * root content.
644
+ */
645
+ virtualized?: boolean;
567
646
  }
568
647
  const DropdownMenuSubContent = React.forwardRef<
569
648
  DropdownMenuSubContentElement,
570
649
  DropdownMenuSubContentProps
571
650
  >((props, forwardedRef) => {
572
- // Get context values directly - memoization at provider level handles stability
573
- const { size, variant, color, highContrast, material } = React.useContext(
574
- DropdownMenuContentContext,
575
- );
576
- // Use state-only hook since we only need behavior and isActive (no actions)
577
- const drillDownState = useDrillDownStateOptional();
651
+ const drillDownStore = useDrillDownStore();
578
652
  const subContext = useSubContext();
579
653
 
580
- const {
581
- className,
582
- children,
583
- container,
584
- forceMount,
585
- material: _,
586
- panelBackground: __,
587
- ...subContentProps
588
- } = extractProps(
589
- { size, variant, color, highContrast, material, ...props },
590
- dropdownMenuSubContentPropDefs,
591
- );
654
+ if (drillDownStore && subContext) {
655
+ return (
656
+ <DrillDownPanel {...props} ref={forwardedRef} store={drillDownStore} sub={subContext} />
657
+ );
658
+ }
659
+ return <CascadeSubContent {...props} ref={forwardedRef} />;
660
+ });
661
+ DropdownMenuSubContent.displayName = 'DropdownMenu.SubContent';
662
+
663
+ const CascadeSubContent = React.forwardRef<DropdownMenuSubContentElement, DropdownMenuSubContentProps>(
664
+ (props, forwardedRef) => {
665
+ const parent = React.useContext(DropdownMenuContentContext);
666
+ const themeContext = useThemeContext();
667
+
668
+ useDeprecatedPanelBackgroundWarning(props.panelBackground);
669
+
670
+ // A submenu inherits the parent surface, and its own props win.
671
+ const size = props.size ?? parent.size;
672
+ const variant = props.variant ?? parent.variant;
673
+ const color = props.color ?? parent.color;
674
+ const highContrast = props.highContrast ?? parent.highContrast;
675
+ const material =
676
+ props.material ?? props.panelBackground ?? parent.material ?? resolveMenuMaterial(undefined, undefined, themeContext);
677
+
678
+ const {
679
+ className,
680
+ children,
681
+ container,
682
+ forceMount,
683
+ color: _color,
684
+ material: _material,
685
+ panelBackground: _panelBackground,
686
+ virtualized = false,
687
+ ...subContentProps
688
+ } = extractProps({ ...props, size, variant, highContrast }, dropdownMenuSubContentPropDefs);
689
+
690
+ const scalarSize = useResolvedResponsiveValue(size, dropdownMenuSubContentPropDefs.size.default);
691
+
692
+ const contextValue = React.useMemo(
693
+ () => ({ size, variant, color, highContrast, material }),
694
+ [size, variant, color, highContrast, material],
695
+ );
696
+
697
+ const body = (
698
+ <MenuProvider size={size}>
699
+ <DropdownMenuContentContext.Provider value={contextValue}>{children}</DropdownMenuContentContext.Provider>
700
+ </MenuProvider>
701
+ );
702
+
703
+ return (
704
+ <DropdownMenuPrimitive.Portal container={container} forceMount={forceMount}>
705
+ <Theme asChild>
706
+ <DropdownMenuPrimitive.SubContent
707
+ data-accent-color={color}
708
+ data-material={material}
709
+ data-panel-background={material}
710
+ alignOffset={getMenuAlignOffset(scalarSize, themeContext.scaling)}
711
+ // Side offset accounts for the outer solid box-shadow
712
+ sideOffset={1}
713
+ collisionPadding={10}
714
+ {...subContentProps}
715
+ asChild={false}
716
+ ref={forwardedRef}
717
+ className={classNames(
718
+ 'rt-PopperContent',
719
+ 'rt-BaseMenuContent',
720
+ 'rt-BaseMenuSubContent',
721
+ 'rt-DropdownMenuContent',
722
+ 'rt-DropdownMenuSubContent',
723
+ className,
724
+ )}
725
+ >
726
+ {virtualized ? (
727
+ <div className={classNames('rt-BaseMenuViewport', 'rt-DropdownMenuViewport', 'rt-virtualized')}>
728
+ {body}
729
+ </div>
730
+ ) : (
731
+ <ScrollArea type="auto" scrollbars="vertical">
732
+ <div className={classNames('rt-BaseMenuViewport', 'rt-DropdownMenuViewport')}>{body}</div>
733
+ </ScrollArea>
734
+ )}
735
+ </DropdownMenuPrimitive.SubContent>
736
+ </Theme>
737
+ </DropdownMenuPrimitive.Portal>
738
+ );
739
+ },
740
+ );
741
+ CascadeSubContent.displayName = 'DropdownMenu.CascadeSubContent';
742
+
743
+ interface DrillDownPanelProps extends DropdownMenuSubContentProps {
744
+ store: NonNullable<ReturnType<typeof useDrillDownStore>>;
745
+ sub: NonNullable<ReturnType<typeof useSubContext>>;
746
+ }
747
+
748
+ /**
749
+ * A submenu rendered inline in drill-down mode. It mounts its children only
750
+ * while it is on the navigation stack, and shows its own items only while it
751
+ * is the top level; deeper panels inside it stay mounted.
752
+ */
753
+ const DrillDownPanel = React.forwardRef<DropdownMenuSubContentElement, DrillDownPanelProps>(
754
+ ({ store, sub, ...props }, forwardedRef) => {
755
+ const position = usePanelPosition(store, sub.id);
756
+ const entry = useStackEntry(store, sub.id);
757
+ const direction = useDrillDownDirection(position === 'top' ? store : null);
758
+
759
+ if (position === 'closed') return null;
592
760
 
593
- // In drill-down mode, render inline instead of in a portal
594
- if (drillDownState?.behavior === 'drill-down' && subContext) {
595
- const isActive = drillDownState.isActive(subContext.id);
761
+ const {
762
+ className,
763
+ children,
764
+ container: _container,
765
+ forceMount: _forceMount,
766
+ virtualized: _virtualized,
767
+ size: _size,
768
+ variant: _variant,
769
+ color: _color,
770
+ highContrast: _highContrast,
771
+ material: _material,
772
+ panelBackground: _panelBackground,
773
+ ...panelProps
774
+ } = omitPopperProps(props);
775
+
776
+ const isTop = position === 'top';
777
+ const name = entry?.name || (typeof sub.label === 'string' ? sub.label : '');
596
778
 
597
779
  return (
598
780
  <div
599
- ref={forwardedRef as React.Ref<HTMLDivElement>}
600
- role="menu"
601
- aria-label={typeof subContext.label === 'string' ? subContext.label : undefined}
602
- data-drill-down-active={isActive ? true : undefined}
603
- data-animation-direction={drillDownState.animationDirection ?? undefined}
604
- className={classNames(
605
- 'rt-DropdownMenuDrillDownPanel',
606
- className,
607
- )}
781
+ role="group"
782
+ aria-label={name || undefined}
783
+ {...panelProps}
784
+ ref={forwardedRef}
785
+ data-drill-down-panel={sub.id}
786
+ data-drill-down-active={isTop ? true : undefined}
787
+ data-animation-direction={isTop ? (direction ?? undefined) : undefined}
788
+ className={classNames('rt-DropdownMenuDrillDownPanel', className)}
608
789
  >
609
- <DrillDownBackItem label={subContext.label} />
610
- <DropdownMenuSeparator />
611
- {children}
790
+ {isTop && (
791
+ <>
792
+ <DrillDownBackItem label={sub.label} name={name} />
793
+ <DropdownMenuPrimitive.Separator className="rt-BaseMenuSeparator rt-DropdownMenuSeparator" />
794
+ </>
795
+ )}
796
+ <DrillDownLevelContext.Provider value={isTop}>{children}</DrillDownLevelContext.Provider>
612
797
  </div>
613
798
  );
614
- }
615
-
616
- // In cascade mode, use Portal and Radix's SubContent
617
- return (
618
- <DropdownMenuPrimitive.Portal container={container} forceMount={forceMount}>
619
- <Theme asChild>
620
- <DropdownMenuPrimitive.SubContent
621
- data-accent-color={color}
622
- data-material={material}
623
- data-panel-background={material}
624
- alignOffset={-Number(size) * 4}
625
- // Side offset accounts for the outer solid box-shadow
626
- sideOffset={1}
627
- collisionPadding={10}
628
- {...subContentProps}
629
- asChild={false}
630
- ref={forwardedRef}
631
- className={classNames(
632
- 'rt-PopperContent',
633
- 'rt-BaseMenuContent',
634
- 'rt-BaseMenuSubContent',
635
- 'rt-DropdownMenuContent',
636
- 'rt-DropdownMenuSubContent',
637
- className,
638
- )}
639
- >
640
- <ScrollArea type="auto">
641
- <div className={classNames('rt-BaseMenuViewport', 'rt-DropdownMenuViewport')}>
642
- {children}
643
- </div>
644
- </ScrollArea>
645
- </DropdownMenuPrimitive.SubContent>
646
- </Theme>
647
- </DropdownMenuPrimitive.Portal>
648
- );
649
- });
650
- DropdownMenuSubContent.displayName = 'DropdownMenu.SubContent';
799
+ },
800
+ );
801
+ DrillDownPanel.displayName = 'DropdownMenu.DrillDownPanel';
651
802
 
652
803
  type DropdownMenuTriggerIconElement = React.ElementRef<'svg'>;
653
804
  interface DropdownMenuTriggerIconProps extends IconProps {}
654
805
  const DropdownMenuTriggerIcon = React.forwardRef<
655
806
  DropdownMenuTriggerIconElement,
656
807
  DropdownMenuTriggerIconProps
657
- >((props, forwardedRef) => (
658
- <ChevronDownIcon {...props} ref={forwardedRef} className="rt-DropdownMenuTriggerIcon" />
808
+ >(({ className, ...props }, forwardedRef) => (
809
+ <ChevronDownIcon
810
+ {...props}
811
+ ref={forwardedRef}
812
+ className={classNames('rt-DropdownMenuTriggerIcon', className)}
813
+ />
659
814
  ));
660
815
  DropdownMenuTriggerIcon.displayName = 'DropdownMenu.TriggerIcon';
661
816