@spatika/react 1.0.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 (406) hide show
  1. package/README.md +72 -0
  2. package/dist/composites/ActionBar.d.ts +7 -0
  3. package/dist/composites/ActionBar.d.ts.map +1 -0
  4. package/dist/composites/ActionBar.js +5 -0
  5. package/dist/composites/AmountInput.d.ts +10 -0
  6. package/dist/composites/AmountInput.d.ts.map +1 -0
  7. package/dist/composites/AmountInput.js +7 -0
  8. package/dist/composites/AppHeader.d.ts +25 -0
  9. package/dist/composites/AppHeader.d.ts.map +1 -0
  10. package/dist/composites/AppHeader.js +13 -0
  11. package/dist/composites/AppSidebar.d.ts +30 -0
  12. package/dist/composites/AppSidebar.d.ts.map +1 -0
  13. package/dist/composites/AppSidebar.js +15 -0
  14. package/dist/composites/BottomSheet.d.ts +36 -0
  15. package/dist/composites/BottomSheet.d.ts.map +1 -0
  16. package/dist/composites/BottomSheet.js +37 -0
  17. package/dist/composites/Breadcrumb.d.ts +10 -0
  18. package/dist/composites/Breadcrumb.d.ts.map +1 -0
  19. package/dist/composites/Breadcrumb.js +10 -0
  20. package/dist/composites/Callout.d.ts +8 -0
  21. package/dist/composites/Callout.d.ts.map +1 -0
  22. package/dist/composites/Callout.js +5 -0
  23. package/dist/composites/Card.d.ts +31 -0
  24. package/dist/composites/Card.d.ts.map +1 -0
  25. package/dist/composites/Card.js +33 -0
  26. package/dist/composites/Chip.d.ts +13 -0
  27. package/dist/composites/Chip.d.ts.map +1 -0
  28. package/dist/composites/Chip.js +59 -0
  29. package/dist/composites/ChipGroup.d.ts +23 -0
  30. package/dist/composites/ChipGroup.d.ts.map +1 -0
  31. package/dist/composites/ChipGroup.js +27 -0
  32. package/dist/composites/CommandSearchField.d.ts +27 -0
  33. package/dist/composites/CommandSearchField.d.ts.map +1 -0
  34. package/dist/composites/CommandSearchField.js +17 -0
  35. package/dist/composites/CoverHero.d.ts +51 -0
  36. package/dist/composites/CoverHero.d.ts.map +1 -0
  37. package/dist/composites/CoverHero.js +71 -0
  38. package/dist/composites/CoverPattern.d.ts +12 -0
  39. package/dist/composites/CoverPattern.d.ts.map +1 -0
  40. package/dist/composites/CoverPattern.js +14 -0
  41. package/dist/composites/DataTableToolbar.d.ts +13 -0
  42. package/dist/composites/DataTableToolbar.d.ts.map +1 -0
  43. package/dist/composites/DataTableToolbar.js +7 -0
  44. package/dist/composites/DividerLabel.d.ts +5 -0
  45. package/dist/composites/DividerLabel.d.ts.map +1 -0
  46. package/dist/composites/DividerLabel.js +5 -0
  47. package/dist/composites/EmptyState.d.ts +16 -0
  48. package/dist/composites/EmptyState.d.ts.map +1 -0
  49. package/dist/composites/EmptyState.js +10 -0
  50. package/dist/composites/EntityCard.d.ts +31 -0
  51. package/dist/composites/EntityCard.d.ts.map +1 -0
  52. package/dist/composites/EntityCard.js +21 -0
  53. package/dist/composites/EntityMediaCard.d.ts +25 -0
  54. package/dist/composites/EntityMediaCard.d.ts.map +1 -0
  55. package/dist/composites/EntityMediaCard.js +17 -0
  56. package/dist/composites/FilterSheet.d.ts +34 -0
  57. package/dist/composites/FilterSheet.d.ts.map +1 -0
  58. package/dist/composites/FilterSheet.js +36 -0
  59. package/dist/composites/FloatingPageChrome.d.ts +90 -0
  60. package/dist/composites/FloatingPageChrome.d.ts.map +1 -0
  61. package/dist/composites/FloatingPageChrome.js +67 -0
  62. package/dist/composites/FormField.d.ts +15 -0
  63. package/dist/composites/FormField.d.ts.map +1 -0
  64. package/dist/composites/FormField.js +9 -0
  65. package/dist/composites/GlassCard.d.ts +13 -0
  66. package/dist/composites/GlassCard.d.ts.map +1 -0
  67. package/dist/composites/GlassCard.js +32 -0
  68. package/dist/composites/HeaderIconButton.d.ts +13 -0
  69. package/dist/composites/HeaderIconButton.d.ts.map +1 -0
  70. package/dist/composites/HeaderIconButton.js +12 -0
  71. package/dist/composites/ListRow.d.ts +9 -0
  72. package/dist/composites/ListRow.d.ts.map +1 -0
  73. package/dist/composites/ListRow.js +6 -0
  74. package/dist/composites/MediaEntryCard.d.ts +25 -0
  75. package/dist/composites/MediaEntryCard.d.ts.map +1 -0
  76. package/dist/composites/MediaEntryCard.js +10 -0
  77. package/dist/composites/MobileTabBar.d.ts +33 -0
  78. package/dist/composites/MobileTabBar.d.ts.map +1 -0
  79. package/dist/composites/MobileTabBar.js +19 -0
  80. package/dist/composites/NotificationBell.d.ts +28 -0
  81. package/dist/composites/NotificationBell.d.ts.map +1 -0
  82. package/dist/composites/NotificationBell.js +25 -0
  83. package/dist/composites/PageHeader.d.ts +11 -0
  84. package/dist/composites/PageHeader.d.ts.map +1 -0
  85. package/dist/composites/PageHeader.js +6 -0
  86. package/dist/composites/PageShell.d.ts +12 -0
  87. package/dist/composites/PageShell.d.ts.map +1 -0
  88. package/dist/composites/PageShell.js +8 -0
  89. package/dist/composites/PageStickyHeader.d.ts +33 -0
  90. package/dist/composites/PageStickyHeader.d.ts.map +1 -0
  91. package/dist/composites/PageStickyHeader.js +19 -0
  92. package/dist/composites/Pagination.d.ts +7 -0
  93. package/dist/composites/Pagination.d.ts.map +1 -0
  94. package/dist/composites/Pagination.js +6 -0
  95. package/dist/composites/PhotoViewer.d.ts +18 -0
  96. package/dist/composites/PhotoViewer.d.ts.map +1 -0
  97. package/dist/composites/PhotoViewer.js +37 -0
  98. package/dist/composites/PinPad.d.ts +29 -0
  99. package/dist/composites/PinPad.d.ts.map +1 -0
  100. package/dist/composites/PinPad.js +58 -0
  101. package/dist/composites/ProfileHero.d.ts +34 -0
  102. package/dist/composites/ProfileHero.d.ts.map +1 -0
  103. package/dist/composites/ProfileHero.js +21 -0
  104. package/dist/composites/QuickFilterRail.d.ts +25 -0
  105. package/dist/composites/QuickFilterRail.d.ts.map +1 -0
  106. package/dist/composites/QuickFilterRail.js +18 -0
  107. package/dist/composites/QuickSettings.d.ts +27 -0
  108. package/dist/composites/QuickSettings.d.ts.map +1 -0
  109. package/dist/composites/QuickSettings.js +18 -0
  110. package/dist/composites/RichTextEditor.d.ts +13 -0
  111. package/dist/composites/RichTextEditor.d.ts.map +1 -0
  112. package/dist/composites/RichTextEditor.js +23 -0
  113. package/dist/composites/SearchField.d.ts +8 -0
  114. package/dist/composites/SearchField.d.ts.map +1 -0
  115. package/dist/composites/SearchField.js +8 -0
  116. package/dist/composites/SectionPanel.d.ts +16 -0
  117. package/dist/composites/SectionPanel.d.ts.map +1 -0
  118. package/dist/composites/SectionPanel.js +8 -0
  119. package/dist/composites/SegmentedControl.d.ts +17 -0
  120. package/dist/composites/SegmentedControl.d.ts.map +1 -0
  121. package/dist/composites/SegmentedControl.js +11 -0
  122. package/dist/composites/StatCard.d.ts +11 -0
  123. package/dist/composites/StatCard.d.ts.map +1 -0
  124. package/dist/composites/StatCard.js +6 -0
  125. package/dist/composites/StatusDot.d.ts +11 -0
  126. package/dist/composites/StatusDot.d.ts.map +1 -0
  127. package/dist/composites/StatusDot.js +23 -0
  128. package/dist/composites/SurfaceCard.d.ts +12 -0
  129. package/dist/composites/SurfaceCard.d.ts.map +1 -0
  130. package/dist/composites/SurfaceCard.js +24 -0
  131. package/dist/composites/Timeline.d.ts +19 -0
  132. package/dist/composites/Timeline.d.ts.map +1 -0
  133. package/dist/composites/Timeline.js +12 -0
  134. package/dist/composites/Toast.d.ts +26 -0
  135. package/dist/composites/Toast.d.ts.map +1 -0
  136. package/dist/composites/Toast.js +49 -0
  137. package/dist/composites/Toolbar.d.ts +6 -0
  138. package/dist/composites/Toolbar.d.ts.map +1 -0
  139. package/dist/composites/Toolbar.js +5 -0
  140. package/dist/composites/ViewFiltersPanel.d.ts +21 -0
  141. package/dist/composites/ViewFiltersPanel.d.ts.map +1 -0
  142. package/dist/composites/ViewFiltersPanel.js +11 -0
  143. package/dist/index.d.ts +96 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +95 -0
  146. package/dist/lib/animations.d.ts +62 -0
  147. package/dist/lib/animations.d.ts.map +1 -0
  148. package/dist/lib/animations.js +37 -0
  149. package/dist/lib/avatar-utils.d.ts +10 -0
  150. package/dist/lib/avatar-utils.d.ts.map +1 -0
  151. package/dist/lib/avatar-utils.js +49 -0
  152. package/dist/lib/chips.d.ts +10 -0
  153. package/dist/lib/chips.d.ts.map +1 -0
  154. package/dist/lib/chips.js +30 -0
  155. package/dist/lib/cn.d.ts +4 -0
  156. package/dist/lib/cn.d.ts.map +1 -0
  157. package/dist/lib/cn.js +6 -0
  158. package/dist/lib/compose-refs.d.ts +6 -0
  159. package/dist/lib/compose-refs.d.ts.map +1 -0
  160. package/dist/lib/compose-refs.js +17 -0
  161. package/dist/lib/cover-chrome.d.ts +9 -0
  162. package/dist/lib/cover-chrome.d.ts.map +1 -0
  163. package/dist/lib/cover-chrome.js +23 -0
  164. package/dist/lib/cover-pattern.d.ts +75 -0
  165. package/dist/lib/cover-pattern.d.ts.map +1 -0
  166. package/dist/lib/cover-pattern.js +135 -0
  167. package/dist/lib/overlay-stack.d.ts +23 -0
  168. package/dist/lib/overlay-stack.d.ts.map +1 -0
  169. package/dist/lib/overlay-stack.js +37 -0
  170. package/dist/lib/portal.d.ts +12 -0
  171. package/dist/lib/portal.d.ts.map +1 -0
  172. package/dist/lib/portal.js +12 -0
  173. package/dist/lib/slot.d.ts +12 -0
  174. package/dist/lib/slot.d.ts.map +1 -0
  175. package/dist/lib/slot.js +55 -0
  176. package/dist/lib/surfaces.d.ts +14 -0
  177. package/dist/lib/surfaces.d.ts.map +1 -0
  178. package/dist/lib/surfaces.js +18 -0
  179. package/dist/lib/themes.d.ts +16 -0
  180. package/dist/lib/themes.d.ts.map +1 -0
  181. package/dist/lib/themes.js +53 -0
  182. package/dist/lib/typography.d.ts +17 -0
  183. package/dist/lib/typography.d.ts.map +1 -0
  184. package/dist/lib/typography.js +16 -0
  185. package/dist/lib/use-controllable-state.d.ts +12 -0
  186. package/dist/lib/use-controllable-state.d.ts.map +1 -0
  187. package/dist/lib/use-controllable-state.js +20 -0
  188. package/dist/lib/use-cover-chrome-bleed.d.ts +9 -0
  189. package/dist/lib/use-cover-chrome-bleed.d.ts.map +1 -0
  190. package/dist/lib/use-cover-chrome-bleed.js +28 -0
  191. package/dist/lib/use-dismissable.d.ts +13 -0
  192. package/dist/lib/use-dismissable.d.ts.map +1 -0
  193. package/dist/lib/use-dismissable.js +33 -0
  194. package/dist/lib/use-floating-position.d.ts +27 -0
  195. package/dist/lib/use-floating-position.d.ts.map +1 -0
  196. package/dist/lib/use-floating-position.js +115 -0
  197. package/dist/lib/use-focus-trap.d.ts +7 -0
  198. package/dist/lib/use-focus-trap.d.ts.map +1 -0
  199. package/dist/lib/use-focus-trap.js +61 -0
  200. package/dist/lib/use-media-query.d.ts +5 -0
  201. package/dist/lib/use-media-query.d.ts.map +1 -0
  202. package/dist/lib/use-media-query.js +19 -0
  203. package/dist/primitives/Accordion.d.ts +24 -0
  204. package/dist/primitives/Accordion.d.ts.map +1 -0
  205. package/dist/primitives/Accordion.js +37 -0
  206. package/dist/primitives/Alert.d.ts +10 -0
  207. package/dist/primitives/Alert.d.ts.map +1 -0
  208. package/dist/primitives/Alert.js +25 -0
  209. package/dist/primitives/AlertDialog.d.ts +33 -0
  210. package/dist/primitives/AlertDialog.d.ts.map +1 -0
  211. package/dist/primitives/AlertDialog.js +96 -0
  212. package/dist/primitives/Avatar.d.ts +15 -0
  213. package/dist/primitives/Avatar.d.ts.map +1 -0
  214. package/dist/primitives/Avatar.js +52 -0
  215. package/dist/primitives/Badge.d.ts +12 -0
  216. package/dist/primitives/Badge.d.ts.map +1 -0
  217. package/dist/primitives/Badge.js +27 -0
  218. package/dist/primitives/Button.d.ts +14 -0
  219. package/dist/primitives/Button.d.ts.map +1 -0
  220. package/dist/primitives/Button.js +37 -0
  221. package/dist/primitives/Checkbox.d.ts +9 -0
  222. package/dist/primitives/Checkbox.d.ts.map +1 -0
  223. package/dist/primitives/Checkbox.js +22 -0
  224. package/dist/primitives/Command.d.ts +17 -0
  225. package/dist/primitives/Command.d.ts.map +1 -0
  226. package/dist/primitives/Command.js +33 -0
  227. package/dist/primitives/Dialog.d.ts +34 -0
  228. package/dist/primitives/Dialog.d.ts.map +1 -0
  229. package/dist/primitives/Dialog.js +96 -0
  230. package/dist/primitives/DropdownMenu.d.ts +65 -0
  231. package/dist/primitives/DropdownMenu.d.ts.map +1 -0
  232. package/dist/primitives/DropdownMenu.js +196 -0
  233. package/dist/primitives/Input.d.ts +4 -0
  234. package/dist/primitives/Input.d.ts.map +1 -0
  235. package/dist/primitives/Input.js +6 -0
  236. package/dist/primitives/Kbd.d.ts +4 -0
  237. package/dist/primitives/Kbd.d.ts.map +1 -0
  238. package/dist/primitives/Kbd.js +6 -0
  239. package/dist/primitives/Label.d.ts +4 -0
  240. package/dist/primitives/Label.d.ts.map +1 -0
  241. package/dist/primitives/Label.js +6 -0
  242. package/dist/primitives/NativeSelect.d.ts +7 -0
  243. package/dist/primitives/NativeSelect.d.ts.map +1 -0
  244. package/dist/primitives/NativeSelect.js +10 -0
  245. package/dist/primitives/Popover.d.ts +29 -0
  246. package/dist/primitives/Popover.d.ts.map +1 -0
  247. package/dist/primitives/Popover.js +95 -0
  248. package/dist/primitives/Progress.d.ts +8 -0
  249. package/dist/primitives/Progress.d.ts.map +1 -0
  250. package/dist/primitives/Progress.js +7 -0
  251. package/dist/primitives/RadioGroup.d.ts +13 -0
  252. package/dist/primitives/RadioGroup.d.ts.map +1 -0
  253. package/dist/primitives/RadioGroup.js +33 -0
  254. package/dist/primitives/Select.d.ts +33 -0
  255. package/dist/primitives/Select.d.ts.map +1 -0
  256. package/dist/primitives/Select.js +178 -0
  257. package/dist/primitives/Separator.d.ts +7 -0
  258. package/dist/primitives/Separator.d.ts.map +1 -0
  259. package/dist/primitives/Separator.js +6 -0
  260. package/dist/primitives/Sheet.d.ts +28 -0
  261. package/dist/primitives/Sheet.d.ts.map +1 -0
  262. package/dist/primitives/Sheet.js +96 -0
  263. package/dist/primitives/Skeleton.d.ts +4 -0
  264. package/dist/primitives/Skeleton.d.ts.map +1 -0
  265. package/dist/primitives/Skeleton.js +6 -0
  266. package/dist/primitives/Slider.d.ts +12 -0
  267. package/dist/primitives/Slider.d.ts.map +1 -0
  268. package/dist/primitives/Slider.js +23 -0
  269. package/dist/primitives/Spinner.d.ts +6 -0
  270. package/dist/primitives/Spinner.d.ts.map +1 -0
  271. package/dist/primitives/Spinner.js +7 -0
  272. package/dist/primitives/StarRating.d.ts +10 -0
  273. package/dist/primitives/StarRating.d.ts.map +1 -0
  274. package/dist/primitives/StarRating.js +11 -0
  275. package/dist/primitives/Switch.d.ts +8 -0
  276. package/dist/primitives/Switch.d.ts.map +1 -0
  277. package/dist/primitives/Switch.js +20 -0
  278. package/dist/primitives/Table.d.ts +13 -0
  279. package/dist/primitives/Table.d.ts.map +1 -0
  280. package/dist/primitives/Table.js +20 -0
  281. package/dist/primitives/Tabs.d.ts +17 -0
  282. package/dist/primitives/Tabs.d.ts.map +1 -0
  283. package/dist/primitives/Tabs.js +30 -0
  284. package/dist/primitives/Textarea.d.ts +4 -0
  285. package/dist/primitives/Textarea.d.ts.map +1 -0
  286. package/dist/primitives/Textarea.js +6 -0
  287. package/dist/primitives/Tooltip.d.ts +31 -0
  288. package/dist/primitives/Tooltip.d.ts.map +1 -0
  289. package/dist/primitives/Tooltip.js +94 -0
  290. package/dist/theme/OriginThemeProvider.d.ts +31 -0
  291. package/dist/theme/OriginThemeProvider.d.ts.map +1 -0
  292. package/dist/theme/OriginThemeProvider.js +37 -0
  293. package/dist/theme/SpatikaThemeProvider.d.ts +36 -0
  294. package/dist/theme/SpatikaThemeProvider.d.ts.map +1 -0
  295. package/dist/theme/SpatikaThemeProvider.js +38 -0
  296. package/package.json +58 -0
  297. package/src/composites/ActionBar.tsx +25 -0
  298. package/src/composites/AmountInput.tsx +40 -0
  299. package/src/composites/AppHeader.tsx +88 -0
  300. package/src/composites/AppSidebar.tsx +120 -0
  301. package/src/composites/BottomSheet.tsx +179 -0
  302. package/src/composites/Breadcrumb.tsx +42 -0
  303. package/src/composites/Callout.tsx +22 -0
  304. package/src/composites/Card.tsx +119 -0
  305. package/src/composites/Chip.tsx +87 -0
  306. package/src/composites/ChipGroup.component.test.tsx +31 -0
  307. package/src/composites/ChipGroup.test.ts +16 -0
  308. package/src/composites/ChipGroup.tsx +86 -0
  309. package/src/composites/CommandSearchField.tsx +124 -0
  310. package/src/composites/CoverHero.tsx +359 -0
  311. package/src/composites/CoverPattern.tsx +90 -0
  312. package/src/composites/DataTableToolbar.tsx +48 -0
  313. package/src/composites/DividerLabel.tsx +22 -0
  314. package/src/composites/EmptyState.tsx +55 -0
  315. package/src/composites/EntityCard.tsx +100 -0
  316. package/src/composites/EntityMediaCard.tsx +118 -0
  317. package/src/composites/FilterSheet.tsx +178 -0
  318. package/src/composites/FloatingPageChrome.tsx +294 -0
  319. package/src/composites/FormField.tsx +44 -0
  320. package/src/composites/GlassCard.tsx +47 -0
  321. package/src/composites/HeaderIconButton.tsx +49 -0
  322. package/src/composites/ListRow.tsx +29 -0
  323. package/src/composites/MediaEntryCard.tsx +95 -0
  324. package/src/composites/MobileTabBar.tsx +130 -0
  325. package/src/composites/NotificationBell.tsx +164 -0
  326. package/src/composites/PageHeader.tsx +27 -0
  327. package/src/composites/PageShell.tsx +20 -0
  328. package/src/composites/PageStickyHeader.tsx +157 -0
  329. package/src/composites/Pagination.tsx +41 -0
  330. package/src/composites/PhotoViewer.tsx +142 -0
  331. package/src/composites/PinPad.tsx +163 -0
  332. package/src/composites/ProfileHero.tsx +163 -0
  333. package/src/composites/QuickFilterRail.tsx +92 -0
  334. package/src/composites/QuickSettings.tsx +120 -0
  335. package/src/composites/RichTextEditor.tsx +103 -0
  336. package/src/composites/SearchField.tsx +38 -0
  337. package/src/composites/SectionPanel.tsx +59 -0
  338. package/src/composites/SegmentedControl.tsx +62 -0
  339. package/src/composites/StatCard.tsx +26 -0
  340. package/src/composites/StatusDot.tsx +40 -0
  341. package/src/composites/SurfaceCard.tsx +44 -0
  342. package/src/composites/Timeline.tsx +70 -0
  343. package/src/composites/Toast.tsx +135 -0
  344. package/src/composites/Toolbar.tsx +19 -0
  345. package/src/composites/ViewFiltersPanel.tsx +82 -0
  346. package/src/composites/chrome.test.tsx +170 -0
  347. package/src/composites/smoke.test.tsx +49 -0
  348. package/src/index.ts +355 -0
  349. package/src/lib/animations.ts +47 -0
  350. package/src/lib/avatar-utils.ts +52 -0
  351. package/src/lib/chips.ts +58 -0
  352. package/src/lib/cn.test.ts +16 -0
  353. package/src/lib/cn.ts +7 -0
  354. package/src/lib/compose-refs.ts +21 -0
  355. package/src/lib/cover-chrome.ts +31 -0
  356. package/src/lib/cover-pattern.ts +242 -0
  357. package/src/lib/overlay-stack.ts +44 -0
  358. package/src/lib/portal.tsx +21 -0
  359. package/src/lib/slot.tsx +69 -0
  360. package/src/lib/surfaces.ts +57 -0
  361. package/src/lib/themes.test.ts +73 -0
  362. package/src/lib/themes.ts +59 -0
  363. package/src/lib/tokens.test.ts +52 -0
  364. package/src/lib/typography.ts +27 -0
  365. package/src/lib/use-controllable-state.ts +38 -0
  366. package/src/lib/use-cover-chrome-bleed.ts +33 -0
  367. package/src/lib/use-dismissable.ts +49 -0
  368. package/src/lib/use-floating-position.ts +168 -0
  369. package/src/lib/use-focus-trap.ts +73 -0
  370. package/src/lib/use-media-query.ts +22 -0
  371. package/src/primitives/Accordion.tsx +114 -0
  372. package/src/primitives/Alert.tsx +57 -0
  373. package/src/primitives/AlertDialog.tsx +270 -0
  374. package/src/primitives/Avatar.tsx +140 -0
  375. package/src/primitives/Badge.test.tsx +15 -0
  376. package/src/primitives/Badge.tsx +43 -0
  377. package/src/primitives/Button.test.tsx +37 -0
  378. package/src/primitives/Button.tsx +58 -0
  379. package/src/primitives/Checkbox.tsx +75 -0
  380. package/src/primitives/Command.tsx +175 -0
  381. package/src/primitives/Dialog.tsx +241 -0
  382. package/src/primitives/DropdownMenu.tsx +552 -0
  383. package/src/primitives/Input.test.tsx +22 -0
  384. package/src/primitives/Input.tsx +22 -0
  385. package/src/primitives/Kbd.tsx +17 -0
  386. package/src/primitives/Label.tsx +20 -0
  387. package/src/primitives/NativeSelect.tsx +22 -0
  388. package/src/primitives/Popover.tsx +209 -0
  389. package/src/primitives/Progress.tsx +31 -0
  390. package/src/primitives/RadioGroup.tsx +116 -0
  391. package/src/primitives/Select.tsx +409 -0
  392. package/src/primitives/Separator.tsx +30 -0
  393. package/src/primitives/Sheet.tsx +265 -0
  394. package/src/primitives/Skeleton.tsx +16 -0
  395. package/src/primitives/Slider.tsx +97 -0
  396. package/src/primitives/Spinner.tsx +21 -0
  397. package/src/primitives/StarRating.tsx +53 -0
  398. package/src/primitives/Switch.tsx +68 -0
  399. package/src/primitives/Table.tsx +134 -0
  400. package/src/primitives/Tabs.tsx +97 -0
  401. package/src/primitives/Textarea.tsx +20 -0
  402. package/src/primitives/Tooltip.tsx +193 -0
  403. package/src/primitives/smoke.test.tsx +34 -0
  404. package/src/test/setup.ts +1 -0
  405. package/src/theme/SpatikaThemeProvider.test.tsx +82 -0
  406. package/src/theme/SpatikaThemeProvider.tsx +112 -0
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState, } from "react";
3
+ import { applyTheme, DEFAULT_THEME, readStoredTheme, THEME_IDS, writeStoredTheme, } from "../lib/themes";
4
+ const SpatikaThemeContext = createContext(null);
5
+ /**
6
+ * MUI-style theme provider for Spatika.
7
+ * Wraps the app (or a subtree) and applies theme classes / data attributes.
8
+ */
9
+ export function SpatikaThemeProvider({ children, theme: themeProp, defaultTheme = DEFAULT_THEME, storageKey = false, onThemeChange, syncDocument = true, target, className, }) {
10
+ const [uncontrolled, setUncontrolled] = useState(() => readStoredTheme(storageKey) ?? defaultTheme);
11
+ const theme = themeProp ?? uncontrolled;
12
+ const setTheme = useCallback((next) => {
13
+ if (themeProp === undefined)
14
+ setUncontrolled(next);
15
+ onThemeChange?.(next);
16
+ }, [onThemeChange, themeProp]);
17
+ useLayoutEffect(() => {
18
+ if (syncDocument)
19
+ applyTheme(theme);
20
+ if (target)
21
+ applyTheme(theme, target);
22
+ writeStoredTheme(storageKey, theme);
23
+ }, [theme, syncDocument, target, storageKey]);
24
+ const value = useMemo(() => ({
25
+ theme,
26
+ setTheme,
27
+ themes: THEME_IDS,
28
+ }), [theme, setTheme]);
29
+ return (_jsx(SpatikaThemeContext.Provider, { value: value, children: _jsx("div", { className: className, "data-spk-theme": theme, "data-slot": "spatika-theme-provider", children: children }) }));
30
+ }
31
+ /** Access current theme and setter from `SpatikaThemeProvider`. */
32
+ export function useSpatikaTheme() {
33
+ const ctx = useContext(SpatikaThemeContext);
34
+ if (!ctx) {
35
+ throw new Error("useSpatikaTheme must be used within <SpatikaThemeProvider>");
36
+ }
37
+ return ctx;
38
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@spatika/react",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "description": "Spatika React primitives and composites",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "test:coverage": "vitest run --coverage"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "peerDependencies": {
33
+ "react": "^18.2.0 || ^19.0.0",
34
+ "react-dom": "^18.2.0 || ^19.0.0"
35
+ },
36
+ "dependencies": {
37
+ "@spatika/tokens": "1.0.0",
38
+ "class-variance-authority": "^0.7.1",
39
+ "clsx": "^2.1.1",
40
+ "cmdk": "^1.1.1",
41
+ "lucide-react": "^0.487.0",
42
+ "tailwind-merge": "^3.2.0",
43
+ "vaul": "^1.1.2"
44
+ },
45
+ "devDependencies": {
46
+ "@testing-library/jest-dom": "^6.9.1",
47
+ "@testing-library/react": "^16.3.2",
48
+ "@testing-library/user-event": "^14.6.1",
49
+ "@types/react": "^19.2.14",
50
+ "@types/react-dom": "^19.2.3",
51
+ "@vitest/coverage-v8": "^4.1.10",
52
+ "jsdom": "^30.0.1",
53
+ "react": "^18.2.0",
54
+ "react-dom": "^18.2.0",
55
+ "typescript": "~5.8.3",
56
+ "vitest": "^4.1.10"
57
+ }
58
+ }
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ export function ActionBar({
5
+ children,
6
+ className,
7
+ sticky = false,
8
+ }: {
9
+ children: ReactNode;
10
+ className?: string;
11
+ sticky?: boolean;
12
+ }) {
13
+ return (
14
+ <div
15
+ data-slot="action-bar"
16
+ className={cn(
17
+ "glass-header flex flex-wrap items-center justify-end gap-2 border-t border-border/40 px-4 py-3",
18
+ sticky && "sticky bottom-0 z-20",
19
+ className,
20
+ )}
21
+ >
22
+ {children}
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,40 @@
1
+ import type { ComponentProps } from "react";
2
+ import { cn } from "../lib/cn";
3
+ import { Input } from "../primitives/Input";
4
+
5
+ export type AmountInputProps = Omit<ComponentProps<"input">, "type" | "value" | "onChange"> & {
6
+ value: string;
7
+ onChange: (value: string) => void;
8
+ currency?: string;
9
+ containerClassName?: string;
10
+ };
11
+
12
+ /** Currency-prefixed amount field for finance forms. */
13
+ export function AmountInput({
14
+ value,
15
+ onChange,
16
+ currency = "INR",
17
+ className,
18
+ containerClassName,
19
+ ...props
20
+ }: AmountInputProps) {
21
+ return (
22
+ <div
23
+ data-slot="amount-input"
24
+ className={cn("relative w-full", containerClassName)}
25
+ >
26
+ <span className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center pl-3 text-xs font-black uppercase tracking-wider text-muted-foreground">
27
+ {currency}
28
+ </span>
29
+ <Input
30
+ type="text"
31
+ inputMode="decimal"
32
+ autoComplete="off"
33
+ value={value}
34
+ onChange={(e) => onChange(e.target.value.replace(/[^0-9.,-]/g, ""))}
35
+ className={cn("h-11 py-0 pl-14 font-bold tabular-nums leading-none", className)}
36
+ {...props}
37
+ />
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from "react";
2
+ import { Menu } from "lucide-react";
3
+ import { cn } from "../lib/cn";
4
+ import { Button } from "../primitives/Button";
5
+ import { appChromeActionClusterClass } from "./FloatingPageChrome";
6
+
7
+ export type AppHeaderProps = {
8
+ brand?: ReactNode;
9
+ title?: string;
10
+ /** Center / search slot */
11
+ children?: ReactNode;
12
+ /** Right-side actions */
13
+ actions?: ReactNode;
14
+ onMenuClick?: () => void;
15
+ showMenuButton?: boolean;
16
+ className?: string;
17
+ sticky?: boolean;
18
+ /**
19
+ * `sticky` — in-flow glass bar.
20
+ * `chrome` — fixed safe-area app chrome (journalD RootLayout).
21
+ */
22
+ variant?: "sticky" | "chrome";
23
+ };
24
+
25
+ /**
26
+ * Top application chrome — glass header with brand, optional hamburger, actions.
27
+ */
28
+ export function AppHeader({
29
+ brand,
30
+ title,
31
+ children,
32
+ actions,
33
+ onMenuClick,
34
+ showMenuButton = false,
35
+ className,
36
+ sticky = true,
37
+ variant = "sticky",
38
+ }: AppHeaderProps) {
39
+ const isChrome = variant === "chrome";
40
+
41
+ return (
42
+ <header
43
+ data-slot="app-header"
44
+ className={cn(
45
+ "glass-header z-40 border-b border-border/50",
46
+ isChrome ? "app-chrome-header" : sticky && "sticky top-0",
47
+ className,
48
+ )}
49
+ >
50
+ <div className={cn("flex items-center gap-3 px-3 sm:px-4", isChrome ? "h-full" : "h-full min-h-14")}>
51
+ {showMenuButton ? (
52
+ <Button
53
+ type="button"
54
+ variant="ghost"
55
+ size="icon"
56
+ aria-label="Open menu"
57
+ onClick={onMenuClick}
58
+ className="lg:hidden"
59
+ >
60
+ <Menu className="size-5" />
61
+ </Button>
62
+ ) : null}
63
+ <div className="flex min-w-0 items-center gap-2" data-app-wordmark>
64
+ {brand}
65
+ {title ? (
66
+ <span className="truncate text-sm font-black tracking-tight sm:text-base">{title}</span>
67
+ ) : null}
68
+ </div>
69
+ <div className="mx-auto hidden min-w-0 flex-1 justify-center md:flex" data-global-search-shell>
70
+ {children}
71
+ </div>
72
+ <div
73
+ className="ml-auto flex shrink-0 items-center gap-1.5 sm:gap-2"
74
+ data-app-chrome-actions
75
+ >
76
+ {actions}
77
+ </div>
78
+ </div>
79
+ {children ? (
80
+ <div className="border-t border-border/30 px-3 py-2 md:hidden" data-global-search-shell>
81
+ {children}
82
+ </div>
83
+ ) : null}
84
+ </header>
85
+ );
86
+ }
87
+
88
+ export { appChromeActionClusterClass };
@@ -0,0 +1,120 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ import { cn } from "../lib/cn";
4
+
5
+ export type SidebarNavItem = {
6
+ id: string;
7
+ label: string;
8
+ icon?: LucideIcon;
9
+ href?: string;
10
+ active?: boolean;
11
+ onClick?: () => void;
12
+ badge?: ReactNode;
13
+ };
14
+
15
+ export type SidebarNavSection = {
16
+ id: string;
17
+ title?: string;
18
+ items: SidebarNavItem[];
19
+ };
20
+
21
+ export type AppSidebarProps = {
22
+ brand?: ReactNode;
23
+ sections: SidebarNavSection[];
24
+ footer?: ReactNode;
25
+ className?: string;
26
+ /** Collapsed rail mode */
27
+ collapsed?: boolean;
28
+ widthClassName?: string;
29
+ };
30
+
31
+ /**
32
+ * Desktop navigation sidebar — glass panel with sectioned nav links.
33
+ */
34
+ export function AppSidebar({
35
+ brand,
36
+ sections,
37
+ footer,
38
+ className,
39
+ collapsed = false,
40
+ widthClassName = "w-[280px]",
41
+ }: AppSidebarProps) {
42
+ return (
43
+ <aside
44
+ data-slot="app-sidebar"
45
+ data-collapsed={collapsed ? "true" : "false"}
46
+ className={cn(
47
+ "glass-panel flex h-full flex-col border-r border-border/40",
48
+ collapsed ? "w-[72px]" : widthClassName,
49
+ className,
50
+ )}
51
+ >
52
+ {brand ? (
53
+ <div className={cn("shrink-0 border-b border-border/40 px-4 py-4", collapsed && "px-2")}>
54
+ {brand}
55
+ </div>
56
+ ) : null}
57
+ <nav className="flex-1 space-y-5 overflow-y-auto px-2 py-4">
58
+ {sections.map((section) => (
59
+ <div key={section.id}>
60
+ {section.title && !collapsed ? (
61
+ <p className="mb-2 px-2 text-[10px] font-black uppercase tracking-widest text-muted-foreground/70">
62
+ {section.title}
63
+ </p>
64
+ ) : null}
65
+ <ul className="space-y-1">
66
+ {section.items.map((item) => {
67
+ const Icon = item.icon;
68
+ const content = (
69
+ <>
70
+ {Icon ? <Icon className="size-4 shrink-0" /> : null}
71
+ {!collapsed ? <span className="truncate">{item.label}</span> : null}
72
+ {!collapsed && item.badge ? (
73
+ <span className="ml-auto">{item.badge}</span>
74
+ ) : null}
75
+ </>
76
+ );
77
+ const classNameItem = cn(
78
+ "flex w-full items-center gap-2.5 rounded-xl px-3 py-2.5 text-sm font-bold transition-colors",
79
+ collapsed && "justify-center px-2",
80
+ item.active
81
+ ? "bg-primary/15 text-primary"
82
+ : "text-muted-foreground hover:bg-muted/50 hover:text-foreground",
83
+ );
84
+ return (
85
+ <li key={item.id}>
86
+ {item.href ? (
87
+ <a
88
+ href={item.href}
89
+ aria-current={item.active ? "page" : undefined}
90
+ className={classNameItem}
91
+ onClick={item.onClick}
92
+ title={collapsed ? item.label : undefined}
93
+ >
94
+ {content}
95
+ </a>
96
+ ) : (
97
+ <button
98
+ type="button"
99
+ className={classNameItem}
100
+ onClick={item.onClick}
101
+ title={collapsed ? item.label : undefined}
102
+ >
103
+ {content}
104
+ </button>
105
+ )}
106
+ </li>
107
+ );
108
+ })}
109
+ </ul>
110
+ </div>
111
+ ))}
112
+ </nav>
113
+ {footer ? (
114
+ <div className={cn("shrink-0 border-t border-border/40 p-3", collapsed && "px-2")}>
115
+ {footer}
116
+ </div>
117
+ ) : null}
118
+ </aside>
119
+ );
120
+ }
@@ -0,0 +1,179 @@
1
+ import * as React from "react";
2
+ import { Drawer as DrawerPrimitive } from "vaul";
3
+ import { cn } from "../lib/cn";
4
+
5
+ /**
6
+ * Vaul drawer root.
7
+ * Disable background scaling + input repositioning by default — both break
8
+ * touch hit-testing on iOS Safari / PWAs after the page has been scrolled.
9
+ */
10
+ const Drawer = ({
11
+ shouldScaleBackground = false,
12
+ repositionInputs = false,
13
+ ...props
14
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
15
+ <DrawerPrimitive.Root
16
+ data-slot="drawer"
17
+ shouldScaleBackground={shouldScaleBackground}
18
+ repositionInputs={repositionInputs}
19
+ {...props}
20
+ />
21
+ );
22
+ Drawer.displayName = "Drawer";
23
+
24
+ const DrawerTrigger = React.forwardRef<
25
+ React.ElementRef<typeof DrawerPrimitive.Trigger>,
26
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Trigger>
27
+ >(({ ...props }, ref) => (
28
+ <DrawerPrimitive.Trigger ref={ref} data-slot="drawer-trigger" {...props} />
29
+ ));
30
+ DrawerTrigger.displayName = "DrawerTrigger";
31
+
32
+ const DrawerPortal = ({
33
+ ...props
34
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) => (
35
+ <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
36
+ );
37
+ DrawerPortal.displayName = "DrawerPortal";
38
+
39
+ const DrawerClose = React.forwardRef<
40
+ React.ElementRef<typeof DrawerPrimitive.Close>,
41
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Close>
42
+ >(({ ...props }, ref) => (
43
+ <DrawerPrimitive.Close ref={ref} data-slot="drawer-close" {...props} />
44
+ ));
45
+ DrawerClose.displayName = "DrawerClose";
46
+
47
+ const DrawerOverlay = React.forwardRef<
48
+ React.ElementRef<typeof DrawerPrimitive.Overlay>,
49
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
50
+ >(({ className, style, ...props }, ref) => (
51
+ <DrawerPrimitive.Overlay
52
+ ref={ref}
53
+ data-slot="drawer-overlay"
54
+ {...props}
55
+ className={cn(
56
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-[9998] bg-black/50",
57
+ className,
58
+ )}
59
+ style={style}
60
+ />
61
+ ));
62
+ DrawerOverlay.displayName = "DrawerOverlay";
63
+
64
+ const DrawerContent = React.forwardRef<
65
+ React.ElementRef<typeof DrawerPrimitive.Content>,
66
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
67
+ >(({ className, children, style, ...props }, ref) => (
68
+ <DrawerPortal data-slot="drawer-portal">
69
+ <DrawerOverlay />
70
+ <DrawerPrimitive.Content
71
+ ref={ref}
72
+ data-slot="drawer-content"
73
+ {...props}
74
+ className={cn(
75
+ "group/drawer-content bg-background fixed z-[9999] flex h-auto flex-col outline-none",
76
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
77
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
78
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
79
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
80
+ className,
81
+ )}
82
+ style={{ touchAction: "manipulation", ...style }}
83
+ >
84
+ <div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
85
+ {children}
86
+ </DrawerPrimitive.Content>
87
+ </DrawerPortal>
88
+ ));
89
+ DrawerContent.displayName = "DrawerContent";
90
+
91
+ const DrawerHeader = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
92
+ ({ className, ...props }, ref) => (
93
+ <div
94
+ ref={ref}
95
+ data-slot="drawer-header"
96
+ className={cn("flex flex-col gap-1.5 p-4", className)}
97
+ {...props}
98
+ />
99
+ ),
100
+ );
101
+ DrawerHeader.displayName = "DrawerHeader";
102
+
103
+ const DrawerFooter = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
104
+ ({ className, ...props }, ref) => (
105
+ <div
106
+ ref={ref}
107
+ data-slot="drawer-footer"
108
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
109
+ {...props}
110
+ />
111
+ ),
112
+ );
113
+ DrawerFooter.displayName = "DrawerFooter";
114
+
115
+ const DrawerTitle = React.forwardRef<
116
+ React.ElementRef<typeof DrawerPrimitive.Title>,
117
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
118
+ >(({ className, ...props }, ref) => (
119
+ <DrawerPrimitive.Title
120
+ ref={ref}
121
+ data-slot="drawer-title"
122
+ className={cn("text-foreground font-semibold", className)}
123
+ {...props}
124
+ />
125
+ ));
126
+ DrawerTitle.displayName = "DrawerTitle";
127
+
128
+ const DrawerDescription = React.forwardRef<
129
+ React.ElementRef<typeof DrawerPrimitive.Description>,
130
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
131
+ >(({ className, ...props }, ref) => (
132
+ <DrawerPrimitive.Description
133
+ ref={ref}
134
+ data-slot="drawer-description"
135
+ className={cn("text-muted-foreground text-sm", className)}
136
+ {...props}
137
+ />
138
+ ));
139
+ DrawerDescription.displayName = "DrawerDescription";
140
+
141
+ export type BottomSheetProps = {
142
+ open: boolean;
143
+ onOpenChange: (open: boolean) => void;
144
+ children: React.ReactNode;
145
+ className?: string;
146
+ };
147
+
148
+ /**
149
+ * Glass bottom sheet (Vaul) — mobile filters, overflow menus, sheet panels.
150
+ * Customize surface via className; defaults match app chrome glass-panel look.
151
+ */
152
+ export function BottomSheet({ open, onOpenChange, children, className }: BottomSheetProps) {
153
+ return (
154
+ <Drawer open={open} onOpenChange={onOpenChange}>
155
+ <DrawerContent
156
+ data-slot="bottom-sheet"
157
+ className={cn(
158
+ "glass-panel border-border/60 bg-background/90 backdrop-blur-xl",
159
+ className,
160
+ )}
161
+ >
162
+ {children}
163
+ </DrawerContent>
164
+ </Drawer>
165
+ );
166
+ }
167
+
168
+ export {
169
+ Drawer,
170
+ DrawerTrigger,
171
+ DrawerClose,
172
+ DrawerPortal,
173
+ DrawerOverlay,
174
+ DrawerContent,
175
+ DrawerHeader,
176
+ DrawerFooter,
177
+ DrawerTitle,
178
+ DrawerDescription,
179
+ };
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from "react";
2
+ import { ChevronRight } from "lucide-react";
3
+ import { cn } from "../lib/cn";
4
+
5
+ export type BreadcrumbItem = {
6
+ label: string;
7
+ href?: string;
8
+ onClick?: () => void;
9
+ };
10
+
11
+ export function Breadcrumb({
12
+ items,
13
+ className,
14
+ }: {
15
+ items: BreadcrumbItem[];
16
+ className?: string;
17
+ }) {
18
+ return (
19
+ <nav data-slot="breadcrumb" aria-label="Breadcrumb" className={cn("flex flex-wrap items-center gap-1 text-sm", className)}>
20
+ {items.map((item, index) => {
21
+ const last = index === items.length - 1;
22
+ const content = last ? (
23
+ <span className="font-bold text-foreground">{item.label}</span>
24
+ ) : item.onClick ? (
25
+ <button type="button" className="font-medium text-muted-foreground hover:text-primary" onClick={item.onClick}>
26
+ {item.label}
27
+ </button>
28
+ ) : (
29
+ <a href={item.href} className="font-medium text-muted-foreground hover:text-primary">
30
+ {item.label}
31
+ </a>
32
+ );
33
+ return (
34
+ <span key={`${item.label}-${index}`} className="inline-flex items-center gap-1">
35
+ {index > 0 ? <ChevronRight className="size-3.5 text-muted-foreground/50" /> : null}
36
+ {content}
37
+ </span>
38
+ );
39
+ })}
40
+ </nav>
41
+ );
42
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+ import { Alert, AlertDescription, AlertTitle } from "../primitives/Alert";
4
+
5
+ export function Callout({
6
+ title,
7
+ children,
8
+ variant = "info",
9
+ className,
10
+ }: {
11
+ title?: string;
12
+ children: ReactNode;
13
+ variant?: "default" | "info" | "warm" | "highlight" | "danger";
14
+ className?: string;
15
+ }) {
16
+ return (
17
+ <Alert data-slot="callout" variant={variant} className={className}>
18
+ {title ? <AlertTitle>{title}</AlertTitle> : null}
19
+ <AlertDescription>{children}</AlertDescription>
20
+ </Alert>
21
+ );
22
+ }