@justanarthur/modo-ui 0.5.0 → 0.6.0-canary.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 (461) hide show
  1. package/DESIGN.md +608 -0
  2. package/README.md +61 -39
  3. package/blocks/sidebar-app/examples.mdx +33 -0
  4. package/blocks/sidebar-app/index.tsx +218 -0
  5. package/blocks/sidebar-app/inset-topbar.tsx +25 -0
  6. package/blocks/sidebar-app/menu-grid.ts +26 -0
  7. package/blocks/sidebar-app/nav-data.ts +36 -0
  8. package/blocks/sidebar-app/search-field.tsx +48 -0
  9. package/blocks/sidebar-app/user-footer.tsx +67 -0
  10. package/blocks/sidebar-app/workspace-header.tsx +129 -0
  11. package/components/AI/input-message/examples.mdx +124 -0
  12. package/components/AI/input-message/file-thumbnail.tsx +173 -0
  13. package/components/AI/input-message/index.tsx +1383 -0
  14. package/components/AI/thinking-indicator/examples.mdx +27 -0
  15. package/components/AI/thinking-indicator/index.tsx +210 -0
  16. package/components/accordion/examples.mdx +148 -0
  17. package/components/accordion/index.tsx +1034 -0
  18. package/components/avatar/examples.mdx +95 -0
  19. package/components/avatar/index.tsx +129 -0
  20. package/components/badge/change-demo.tsx +29 -0
  21. package/components/badge/examples.mdx +120 -0
  22. package/components/badge/index.tsx +225 -0
  23. package/components/badge/variants.ts +73 -0
  24. package/components/button/examples.mdx +164 -0
  25. package/components/button/index.tsx +416 -0
  26. package/components/button/loading-demo.tsx +25 -0
  27. package/components/button/variants.ts +166 -0
  28. package/components/card/examples.mdx +212 -0
  29. package/components/card/index.tsx +1037 -0
  30. package/components/copy/examples.mdx +87 -0
  31. package/components/copy/index.tsx +482 -0
  32. package/components/inputs/checkbox/examples.mdx +83 -0
  33. package/components/inputs/checkbox/index.tsx +611 -0
  34. package/components/inputs/checkbox/select-all-demo.tsx +48 -0
  35. package/components/inputs/color-picker/examples.mdx +72 -0
  36. package/components/inputs/color-picker/index.tsx +1955 -0
  37. package/components/inputs/color-picker/variants.ts +401 -0
  38. package/components/inputs/combobox/examples.mdx +273 -0
  39. package/components/inputs/combobox/index.tsx +1436 -0
  40. package/components/inputs/combobox/remote-demo.tsx +101 -0
  41. package/components/inputs/date-time/calendar/examples.mdx +34 -0
  42. package/components/inputs/date-time/calendar/index.tsx +351 -0
  43. package/components/inputs/date-time/calendar/use-month-window.ts +163 -0
  44. package/components/inputs/date-time/date-picker/examples.mdx +166 -0
  45. package/components/inputs/date-time/date-picker/index.tsx +385 -0
  46. package/components/inputs/date-time/date-picker/masked-date-input.tsx +336 -0
  47. package/components/inputs/date-time/date-range-picker/examples.mdx +29 -0
  48. package/components/inputs/date-time/date-range-picker/index.tsx +217 -0
  49. package/components/inputs/date-time/duration-picker/examples.mdx +16 -0
  50. package/components/inputs/date-time/duration-picker/index.tsx +189 -0
  51. package/components/inputs/date-time/month-picker/examples.mdx +16 -0
  52. package/components/inputs/date-time/month-picker/index.tsx +190 -0
  53. package/components/inputs/date-time/picker-shell.tsx +395 -0
  54. package/components/inputs/date-time/time-columns.tsx +91 -0
  55. package/components/inputs/date-time/time-picker/examples.mdx +26 -0
  56. package/components/inputs/date-time/time-picker/index.tsx +166 -0
  57. package/components/inputs/field/examples.mdx +113 -0
  58. package/components/inputs/field/index.tsx +267 -0
  59. package/components/inputs/file-dropzone/examples.mdx +73 -0
  60. package/components/inputs/file-dropzone/file-row.tsx +201 -0
  61. package/components/inputs/file-dropzone/index.tsx +296 -0
  62. package/components/inputs/file-dropzone/upload-demo.tsx +67 -0
  63. package/components/inputs/file-dropzone/variants.ts +94 -0
  64. package/components/inputs/input/examples.mdx +135 -0
  65. package/components/inputs/input/index.tsx +494 -0
  66. package/components/inputs/input/password-demo.tsx +30 -0
  67. package/components/inputs/label/examples.mdx +67 -0
  68. package/components/inputs/label/index.tsx +85 -0
  69. package/components/inputs/number-input/examples.mdx +56 -0
  70. package/components/inputs/number-input/index.tsx +214 -0
  71. package/components/inputs/radio/examples.mdx +77 -0
  72. package/components/inputs/radio/index.tsx +636 -0
  73. package/components/inputs/search/examples.mdx +74 -0
  74. package/components/inputs/search/index.tsx +333 -0
  75. package/components/inputs/select/examples.mdx +218 -0
  76. package/components/inputs/select/index.tsx +862 -0
  77. package/components/inputs/select/variants.ts +32 -0
  78. package/components/inputs/slider/examples.mdx +127 -0
  79. package/components/inputs/slider/index.tsx +1725 -0
  80. package/components/inputs/switch/examples.mdx +94 -0
  81. package/components/inputs/switch/index.tsx +504 -0
  82. package/components/inputs/textarea/examples.mdx +88 -0
  83. package/components/inputs/textarea/index.tsx +161 -0
  84. package/components/navigation/navigation-menu/examples.mdx +109 -0
  85. package/components/navigation/navigation-menu/index.tsx +613 -0
  86. package/components/navigation/pagination/examples.mdx +64 -0
  87. package/components/navigation/pagination/index.tsx +395 -0
  88. package/components/navigation/sidebar/examples.mdx +319 -0
  89. package/components/navigation/sidebar/index.tsx +453 -0
  90. package/components/navigation/sidebar/sidebar-core.tsx +1408 -0
  91. package/components/navigation/sidebar/sidebar-menu.tsx +1495 -0
  92. package/components/navigation/sidebar/sidebar.css +74 -0
  93. package/components/navigation/sidebar/variants.ts +55 -0
  94. package/components/navigation/tabs/examples.mdx +89 -0
  95. package/components/navigation/tabs/index.tsx +494 -0
  96. package/components/navigation/tabs-subtle/examples.mdx +49 -0
  97. package/components/navigation/tabs-subtle/index.tsx +512 -0
  98. package/components/overlays/alert-dialog/examples.mdx +123 -0
  99. package/components/overlays/alert-dialog/index.tsx +258 -0
  100. package/components/overlays/alert-dialog/pending-demo.tsx +36 -0
  101. package/components/overlays/command/examples.mdx +113 -0
  102. package/components/overlays/command/index.tsx +416 -0
  103. package/components/overlays/context-menu/examples.mdx +56 -0
  104. package/components/overlays/context-menu/index.tsx +279 -0
  105. package/components/overlays/dialog/examples.mdx +144 -0
  106. package/components/overlays/dialog/index.tsx +366 -0
  107. package/components/overlays/dropdown/dropdown-search.tsx +395 -0
  108. package/components/overlays/dropdown/examples.mdx +294 -0
  109. package/components/overlays/dropdown/index.tsx +1168 -0
  110. package/components/overlays/dropdown/menu-item.tsx +464 -0
  111. package/components/overlays/dropdown/menu-parts.tsx +660 -0
  112. package/components/overlays/popover/anchored-demo.tsx +59 -0
  113. package/components/overlays/popover/examples.mdx +180 -0
  114. package/components/overlays/popover/index.tsx +315 -0
  115. package/components/overlays/preview-card/examples.mdx +61 -0
  116. package/components/overlays/preview-card/index.tsx +204 -0
  117. package/components/overlays/sheet/examples.mdx +89 -0
  118. package/components/overlays/sheet/index.tsx +247 -0
  119. package/components/overlays/toast/examples.mdx +68 -0
  120. package/components/overlays/toast/global-demo.tsx +72 -0
  121. package/components/overlays/toast/index.tsx +474 -0
  122. package/components/overlays/toast/manager.ts +193 -0
  123. package/components/overlays/tooltip/examples.mdx +106 -0
  124. package/components/overlays/tooltip/index.tsx +519 -0
  125. package/components/progress/examples.mdx +59 -0
  126. package/components/progress/index.tsx +174 -0
  127. package/components/progress/progress.css +49 -0
  128. package/components/progress/step-demo.tsx +27 -0
  129. package/components/table/examples.mdx +184 -0
  130. package/components/table/index.tsx +510 -0
  131. package/components/table/sort-demo.tsx +61 -0
  132. package/dist/base.css +100 -0
  133. package/dist/blocks/sidebar-app/index.js +39 -26
  134. package/dist/components/AI/input-message/index.js +22 -16
  135. package/dist/components/AI/thinking-indicator/index.js +9 -6
  136. package/dist/components/accordion/index.js +16 -13
  137. package/dist/components/avatar/index.d.ts +46 -0
  138. package/dist/components/avatar/index.js +18 -0
  139. package/dist/components/badge/index.d.ts +18 -11
  140. package/dist/components/badge/index.js +11 -7
  141. package/dist/components/badge/variants.d.ts +33 -0
  142. package/dist/components/badge/variants.js +10 -0
  143. package/dist/components/button/index.d.ts +53 -17
  144. package/dist/components/button/index.js +10 -6
  145. package/dist/components/button/variants.d.ts +39 -0
  146. package/dist/components/button/variants.js +8 -0
  147. package/dist/components/card/index.d.ts +16 -3
  148. package/dist/components/card/index.js +16 -11
  149. package/dist/components/copy/index.js +13 -9
  150. package/dist/components/inputs/checkbox/index.d.ts +28 -9
  151. package/dist/components/inputs/checkbox/index.js +17 -13
  152. package/dist/components/inputs/color-picker/index.d.ts +13 -13
  153. package/dist/components/inputs/color-picker/index.js +25 -18
  154. package/dist/components/inputs/color-picker/variants.d.ts +61 -0
  155. package/dist/components/inputs/color-picker/variants.js +28 -0
  156. package/dist/components/inputs/combobox/index.d.ts +60 -17
  157. package/dist/components/inputs/combobox/index.js +26 -18
  158. package/dist/components/inputs/date-time/calendar/index.js +14 -11
  159. package/dist/components/inputs/date-time/date-picker/index.d.ts +53 -11
  160. package/dist/components/inputs/date-time/date-picker/index.js +33 -25
  161. package/dist/components/inputs/date-time/date-range-picker/index.d.ts +3 -3
  162. package/dist/components/inputs/date-time/date-range-picker/index.js +32 -24
  163. package/dist/components/inputs/date-time/duration-picker/index.d.ts +3 -3
  164. package/dist/components/inputs/date-time/duration-picker/index.js +32 -24
  165. package/dist/components/inputs/date-time/month-picker/index.d.ts +3 -3
  166. package/dist/components/inputs/date-time/month-picker/index.js +31 -23
  167. package/dist/components/inputs/date-time/time-picker/index.d.ts +3 -3
  168. package/dist/components/inputs/date-time/time-picker/index.js +32 -24
  169. package/dist/components/inputs/field/index.d.ts +116 -0
  170. package/dist/components/inputs/field/index.js +28 -0
  171. package/dist/components/inputs/file-dropzone/index.d.ts +165 -0
  172. package/dist/components/inputs/file-dropzone/index.js +32 -0
  173. package/dist/components/inputs/file-dropzone/variants.d.ts +35 -0
  174. package/dist/components/inputs/file-dropzone/variants.js +12 -0
  175. package/dist/components/inputs/input/index.d.ts +95 -13
  176. package/dist/components/inputs/input/index.js +17 -9
  177. package/dist/components/inputs/label/index.d.ts +32 -0
  178. package/dist/components/inputs/label/index.js +11 -0
  179. package/dist/components/inputs/number-input/index.d.ts +84 -0
  180. package/dist/components/inputs/number-input/index.js +20 -0
  181. package/dist/components/inputs/radio/index.d.ts +20 -5
  182. package/dist/components/inputs/radio/index.js +15 -12
  183. package/dist/components/inputs/search/index.js +25 -18
  184. package/dist/components/inputs/select/index.d.ts +41 -11
  185. package/dist/components/inputs/select/index.js +27 -14
  186. package/dist/components/inputs/select/variants.d.ts +5 -0
  187. package/dist/components/inputs/select/variants.js +6 -0
  188. package/dist/components/inputs/slider/index.js +7 -5
  189. package/dist/components/inputs/switch/index.d.ts +8 -2
  190. package/dist/components/inputs/switch/index.js +14 -12
  191. package/dist/components/inputs/textarea/index.d.ts +4 -0
  192. package/dist/components/inputs/textarea/index.js +8 -6
  193. package/dist/components/navigation/navigation-menu/index.d.ts +114 -0
  194. package/dist/components/navigation/navigation-menu/index.js +40 -0
  195. package/dist/components/navigation/pagination/index.d.ts +73 -0
  196. package/dist/components/navigation/pagination/index.js +39 -0
  197. package/dist/components/navigation/sidebar/index.d.ts +44 -21
  198. package/dist/components/navigation/sidebar/index.js +41 -31
  199. package/dist/components/navigation/sidebar/variants.d.ts +25 -0
  200. package/dist/components/navigation/sidebar/variants.js +28 -0
  201. package/dist/components/navigation/tabs/index.d.ts +8 -2
  202. package/dist/components/navigation/tabs/index.js +14 -10
  203. package/dist/components/navigation/tabs-subtle/index.js +12 -10
  204. package/dist/components/overlays/alert-dialog/index.d.ts +121 -15
  205. package/dist/components/overlays/alert-dialog/index.js +22 -13
  206. package/dist/components/overlays/command/index.js +24 -17
  207. package/dist/components/overlays/context-menu/index.d.ts +33 -17
  208. package/dist/components/overlays/context-menu/index.js +27 -18
  209. package/dist/components/overlays/dialog/index.js +17 -12
  210. package/dist/components/overlays/dropdown/index.d.ts +257 -31
  211. package/dist/components/overlays/dropdown/index.js +42 -17
  212. package/dist/components/overlays/popover/index.d.ts +31 -9
  213. package/dist/components/overlays/popover/index.js +12 -9
  214. package/dist/components/overlays/preview-card/index.js +11 -8
  215. package/dist/components/overlays/sheet/index.js +18 -13
  216. package/dist/components/overlays/toast/index.d.ts +123 -47
  217. package/dist/components/overlays/toast/index.js +18 -10
  218. package/dist/components/overlays/tooltip/index.d.ts +113 -21
  219. package/dist/components/overlays/tooltip/index.js +15 -6
  220. package/dist/components/progress/index.d.ts +56 -0
  221. package/dist/components/progress/index.js +15 -0
  222. package/dist/components/table/index.d.ts +63 -8
  223. package/dist/components/table/index.js +19 -9
  224. package/dist/index.d.ts +2201 -736
  225. package/dist/index.js +223 -82
  226. package/dist/lib/date-locale.d.ts +14 -0
  227. package/dist/lib/date-locale.js +16 -0
  228. package/dist/lib/field-classes.d.ts +144 -0
  229. package/dist/lib/field-classes.js +40 -0
  230. package/dist/lib/field-variants.d.ts +47 -0
  231. package/dist/lib/field-variants.js +26 -0
  232. package/dist/lib/fluid-hover-highlight.d.ts +112 -0
  233. package/dist/lib/fluid-hover-highlight.js +17 -0
  234. package/dist/lib/focus-ring.d.ts +18 -0
  235. package/dist/lib/focus-ring.js +10 -0
  236. package/dist/lib/goo-indicator.d.ts +46 -0
  237. package/dist/lib/goo-indicator.js +14 -0
  238. package/dist/lib/icon-context.d.ts +30 -0
  239. package/dist/lib/icon-context.js +12 -0
  240. package/dist/lib/inert.d.ts +9 -0
  241. package/dist/lib/inert.js +8 -0
  242. package/dist/lib/morph-layers.d.ts +93 -0
  243. package/dist/lib/morph-layers.js +13 -0
  244. package/dist/lib/morph-part.d.ts +21 -0
  245. package/dist/lib/morph-part.js +12 -0
  246. package/dist/lib/popup.d.ts +20 -0
  247. package/dist/lib/popup.js +12 -0
  248. package/dist/lib/reduced-motion.d.ts +9 -0
  249. package/dist/lib/reduced-motion.js +6 -0
  250. package/dist/lib/row-index.d.ts +10 -0
  251. package/dist/lib/row-index.js +8 -0
  252. package/dist/lib/row-order.d.ts +28 -0
  253. package/dist/lib/row-order.js +10 -0
  254. package/dist/lib/scroll-column.d.ts +27 -0
  255. package/dist/lib/scroll-column.js +18 -0
  256. package/dist/lib/shape-context.d.ts +30 -0
  257. package/dist/lib/shape-context.js +12 -0
  258. package/dist/lib/size-context.d.ts +94 -0
  259. package/dist/lib/size-context.js +20 -0
  260. package/dist/lib/size-scale.d.ts +79 -0
  261. package/dist/lib/size-scale.js +8 -0
  262. package/dist/lib/slot.d.ts +12 -0
  263. package/dist/lib/slot.js +6 -0
  264. package/dist/lib/springs.d.ts +44 -0
  265. package/dist/lib/springs.js +8 -0
  266. package/dist/lib/surface-classes.d.ts +19 -0
  267. package/dist/lib/surface-classes.js +10 -0
  268. package/dist/lib/surface-context.d.ts +9 -0
  269. package/dist/lib/surface-context.js +8 -0
  270. package/dist/lib/use-controllable-state.d.ts +2 -0
  271. package/dist/lib/use-controllable-state.js +6 -0
  272. package/dist/lib/use-fluid-hover.d.ts +138 -0
  273. package/dist/lib/use-fluid-hover.js +14 -0
  274. package/dist/lib/use-is-mobile.d.ts +2 -0
  275. package/dist/lib/use-is-mobile.js +6 -0
  276. package/dist/lib/use-keyboard-nav-gate.d.ts +20 -0
  277. package/dist/lib/use-keyboard-nav-gate.js +7 -0
  278. package/dist/lib/use-merge-split.d.ts +47 -0
  279. package/dist/lib/use-merge-split.js +15 -0
  280. package/dist/lib/use-morph.d.ts +101 -0
  281. package/dist/lib/use-morph.js +20 -0
  282. package/dist/lib/use-touch-primary.d.ts +2 -0
  283. package/dist/lib/use-touch-primary.js +6 -0
  284. package/dist/lib/utils.d.ts +3 -0
  285. package/dist/lib/utils.js +6 -0
  286. package/dist/preset.d.ts +17 -0
  287. package/dist/preset.js +22 -2
  288. package/dist/primitives/code/index.js +7 -5
  289. package/dist/primitives/craft/index.js +9 -6
  290. package/dist/primitives/fluid-hover/index.js +9 -8
  291. package/dist/primitives/morph/index.js +15 -10
  292. package/dist/primitives/motion/index.js +10 -8
  293. package/dist/primitives/scroll-area/index.js +4 -3
  294. package/dist/primitives/separator/index.d.ts +25 -0
  295. package/dist/primitives/separator/index.js +9 -0
  296. package/dist/primitives/sizes/index.js +6 -4
  297. package/dist/primitives/skeleton/index.d.ts +35 -0
  298. package/dist/primitives/skeleton/index.js +12 -0
  299. package/dist/primitives/surface/index.js +6 -4
  300. package/dist/shared/{components-3mkepka5.js → components-0jmqhx2k.js} +28 -25
  301. package/dist/shared/components-124epb7f.js +53 -0
  302. package/dist/shared/{components-gz8zf3ec.js → components-1k0wv53d.js} +15 -43
  303. package/dist/shared/components-26snnbs9.js +322 -0
  304. package/dist/shared/{components-2dxkv6zm.js → components-2c0qcfx4.js} +8 -7
  305. package/dist/shared/{components-473g814w.js → components-2mt9te6d.js} +243 -137
  306. package/dist/shared/{components-hawvy2qz.js → components-2z48g4fp.js} +9 -30
  307. package/dist/shared/components-34txk9rj.js +35 -0
  308. package/dist/shared/components-3h92d94m.js +23 -0
  309. package/dist/shared/{components-zfnmn5d4.js → components-3sp623ae.js} +36 -31
  310. package/dist/shared/{components-jwst3vtj.js → components-3wxkjgqn.js} +186 -76
  311. package/dist/shared/{components-s2bz1xpk.js → components-49bsynbw.js} +8 -5
  312. package/dist/shared/components-4bwsd46w.js +71 -0
  313. package/dist/shared/components-4n4f5m3e.js +297 -0
  314. package/dist/shared/{components-0q0sqfh4.js → components-4y816hwp.js} +27 -49
  315. package/dist/shared/components-52ngsgca.js +399 -0
  316. package/dist/shared/{components-y9y0g8er.js → components-52r5763g.js} +22 -19
  317. package/dist/shared/components-559tf7yb.js +28 -0
  318. package/dist/shared/components-5mkga9hj.js +440 -0
  319. package/dist/shared/{components-kwa6mrf9.js → components-5s0nfqvw.js} +14 -9
  320. package/dist/shared/{components-dk0gjr5a.js → components-60atskhp.js} +19 -18
  321. package/dist/shared/{components-rr7a6ja9.js → components-658b26nb.js} +21 -18
  322. package/dist/shared/{components-xxvdw7k1.js → components-65vwfjs6.js} +37 -34
  323. package/dist/shared/{components-frw34b65.js → components-6j2fh74k.js} +114 -123
  324. package/dist/shared/components-6zg1yft7.js +167 -0
  325. package/dist/shared/components-6zng858q.js +53 -0
  326. package/dist/shared/components-72w4ge68.js +64 -0
  327. package/dist/shared/{components-cv16mryv.js → components-78x40gee.js} +13 -7
  328. package/dist/shared/{components-b2wyq2cq.js → components-7d4r7hh1.js} +39 -57
  329. package/dist/shared/components-7ntvvegr.js +316 -0
  330. package/dist/shared/components-8nwsqjn8.js +34 -0
  331. package/dist/shared/{components-5xc2j8jz.js → components-8tt3xbb5.js} +19 -18
  332. package/dist/shared/{components-vc7vq5nz.js → components-9as3rf68.js} +6 -3
  333. package/dist/shared/components-9mys9jna.js +255 -0
  334. package/dist/shared/components-adkarj8n.js +35 -0
  335. package/dist/shared/{components-b3c5et5b.js → components-ae9fncz4.js} +32 -31
  336. package/dist/shared/{components-hqeq3w9q.js → components-awra5sff.js} +24 -16
  337. package/dist/shared/components-b1e239g3.js +28 -0
  338. package/dist/shared/{components-q5xj5c16.js → components-b1f2bjf1.js} +9 -8
  339. package/dist/shared/{components-qmmjx1qw.js → components-bn42vq6d.js} +71 -72
  340. package/dist/shared/components-c96g82th.js +1444 -0
  341. package/dist/shared/components-c9yr4ed1.js +44 -0
  342. package/dist/shared/components-d3shbwqq.js +95 -0
  343. package/dist/shared/{components-jjw69c4c.js → components-dcc02hs2.js} +7 -4
  344. package/dist/shared/{components-men63rwj.js → components-de99ykt7.js} +3 -2
  345. package/dist/shared/components-dhgk6mbt.js +16 -0
  346. package/dist/shared/components-drxrtnp2.js +25 -0
  347. package/dist/shared/{components-6p8tfcd6.js → components-ds9fqat9.js} +21 -18
  348. package/dist/shared/{components-mjthm1kf.js → components-egy4tyjt.js} +11 -8
  349. package/dist/shared/components-f0xfpkhh.js +82 -0
  350. package/dist/shared/components-f7h57m6t.js +59 -0
  351. package/dist/shared/{components-d6xtjht3.js → components-fjkpbg6n.js} +33 -30
  352. package/dist/shared/{components-gc5vtq2f.js → components-gh0qeey3.js} +63 -29
  353. package/dist/shared/{components-39jq9va1.js → components-gvbapfen.js} +29 -24
  354. package/dist/shared/{components-5vgpnxgy.js → components-gzesg7hy.js} +19 -16
  355. package/dist/shared/components-h6p86be3.js +19 -0
  356. package/dist/shared/components-h83az0bz.js +33 -0
  357. package/dist/shared/components-jjrj6ymn.js +104 -0
  358. package/dist/shared/{components-wvp1m52g.js → components-jp5v0m0f.js} +15 -14
  359. package/dist/shared/{components-zrkzcayz.js → components-jqpefjsn.js} +54 -6
  360. package/dist/shared/{components-1z6tdadk.js → components-k74j4hwh.js} +20 -17
  361. package/dist/shared/components-kswxnm3h.js +24 -0
  362. package/dist/shared/{components-jbbmdr74.js → components-mkkbbgb7.js} +36 -17
  363. package/dist/shared/{components-pbmqctfa.js → components-mpw667rb.js} +169 -65
  364. package/dist/shared/{components-s5ngg32c.js → components-paa08427.js} +121 -47
  365. package/dist/shared/{components-vfbhxf5r.js → components-pd42emw3.js} +6 -5
  366. package/dist/shared/{components-hw9yk32z.js → components-pmfszv7a.js} +35 -23
  367. package/dist/shared/{components-chacnk61.js → components-pw06rbxq.js} +19 -19
  368. package/dist/shared/components-q03cktx8.js +246 -0
  369. package/dist/shared/{components-kvr7f9hy.js → components-qdm3yh26.js} +19 -10
  370. package/dist/shared/{components-27nnvrwe.js → components-qs4rkqsj.js} +15 -14
  371. package/dist/shared/{components-s083f38z.js → components-r6gbmkew.js} +22 -21
  372. package/dist/shared/{components-ta7shw28.js → components-r8eqs7gz.js} +4 -4
  373. package/dist/shared/{components-sn1rxx1d.js → components-rm02h9nt.js} +12 -11
  374. package/dist/shared/{components-c6s6b43g.js → components-rrbpmfx4.js} +3 -2
  375. package/dist/shared/{components-n7mjr357.js → components-tz1fv35x.js} +3 -2
  376. package/dist/shared/components-tz4gaf1v.js +23 -0
  377. package/dist/shared/{components-zx6b6yby.js → components-vf45j12c.js} +4 -18
  378. package/dist/shared/{components-3ky44908.js → components-vfjc9jzz.js} +2 -3
  379. package/dist/shared/{components-39vmfq9s.js → components-vsa625vk.js} +21 -20
  380. package/dist/shared/components-wawnmkph.js +500 -0
  381. package/dist/shared/components-wktrrb4m.js +38 -0
  382. package/dist/shared/{components-8snj490h.js → components-wwd363me.js} +78 -32
  383. package/dist/shared/{components-5pnw8183.js → components-xcwvgcrd.js} +23 -22
  384. package/dist/shared/components-xdqmt6ed.js +20 -0
  385. package/dist/shared/{components-h2ajmhwn.js → components-y1w3a6wx.js} +107 -35
  386. package/dist/shared/{components-ar66gnt0.js → components-y6578b8h.js} +37 -34
  387. package/dist/shared/{components-59efxjd9.js → components-yhgq0qxx.js} +13 -12
  388. package/dist/shared/{components-jthe1pwb.js → components-yz84gpkq.js} +101 -404
  389. package/dist/shared/components-z3v0n0de.js +11 -0
  390. package/dist/shared/{components-7r3etddy.js → components-z9n19fyk.js} +19 -54
  391. package/dist/shared/components-zbvjzh1p.js +141 -0
  392. package/dist/styles.css +241 -16
  393. package/dist/tailwind.css +13 -0
  394. package/dist/tokens.css +34 -0
  395. package/index.ts +60 -0
  396. package/lib/date-locale.ts +50 -0
  397. package/lib/field-classes.ts +115 -0
  398. package/lib/field-variants.ts +117 -0
  399. package/lib/fluid-hover-highlight.tsx +139 -0
  400. package/lib/focus-ring.tsx +46 -0
  401. package/lib/goo-indicator.tsx +261 -0
  402. package/lib/icon-context.tsx +310 -0
  403. package/lib/inert.ts +22 -0
  404. package/lib/morph-layers.tsx +102 -0
  405. package/lib/morph-part.tsx +49 -0
  406. package/lib/popup.ts +52 -0
  407. package/lib/reduced-motion.ts +19 -0
  408. package/lib/row-index.tsx +29 -0
  409. package/lib/row-order.tsx +115 -0
  410. package/lib/scroll-column.tsx +244 -0
  411. package/lib/shape-context.tsx +124 -0
  412. package/lib/size-context.tsx +66 -0
  413. package/lib/size-scale.ts +115 -0
  414. package/lib/slot.ts +25 -0
  415. package/lib/springs.ts +62 -0
  416. package/lib/surface-classes.ts +58 -0
  417. package/lib/surface-context.tsx +21 -0
  418. package/lib/use-controllable-state.ts +44 -0
  419. package/lib/use-fluid-hover.ts +541 -0
  420. package/lib/use-is-mobile.ts +25 -0
  421. package/lib/use-keyboard-nav-gate.ts +45 -0
  422. package/lib/use-merge-split.tsx +209 -0
  423. package/lib/use-morph.ts +557 -0
  424. package/lib/use-touch-primary.tsx +43 -0
  425. package/lib/utils.ts +37 -0
  426. package/package.json +177 -4
  427. package/preset.ts +196 -0
  428. package/primitives/code/examples.mdx +27 -0
  429. package/primitives/code/index.tsx +80 -0
  430. package/primitives/craft/demos.tsx +173 -0
  431. package/primitives/craft/examples.mdx +138 -0
  432. package/primitives/craft/index.tsx +76 -0
  433. package/primitives/fluid-hover/examples.mdx +84 -0
  434. package/primitives/fluid-hover/index.tsx +316 -0
  435. package/primitives/morph/examples.mdx +135 -0
  436. package/primitives/morph/index.tsx +192 -0
  437. package/primitives/motion/examples.mdx +100 -0
  438. package/primitives/motion/index.tsx +160 -0
  439. package/primitives/scroll-area/examples.mdx +58 -0
  440. package/primitives/scroll-area/index.tsx +200 -0
  441. package/primitives/separator/examples.mdx +30 -0
  442. package/primitives/separator/index.tsx +53 -0
  443. package/primitives/sizes/examples.mdx +167 -0
  444. package/primitives/sizes/index.tsx +89 -0
  445. package/primitives/skeleton/examples.mdx +51 -0
  446. package/primitives/skeleton/index.tsx +72 -0
  447. package/primitives/skeleton/skeleton.css +15 -0
  448. package/primitives/surface/examples.mdx +173 -0
  449. package/primitives/surface/index.tsx +109 -0
  450. package/primitives/surface/surface.css +176 -0
  451. package/dist/shared/components-0sn7ypjw.js +0 -61
  452. package/dist/shared/components-1434p1mh.js +0 -123
  453. package/dist/shared/components-a86dytt0.js +0 -986
  454. package/dist/shared/components-gf5s6ap9.js +0 -42
  455. package/dist/shared/components-hyq23phv.js +0 -310
  456. package/dist/shared/components-ng72k97e.js +0 -128
  457. package/dist/shared/components-raknhppb.js +0 -64
  458. package/dist/shared/components-vynjtrk6.js +0 -19
  459. package/dist/shared/components-wy9jpqny.js +0 -146
  460. /package/dist/shared/{components-1v7rxytx.js → components-3ey0hzpb.js} +0 -0
  461. /package/dist/shared/{components-ezxhrmer.js → components-jcnjay5g.js} +0 -0
@@ -0,0 +1,613 @@
1
+ /*
2
+ * Local addition (not part of Fluid Functionalism): a site navigation bar
3
+ * whose flyout panels grow out of their trigger through the morph engine
4
+ * (`lib/use-morph.ts`), on Base UI's NavigationMenu. One panel is shared by
5
+ * every item: moving to another trigger springs its size to the new content
6
+ * while the content crossfades in from the side it came from. The strip has
7
+ * one fluid hover highlight, and `NavigationMenu.Indicator` is a liquid
8
+ * marker (`lib/goo-indicator.tsx`) under the open or current item. A panel
9
+ * draws one keyboard ring (`lib/focus-ring.tsx`, on the surrounding shape).
10
+ */
11
+
12
+ import { NavigationMenu as NavigationMenuPrimitive } from '@base-ui/react/navigation-menu'
13
+ import { motion } from 'motion/react'
14
+ import {
15
+ type ComponentPropsWithoutRef,
16
+ createContext,
17
+ forwardRef,
18
+ type MutableRefObject,
19
+ type ReactNode,
20
+ type RefObject,
21
+ useCallback,
22
+ useContext,
23
+ useId,
24
+ useLayoutEffect,
25
+ useMemo,
26
+ useRef,
27
+ useState,
28
+ } from 'react'
29
+ import { FluidHoverHighlight } from '../../../lib/fluid-hover-highlight'
30
+ import { FocusRing } from '../../../lib/focus-ring'
31
+ import { GooIndicator } from '../../../lib/goo-indicator'
32
+ import { type IconComponent, useIcon } from '../../../lib/icon-context'
33
+ import { MorphSurface } from '../../../lib/morph-layers'
34
+ import { useReduceMotion } from '../../../lib/reduced-motion'
35
+ import { useShape } from '../../../lib/shape-context'
36
+ import { type SizeVariant, useSize } from '../../../lib/size-context'
37
+ import { spring } from '../../../lib/springs'
38
+ import { SURFACE_BG, SURFACE_SHADOW } from '../../../lib/surface-classes'
39
+ import { SurfaceProvider, useSurface } from '../../../lib/surface-context'
40
+ import { useControllableState } from '../../../lib/use-controllable-state'
41
+ import { type ItemRect, type UseFluidHoverReturn, useFluidHover } from '../../../lib/use-fluid-hover'
42
+ import { useKeyboardNavGate } from '../../../lib/use-keyboard-nav-gate'
43
+ import { useMorph, useMorphOrigin } from '../../../lib/use-morph'
44
+ import { cn } from '../../../lib/utils'
45
+ import { SizeProvider } from '../../../primitives/sizes'
46
+
47
+ /* ─────────────────────── Shared ─────────────────────── */
48
+
49
+ const TARGET = 'data-navigation-menu-target'
50
+ const ROW = 'data-navigation-menu-row'
51
+
52
+ interface PanelSize {
53
+ width: number
54
+ height: number
55
+ }
56
+
57
+ interface RootContextValue {
58
+ value: string | null
59
+ triggers: MutableRefObject<Map<string, HTMLElement>>
60
+ setList: (el: HTMLElement | null) => void
61
+ setPanelSize: (size: PanelSize) => void
62
+ }
63
+
64
+ const RootContext = createContext<RootContextValue | null>(null)
65
+
66
+ function useRoot() {
67
+ const ctx = useContext(RootContext)
68
+ if (!ctx) throw new Error('NavigationMenu parts must be used within a NavigationMenu')
69
+ return ctx
70
+ }
71
+
72
+ interface ListContextValue {
73
+ hover: UseFluidHoverReturn
74
+ indicatorRect: ItemRect | undefined
75
+ }
76
+
77
+ const ListContext = createContext<ListContextValue | null>(null)
78
+
79
+ const ItemContext = createContext<string>('')
80
+
81
+ // True inside a panel: links there are rows, not strip items.
82
+ const InContentContext = createContext(false)
83
+
84
+ /**
85
+ * Registers every element matching `selector` inside the container with the
86
+ * fluid hover, in document order, so parts need no `index`. Runs after every
87
+ * render and only re-registers when the set changed.
88
+ */
89
+ function useRegisterBySelector(
90
+ container: RefObject<HTMLElement | null>,
91
+ selector: string,
92
+ register: UseFluidHoverReturn['registerItem'],
93
+ ) {
94
+ const registered = useRef<HTMLElement[]>([])
95
+ const [items, setItems] = useState<HTMLElement[]>([])
96
+
97
+ useLayoutEffect(() => {
98
+ const root = container.current
99
+ if (!root) return
100
+ const next = [...root.querySelectorAll<HTMLElement>(selector)]
101
+ const previous = registered.current
102
+ if (next.length === previous.length && next.every((el, i) => el === previous[i])) return
103
+ for (let i = next.length; i < previous.length; i++) register(i, null)
104
+ next.forEach((el, i) => {
105
+ if (previous[i] !== el) register(i, el)
106
+ })
107
+ registered.current = next
108
+ setItems(next)
109
+ })
110
+
111
+ return items
112
+ }
113
+
114
+ /** Writes a node into a forwarded ref as well as a local one. */
115
+ function assignRef<T>(ref: React.ForwardedRef<T>, node: T | null) {
116
+ if (typeof ref === 'function') ref(node)
117
+ else if (ref) ref.current = node
118
+ }
119
+
120
+ /* ─────────────────────── List ─────────────────────── */
121
+
122
+ interface NavigationMenuListProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List> {
123
+ /** `NavigationMenu.Item`s, and optionally a `NavigationMenu.Indicator`. */
124
+ children?: ReactNode
125
+ }
126
+
127
+ const NavigationMenuList = forwardRef<HTMLUListElement, NavigationMenuListProps>(
128
+ ({ className, children, ...props }, ref) => {
129
+ const { value, setList } = useRoot()
130
+ const containerRef = useRef<HTMLUListElement | null>(null)
131
+ const hover = useFluidHover(containerRef, { axis: 'x' })
132
+ const shape = useShape()
133
+ const items = useRegisterBySelector(containerRef, `[${TARGET}]`, hover.registerItem)
134
+
135
+ // The marker sits under the open item, else under the current page's item.
136
+ const [marked, setMarked] = useState(-1)
137
+ useLayoutEffect(() => {
138
+ const open = value === null ? -1 : items.findIndex(el => el.dataset.navigationMenuValue === value)
139
+ const next = open >= 0 ? open : items.findIndex(el => el.hasAttribute('data-active'))
140
+ setMarked(next)
141
+ })
142
+ const indicatorRect = hover.isMeasured && marked >= 0 ? hover.itemRects[marked] : undefined
143
+
144
+ const ctx = useMemo(() => ({ hover, indicatorRect }), [hover, indicatorRect])
145
+
146
+ return (
147
+ <ListContext.Provider value={ctx}>
148
+ <NavigationMenuPrimitive.List
149
+ ref={node => {
150
+ containerRef.current = node as HTMLUListElement | null
151
+ setList(node as HTMLElement | null)
152
+ assignRef(ref, node as HTMLUListElement | null)
153
+ }}
154
+ {...hover.handlers}
155
+ onFocus={event => {
156
+ const index = items.indexOf(
157
+ (event.target as HTMLElement).closest<HTMLElement>(`[${TARGET}]`) as HTMLElement,
158
+ )
159
+ if (index >= 0) hover.setActiveIndex(index)
160
+ }}
161
+ className={cn('relative m-0 flex list-none items-center p-0', className)}
162
+ {...props}
163
+ >
164
+ <FluidHoverHighlight hover={hover} className={shape.bg} />
165
+ {children}
166
+ </NavigationMenuPrimitive.List>
167
+ </ListContext.Provider>
168
+ )
169
+ },
170
+ )
171
+ NavigationMenuList.displayName = 'NavigationMenuList'
172
+
173
+ /* ─────────────────────── Indicator ─────────────────────── */
174
+
175
+ interface NavigationMenuIndicatorProps {
176
+ /** Classes for the marker: its fill. Defaults to the `bg-active` overlay. */
177
+ className?: string
178
+ }
179
+
180
+ /** A liquid marker under the open item, or the current page's item when nothing is open. */
181
+ function NavigationMenuIndicator({ className }: NavigationMenuIndicatorProps) {
182
+ const ctx = useContext(ListContext)
183
+ const shape = useShape()
184
+ const rect = ctx?.indicatorRect
185
+ // Rendered as a list item so the list stays valid markup.
186
+ return (
187
+ <li aria-hidden="true" className="contents">
188
+ {rect && <GooIndicator rect={rect} className={cn('bg-active', shape.bg, className)} />}
189
+ </li>
190
+ )
191
+ }
192
+
193
+ /* ─────────────────────── Item ─────────────────────── */
194
+
195
+ interface NavigationMenuItemProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item> {
196
+ /** Identifies the item for a controlled `value`. Defaults to a generated id. */
197
+ value?: string
198
+ }
199
+
200
+ const NavigationMenuItem = forwardRef<HTMLLIElement, NavigationMenuItemProps>(({ value, ...props }, ref) => {
201
+ const generated = useId()
202
+ const itemValue = value ?? generated
203
+ return (
204
+ <ItemContext.Provider value={itemValue}>
205
+ <NavigationMenuPrimitive.Item ref={ref} value={itemValue} {...props} />
206
+ </ItemContext.Provider>
207
+ )
208
+ })
209
+ NavigationMenuItem.displayName = 'NavigationMenuItem'
210
+
211
+ /* ─────────────────────── Strip label ─────────────────────── */
212
+
213
+ /** The strip's label: semibold while open or current, with an invisible semibold copy holding its width. */
214
+ function StripLabel({ emphasized, children }: { emphasized: boolean; children: ReactNode }) {
215
+ const size = useSize()
216
+ return (
217
+ <span className={cn('inline-grid whitespace-nowrap', size.text)}>
218
+ <span
219
+ aria-hidden="true"
220
+ className="invisible col-start-1 row-start-1 weight-semibold [text-box:trim-both_cap_alphabetic]"
221
+ >
222
+ {children}
223
+ </span>
224
+ <span
225
+ className={cn(
226
+ 'col-start-1 row-start-1 transition-[font-variation-settings] duration-fast [text-box:trim-both_cap_alphabetic]',
227
+ emphasized ? 'weight-semibold' : 'weight-normal',
228
+ )}
229
+ >
230
+ {children}
231
+ </span>
232
+ </span>
233
+ )
234
+ }
235
+
236
+ const STRIP_ITEM =
237
+ 'group/item relative z-10 flex cursor-pointer select-none items-center bg-transparent text-muted-foreground no-underline outline-none transition-[color] duration-fast focus-visible:ring-1 focus-visible:ring-focus-ring data-[fluid-hover-active]:text-foreground'
238
+
239
+ /* ─────────────────────── Trigger ─────────────────────── */
240
+
241
+ interface NavigationMenuTriggerProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> {
242
+ /** The trigger's label. */
243
+ children?: ReactNode
244
+ }
245
+
246
+ const NavigationMenuTrigger = forwardRef<HTMLButtonElement, NavigationMenuTriggerProps>(
247
+ ({ className, children, ...props }, ref) => {
248
+ const { value, triggers } = useRoot()
249
+ const itemValue = useContext(ItemContext)
250
+ const size = useSize()
251
+ const shape = useShape()
252
+ const reduced = useReduceMotion()
253
+ const Chevron = useIcon('chevron-down')
254
+ const open = value === itemValue
255
+
256
+ return (
257
+ <NavigationMenuPrimitive.Trigger
258
+ ref={node => {
259
+ if (node) triggers.current.set(itemValue, node)
260
+ else triggers.current.delete(itemValue)
261
+ assignRef(ref, node)
262
+ }}
263
+ {...{ [TARGET]: '', 'data-navigation-menu-value': itemValue }}
264
+ className={cn(STRIP_ITEM, size.control, size.px, size.gap, shape.button, open && 'text-foreground', className)}
265
+ {...props}
266
+ >
267
+ <StripLabel emphasized={open}>{children}</StripLabel>
268
+ <motion.span
269
+ aria-hidden="true"
270
+ className="flex"
271
+ initial={false}
272
+ animate={{ rotate: open ? 180 : 0 }}
273
+ transition={reduced ? { duration: 0 } : spring.moderate}
274
+ >
275
+ <Chevron
276
+ size={size.icon}
277
+ strokeWidth={1.5}
278
+ className={cn(
279
+ 'transition-[stroke-width] duration-fast group-[[data-fluid-hover-active]]/item:stroke-2',
280
+ open && 'stroke-2',
281
+ )}
282
+ />
283
+ </motion.span>
284
+ </NavigationMenuPrimitive.Trigger>
285
+ )
286
+ },
287
+ )
288
+ NavigationMenuTrigger.displayName = 'NavigationMenuTrigger'
289
+
290
+ /* ─────────────────────── Content ─────────────────────── */
291
+
292
+ interface NavigationMenuContentProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content> {
293
+ /** Classes for the panel's content: its width and layout (a grid of links, a feature column). */
294
+ className?: string
295
+ /** The panel: `NavigationMenu.Link`s and anything else. */
296
+ children?: ReactNode
297
+ }
298
+
299
+ const NavigationMenuContent = forwardRef<HTMLDivElement, NavigationMenuContentProps>(
300
+ ({ className, children, ...props }, ref) => {
301
+ const { value, setPanelSize } = useRoot()
302
+ const itemValue = useContext(ItemContext)
303
+ const open = value === itemValue
304
+ const reduced = useReduceMotion()
305
+ const shape = useShape()
306
+ const containerRef = useRef<HTMLDivElement | null>(null)
307
+ const [element, setElement] = useState<HTMLDivElement | null>(null)
308
+ const hover = useFluidHover(containerRef, { axis: 'xy' })
309
+ const rows = useRegisterBySelector(containerRef, `[${ROW}]`, hover.registerItem)
310
+ // Only keyboard focus earns the ring: Tab or an arrow key, or focus that arrived as :focus-visible.
311
+ const { keyboardNavRef, trackKeyboardNav } = useKeyboardNavGate(open)
312
+ const [focused, setFocused] = useState<number | null>(null)
313
+
314
+ // The open panel tells the shared frame its natural size, and keeps telling it as it changes.
315
+ useLayoutEffect(() => {
316
+ if (!open || !element) return
317
+ const report = () => setPanelSize({ width: element.offsetWidth, height: element.offsetHeight })
318
+ report()
319
+ const observer = new ResizeObserver(report)
320
+ observer.observe(element)
321
+ return () => observer.disconnect()
322
+ }, [open, element, setPanelSize])
323
+
324
+ const setRefs = useCallback(
325
+ (node: HTMLDivElement | null) => {
326
+ containerRef.current = node
327
+ setElement(node)
328
+ assignRef(ref, node)
329
+ },
330
+ [ref],
331
+ )
332
+
333
+ return (
334
+ <InContentContext.Provider value={true}>
335
+ <NavigationMenuPrimitive.Content
336
+ ref={setRefs}
337
+ {...hover.handlers}
338
+ onKeyDown={event => {
339
+ if (event.key === 'Tab') keyboardNavRef.current = true
340
+ trackKeyboardNav(event)
341
+ }}
342
+ onFocus={event => {
343
+ const target = event.target as HTMLElement
344
+ const index = rows.indexOf(target.closest<HTMLElement>(`[${ROW}]`) as HTMLElement)
345
+ if (index < 0) return
346
+ hover.setActiveIndex(index)
347
+ setFocused(keyboardNavRef.current || target.matches(':focus-visible') ? index : null)
348
+ }}
349
+ onBlur={event => {
350
+ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setFocused(null)
351
+ }}
352
+ className={cn(
353
+ 'relative w-max p-1 transition-[opacity,transform] duration-moderate data-[ending-style]:duration-moderate-exit data-[starting-style]:opacity-0 data-[ending-style]:opacity-0',
354
+ !reduced &&
355
+ 'data-[starting-style]:data-[activation-direction=right]:translate-x-4 data-[starting-style]:data-[activation-direction=left]:-translate-x-4 data-[ending-style]:data-[activation-direction=right]:-translate-x-4 data-[ending-style]:data-[activation-direction=left]:translate-x-4',
356
+ className,
357
+ )}
358
+ {...props}
359
+ >
360
+ <FluidHoverHighlight hover={hover} className={shape.bg} />
361
+ <FocusRing rect={open && focused !== null ? hover.itemRects[focused] : undefined} />
362
+ {children}
363
+ </NavigationMenuPrimitive.Content>
364
+ </InContentContext.Provider>
365
+ )
366
+ },
367
+ )
368
+ NavigationMenuContent.displayName = 'NavigationMenuContent'
369
+
370
+ /* ─────────────────────── Link ─────────────────────── */
371
+
372
+ interface NavigationMenuLinkProps extends ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Link> {
373
+ /** Marks the link as the current page (`aria-current`); in the strip, the indicator rests under it. Defaults to `false`. */
374
+ active?: boolean
375
+ /** Leading icon of a link inside a panel. */
376
+ icon?: IconComponent
377
+ /** A muted line under the title of a link inside a panel. */
378
+ description?: ReactNode
379
+ /** Close the panel when the link is pressed. Defaults to `false`. */
380
+ closeOnClick?: boolean
381
+ /** The link's title. */
382
+ children?: ReactNode
383
+ }
384
+
385
+ const NavigationMenuLink = forwardRef<HTMLAnchorElement, NavigationMenuLinkProps>(
386
+ ({ active = false, icon: Icon, description, className, children, ...props }, ref) => {
387
+ const inContent = useContext(InContentContext)
388
+ const size = useSize()
389
+ const shape = useShape()
390
+
391
+ if (!inContent) {
392
+ return (
393
+ <NavigationMenuPrimitive.Link
394
+ ref={ref}
395
+ active={active}
396
+ {...{ [TARGET]: '' }}
397
+ className={cn(STRIP_ITEM, size.control, size.px, shape.button, active && 'text-foreground', className)}
398
+ {...props}
399
+ >
400
+ <StripLabel emphasized={active}>{children}</StripLabel>
401
+ </NavigationMenuPrimitive.Link>
402
+ )
403
+ }
404
+
405
+ const compact = size.variant === 'compact'
406
+ return (
407
+ <NavigationMenuPrimitive.Link
408
+ ref={ref}
409
+ active={active}
410
+ {...{ [ROW]: '' }}
411
+ className={cn(
412
+ 'group/row relative z-10 flex min-w-0 cursor-pointer items-start no-underline outline-none',
413
+ description ? 'py-2' : cn(size.control, 'items-center'),
414
+ size.itemPx,
415
+ size.gap,
416
+ shape.item,
417
+ className,
418
+ )}
419
+ {...props}
420
+ >
421
+ {Icon && (
422
+ <Icon
423
+ size={size.icon}
424
+ strokeWidth={1.5}
425
+ className={cn(
426
+ 'shrink-0 text-muted-foreground transition-[color,stroke-width] duration-fast group-[[data-fluid-hover-active]]/row:stroke-2 group-[[data-fluid-hover-active]]/row:text-foreground',
427
+ description && 'mt-0.5',
428
+ )}
429
+ />
430
+ )}
431
+ <span className="flex min-w-0 flex-col gap-0.5">
432
+ <span className={cn('truncate text-foreground', active ? 'weight-semibold' : 'weight-normal', size.text)}>
433
+ {children}
434
+ </span>
435
+ {description && (
436
+ <span className={cn('text-muted-foreground', compact ? 'text-caption-compact' : 'text-caption')}>
437
+ {description}
438
+ </span>
439
+ )}
440
+ </span>
441
+ </NavigationMenuPrimitive.Link>
442
+ )
443
+ },
444
+ )
445
+ NavigationMenuLink.displayName = 'NavigationMenuLink'
446
+
447
+ /* ─────────────────────── Root ─────────────────────── */
448
+
449
+ interface NavigationMenuProps {
450
+ /** Controlled open item (its `value`), or `null` when closed. Pair with `onValueChange`. */
451
+ value?: string | null
452
+ /** Initially open item, for an uncontrolled menu. Defaults to `null`. */
453
+ defaultValue?: string | null
454
+ /** Called with the open item's value, or `null` as the menu closes. */
455
+ onValueChange?: (value: string | null) => void
456
+ /** Hover delay before a panel opens, in ms. Defaults to `50`. */
457
+ delay?: number
458
+ /** Delay before the panel closes once the pointer leaves, in ms. Defaults to `50`. */
459
+ closeDelay?: number
460
+ /** Alignment of the panel along the strip. Defaults to `'start'`. */
461
+ align?: 'start' | 'center' | 'end'
462
+ /** Gap between strip and panel in px — the length of the goo neck. Defaults to `12`. */
463
+ sideOffset?: number
464
+ /** Where the panel grows from (see Morph): the pressed trigger, the press point, its own center, a viewport edge, or a ref to any element. Defaults to `'trigger'`. */
465
+ from?: 'trigger' | 'pointer' | 'center' | 'top' | 'right' | 'bottom' | 'left' | RefObject<HTMLElement | null>
466
+ /** How it grows (see Morph): with the liquid goo neck, a plain morph, a slide or a fade. Defaults to `'goo'`. */
467
+ effect?: 'goo' | 'morph' | 'slide' | 'fade'
468
+ /** Hide the trigger while open, so it reads as turning into the panel. Defaults to `false`. */
469
+ hideSource?: boolean
470
+ /** Spring tier of a plain morph, slide or fade; goo runs on its own `spring.goo`. Defaults to `'slow'`. */
471
+ tier?: 'moderate' | 'slow'
472
+ /** Pins the strip and its panels to one step of the size ladder (36px default, 28px compact). Defaults to the surrounding SizeProvider. */
473
+ size?: SizeVariant
474
+ /** Classes for the `nav` root. */
475
+ className?: string
476
+ /** A `NavigationMenu.List`. */
477
+ children?: ReactNode
478
+ }
479
+
480
+ /**
481
+ * A site navigation bar whose items open flyout panels of links.
482
+ *
483
+ * The panel grows out of the trigger that opened it (see Morph) and closes
484
+ * back into it. Moving to another trigger keeps the one panel open: it
485
+ * springs to the new content's size while the content slides in from the
486
+ * side the pointer came from. One fluid hover highlight runs along the
487
+ * strip and another inside each panel, and `NavigationMenu.Indicator` melts
488
+ * from item to item under the open one, or under the current page's link.
489
+ * The panel lifts 2 surface levels off its substrate. Built on Base UI's
490
+ * NavigationMenu: panels open on hover or press, arrow keys move between
491
+ * items, Escape closes; controlled with `value` + `onValueChange`.
492
+ *
493
+ * Statics:
494
+ * - `NavigationMenu.List` — the strip.
495
+ * - `NavigationMenu.Item` — one entry: a Trigger + Content pair, or a Link;
496
+ * `value`.
497
+ * - `NavigationMenu.Trigger` — opens its item's panel; the chevron turns and
498
+ * the label turns semibold while open.
499
+ * - `NavigationMenu.Content` — the item's panel; size it with `className`.
500
+ * - `NavigationMenu.Link` — a link: in the strip, a top-level item; in a
501
+ * panel, a row with `icon` and `description`. `active` marks the current
502
+ * page; `closeOnClick`.
503
+ * - `NavigationMenu.Indicator` — the liquid marker; put it in the List.
504
+ *
505
+ * @example {@include ./examples.mdx}
506
+ */
507
+ function NavigationMenu({
508
+ value,
509
+ defaultValue = null,
510
+ onValueChange,
511
+ delay,
512
+ closeDelay,
513
+ align = 'start',
514
+ sideOffset = 12,
515
+ from,
516
+ effect,
517
+ hideSource,
518
+ tier,
519
+ size,
520
+ className,
521
+ children,
522
+ }: NavigationMenuProps) {
523
+ const [current, setCurrent] = useControllableState<string | null>(value, defaultValue, onValueChange)
524
+ const { origin, capture } = useMorphOrigin()
525
+ const triggers = useRef(new Map<string, HTMLElement>())
526
+ const [list, setList] = useState<HTMLElement | null>(null)
527
+ const [panelSize, setPanelSize] = useState<PanelSize | null>(null)
528
+ const open = current !== null
529
+ const morph = useMorph(open, origin, { from, effect, hideSource, tier })
530
+ const reduced = useReduceMotion()
531
+ const shape = useShape()
532
+ const level = Math.min(useSurface() + 2, 8)
533
+
534
+ // A fresh open measures the panel at its natural size.
535
+ if (!open && panelSize) setPanelSize(null)
536
+
537
+ const ctx = useMemo(() => ({ value: current, triggers, setList, setPanelSize }), [current])
538
+
539
+ const root = (
540
+ <RootContext.Provider value={ctx}>
541
+ <NavigationMenuPrimitive.Root
542
+ value={current}
543
+ onValueChange={(next, details) => {
544
+ const nextValue = (next as string | null) ?? null
545
+ // Every switch re-points the origin, so the panel closes into the trigger that is open.
546
+ if (nextValue !== null) capture({ trigger: triggers.current.get(nextValue), event: details.event })
547
+ setCurrent(nextValue)
548
+ }}
549
+ delay={delay}
550
+ closeDelay={closeDelay}
551
+ className={cn('relative', className)}
552
+ >
553
+ {children}
554
+ <NavigationMenuPrimitive.Portal>
555
+ <NavigationMenuPrimitive.Positioner
556
+ anchor={list}
557
+ side="bottom"
558
+ align={align}
559
+ sideOffset={sideOffset}
560
+ collisionPadding={8}
561
+ className="z-50 outline-none"
562
+ >
563
+ <NavigationMenuPrimitive.Popup ref={morph.popupRef} className="relative outline-none">
564
+ <SurfaceProvider value={level}>
565
+ <MorphSurface morph={morph} bg={SURFACE_BG[level]} shadow={SURFACE_SHADOW[3]} radius={shape.container}>
566
+ <motion.div
567
+ className="relative overflow-hidden"
568
+ initial={false}
569
+ animate={panelSize ? { width: panelSize.width, height: panelSize.height } : undefined}
570
+ transition={reduced ? { duration: 0 } : spring.moderate}
571
+ >
572
+ <NavigationMenuPrimitive.Viewport className="relative" />
573
+ </motion.div>
574
+ </MorphSurface>
575
+ </SurfaceProvider>
576
+ </NavigationMenuPrimitive.Popup>
577
+ </NavigationMenuPrimitive.Positioner>
578
+ </NavigationMenuPrimitive.Portal>
579
+ </NavigationMenuPrimitive.Root>
580
+ </RootContext.Provider>
581
+ )
582
+
583
+ // A size prop pins the strip and its panels to one ladder step.
584
+ return size ? <SizeProvider size={size}>{root}</SizeProvider> : root
585
+ }
586
+
587
+ NavigationMenu.List = NavigationMenuList
588
+ NavigationMenu.Item = NavigationMenuItem
589
+ NavigationMenu.Trigger = NavigationMenuTrigger
590
+ NavigationMenu.Content = NavigationMenuContent
591
+ NavigationMenu.Link = NavigationMenuLink
592
+ NavigationMenu.Indicator = NavigationMenuIndicator
593
+
594
+ export type {
595
+ NavigationMenuContentProps,
596
+ NavigationMenuIndicatorProps,
597
+ NavigationMenuItemProps,
598
+ NavigationMenuLinkProps,
599
+ NavigationMenuListProps,
600
+ NavigationMenuProps,
601
+ NavigationMenuTriggerProps,
602
+ }
603
+ export {
604
+ NavigationMenu,
605
+ NavigationMenuContent,
606
+ NavigationMenuIndicator,
607
+ NavigationMenuItem,
608
+ NavigationMenuLink,
609
+ NavigationMenuList,
610
+ NavigationMenuTrigger,
611
+ }
612
+
613
+ export default NavigationMenu
@@ -0,0 +1,64 @@
1
+ import Pagination from './index'
2
+
3
+ # Pages
4
+
5
+ The current page melts from page to page, and the hover glides along the strip. A click between two pages lands on the lit one.
6
+
7
+ <Pagination pageCount={12} defaultPage={5} />
8
+
9
+ # Few pages
10
+
11
+ With nothing to skip, every page shows.
12
+
13
+ <Pagination pageCount={5} defaultPage={2} />
14
+
15
+ # First and last
16
+
17
+ `edges` adds buttons to the ends, for long runs of pages.
18
+
19
+ <Pagination pageCount={40} defaultPage={18} edges />
20
+
21
+ # Jump to a page
22
+
23
+ `variant="input"` swaps the strip for a field: type a page and press Enter. First and last come with it.
24
+
25
+ <Pagination pageCount={24} defaultPage={3} variant="input" />
26
+
27
+ # Page size
28
+
29
+ Setting `defaultPageSize` (or `pageSize`) adds a Select of `pageSizes`, the layout a data table footer uses.
30
+
31
+ <Pagination pageCount={8} variant="input" defaultPageSize={20} />
32
+
33
+ # Translated labels
34
+
35
+ Every label is a prop: the buttons' names, the field's name and the page-size caption.
36
+
37
+ <Pagination
38
+ pageCount={8}
39
+ defaultPage={2}
40
+ variant="input"
41
+ defaultPageSize={10}
42
+ label="Stránkovanie"
43
+ firstLabel="Prvá strana"
44
+ previousLabel="Predchádzajúca strana"
45
+ nextLabel="Ďalšia strana"
46
+ lastLabel="Posledná strana"
47
+ pageLabel="Strana"
48
+ pageSizeLabel="Riadkov na stranu"
49
+ />
50
+
51
+ # Compact
52
+
53
+ The strip steps down the size ladder with everything around it, the page-size menu included.
54
+
55
+ <div className="flex flex-col gap-4">
56
+ <Pagination pageCount={20} defaultPage={10} size="compact" />
57
+ <Pagination pageCount={20} defaultPage={10} variant="input" defaultPageSize={10} size="compact" />
58
+ </div>
59
+
60
+ # Disabled
61
+
62
+ `disabled` holds every control, e.g. while the next page loads.
63
+
64
+ <Pagination pageCount={12} defaultPage={4} disabled />