@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,49 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/cn";
3
+ import { Button } from "../primitives/Button";
4
+
5
+ export type HeaderIconButtonProps = React.ComponentProps<"button"> & {
6
+ active?: boolean;
7
+ /** Unread / status count shown as a pill */
8
+ badge?: number | string | boolean;
9
+ size?: "sm" | "md";
10
+ };
11
+
12
+ /**
13
+ * Square frosted toolbar control used in sticky headers and app chrome.
14
+ * Fully customizable via className; children typically an icon.
15
+ */
16
+ export function HeaderIconButton({
17
+ className,
18
+ active,
19
+ badge,
20
+ size = "md",
21
+ children,
22
+ ...props
23
+ }: HeaderIconButtonProps) {
24
+ const dim = size === "sm" ? "h-8 w-8" : "h-9 w-9";
25
+ const showBadge = badge === true || (typeof badge === "number" && badge > 0) || typeof badge === "string";
26
+
27
+ return (
28
+ <Button
29
+ type="button"
30
+ variant="ghost"
31
+ data-slot="header-icon-button"
32
+ data-active={active ? "true" : "false"}
33
+ className={cn(
34
+ dim,
35
+ "relative rounded-xl border border-transparent text-muted-foreground hover:bg-muted/40 hover:text-foreground",
36
+ active && "border-primary/30 bg-primary/10 text-primary",
37
+ className,
38
+ )}
39
+ {...props}
40
+ >
41
+ {children}
42
+ {showBadge ? (
43
+ <span className="absolute -right-1 -top-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[9px] font-black text-primary-foreground shadow-sm">
44
+ {badge === true ? "" : badge}
45
+ </span>
46
+ ) : null}
47
+ </Button>
48
+ );
49
+ }
@@ -0,0 +1,29 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+ import { listRowToggleClass } from "../lib/chips";
4
+
5
+ export function ListRow({
6
+ active = false,
7
+ className,
8
+ children,
9
+ trailing,
10
+ onClick,
11
+ }: {
12
+ active?: boolean;
13
+ className?: string;
14
+ children: ReactNode;
15
+ trailing?: ReactNode;
16
+ onClick?: () => void;
17
+ }) {
18
+ return (
19
+ <button
20
+ type="button"
21
+ data-slot="list-row"
22
+ className={cn(listRowToggleClass(active), className)}
23
+ onClick={onClick}
24
+ >
25
+ <span className="min-w-0 flex-1 text-left">{children}</span>
26
+ {trailing}
27
+ </button>
28
+ );
29
+ }
@@ -0,0 +1,95 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ export type MediaEntryCardProps = {
5
+ title: string;
6
+ body?: string;
7
+ imageUrl?: string;
8
+ hideImage?: boolean;
9
+ /** Soft accent stripe when no image */
10
+ accentClassName?: string;
11
+ chips?: ReactNode;
12
+ meta?: ReactNode;
13
+ footerLeft?: ReactNode;
14
+ footerRight?: ReactNode;
15
+ menu?: ReactNode;
16
+ highlighted?: boolean;
17
+ onClick?: () => void;
18
+ className?: string;
19
+ /** Override media width on sm+ */
20
+ mediaClassName?: string;
21
+ };
22
+
23
+ /**
24
+ * Long horizontal entry card (story / activity style) with optional side media.
25
+ * All content regions are slots for full customization.
26
+ */
27
+ export function MediaEntryCard({
28
+ title,
29
+ body,
30
+ imageUrl,
31
+ hideImage,
32
+ accentClassName = "bg-primary",
33
+ chips,
34
+ meta,
35
+ footerLeft,
36
+ footerRight,
37
+ menu,
38
+ highlighted,
39
+ onClick,
40
+ className,
41
+ mediaClassName,
42
+ }: MediaEntryCardProps) {
43
+ const Comp = onClick ? "button" : "div";
44
+ return (
45
+ <Comp
46
+ type={onClick ? "button" : undefined}
47
+ data-slot="media-entry-card"
48
+ onClick={onClick}
49
+ className={cn(
50
+ "group relative flex w-full overflow-hidden rounded-2xl border border-border/40 bg-card/80 text-left shadow-sm transition-[transform,box-shadow,border-color] duration-200",
51
+ "hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-lg",
52
+ highlighted && "border-primary/50 shadow-[0_0_0_3px_color-mix(in_srgb,var(--primary)_12%,transparent)]",
53
+ className,
54
+ )}
55
+ >
56
+ {!hideImage && imageUrl ? (
57
+ <div
58
+ className={cn(
59
+ "relative hidden w-[38%] shrink-0 sm:block",
60
+ mediaClassName,
61
+ )}
62
+ >
63
+ <img src={imageUrl} alt="" className="absolute inset-0 size-full object-cover" />
64
+ <div className="absolute inset-y-0 right-0 w-16 bg-gradient-to-l from-card/80 to-transparent" />
65
+ </div>
66
+ ) : (
67
+ <div className={cn("w-1 shrink-0 self-stretch opacity-80", accentClassName)} />
68
+ )}
69
+
70
+ <div className="flex min-h-[160px] min-w-0 flex-1 flex-col gap-3 p-4 sm:p-5">
71
+ <div className="flex items-start justify-between gap-2">
72
+ <div className="min-w-0">
73
+ <h3 className="text-base font-black tracking-tight text-foreground">{title}</h3>
74
+ {chips ? <div className="mt-1.5 flex flex-wrap gap-1.5">{chips}</div> : null}
75
+ </div>
76
+ {menu}
77
+ </div>
78
+ {meta ? (
79
+ <div className="flex flex-wrap items-center gap-2 text-xs font-medium text-muted-foreground">
80
+ {meta}
81
+ </div>
82
+ ) : null}
83
+ {body ? (
84
+ <p className="line-clamp-3 text-sm leading-relaxed text-muted-foreground">{body}</p>
85
+ ) : null}
86
+ {(footerLeft || footerRight) && (
87
+ <div className="mt-auto flex items-center justify-between gap-2 border-t border-border/20 pt-2">
88
+ <div className="min-w-0 flex flex-wrap gap-1.5">{footerLeft}</div>
89
+ <div className="shrink-0">{footerRight}</div>
90
+ </div>
91
+ )}
92
+ </div>
93
+ </Comp>
94
+ );
95
+ }
@@ -0,0 +1,130 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ import { cn } from "../lib/cn";
4
+
5
+ export type MobileTabItem = {
6
+ id: string;
7
+ label: string;
8
+ icon: LucideIcon;
9
+ active?: boolean;
10
+ onClick?: () => void;
11
+ href?: string;
12
+ badge?: ReactNode;
13
+ };
14
+
15
+ /** Main-content padding that clears the floating bar until the desktop sidebar takes over. */
16
+ export const APP_TABBAR_CLEARANCE_CLASS = "app-tabbar-clearance xl:pb-6";
17
+
18
+ export type MobileTabBarProps = {
19
+ items: MobileTabItem[];
20
+ className?: string;
21
+ /** Extra trailing control (e.g. More) */
22
+ trailing?: ReactNode;
23
+ ariaLabel?: string;
24
+ /** Slide the bar away (e.g. while a text field is focused on iOS). */
25
+ hidden?: boolean;
26
+ /**
27
+ * Position inside a parent instead of the viewport, and keep the bar visible
28
+ * on large screens (docs previews, device frames).
29
+ */
30
+ contained?: boolean;
31
+ };
32
+
33
+ /**
34
+ * iOS 26-style floating liquid-glass tab bar.
35
+ * Sits above the home indicator as a capsule (not a full-bleed dock).
36
+ */
37
+ export function MobileTabBar({
38
+ items,
39
+ className,
40
+ trailing,
41
+ ariaLabel = "Primary",
42
+ hidden = false,
43
+ contained = false,
44
+ }: MobileTabBarProps) {
45
+ return (
46
+ <div
47
+ data-slot="mobile-tab-bar"
48
+ aria-hidden={hidden || undefined}
49
+ data-mobile-tabbar-hidden={hidden ? "true" : undefined}
50
+ className={cn(
51
+ "pointer-events-none z-50 flex justify-center",
52
+ contained ? "absolute inset-x-0 bottom-0" : "fixed inset-x-0 bottom-0 xl:hidden",
53
+ "transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none",
54
+ hidden &&
55
+ "translate-y-[calc(100%+var(--app-tabbar-inset-y)+env(safe-area-inset-bottom,0px))] opacity-0",
56
+ className,
57
+ )}
58
+ >
59
+ <div
60
+ data-app-tabbar-shell
61
+ className={cn(
62
+ "rounded-full",
63
+ contained
64
+ ? "mb-1.5 w-[calc(100%-1rem)] max-w-[26.5rem]"
65
+ : "mb-[calc(var(--app-tabbar-inset-y)+env(safe-area-inset-bottom,0px))] w-[calc(100%-2*var(--app-tabbar-inset-x))] max-w-[26.5rem]",
66
+ hidden ? "pointer-events-none" : "pointer-events-auto",
67
+ )}
68
+ >
69
+ <nav
70
+ data-app-tabbar
71
+ aria-label={ariaLabel}
72
+ className="glass-tabbar flex h-[var(--app-tabbar-height)] items-stretch gap-0.5 px-1.5"
73
+ >
74
+ {items.map((item) => {
75
+ const Icon = item.icon;
76
+ const classNameItem = cn(
77
+ "relative z-[1] flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-0.5 rounded-full px-1 py-1",
78
+ "touch-manipulation select-none transition-[color,transform] duration-150 active:scale-[0.96]",
79
+ item.active ? "text-primary" : "text-muted-foreground hover:text-foreground",
80
+ );
81
+ const body = (
82
+ <>
83
+ {item.active ? (
84
+ <span
85
+ className="absolute inset-[3px] rounded-full bg-white/50 shadow-[inset_0_1px_0_var(--glass-highlight)] dark:bg-white/[0.14]"
86
+ aria-hidden
87
+ />
88
+ ) : null}
89
+ <span className="relative">
90
+ <Icon
91
+ className="relative h-5 w-5 shrink-0"
92
+ strokeWidth={item.active ? 2.25 : 1.9}
93
+ fill={item.active ? "currentColor" : "none"}
94
+ fillOpacity={item.active ? 0.18 : 0}
95
+ aria-hidden
96
+ />
97
+ {item.badge}
98
+ </span>
99
+ <span
100
+ className={cn(
101
+ "relative max-w-full truncate text-[10px] leading-none tracking-tight",
102
+ item.active ? "font-semibold text-primary" : "font-medium text-muted-foreground",
103
+ )}
104
+ >
105
+ {item.label}
106
+ </span>
107
+ </>
108
+ );
109
+ return item.href ? (
110
+ <a
111
+ key={item.id}
112
+ href={item.href}
113
+ className={classNameItem}
114
+ onClick={item.onClick}
115
+ aria-current={item.active ? "page" : undefined}
116
+ >
117
+ {body}
118
+ </a>
119
+ ) : (
120
+ <button key={item.id} type="button" className={classNameItem} onClick={item.onClick}>
121
+ {body}
122
+ </button>
123
+ );
124
+ })}
125
+ {trailing}
126
+ </nav>
127
+ </div>
128
+ </div>
129
+ );
130
+ }
@@ -0,0 +1,164 @@
1
+ import { useEffect, type ReactNode } from "react";
2
+ import { Bell } from "lucide-react";
3
+ import { cn } from "../lib/cn";
4
+ import { Button } from "../primitives/Button";
5
+ import { HeaderIconButton } from "./HeaderIconButton";
6
+
7
+ export type NotificationItem = {
8
+ id: string;
9
+ title: string;
10
+ body?: string;
11
+ createdAtLabel?: string;
12
+ isRead?: boolean;
13
+ icon?: ReactNode;
14
+ onOpen?: () => void;
15
+ onDismiss?: () => void;
16
+ };
17
+
18
+ export type NotificationBellProps = {
19
+ unreadCount?: number;
20
+ items?: NotificationItem[];
21
+ open: boolean;
22
+ onOpenChange: (open: boolean) => void;
23
+ onMarkAll?: () => void;
24
+ emptyState?: ReactNode;
25
+ className?: string;
26
+ panelClassName?: string;
27
+ title?: string;
28
+ };
29
+
30
+ /**
31
+ * Notification trigger + glass panel list. Customize items and actions fully.
32
+ * Panel uses an opaque surface so sticky page chrome (e.g. "+ Record") cannot show through.
33
+ */
34
+ export function NotificationBell({
35
+ unreadCount = 0,
36
+ items = [],
37
+ open,
38
+ onOpenChange,
39
+ onMarkAll,
40
+ emptyState,
41
+ className,
42
+ panelClassName,
43
+ title = "Notifications",
44
+ }: NotificationBellProps) {
45
+ useEffect(() => {
46
+ if (!open) return;
47
+ const onKey = (e: KeyboardEvent) => {
48
+ if (e.key === "Escape") onOpenChange(false);
49
+ };
50
+ window.addEventListener("keydown", onKey);
51
+ return () => window.removeEventListener("keydown", onKey);
52
+ }, [open, onOpenChange]);
53
+
54
+ return (
55
+ <div data-slot="notification-bell" className={cn("relative z-20", className)}>
56
+ <HeaderIconButton
57
+ badge={unreadCount > 0 ? (unreadCount > 99 ? "99+" : unreadCount) : undefined}
58
+ active={open}
59
+ aria-label={title}
60
+ aria-expanded={open}
61
+ onClick={() => onOpenChange(!open)}
62
+ >
63
+ <Bell className={cn("h-4 w-4", unreadCount > 0 && "text-primary")} />
64
+ </HeaderIconButton>
65
+
66
+ {open ? (
67
+ <>
68
+ <button
69
+ type="button"
70
+ className="fixed inset-0 z-[90]"
71
+ aria-label="Close notifications"
72
+ onClick={() => onOpenChange(false)}
73
+ />
74
+ <div
75
+ role="dialog"
76
+ aria-label={title}
77
+ className={cn(
78
+ "absolute right-0 top-full z-[95] mt-2.5 w-[min(calc(100vw-1.5rem),24rem)] overflow-hidden rounded-2xl border border-border/60 bg-background shadow-[0_28px_64px_-20px_rgba(15,23,42,0.45)] dark:border-white/10 dark:bg-card dark:shadow-[0_28px_64px_-20px_rgba(0,0,0,0.65)]",
79
+ panelClassName,
80
+ )}
81
+ >
82
+ <div className="flex items-center justify-between gap-2 px-4 py-3.5">
83
+ <p className="text-[10px] font-black uppercase tracking-[0.2em] text-muted-foreground/60">
84
+ {title}
85
+ </p>
86
+ {onMarkAll && unreadCount > 0 ? (
87
+ <Button type="button" variant="ghost" size="sm" onClick={onMarkAll}>
88
+ Mark all
89
+ </Button>
90
+ ) : null}
91
+ </div>
92
+ <div className="mx-4 h-px bg-border/40" />
93
+ <div className="max-h-80 overflow-y-auto py-2">
94
+ {items.length === 0
95
+ ? emptyState ?? (
96
+ <p className="px-4 py-8 text-center text-sm text-muted-foreground">
97
+ You’re all caught up.
98
+ </p>
99
+ )
100
+ : items.map((item) => (
101
+ <div
102
+ key={item.id}
103
+ className={cn(
104
+ "flex gap-3 px-4 py-3 transition-colors hover:bg-muted/30",
105
+ !item.isRead && "bg-primary/[0.035]",
106
+ )}
107
+ >
108
+ <div className="mt-0.5 shrink-0 text-muted-foreground">
109
+ {item.icon ?? <Bell className="h-4 w-4" />}
110
+ </div>
111
+ <div className="min-w-0 flex-1">
112
+ <div className="flex items-start gap-2">
113
+ <p
114
+ className={cn(
115
+ "min-w-0 flex-1 text-sm",
116
+ item.isRead ? "font-medium" : "font-bold",
117
+ )}
118
+ >
119
+ {item.title}
120
+ </p>
121
+ {!item.isRead ? (
122
+ <span className="mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-primary" />
123
+ ) : null}
124
+ </div>
125
+ {item.body ? (
126
+ <p className="mt-0.5 line-clamp-2 text-xs text-muted-foreground">
127
+ {item.body}
128
+ </p>
129
+ ) : null}
130
+ <div className="mt-2 flex flex-wrap items-center gap-2">
131
+ {item.createdAtLabel ? (
132
+ <span className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70">
133
+ {item.createdAtLabel}
134
+ </span>
135
+ ) : null}
136
+ {item.onOpen ? (
137
+ <button
138
+ type="button"
139
+ className="text-[10px] font-black uppercase tracking-widest text-primary"
140
+ onClick={item.onOpen}
141
+ >
142
+ Open
143
+ </button>
144
+ ) : null}
145
+ {item.onDismiss ? (
146
+ <button
147
+ type="button"
148
+ className="text-[10px] font-black uppercase tracking-widest text-muted-foreground"
149
+ onClick={item.onDismiss}
150
+ >
151
+ Dismiss
152
+ </button>
153
+ ) : null}
154
+ </div>
155
+ </div>
156
+ </div>
157
+ ))}
158
+ </div>
159
+ </div>
160
+ </>
161
+ ) : null}
162
+ </div>
163
+ );
164
+ }
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ export type PageHeaderProps = {
5
+ kicker?: string;
6
+ title: string;
7
+ description?: string;
8
+ actions?: ReactNode;
9
+ className?: string;
10
+ };
11
+
12
+ /** Page title block with optional kicker and action slot. */
13
+ export function PageHeader({ kicker, title, description, actions, className }: PageHeaderProps) {
14
+ return (
15
+ <div
16
+ data-slot="page-header"
17
+ className={cn("flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", className)}
18
+ >
19
+ <div className="min-w-0">
20
+ {kicker ? <p className="app-section-kicker">{kicker}</p> : null}
21
+ <h1 className="app-title">{title}</h1>
22
+ {description ? <p className="app-subtitle">{description}</p> : null}
23
+ </div>
24
+ {actions ? <div className="flex shrink-0 flex-wrap gap-2">{actions}</div> : null}
25
+ </div>
26
+ );
27
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ export type PageShellProps = {
5
+ children: ReactNode;
6
+ className?: string;
7
+ /** Include .app-shell horizontal padding. */
8
+ padded?: boolean;
9
+ };
10
+
11
+ /**
12
+ * Full-page backdrop with theme accent washes (.app-page).
13
+ */
14
+ export function PageShell({ children, className, padded = true }: PageShellProps) {
15
+ return (
16
+ <div data-slot="page-shell" className={cn("app-page", className)}>
17
+ <div className={cn(padded && "app-shell")}>{children}</div>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,157 @@
1
+ import * as React from "react";
2
+ import type { ReactNode } from "react";
3
+ import { ArrowLeft } from "lucide-react";
4
+ import { cn } from "../lib/cn";
5
+ import { Button } from "../primitives/Button";
6
+
7
+ export type PageStickyHeaderProps = React.ComponentProps<"div"> & {
8
+ /** Offset below a fixed app bar (defaults to top-16 → --app-chrome-offset). */
9
+ offsetClassName?: string;
10
+ title?: ReactNode;
11
+ subtitle?: ReactNode;
12
+ count?: ReactNode;
13
+ leading?: ReactNode;
14
+ icon?: ReactNode;
15
+ search?: ReactNode;
16
+ actions?: ReactNode;
17
+ /** Full-width row under the main toolbar (mobile search / secondary actions). */
18
+ below?: ReactNode;
19
+ belowClassName?: string;
20
+ showBack?: boolean;
21
+ onBack?: () => void;
22
+ titleClassName?: string;
23
+ subtitleClassName?: string;
24
+ density?: "compact" | "comfortable";
25
+ mobileSearch?: ReactNode;
26
+ };
27
+
28
+ export function PageHeaderBadge({
29
+ children,
30
+ className,
31
+ }: {
32
+ children: ReactNode;
33
+ className?: string;
34
+ }) {
35
+ return (
36
+ <div
37
+ className={cn(
38
+ "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-primary/20 bg-primary/10 shadow-sm dark:border-primary/30 dark:bg-primary/15",
39
+ className,
40
+ )}
41
+ aria-hidden
42
+ >
43
+ {children}
44
+ </div>
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Sticky glass page chrome — title, icon badge, search & action slots.
50
+ * Matches journalD PageHeader (density, count, below row, page-header-* type).
51
+ */
52
+ const PageStickyHeader = React.forwardRef<HTMLDivElement, PageStickyHeaderProps>(
53
+ (
54
+ {
55
+ className,
56
+ offsetClassName = "top-16",
57
+ title,
58
+ subtitle,
59
+ count,
60
+ leading,
61
+ icon,
62
+ search,
63
+ actions,
64
+ below,
65
+ belowClassName,
66
+ showBack,
67
+ onBack,
68
+ titleClassName,
69
+ subtitleClassName,
70
+ density = "compact",
71
+ mobileSearch,
72
+ children,
73
+ ...props
74
+ },
75
+ ref,
76
+ ) => {
77
+ const shouldShowBack = showBack ?? Boolean(onBack);
78
+ const belowRow = below ?? mobileSearch;
79
+
80
+ return (
81
+ <div
82
+ ref={ref}
83
+ data-slot="page-sticky-header"
84
+ className={cn(
85
+ "glass-header sticky z-30 shrink-0 border-b border-border/50",
86
+ offsetClassName,
87
+ className,
88
+ )}
89
+ {...props}
90
+ >
91
+ {children ?? (
92
+ <div
93
+ className={cn(
94
+ "app-frame-pad",
95
+ density === "comfortable" ? "py-2.5 sm:py-3" : "py-1.5 sm:py-2",
96
+ )}
97
+ >
98
+ <div
99
+ className={cn(
100
+ "flex w-full min-w-0 items-center",
101
+ density === "comfortable" ? "gap-2.5 sm:gap-3" : "gap-2",
102
+ )}
103
+ >
104
+ {shouldShowBack ? (
105
+ <Button
106
+ type="button"
107
+ variant="ghost"
108
+ size="icon"
109
+ className="h-8 w-8 shrink-0 rounded-xl"
110
+ onClick={onBack}
111
+ aria-label="Go back"
112
+ >
113
+ <ArrowLeft className="h-4 w-4" />
114
+ </Button>
115
+ ) : null}
116
+ {leading}
117
+ {icon ? <PageHeaderBadge>{icon}</PageHeaderBadge> : null}
118
+ {(title || subtitle || count != null) && (
119
+ <div className="min-w-0 flex-1">
120
+ <div className="flex min-w-0 items-center gap-2">
121
+ {title ? (
122
+ <h1 className={cn("page-header-title", titleClassName ?? "truncate")}>
123
+ {title}
124
+ </h1>
125
+ ) : null}
126
+ {count != null && count !== false ? (
127
+ typeof count === "string" || typeof count === "number" ? (
128
+ <span className="page-header-count">{count}</span>
129
+ ) : (
130
+ count
131
+ )
132
+ ) : null}
133
+ </div>
134
+ {subtitle ? (
135
+ <div className={cn("page-header-subtitle mt-0.5 truncate", subtitleClassName)}>
136
+ {subtitle}
137
+ </div>
138
+ ) : null}
139
+ </div>
140
+ )}
141
+ {search ? (
142
+ <div className="hidden min-w-0 flex-1 md:block md:max-w-xs lg:max-w-sm">{search}</div>
143
+ ) : null}
144
+ {actions ? (
145
+ <div className="ml-auto flex shrink-0 items-center gap-1.5">{actions}</div>
146
+ ) : null}
147
+ </div>
148
+ {belowRow ? <div className={cn("mt-2", belowClassName)}>{belowRow}</div> : null}
149
+ </div>
150
+ )}
151
+ </div>
152
+ );
153
+ },
154
+ );
155
+ PageStickyHeader.displayName = "PageStickyHeader";
156
+
157
+ export { PageStickyHeader };