@womp/kookie-ui 0.29.2 → 0.31.0

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 (307) hide show
  1. package/components.css +127 -0
  2. package/dist/cjs/components/_internal/navigator-host.d.ts +21 -0
  3. package/dist/cjs/components/_internal/navigator-host.d.ts.map +1 -0
  4. package/dist/cjs/components/_internal/navigator-host.js +3 -0
  5. package/dist/cjs/components/_internal/navigator-host.js.map +7 -0
  6. package/dist/cjs/components/_internal/page-scope.d.ts +48 -0
  7. package/dist/cjs/components/_internal/page-scope.d.ts.map +1 -0
  8. package/dist/cjs/components/_internal/page-scope.js +3 -0
  9. package/dist/cjs/components/_internal/page-scope.js.map +7 -0
  10. package/dist/cjs/components/_internal/portal-theme.d.ts +2 -1
  11. package/dist/cjs/components/_internal/portal-theme.d.ts.map +1 -1
  12. package/dist/cjs/components/_internal/portal-theme.js +1 -1
  13. package/dist/cjs/components/_internal/portal-theme.js.map +4 -4
  14. package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
  15. package/dist/cjs/components/_internal/shell-bottom.js +1 -1
  16. package/dist/cjs/components/_internal/shell-bottom.js.map +4 -4
  17. package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
  18. package/dist/cjs/components/_internal/shell-inspector.js +1 -1
  19. package/dist/cjs/components/_internal/shell-inspector.js.map +4 -4
  20. package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
  21. package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
  22. package/dist/cjs/components/_internal/shell-sidebar.js.map +4 -4
  23. package/dist/cjs/components/alert-dialog.js +1 -1
  24. package/dist/cjs/components/alert-dialog.js.map +4 -4
  25. package/dist/cjs/components/button.js +1 -1
  26. package/dist/cjs/components/button.js.map +4 -4
  27. package/dist/cjs/components/chatbar.js +1 -1
  28. package/dist/cjs/components/chatbar.js.map +4 -4
  29. package/dist/cjs/components/chip.js +1 -1
  30. package/dist/cjs/components/chip.js.map +4 -4
  31. package/dist/cjs/components/combobox.js +1 -1
  32. package/dist/cjs/components/combobox.js.map +4 -4
  33. package/dist/cjs/components/context-menu.js +1 -1
  34. package/dist/cjs/components/context-menu.js.map +4 -4
  35. package/dist/cjs/components/dialog.d.ts.map +1 -1
  36. package/dist/cjs/components/dialog.js +1 -1
  37. package/dist/cjs/components/dialog.js.map +4 -4
  38. package/dist/cjs/components/dropdown-menu.js +1 -1
  39. package/dist/cjs/components/dropdown-menu.js.map +4 -4
  40. package/dist/cjs/components/index.d.ts +2 -0
  41. package/dist/cjs/components/index.d.ts.map +1 -1
  42. package/dist/cjs/components/index.js +2 -2
  43. package/dist/cjs/components/index.js.map +4 -4
  44. package/dist/cjs/components/message-scroller.js +1 -1
  45. package/dist/cjs/components/message-scroller.js.map +4 -4
  46. package/dist/cjs/components/navigator.d.ts +103 -0
  47. package/dist/cjs/components/navigator.d.ts.map +1 -0
  48. package/dist/cjs/components/navigator.js +3 -0
  49. package/dist/cjs/components/navigator.js.map +7 -0
  50. package/dist/cjs/components/notice.js +1 -1
  51. package/dist/cjs/components/notice.js.map +4 -4
  52. package/dist/cjs/components/page.d.ts +31 -0
  53. package/dist/cjs/components/page.d.ts.map +1 -0
  54. package/dist/cjs/components/page.js +3 -0
  55. package/dist/cjs/components/page.js.map +7 -0
  56. package/dist/cjs/components/popover.d.ts.map +1 -1
  57. package/dist/cjs/components/popover.js +1 -1
  58. package/dist/cjs/components/popover.js.map +4 -4
  59. package/dist/cjs/components/select.js +1 -1
  60. package/dist/cjs/components/select.js.map +4 -4
  61. package/dist/cjs/components/sheet.d.ts.map +1 -1
  62. package/dist/cjs/components/sheet.js +1 -1
  63. package/dist/cjs/components/sheet.js.map +4 -4
  64. package/dist/cjs/components/shell.d.ts.map +1 -1
  65. package/dist/cjs/components/shell.js +1 -1
  66. package/dist/cjs/components/shell.js.map +4 -4
  67. package/dist/cjs/components/sidebar.js +1 -1
  68. package/dist/cjs/components/sidebar.js.map +4 -4
  69. package/dist/cjs/components/toggle.js +1 -1
  70. package/dist/cjs/components/toggle.js.map +4 -4
  71. package/dist/cjs/components/toolbar.d.ts +5 -1
  72. package/dist/cjs/components/toolbar.d.ts.map +1 -1
  73. package/dist/cjs/components/toolbar.js +1 -1
  74. package/dist/cjs/components/toolbar.js.map +4 -4
  75. package/dist/cjs/components/tooltip.js +1 -1
  76. package/dist/cjs/components/tooltip.js.map +4 -4
  77. package/dist/cjs/index.js +5 -5
  78. package/dist/cjs/index.js.map +4 -4
  79. package/dist/esm/{chunk-HBO7W7SS.js → chunk-26O4RBOJ.js} +2 -2
  80. package/dist/esm/{chunk-FYTLUDL3.js → chunk-3M5ZM67R.js} +2 -2
  81. package/dist/esm/chunk-4B3S2DM7.js +3 -0
  82. package/dist/esm/chunk-4B3S2DM7.js.map +7 -0
  83. package/dist/esm/chunk-4KBKS3ZE.js +3 -0
  84. package/dist/esm/chunk-4KBKS3ZE.js.map +7 -0
  85. package/dist/esm/{chunk-2FYUM6TA.js → chunk-4ZHBNYXM.js} +2 -2
  86. package/dist/esm/{chunk-TSS4K2U5.js → chunk-5VYQDA5S.js} +2 -2
  87. package/dist/esm/chunk-65URNXF2.js +3 -0
  88. package/dist/esm/chunk-65URNXF2.js.map +7 -0
  89. package/dist/esm/chunk-6SGJWML7.js +3 -0
  90. package/dist/esm/chunk-6SGJWML7.js.map +7 -0
  91. package/dist/esm/{chunk-WAXRIARV.js → chunk-6WHKGVWY.js} +2 -2
  92. package/dist/esm/{chunk-7V6M3MIR.js → chunk-7YXHLU4C.js} +2 -2
  93. package/dist/esm/chunk-AKMRPGNP.js +3 -0
  94. package/dist/esm/chunk-AKMRPGNP.js.map +7 -0
  95. package/dist/esm/{chunk-ENQV6SEA.js → chunk-AKSAKP35.js} +2 -2
  96. package/dist/esm/{chunk-C67MM24U.js → chunk-B7STVZPU.js} +2 -2
  97. package/dist/esm/{chunk-2E57H3NH.js → chunk-BQOFHLDP.js} +2 -2
  98. package/dist/esm/{chunk-324VC67W.js → chunk-COLJWVN7.js} +2 -2
  99. package/dist/esm/{chunk-K3GDM26A.js → chunk-D42VCLKH.js} +2 -2
  100. package/dist/esm/{chunk-MGWGD6KI.js → chunk-DFQIIYLD.js} +2 -2
  101. package/dist/esm/{chunk-NWXODCTD.js → chunk-EERKYRYI.js} +2 -2
  102. package/dist/esm/{chunk-4MNRLB57.js → chunk-FICBVI4M.js} +2 -2
  103. package/dist/esm/chunk-HGF4CKRY.js +3 -0
  104. package/dist/esm/chunk-HGF4CKRY.js.map +7 -0
  105. package/dist/esm/{chunk-JD4KR6VZ.js → chunk-HVMRB3AH.js} +2 -2
  106. package/dist/esm/{chunk-HLIOHDEX.js → chunk-IFHBMTFX.js} +2 -2
  107. package/dist/esm/{chunk-J54GYD7A.js → chunk-JFJV4HL3.js} +2 -2
  108. package/dist/esm/{chunk-KJZSXLIX.js → chunk-JQMWNI62.js} +2 -2
  109. package/dist/esm/chunk-K35JUIUT.js +3 -0
  110. package/dist/esm/chunk-K35JUIUT.js.map +7 -0
  111. package/dist/esm/{chunk-67UEMB4K.js → chunk-KBSH6CBD.js} +2 -2
  112. package/dist/esm/{chunk-SUQJ7DD4.js → chunk-L7PRLW6A.js} +2 -2
  113. package/dist/esm/{chunk-D2C2A2ZK.js → chunk-LFAUMDU5.js} +2 -2
  114. package/dist/esm/{chunk-LABMK6Q7.js → chunk-LG5DPQ46.js} +2 -2
  115. package/dist/esm/{chunk-MJROOEPE.js → chunk-NFSW36RG.js} +2 -2
  116. package/dist/esm/{chunk-6YWHNLSC.js → chunk-NJ7RQYLX.js} +2 -2
  117. package/dist/esm/{chunk-WD66RYF6.js → chunk-NYIYRILR.js} +2 -2
  118. package/dist/esm/{chunk-JP36JSNP.js → chunk-OBSZOD5M.js} +2 -2
  119. package/dist/esm/{chunk-DP7MPGR7.js → chunk-OMPLFDS6.js} +2 -2
  120. package/dist/esm/{chunk-H2J25HXP.js → chunk-OPYTQ34H.js} +2 -2
  121. package/dist/esm/{chunk-REORAMUH.js → chunk-QBONJMSE.js} +2 -2
  122. package/dist/esm/chunk-QDAM2I6O.js +3 -0
  123. package/dist/esm/chunk-QDAM2I6O.js.map +7 -0
  124. package/dist/esm/chunk-RSG4XWHU.js +2 -0
  125. package/dist/esm/{chunk-2ZKEQS76.js → chunk-SGKLJDA3.js} +2 -2
  126. package/dist/esm/{chunk-676Y7ZUT.js → chunk-TMINGBSQ.js} +2 -2
  127. package/dist/esm/{chunk-2S7HPQVX.js → chunk-TRJAVWCK.js} +2 -2
  128. package/dist/esm/chunk-TUZ665BA.js +3 -0
  129. package/dist/esm/chunk-TUZ665BA.js.map +7 -0
  130. package/dist/esm/{chunk-JKQN6QCP.js → chunk-VBF3XALY.js} +2 -2
  131. package/dist/esm/chunk-VWRPVM5K.js +3 -0
  132. package/dist/esm/chunk-VWRPVM5K.js.map +7 -0
  133. package/dist/esm/{chunk-B6OFKABP.js → chunk-VYF6KKYM.js} +2 -2
  134. package/dist/esm/chunk-W35ZKQMQ.js +3 -0
  135. package/dist/esm/chunk-W35ZKQMQ.js.map +7 -0
  136. package/dist/esm/{chunk-EPGIYTFK.js → chunk-X7DGYRAY.js} +2 -2
  137. package/dist/esm/{chunk-X7JHZ3HA.js → chunk-XF6Z5NKE.js} +2 -2
  138. package/dist/esm/{chunk-K5V53KTJ.js → chunk-Y44DYJA5.js} +2 -2
  139. package/dist/esm/{chunk-MMDZBPSX.js → chunk-YOTTLPJ3.js} +2 -2
  140. package/dist/esm/chunk-ZBSGH74F.js +3 -0
  141. package/dist/esm/chunk-ZBSGH74F.js.map +7 -0
  142. package/dist/esm/chunk-ZHXEEOGI.js +3 -0
  143. package/dist/esm/chunk-ZHXEEOGI.js.map +7 -0
  144. package/dist/esm/{chunk-FMVOB367.js → chunk-ZLGOZKB3.js} +2 -2
  145. package/dist/esm/components/_internal/base-button.js +1 -1
  146. package/dist/esm/components/_internal/base-menu.utils.js +1 -1
  147. package/dist/esm/components/_internal/menu-context.js +1 -1
  148. package/dist/esm/components/_internal/navigator-host.d.ts +21 -0
  149. package/dist/esm/components/_internal/navigator-host.d.ts.map +1 -0
  150. package/dist/esm/components/_internal/navigator-host.js +3 -0
  151. package/dist/esm/components/_internal/navigator-host.js.map +7 -0
  152. package/dist/esm/components/_internal/page-scope.d.ts +48 -0
  153. package/dist/esm/components/_internal/page-scope.d.ts.map +1 -0
  154. package/dist/esm/components/_internal/page-scope.js +3 -0
  155. package/dist/esm/components/_internal/page-scope.js.map +7 -0
  156. package/dist/esm/components/_internal/pane-chrome.js +1 -1
  157. package/dist/esm/components/_internal/portal-theme.d.ts +2 -1
  158. package/dist/esm/components/_internal/portal-theme.d.ts.map +1 -1
  159. package/dist/esm/components/_internal/portal-theme.js +1 -1
  160. package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
  161. package/dist/esm/components/_internal/shell-bottom.js +1 -1
  162. package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
  163. package/dist/esm/components/_internal/shell-inspector.js +1 -1
  164. package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
  165. package/dist/esm/components/_internal/shell-sidebar.js +1 -1
  166. package/dist/esm/components/accordion.js +1 -1
  167. package/dist/esm/components/alert-dialog.js +1 -1
  168. package/dist/esm/components/avatar.js +1 -1
  169. package/dist/esm/components/blockquote.js +1 -1
  170. package/dist/esm/components/button.js +1 -1
  171. package/dist/esm/components/card.js +1 -1
  172. package/dist/esm/components/chatbar.js +1 -1
  173. package/dist/esm/components/checkbox-group.js +1 -1
  174. package/dist/esm/components/checkbox.js +1 -1
  175. package/dist/esm/components/chip.js +1 -1
  176. package/dist/esm/components/code.js +1 -1
  177. package/dist/esm/components/combobox.js +1 -1
  178. package/dist/esm/components/context-menu.js +1 -1
  179. package/dist/esm/components/dialog.d.ts.map +1 -1
  180. package/dist/esm/components/dialog.js +1 -1
  181. package/dist/esm/components/dropdown-menu.js +1 -1
  182. package/dist/esm/components/heading.js +1 -1
  183. package/dist/esm/components/image.js +1 -1
  184. package/dist/esm/components/index.d.ts +2 -0
  185. package/dist/esm/components/index.d.ts.map +1 -1
  186. package/dist/esm/components/index.js +1 -1
  187. package/dist/esm/components/link.js +1 -1
  188. package/dist/esm/components/message-scroller.js +1 -1
  189. package/dist/esm/components/navbar.js +1 -1
  190. package/dist/esm/components/navigator.d.ts +103 -0
  191. package/dist/esm/components/navigator.d.ts.map +1 -0
  192. package/dist/esm/components/navigator.js +3 -0
  193. package/dist/esm/components/navigator.js.map +7 -0
  194. package/dist/esm/components/notice.js +1 -1
  195. package/dist/esm/components/page.d.ts +31 -0
  196. package/dist/esm/components/page.d.ts.map +1 -0
  197. package/dist/esm/components/page.js +3 -0
  198. package/dist/esm/components/page.js.map +7 -0
  199. package/dist/esm/components/popover.d.ts.map +1 -1
  200. package/dist/esm/components/popover.js +1 -1
  201. package/dist/esm/components/progress.js +1 -1
  202. package/dist/esm/components/radio-group.js +1 -1
  203. package/dist/esm/components/radio.js +1 -1
  204. package/dist/esm/components/schemas/index.js +1 -1
  205. package/dist/esm/components/scroll-area.js +1 -1
  206. package/dist/esm/components/segmented-control.js +1 -1
  207. package/dist/esm/components/select.js +1 -1
  208. package/dist/esm/components/separator.js +1 -1
  209. package/dist/esm/components/sheet.d.ts.map +1 -1
  210. package/dist/esm/components/sheet.js +1 -1
  211. package/dist/esm/components/shell.d.ts.map +1 -1
  212. package/dist/esm/components/shell.js +1 -1
  213. package/dist/esm/components/sidebar.js +1 -1
  214. package/dist/esm/components/slider.js +1 -1
  215. package/dist/esm/components/switch.js +1 -1
  216. package/dist/esm/components/table.js +1 -1
  217. package/dist/esm/components/tabs.js +1 -1
  218. package/dist/esm/components/text-area.js +1 -1
  219. package/dist/esm/components/text-field.js +1 -1
  220. package/dist/esm/components/text.js +1 -1
  221. package/dist/esm/components/toggle.js +1 -1
  222. package/dist/esm/components/toolbar.d.ts +5 -1
  223. package/dist/esm/components/toolbar.d.ts.map +1 -1
  224. package/dist/esm/components/toolbar.js +1 -1
  225. package/dist/esm/components/tooltip.js +1 -1
  226. package/dist/esm/components/virtual-menu.js +1 -1
  227. package/dist/esm/hooks/index.js +1 -1
  228. package/dist/esm/index.js +1 -1
  229. package/package.json +1 -1
  230. package/src/components/_internal/navigator-host.ts +17 -0
  231. package/src/components/_internal/page-scope.tsx +119 -0
  232. package/src/components/_internal/portal-theme.tsx +6 -2
  233. package/src/components/_internal/shell-bottom.tsx +4 -1
  234. package/src/components/_internal/shell-inspector.tsx +4 -1
  235. package/src/components/_internal/shell-sidebar.tsx +4 -1
  236. package/src/components/dialog.tsx +8 -0
  237. package/src/components/index.css +2 -0
  238. package/src/components/index.tsx +15 -0
  239. package/src/components/navigator.css +85 -0
  240. package/src/components/navigator.tsx +415 -0
  241. package/src/components/page.css +38 -0
  242. package/src/components/page.tsx +93 -0
  243. package/src/components/popover.tsx +18 -1
  244. package/src/components/sheet.tsx +8 -0
  245. package/src/components/shell.tsx +8 -2
  246. package/src/components/toolbar.css +11 -0
  247. package/src/components/toolbar.tsx +17 -5
  248. package/styles.css +127 -0
  249. package/dist/esm/chunk-4LV37PHM.js +0 -3
  250. package/dist/esm/chunk-4LV37PHM.js.map +0 -7
  251. package/dist/esm/chunk-5BX2YZV6.js +0 -3
  252. package/dist/esm/chunk-5BX2YZV6.js.map +0 -7
  253. package/dist/esm/chunk-AQKSO3SW.js +0 -3
  254. package/dist/esm/chunk-AQKSO3SW.js.map +0 -7
  255. package/dist/esm/chunk-AS2STDGA.js +0 -3
  256. package/dist/esm/chunk-AS2STDGA.js.map +0 -7
  257. package/dist/esm/chunk-HR2VHGJ5.js +0 -3
  258. package/dist/esm/chunk-HR2VHGJ5.js.map +0 -7
  259. package/dist/esm/chunk-JTMLFTNZ.js +0 -3
  260. package/dist/esm/chunk-JTMLFTNZ.js.map +0 -7
  261. package/dist/esm/chunk-MJKVVGGV.js +0 -3
  262. package/dist/esm/chunk-MJKVVGGV.js.map +0 -7
  263. package/dist/esm/chunk-NG54K6FL.js +0 -2
  264. package/dist/esm/chunk-QRV32INS.js +0 -3
  265. package/dist/esm/chunk-QRV32INS.js.map +0 -7
  266. package/dist/esm/chunk-YMUERC2J.js +0 -3
  267. package/dist/esm/chunk-YMUERC2J.js.map +0 -7
  268. /package/dist/esm/{chunk-HBO7W7SS.js.map → chunk-26O4RBOJ.js.map} +0 -0
  269. /package/dist/esm/{chunk-FYTLUDL3.js.map → chunk-3M5ZM67R.js.map} +0 -0
  270. /package/dist/esm/{chunk-2FYUM6TA.js.map → chunk-4ZHBNYXM.js.map} +0 -0
  271. /package/dist/esm/{chunk-TSS4K2U5.js.map → chunk-5VYQDA5S.js.map} +0 -0
  272. /package/dist/esm/{chunk-WAXRIARV.js.map → chunk-6WHKGVWY.js.map} +0 -0
  273. /package/dist/esm/{chunk-7V6M3MIR.js.map → chunk-7YXHLU4C.js.map} +0 -0
  274. /package/dist/esm/{chunk-ENQV6SEA.js.map → chunk-AKSAKP35.js.map} +0 -0
  275. /package/dist/esm/{chunk-C67MM24U.js.map → chunk-B7STVZPU.js.map} +0 -0
  276. /package/dist/esm/{chunk-2E57H3NH.js.map → chunk-BQOFHLDP.js.map} +0 -0
  277. /package/dist/esm/{chunk-324VC67W.js.map → chunk-COLJWVN7.js.map} +0 -0
  278. /package/dist/esm/{chunk-K3GDM26A.js.map → chunk-D42VCLKH.js.map} +0 -0
  279. /package/dist/esm/{chunk-MGWGD6KI.js.map → chunk-DFQIIYLD.js.map} +0 -0
  280. /package/dist/esm/{chunk-NWXODCTD.js.map → chunk-EERKYRYI.js.map} +0 -0
  281. /package/dist/esm/{chunk-4MNRLB57.js.map → chunk-FICBVI4M.js.map} +0 -0
  282. /package/dist/esm/{chunk-JD4KR6VZ.js.map → chunk-HVMRB3AH.js.map} +0 -0
  283. /package/dist/esm/{chunk-HLIOHDEX.js.map → chunk-IFHBMTFX.js.map} +0 -0
  284. /package/dist/esm/{chunk-J54GYD7A.js.map → chunk-JFJV4HL3.js.map} +0 -0
  285. /package/dist/esm/{chunk-KJZSXLIX.js.map → chunk-JQMWNI62.js.map} +0 -0
  286. /package/dist/esm/{chunk-67UEMB4K.js.map → chunk-KBSH6CBD.js.map} +0 -0
  287. /package/dist/esm/{chunk-SUQJ7DD4.js.map → chunk-L7PRLW6A.js.map} +0 -0
  288. /package/dist/esm/{chunk-D2C2A2ZK.js.map → chunk-LFAUMDU5.js.map} +0 -0
  289. /package/dist/esm/{chunk-LABMK6Q7.js.map → chunk-LG5DPQ46.js.map} +0 -0
  290. /package/dist/esm/{chunk-MJROOEPE.js.map → chunk-NFSW36RG.js.map} +0 -0
  291. /package/dist/esm/{chunk-6YWHNLSC.js.map → chunk-NJ7RQYLX.js.map} +0 -0
  292. /package/dist/esm/{chunk-WD66RYF6.js.map → chunk-NYIYRILR.js.map} +0 -0
  293. /package/dist/esm/{chunk-JP36JSNP.js.map → chunk-OBSZOD5M.js.map} +0 -0
  294. /package/dist/esm/{chunk-DP7MPGR7.js.map → chunk-OMPLFDS6.js.map} +0 -0
  295. /package/dist/esm/{chunk-H2J25HXP.js.map → chunk-OPYTQ34H.js.map} +0 -0
  296. /package/dist/esm/{chunk-REORAMUH.js.map → chunk-QBONJMSE.js.map} +0 -0
  297. /package/dist/esm/{chunk-NG54K6FL.js.map → chunk-RSG4XWHU.js.map} +0 -0
  298. /package/dist/esm/{chunk-2ZKEQS76.js.map → chunk-SGKLJDA3.js.map} +0 -0
  299. /package/dist/esm/{chunk-676Y7ZUT.js.map → chunk-TMINGBSQ.js.map} +0 -0
  300. /package/dist/esm/{chunk-2S7HPQVX.js.map → chunk-TRJAVWCK.js.map} +0 -0
  301. /package/dist/esm/{chunk-JKQN6QCP.js.map → chunk-VBF3XALY.js.map} +0 -0
  302. /package/dist/esm/{chunk-B6OFKABP.js.map → chunk-VYF6KKYM.js.map} +0 -0
  303. /package/dist/esm/{chunk-EPGIYTFK.js.map → chunk-X7DGYRAY.js.map} +0 -0
  304. /package/dist/esm/{chunk-X7JHZ3HA.js.map → chunk-XF6Z5NKE.js.map} +0 -0
  305. /package/dist/esm/{chunk-K5V53KTJ.js.map → chunk-Y44DYJA5.js.map} +0 -0
  306. /package/dist/esm/{chunk-MMDZBPSX.js.map → chunk-YOTTLPJ3.js.map} +0 -0
  307. /package/dist/esm/{chunk-FMVOB367.js.map → chunk-ZLGOZKB3.js.map} +0 -0
@@ -0,0 +1,415 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Navigator, from v2 (2026-11-03): a stack of screens, any depth. Any screen can open another on
5
+ * top of it, and Back removes the top one. The first screen is the root and never goes.
6
+ *
7
+ * THE PATH IS DATA. The screens above the root are a list of values the app owns, and `screen`
8
+ * turns a value into a screen, so an app can keep the path in its URL, restore it, or open three
9
+ * levels deep from a link.
10
+ *
11
+ * IT HAS NO REGION OF ITS OWN. The host decides how a screen carries its title. In
12
+ * `Shell.Content` a screen is a `Page`, whose large title collapses into the band with Back. In a
13
+ * side pane (Sidebar, Panel, Inspector, Bottom) the band holds Back and the title from the start.
14
+ * Anywhere else (a card, a Dialog, a Sheet, a Popover) each screen draws its own band: Back, the
15
+ * title, and the overlay's close when it has one.
16
+ *
17
+ * SCREENS UNDER THE TOP STAY MOUNTED, hidden, so a list keeps its scroll and a field its draft.
18
+ *
19
+ * Focus: a pushed screen takes focus at its top; on pop, focus returns to the control that opened
20
+ * the screen. Escape never goes back; it belongs to whatever overlay holds this.
21
+ *
22
+ * Differs from v2 in look only: no motion, since v1 has none. v1 has no Row yet, so
23
+ * `NavigatorLink` is its own row.
24
+ */
25
+
26
+ import * as React from 'react';
27
+ import classNames from 'classnames';
28
+
29
+ import { NavigatorHostContext } from './_internal/navigator-host.js';
30
+ import { PaneHeader, Scroll, ShellContentContext } from './_internal/pane-chrome.js';
31
+ import { setPageCollapsed, useClaimPage, usePageScope } from './_internal/page-scope.js';
32
+ import { Flex } from './flex.js';
33
+ import { Heading } from './heading.js';
34
+ import { Page } from './page.js';
35
+ import { Toolbar, ToolbarButton, ToolbarTitle } from './toolbar.js';
36
+
37
+ type NavigatorApi<T> = {
38
+ /** The values above the root, bottom first. Empty at the root. */
39
+ path: readonly T[];
40
+ /** Open a screen on top. */
41
+ push: (value: T) => void;
42
+ /** Close the top screen. Does nothing at the root. */
43
+ pop: () => void;
44
+ /** Close every screen above `depth`, where 0 is the root. */
45
+ popTo: (depth: number) => void;
46
+ /** Whether there is a screen to go back to. */
47
+ canGoBack: boolean;
48
+ };
49
+
50
+ type NavigatorState = NavigatorApi<unknown> & {
51
+ /** Each level's title, so Back can name the screen it returns to and a pane's band can show it. */
52
+ titles: React.MutableRefObject<(string | undefined)[]>;
53
+ setTitle: (depth: number, title: string) => void;
54
+ /** The control that opened each level, so focus can go back to it. */
55
+ openers: React.MutableRefObject<(HTMLElement | null)[]>;
56
+ direction: 'push' | 'pop' | null;
57
+ inPane: boolean;
58
+ };
59
+
60
+ const NavigatorContext = React.createContext<NavigatorState | null>(null);
61
+ const LevelContext = React.createContext<number>(0);
62
+ const OutletContext = React.createContext<React.ReactNode>(null);
63
+
64
+ /**
65
+ * True in a Shell pane, `Shell.Content` included, and not in an overlay opened from one: there
66
+ * the pane's band carries Back and the title. Overlays cut the pane's page scope at the portal.
67
+ */
68
+ function useInPane() {
69
+ const scope = usePageScope();
70
+ const host = React.useContext(NavigatorHostContext);
71
+ return scope !== null && host === null;
72
+ }
73
+
74
+ // Activity keeps a hidden screen's state and cleans up its effects (React 19.2). On an older
75
+ // React, the screen is kept in a hidden element instead, with its effects still running.
76
+ const Activity = (React as { Activity?: React.ComponentType<{ mode: 'visible' | 'hidden'; children: React.ReactNode }> }).Activity;
77
+
78
+ function Level({ visible, children }: { visible: boolean; children: React.ReactNode }) {
79
+ if (Activity) return <Activity mode={visible ? 'visible' : 'hidden'}>{children}</Activity>;
80
+ return (
81
+ <div hidden={!visible} style={{ display: visible ? 'contents' : 'none' }}>
82
+ {children}
83
+ </div>
84
+ );
85
+ }
86
+
87
+ interface NavigatorProps<T> {
88
+ /** The screens above the root, as values. Use it with `onPathChange` to control the path. */
89
+ path?: readonly T[];
90
+ /** The path to start with when the navigator controls its own path. */
91
+ defaultPath?: readonly T[];
92
+ /** Called with the new path whenever a screen opens or closes. */
93
+ onPathChange?: (path: T[]) => void;
94
+ /** Turns a value from the path into its screen, usually a `NavigatorScreen`. */
95
+ screen: (value: T) => React.ReactNode;
96
+ /** The root screen, usually a `NavigatorScreen`. It is always at the bottom and never closes. */
97
+ root: React.ReactNode;
98
+ /**
99
+ * The frame around the screens, when you need your own: a `Shell.PaneHeader` with actions, and
100
+ * a `Shell.Scroll` holding `NavigatorOutlet`. Leave it out and the navigator draws the frame: in
101
+ * a Shell pane, the pane's band with Back and the title, and its scroller; anywhere else, the
102
+ * screens on their own.
103
+ */
104
+ children?: React.ReactNode;
105
+ className?: string;
106
+ style?: React.CSSProperties;
107
+ }
108
+
109
+ /**
110
+ * A stack of screens: any screen can open another on top, and Back returns to the one below.
111
+ * `root` is the first screen, `screen` turns each value in the path into its screen, and
112
+ * `children`, when given, is the frame the screens go into through `NavigatorOutlet`.
113
+ */
114
+ function Navigator<T>({ path: pathProp, defaultPath, onPathChange, screen, root, children, className, style }: NavigatorProps<T>) {
115
+ const inPane = useInPane();
116
+ const [own, setOwn] = React.useState<readonly T[]>(defaultPath ?? []);
117
+ const path = pathProp ?? own;
118
+ // The direction comes from the path itself, so a path the app sets moves focus the same way as
119
+ // one changed by push and pop. Equal length with a new top value is a new screen arriving.
120
+ const [seen, setSeen] = React.useState<{ path: readonly T[]; direction: 'push' | 'pop' | null }>({ path, direction: null });
121
+ let direction = seen.direction;
122
+ // Compared by content, not identity: a controlled `path` may be a new array every render.
123
+ if (seen.path.length !== path.length || seen.path.some((value, i) => !Object.is(value, path[i]))) {
124
+ const before = seen.path;
125
+ direction = path.length < before.length ? 'pop' : path.length > before.length || !Object.is(path[path.length - 1], before[before.length - 1]) ? 'push' : seen.direction;
126
+ setSeen({ path, direction });
127
+ }
128
+ const titles = React.useRef<(string | undefined)[]>([]);
129
+ const openers = React.useRef<(HTMLElement | null)[]>([]);
130
+ const [version, setVersion] = React.useState(0);
131
+
132
+ const change = React.useCallback(
133
+ (next: T[]) => {
134
+ if (pathProp === undefined) setOwn(next);
135
+ onPathChange?.(next);
136
+ },
137
+ [pathProp, onPathChange],
138
+ );
139
+
140
+ const api = React.useMemo<NavigatorState>(
141
+ () => ({
142
+ path,
143
+ canGoBack: path.length > 0,
144
+ push: (value) => {
145
+ openers.current[path.length + 1] = typeof document === 'undefined' ? null : (document.activeElement as HTMLElement | null);
146
+ change([...path, value as T]);
147
+ },
148
+ pop: () => {
149
+ if (path.length > 0) change(path.slice(0, -1));
150
+ },
151
+ popTo: (depth) => {
152
+ if (depth < path.length) change(path.slice(0, Math.max(0, depth)));
153
+ },
154
+ titles,
155
+ openers,
156
+ setTitle: (depth, title) => {
157
+ if (titles.current[depth] === title) return;
158
+ titles.current[depth] = title;
159
+ setVersion((v) => v + 1);
160
+ },
161
+ direction,
162
+ inPane,
163
+ }),
164
+ // `version` is read through `titles`; it is here so a new title reaches the band and Back.
165
+ // eslint-disable-next-line react-hooks/exhaustive-deps
166
+ [path, change, version, direction, inPane],
167
+ );
168
+
169
+ // On pop, focus goes back to the control that opened the screen that just closed.
170
+ const previousDepth = React.useRef(path.length);
171
+ React.useEffect(() => {
172
+ const before = previousDepth.current;
173
+ previousDepth.current = path.length;
174
+ if (path.length < before) {
175
+ const opener = openers.current[before];
176
+ openers.current.length = path.length + 1;
177
+ if (opener && opener.isConnected) opener.focus();
178
+ }
179
+ }, [path.length]);
180
+
181
+ const levels = [root, ...path.map((value) => screen(value))];
182
+ const top = levels.length - 1;
183
+ const outlet = (
184
+ <div className={classNames('kui-Navigator', className)} style={style}>
185
+ {levels.map((level, depth) => (
186
+ // Keyed by depth AND value, so a path that replaces the screen at a depth mounts a new
187
+ // screen instead of handing it the old one's state.
188
+ <Level key={depth === 0 ? 'root' : `${depth}:${String(path[depth - 1])}`} visible={depth === top}>
189
+ <LevelContext.Provider value={depth}>
190
+ <div className="kui-NavigatorLevel">{level}</div>
191
+ </LevelContext.Provider>
192
+ </Level>
193
+ ))}
194
+ </div>
195
+ );
196
+
197
+ return (
198
+ <NavigatorContext.Provider value={api}>
199
+ <OutletContext.Provider value={outlet}>{children ?? (inPane ? <PaneFrame /> : outlet)}</OutletContext.Provider>
200
+ </NavigatorContext.Provider>
201
+ );
202
+ }
203
+
204
+ /**
205
+ * The pane's frame: Back and the top screen's title as one cluster at the start of the pane's
206
+ * band, and the screens in the pane's scroller. Pass children to the Navigator instead when the
207
+ * band needs actions of its own, and put `NavigatorBack` and `NavigatorTitle` in it, together in
208
+ * a `Flex`.
209
+ */
210
+ function PaneFrame() {
211
+ return (
212
+ <>
213
+ <PaneHeader>
214
+ <Toolbar>
215
+ {/* One cluster: as two loose toolbar children, the band splits them to its two ends. */}
216
+ <Flex align="center">
217
+ <NavigatorBack />
218
+ <ToolbarTitle />
219
+ </Flex>
220
+ </Toolbar>
221
+ </PaneHeader>
222
+ <Scroll>
223
+ <NavigatorOutlet />
224
+ </Scroll>
225
+ </>
226
+ );
227
+ }
228
+
229
+ /** Where the screens go, inside a frame passed as the navigator's children. */
230
+ function NavigatorOutlet() {
231
+ return <>{React.useContext(OutletContext)}</>;
232
+ }
233
+
234
+ function useNavigatorState(): NavigatorState {
235
+ const state = React.useContext(NavigatorContext);
236
+ if (!state) throw new Error('Kookie: this part must be inside a <Navigator>.');
237
+ return state;
238
+ }
239
+
240
+ /** The navigator around this component: its path, and `push`, `pop` and `popTo`. */
241
+ function useNavigator<T = unknown>(): NavigatorApi<T> {
242
+ return useNavigatorState() as NavigatorApi<T>;
243
+ }
244
+
245
+ function BackGlyph() {
246
+ return (
247
+ <svg className="kui-NavigatorBackGlyph" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
248
+ <path d="m15 18-6-6 6-6" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" />
249
+ </svg>
250
+ );
251
+ }
252
+
253
+ function CloseGlyph() {
254
+ return (
255
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
256
+ <path d="M18 6 6 18M6 6l12 12" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" />
257
+ </svg>
258
+ );
259
+ }
260
+
261
+ function backLabel(state: NavigatorState, depth: number) {
262
+ const previous = depth > 0 ? state.titles.current[depth - 1] : undefined;
263
+ return previous ? `Back to ${previous}` : 'Back';
264
+ }
265
+
266
+ interface NavigatorBackProps {
267
+ /** The accessible name. The default is "Back to" and the previous screen's title. */
268
+ 'aria-label'?: string;
269
+ }
270
+
271
+ /**
272
+ * Back, for a toolbar: a chevron that closes the top screen. It is absent at the root rather than
273
+ * disabled, because there is nowhere to go back to. It names the screen it returns to, so a screen
274
+ * reader hears "Back to Settings".
275
+ */
276
+ function NavigatorBack(props: NavigatorBackProps) {
277
+ const state = useNavigatorState();
278
+ if (!state.canGoBack) return null;
279
+ return (
280
+ <ToolbarButton iconOnly aria-label={props['aria-label'] ?? backLabel(state, state.path.length)} onClick={state.pop}>
281
+ <BackGlyph />
282
+ </ToolbarButton>
283
+ );
284
+ }
285
+
286
+ /**
287
+ * The top screen's title, for a toolbar in a frame of your own. In a pane it is the band's
288
+ * mirroring `Toolbar.Title`, which shows once the page's large title has scrolled away.
289
+ */
290
+ function NavigatorTitle() {
291
+ const state = useNavigatorState();
292
+ if (state.inPane) return <ToolbarTitle />;
293
+ return <ToolbarTitle>{state.titles.current[state.path.length]}</ToolbarTitle>;
294
+ }
295
+
296
+ interface NavigatorScreenProps {
297
+ /** The screen's title. In a pane it shows in the pane's band; elsewhere in the screen's own. */
298
+ title: string;
299
+ /** The screen's content, under its title. */
300
+ children?: React.ReactNode;
301
+ className?: string;
302
+ }
303
+
304
+ /**
305
+ * One screen in a navigator. In a Shell pane the pane's band carries Back and its title, so the
306
+ * screen is only its content. Anywhere else it draws its own band: Back, the title, and the
307
+ * overlay's close when it sits in one. In a Dialog, Sheet or Popover the title names the panel.
308
+ */
309
+ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
310
+ const state = useNavigatorState();
311
+ const depth = React.useContext(LevelContext);
312
+ const Host = React.useContext(NavigatorHostContext);
313
+ const inWorkArea = React.useContext(ShellContentContext);
314
+ const node = React.useRef<HTMLDivElement>(null);
315
+
316
+ React.useEffect(() => {
317
+ state.setTitle(depth, title);
318
+ }, [state, depth, title]);
319
+
320
+ // A pushed screen takes focus at its top, so a keyboard and a screen reader start there. In a
321
+ // pane the title is in the band, outside the screen, so the screen itself takes it.
322
+ React.useEffect(() => {
323
+ if (depth === 0 || state.direction !== 'push') return;
324
+ const target = node.current?.querySelector<HTMLElement>('.kui-NavigatorBar .kui-ToolbarTitle, h1') ?? node.current;
325
+ if (target) {
326
+ target.tabIndex = -1;
327
+ target.focus({ preventScroll: true });
328
+ }
329
+ // Only on arrival: a later re-render must not pull focus back to the title.
330
+ // eslint-disable-next-line react-hooks/exhaustive-deps
331
+ }, []);
332
+
333
+ if (state.inPane && inWorkArea) {
334
+ return (
335
+ <div ref={node} className={classNames('kui-NavigatorScreen', className)}>
336
+ <Page title={title}>{children}</Page>
337
+ </div>
338
+ );
339
+ }
340
+ if (state.inPane) {
341
+ // A side pane keeps its title in its band: a page's large title would dwarf a narrow
342
+ // inspector's rows. The screen claims the pane's title, collapsed from the start.
343
+ return (
344
+ <div ref={node} className={classNames('kui-NavigatorScreen', className)}>
345
+ <SidePaneTitle title={title} />
346
+ {children}
347
+ </div>
348
+ );
349
+ }
350
+
351
+ // Only the top screen names the panel. Screens underneath stay mounted, and if each wrapped its
352
+ // title in the overlay's title part they would share its id, and the panel would keep the root's name.
353
+ const isTop = depth === state.path.length;
354
+ const heading = (
355
+ <Heading as="h2" size="4" weight="medium" truncate>
356
+ {title}
357
+ </Heading>
358
+ );
359
+ return (
360
+ <div ref={node} className={classNames('kui-NavigatorScreen', className)}>
361
+ {/* One row: Back, the title, and the overlay's close. The band sits one step closer to the
362
+ panel's edge than the content does. */}
363
+ <div className="kui-NavigatorBar">
364
+ <Toolbar aria-label="Navigation">
365
+ {state.canGoBack && depth > 0 ? (
366
+ <ToolbarButton iconOnly aria-label={backLabel(state, depth)} onClick={state.pop}>
367
+ <BackGlyph />
368
+ </ToolbarButton>
369
+ ) : null}
370
+ <ToolbarTitle className="kui-NavigatorBarTitle">{Host && isTop ? <Host.Title>{heading}</Host.Title> : heading}</ToolbarTitle>
371
+ {Host ? (
372
+ <Host.Close>
373
+ <ToolbarButton iconOnly aria-label="Close">
374
+ <CloseGlyph />
375
+ </ToolbarButton>
376
+ </Host.Close>
377
+ ) : null}
378
+ </Toolbar>
379
+ </div>
380
+ <div className="kui-NavigatorBody">{children}</div>
381
+ </div>
382
+ );
383
+ }
384
+
385
+ function SidePaneTitle({ title }: { title: string }) {
386
+ const store = usePageScope();
387
+ useClaimPage(store, title);
388
+ React.useEffect(() => {
389
+ if (!store) return;
390
+ setPageCollapsed(store, true);
391
+ return () => setPageCollapsed(store, false);
392
+ }, [store]);
393
+ return null;
394
+ }
395
+
396
+ interface NavigatorLinkProps<T> extends Omit<React.ComponentPropsWithoutRef<'button'>, 'value' | 'onClick'> {
397
+ /** The value pushed onto the path when the row is pressed. */
398
+ value: T;
399
+ }
400
+
401
+ /** A row that opens the next screen, with the chevron that says so. */
402
+ function NavigatorLink<T>({ value, className, children, ...props }: NavigatorLinkProps<T>) {
403
+ const state = useNavigatorState();
404
+ return (
405
+ <button type="button" {...props} className={classNames('kui-reset', 'kui-NavigatorLink', className)} onClick={() => state.push(value)}>
406
+ <span className="kui-NavigatorLinkLabel">{children}</span>
407
+ <svg className="kui-NavigatorLinkGlyph" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
408
+ <path d="m9 18 6-6-6-6" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" />
409
+ </svg>
410
+ </button>
411
+ );
412
+ }
413
+
414
+ export { Navigator, NavigatorOutlet, NavigatorBack, NavigatorTitle, NavigatorScreen, NavigatorLink, useNavigator };
415
+ export type { NavigatorProps, NavigatorBackProps, NavigatorScreenProps, NavigatorLinkProps, NavigatorApi };
@@ -0,0 +1,38 @@
1
+ /* Page (from v2, 2026-11-03): the screen you navigated to. Three distances and a marker; the
2
+ * width is the frame's to state. */
3
+ .kui-Page {
4
+ display: flex;
5
+ flex-direction: column;
6
+ /* From the title block to whatever the page begins with: two steps clear of the title-to-
7
+ description interval, so a section does not read as a third line of the description. */
8
+ gap: var(--layout-space-8);
9
+ /* Air above the title. A floating band's reach is already cleared by `Shell.Scroll`, which
10
+ pads its content by it, so only the air is left to add. */
11
+ padding-block-start: var(--layout-space-7);
12
+ }
13
+
14
+ /* The title and its description are one block, and the marker is placed against it. */
15
+ .kui-PageHeader {
16
+ position: relative;
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: var(--layout-space-6);
20
+ }
21
+
22
+ /* The mark and the title are a lockup, so they sit closer than the title and description. */
23
+ .kui-PageMark {
24
+ margin-block-end: calc(var(--layout-space-4) - var(--layout-space-6));
25
+ }
26
+
27
+ /* Where the title stops being visible. The band floats over the scroller, so the marker sits the
28
+ band's reach above the end of the title block, and crosses the scroller's top edge at the
29
+ moment the title passes behind the band. A hairline tall, since a zero-area box is not
30
+ reliably reported as intersecting; out of flow, and it paints nothing. */
31
+ .kui-PageMarker {
32
+ position: absolute;
33
+ inset-block-end: var(--kui-pane-inset-start, 0px);
34
+ inset-inline-start: 0;
35
+ block-size: 1px;
36
+ inline-size: 100%;
37
+ pointer-events: none;
38
+ }
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import classNames from 'classnames';
5
+
6
+ import { setPageCollapsed, useClaimPage, usePageScope } from './_internal/page-scope.js';
7
+ import { usePageScrollMedia } from './_internal/shell-page-scroll.js';
8
+ import { Heading } from './heading.js';
9
+ import { Text } from './text.js';
10
+
11
+ interface PageProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'title'> {
12
+ /**
13
+ * The title of the page. It shows large at the top. When it scrolls away, it also shows in
14
+ * the `Toolbar.Title` of the pane's band. It is a string, because it shows in two places.
15
+ */
16
+ title: string;
17
+ /** A sentence under the title that describes the page. Optional. */
18
+ description?: React.ReactNode;
19
+ /** A logo or mark above the title, such as an app's logo on its home page. Most pages don't need it. */
20
+ mark?: React.ReactNode;
21
+ }
22
+
23
+ /**
24
+ * Page, from v2 (2026-11-03): the screen you navigated to, and its title.
25
+ *
26
+ * A page is not a band. The pinned row with the way back and the tools is the pane's
27
+ * (`Shell.PaneHeader`): it stays while you move from page to page. The large title that arrives
28
+ * and leaves with the content is the page's, and it is this.
29
+ *
30
+ * It owns three things: the air above its title (a floating band's reach is already cleared by
31
+ * `Shell.Scroll`), the title and its description at the page's type steps, and the collapse:
32
+ * when the title passes behind the band, every `Toolbar.Title` with no words of its own in this
33
+ * pane shows the page's title.
34
+ *
35
+ * It takes no width (the frame around it states one), no level (a page is the document's one
36
+ * `h1`), no size (an app has one page step) and no actions (they go in the toolbar).
37
+ */
38
+ const Page = React.forwardRef<HTMLDivElement, PageProps>(({ title, description, mark, className, children, ...props }, forwardedRef) => {
39
+ const store = usePageScope();
40
+ const marker = React.useRef<HTMLDivElement>(null);
41
+ useClaimPage(store, title);
42
+
43
+ // Re-read when a touch window crosses the phone boundary: the scroller changes from the
44
+ // content's viewport to the page, and an observer built for one never fires for the other.
45
+ const pageScrolls = usePageScrollMedia();
46
+ React.useEffect(() => {
47
+ const el = marker.current;
48
+ if (!el || !store) return;
49
+ // The scroller is the viewport around this, if it scrolls; on a phone window Shell the
50
+ // document scrolls and the viewport is `overflow: visible`, so the root is the document.
51
+ const viewport = el.closest<HTMLElement>('.kui-ScrollAreaViewport');
52
+ const scrolls = viewport !== null && !/^(visible|clip)$/.test(getComputedStyle(viewport).overflowY);
53
+ const observer = new IntersectionObserver(
54
+ (entries) => {
55
+ const entry = entries[entries.length - 1];
56
+ if (!entry) return;
57
+ // Gone off the top, not merely gone: a marker below the fold is also out of view.
58
+ const top = entry.rootBounds?.top;
59
+ setPageCollapsed(store, !entry.isIntersecting && (top === undefined || entry.boundingClientRect.top <= top));
60
+ },
61
+ { root: scrolls ? viewport : null, threshold: 0 },
62
+ );
63
+ observer.observe(el);
64
+ return () => {
65
+ observer.disconnect();
66
+ // A page that unmounts takes its collapse with it, or the next page's band opens collapsed.
67
+ setPageCollapsed(store, false);
68
+ };
69
+ }, [store, pageScrolls]);
70
+
71
+ return (
72
+ <div {...props} ref={forwardedRef} className={classNames('kui-Page', className)}>
73
+ <div className="kui-PageHeader">
74
+ {mark === undefined || mark === null ? null : <div className="kui-PageMark">{mark}</div>}
75
+ <Heading as="h1" size="9" weight="medium">
76
+ {title}
77
+ </Heading>
78
+ {description === undefined || description === null ? null : (
79
+ <Text as="p" size="4">
80
+ {description}
81
+ </Text>
82
+ )}
83
+ {/* Out of flow, so it displaces nothing and a page with no band behaves the same. */}
84
+ <div ref={marker} className="kui-PageMarker" aria-hidden="true" />
85
+ </div>
86
+ {children}
87
+ </div>
88
+ );
89
+ });
90
+ Page.displayName = 'Page';
91
+
92
+ export { Page };
93
+ export type { PageProps };
@@ -7,6 +7,7 @@ import { useThemeSizeProps } from '../hooks/theme-size.js';
7
7
  import { requireReactElement } from '../helpers/require-react-element.js';
8
8
  import { popoverContentPropDefs } from './popover.props.js';
9
9
  import { PortalTheme } from './_internal/portal-theme.js';
10
+ import { NavigatorHostContext, type NavigatorHost } from './_internal/navigator-host.js';
10
11
 
11
12
  import type { PopoverContentOwnProps } from './popover.props.js';
12
13
  import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
@@ -31,6 +32,22 @@ const usePopoverContentContext = (caller: string) => {
31
32
  };
32
33
 
33
34
  interface PopoverRootProps extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root> {}
35
+ /** A Navigator screen's title names the popover through the content's label. */
36
+ function PopoverNavigatorTitle({ children }: { children: React.ReactElement<{ id?: string }> }) {
37
+ const setLabelId = usePopoverContentContext('Navigator')?.setLabelId;
38
+ const id = React.useId();
39
+ React.useEffect(() => {
40
+ setLabelId?.(id);
41
+ return () => setLabelId?.(undefined);
42
+ }, [id, setLabelId]);
43
+ return React.cloneElement(children, { id });
44
+ }
45
+
46
+ const popoverNavigatorHost: NavigatorHost = {
47
+ Title: PopoverNavigatorTitle,
48
+ Close: ({ children }) => <PopoverPrimitive.Close asChild>{children}</PopoverPrimitive.Close>,
49
+ };
50
+
34
51
  const PopoverRoot: React.FC<PopoverRootProps> = (props: PopoverRootProps) => (
35
52
  <PopoverPrimitive.Root {...props} />
36
53
  );
@@ -81,7 +98,7 @@ const PopoverContent = React.forwardRef<PopoverContentElement, PopoverContentPro
81
98
  className={classNames('kui-PopperContent', 'kui-PopoverContent', className)}
82
99
  >
83
100
  <PopoverContentContext.Provider value={contextValue}>
84
- {props.children}
101
+ <NavigatorHostContext.Provider value={popoverNavigatorHost}>{props.children}</NavigatorHostContext.Provider>
85
102
  </PopoverContentContext.Provider>
86
103
  </PopoverPrimitive.Content>
87
104
  </PortalTheme>
@@ -39,6 +39,7 @@ import { Dialog as DialogPrimitive } from 'radix-ui';
39
39
  import { dialogContentPropDefs } from './dialog.props.js';
40
40
  import type { DialogContentOwnProps } from './dialog.props.js';
41
41
  import { PortalTheme } from './_internal/portal-theme.js';
42
+ import { NavigatorHostContext, type NavigatorHost } from './_internal/navigator-host.js';
42
43
  import { Heading } from './heading.js';
43
44
  import { Text } from './text.js';
44
45
  import { extractProps } from '../helpers/extract-props.js';
@@ -89,6 +90,12 @@ Trigger.displayName = 'Sheet.Trigger';
89
90
  const { align: _alignPropDef, ...dialogPropDefsForSheet } = dialogContentPropDefs;
90
91
  const sheetPropDefs = dialogPropDefsForSheet;
91
92
 
93
+ /** What a Navigator screen in this panel uses: its title names the sheet, its close closes it. */
94
+ const sheetNavigatorHost: NavigatorHost = {
95
+ Title: ({ children }) => <DialogPrimitive.Title asChild>{children}</DialogPrimitive.Title>,
96
+ Close: ({ children }) => <DialogPrimitive.Close asChild>{children}</DialogPrimitive.Close>,
97
+ };
98
+
92
99
  /** Counts the Sheet.Title elements mounted inside a Sheet.Content, for the dev-only name check. */
93
100
  const SheetTitleCountContext = React.createContext<React.MutableRefObject<number> | null>(null);
94
101
 
@@ -155,6 +162,7 @@ const Content = React.forwardRef<SheetContentElement, SheetContentProps>((allPro
155
162
  className={classNames('kui-BaseDialogContent', 'kui-SheetContent', className, extractedClassName)}
156
163
  data-side={normalizedSide}
157
164
  aria-modal="true"
165
+ children={<NavigatorHostContext.Provider value={sheetNavigatorHost}>{contentProps.children}</NavigatorHostContext.Provider>}
158
166
  />
159
167
  </DialogPrimitive.Overlay>
160
168
  </PortalTheme>
@@ -52,6 +52,7 @@ import { useShell, useSidebarMode } from './shell.context.js';
52
52
  import { useResponsivePresentation } from './shell.hooks.js';
53
53
  import { readBreakpoint } from '../hooks/use-breakpoint.js';
54
54
  import { useThemeSize } from '../hooks/theme-size.js';
55
+ import { PageScope } from './_internal/page-scope.js';
55
56
  import { PaneChromeScope, PaneFooter, PaneHeader, Scroll, ShellContentContext, ShellSizeContext, ShellWindowContext } from './_internal/pane-chrome.js';
56
57
  import { onPageScrollMediaChange, pageScrollMatches } from './_internal/shell-page-scroll.js';
57
58
  import type { PaneSize, PaneHeaderProps, PaneFooterProps, ScrollProps } from './_internal/pane-chrome.js';
@@ -784,7 +785,9 @@ const Panel = React.forwardRef<HTMLDivElement, PanelPublicProps>((props, ref) =>
784
785
  }
785
786
  >
786
787
  <div className="kui-ShellPanelContent">
787
- <PaneChromeScope>{content}</PaneChromeScope>
788
+ <PaneChromeScope>
789
+ <PageScope>{content}</PageScope>
790
+ </PaneChromeScope>
788
791
  </div>
789
792
  {handle}
790
793
  </div>
@@ -808,7 +811,10 @@ const Content = React.forwardRef<HTMLElement, ShellContentProps>(({ className, i
808
811
  useInsetRegistration('content', inset);
809
812
  return (
810
813
  <main {...props} ref={ref} className={classNames('kui-ShellContent', className)} data-inset={inset || undefined}>
811
- <ShellContentContext.Provider value={true}>{children}</ShellContentContext.Provider>
814
+ {/* Each pane holds its own page store, so a band's title can mirror the page beside it. */}
815
+ <ShellContentContext.Provider value={true}>
816
+ <PageScope>{children}</PageScope>
817
+ </ShellContentContext.Provider>
812
818
  </main>
813
819
  );
814
820
  });
@@ -158,6 +158,17 @@
158
158
  flex-shrink: 1;
159
159
  }
160
160
 
161
+ /* A mirroring title is the page's own, said again in the band: silent while the page's large
162
+ title is on screen, shown at once when it is not. Paint only, so the clusters beside it never
163
+ move when someone scrolls. */
164
+ .kui-ToolbarTitle:where([data-mirror]) {
165
+ opacity: 0;
166
+ }
167
+
168
+ .kui-ToolbarTitle:where([data-mirror][data-collapsed]) {
169
+ opacity: 1;
170
+ }
171
+
161
172
  /* A title is a different kind of thing from the controls beside it, so it takes more air than
162
173
  they take from each other. It reaches inside a cluster, since a band is often written
163
174
  <Flex>{back}{title}</Flex>. No air against the row's own ends. */