@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,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ChevronLeft, Search, X } from "lucide-react";
3
+ import { cn } from "../lib/cn";
4
+ import { Input } from "../primitives/Input";
5
+ /** Shared control height for the sticky chrome row. */
6
+ export const FLOATING_PAGE_CHROME_HEIGHT_CLASS = "h-9";
7
+ /**
8
+ * Pinned rail below the fixed app chrome. Not sticky — pair with
9
+ * `app-page-chrome` so page content scrolls in `.app-page-chrome-body`.
10
+ */
11
+ export const floatingPageChromeShellClass = "shrink-0 app-frame-pad pt-2 pb-1.5";
12
+ /** Inner Apple glass capsule that holds identity, search, and actions. */
13
+ export const floatingPageChromeTrayClass = "glass-page-toolbar flex w-full min-w-0 flex-col gap-2 px-1.5 py-1 sm:px-2";
14
+ /** App-bar utility island (settings + notifications) — still a standalone chip. */
15
+ const glassChip = "border border-border/50 bg-card/80 shadow-lg backdrop-blur-xl dark:border-white/10 dark:bg-zinc-900/70 dark:shadow-black/30";
16
+ /** Inset control sitting on the page toolbar glass (not a second chip). */
17
+ const pageToolbarControl = "border border-white/35 bg-white/20 shadow-none backdrop-blur-sm dark:border-white/10 dark:bg-white/[0.07]";
18
+ /** Combined back + page name — flat on the glass tray. */
19
+ export const floatingPageChromeIdentityClass = "flex h-9 min-w-0 shrink items-center rounded-full";
20
+ /** Search field as an inset pill on the glass tray. */
21
+ export const floatingPageChromeSearchWrapClass = cn("relative flex h-9 w-full max-w-md items-center rounded-full", pageToolbarControl);
22
+ export const floatingPageChromeSearchInputClass = "h-9 w-full rounded-full border-0 bg-transparent py-0 pl-9 pr-8 text-sm leading-none shadow-none focus-visible:ring-0";
23
+ /** View & filters / sort — inset on the glass tray, same height as search. */
24
+ export const floatingPageChromePillClass = cn("h-9 gap-1.5 rounded-full px-2.5 text-xs font-semibold hover:bg-white/40 data-[state=open]:bg-white/50 dark:hover:bg-white/10 dark:data-[state=open]:bg-white/12", pageToolbarControl);
25
+ /** Icon-only control (mobile search, mobile sort). */
26
+ export const floatingPageChromeIconPillClass = cn("h-9 w-9 shrink-0 rounded-full hover:bg-white/40 data-[state=open]:bg-white/50 dark:hover:bg-white/10 dark:data-[state=open]:bg-white/12", pageToolbarControl);
27
+ /** Cluster of related icon actions (add / layout / more). */
28
+ export const floatingPageChromeClusterClass = cn("inline-flex h-9 w-fit items-center gap-0.5 rounded-full p-0.5", pageToolbarControl);
29
+ /** App-bar utility island (settings + notifications). */
30
+ export const appChromeActionClusterClass = cn("inline-flex h-10 w-fit items-center gap-0.5 rounded-full p-0.5", glassChip);
31
+ /** Primary CTA in the chrome row. */
32
+ export const floatingPageChromePrimaryClass = "h-9 gap-1.5 rounded-full px-3 text-xs font-semibold shadow-lg";
33
+ /** Gap between a trigger pill and its attached menu (leaves room for the caret). */
34
+ export const FLOATING_PAGE_CHROME_POPOVER_OFFSET = 10;
35
+ export const floatingPageChromeAttachedPopoverClass = "overflow-visible rounded-2xl border border-border/50 bg-card/95 p-0 shadow-[0_16px_40px_-18px_rgba(15,23,42,0.42)] backdrop-blur-xl dark:border-white/10 dark:bg-zinc-900/92";
36
+ /** Inner buttons sitting inside a glass cluster (no second chip). */
37
+ export const appChromeClusterButtonClass = "relative z-[70] h-8 w-8 shrink-0 rounded-full px-0 text-muted-foreground transition-colors hover:bg-muted/70 hover:text-foreground data-[state=open]:bg-muted/80 data-[state=open]:text-foreground";
38
+ /**
39
+ * Small pointer that visually joins a popover to its trigger.
40
+ * `align="end"` matches popover `align="end"` (caret sits under the right-hand control).
41
+ */
42
+ export function ChromeAttachedCaret({ align = "end", }) {
43
+ return (_jsx("span", { "aria-hidden": true, className: cn("pointer-events-none absolute -top-[7px] h-3.5 w-3.5 rotate-45 border-l border-t border-border/50 bg-card/95 dark:border-white/10 dark:bg-zinc-900/92", align === "end" ? "right-5" : "left-5") }));
44
+ }
45
+ export function FloatingPageChromeIdentity({ onBack, title, count, countLabel, backLabel = "Go back", className, }) {
46
+ const backClassName = "flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-foreground transition-colors hover:bg-muted/70 active:scale-[0.96]";
47
+ return (_jsxs("div", { "data-slot": "floating-page-chrome-identity", className: cn(floatingPageChromeIdentityClass, "max-w-[min(28rem,calc(100%-6.5rem))]", className), children: [onBack ? (_jsx("button", { type: "button", onClick: onBack, "aria-label": backLabel, className: backClassName, children: _jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": true }) })) : (_jsx("span", { className: "w-2.5 shrink-0", "aria-hidden": true })), _jsx("h1", { className: "min-w-0 truncate text-sm font-semibold tracking-tight text-foreground", children: title }), count != null && count !== false ? (_jsx("span", { className: "page-header-count ml-1.5 mr-2.5", "aria-label": countLabel, children: count })) : (_jsx("span", { className: "w-2.5 shrink-0", "aria-hidden": true }))] }));
48
+ }
49
+ /** Search pill used in the chrome row and the mobile reveal row. */
50
+ export function FloatingPageChromeSearchField({ value, onChange, onClear, placeholder, inputRef, className, fullWidth = false, type = "search", "aria-label": ariaLabel, }) {
51
+ return (_jsxs("div", { "data-slot": "floating-page-chrome-search", className: cn(floatingPageChromeSearchWrapClass, fullWidth && "max-w-none", className), children: [_jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/50" }), _jsx(Input, { ref: inputRef, type: type, enterKeyHint: "search", placeholder: placeholder, value: value, onChange: (e) => onChange(e.target.value), className: floatingPageChromeSearchInputClass, autoComplete: "off", "aria-label": ariaLabel }), value ? (_jsx("button", { type: "button", onClick: onClear, className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground", "aria-label": "Clear search", children: _jsx(X, { className: "h-3.5 w-3.5" }) })) : null] }));
52
+ }
53
+ /**
54
+ * Shared chrome row: identity, optional centered search, trailing actions.
55
+ * `below` is a second row (mobile search, compact TOC) and is not itself a full-bleed bar.
56
+ * Pass `children` to pin this bar above a nested scroller (content never slides under it).
57
+ */
58
+ export function FloatingPageChromeBar({ identity, search, actions, below, children, className, }) {
59
+ const bar = (_jsx("div", { "data-slot": "floating-page-chrome", className: cn(floatingPageChromeShellClass, children == null && className), children: _jsxs("div", { className: floatingPageChromeTrayClass, children: [_jsxs("div", { className: "flex h-9 w-full min-w-0 items-center gap-2", children: [identity, search ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "min-w-0 flex-1 md:hidden", "aria-hidden": true }), _jsx("div", { className: "hidden min-w-0 flex-1 justify-center px-0.5 sm:px-2 md:flex", children: search })] })) : (_jsx("div", { className: "min-w-0 flex-1", "aria-hidden": true })), actions ? (_jsx("div", { className: "flex h-9 shrink-0 items-center justify-end gap-1.5", children: actions })) : null] }), below] }) }));
60
+ if (children == null)
61
+ return bar;
62
+ return (_jsxs("div", { className: cn("app-page-chrome", className), children: [bar, _jsx("div", { className: "app-page-chrome-body", children: children })] }));
63
+ }
64
+ /** Shell + glass tray for pages that compose the chrome row by hand. */
65
+ export function FloatingPageChromeShell({ children, below, className, }) {
66
+ return (_jsx("div", { "data-slot": "floating-page-chrome-shell", className: cn(floatingPageChromeShellClass, className), children: _jsxs("div", { className: floatingPageChromeTrayClass, children: [children, below] }) }));
67
+ }
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react";
2
+ export type FormFieldProps = {
3
+ id: string;
4
+ label: string;
5
+ description?: string;
6
+ error?: string;
7
+ children: ReactNode;
8
+ className?: string;
9
+ required?: boolean;
10
+ };
11
+ /**
12
+ * Label + control + helper/error stack for consistent form density.
13
+ */
14
+ export declare function FormField({ id, label, description, error, children, className, required, }: FormFieldProps): import("react").JSX.Element;
15
+ //# sourceMappingURL=FormField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../../src/composites/FormField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACxB,EAAE,EACF,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BAkBhB"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ import { Label } from "../primitives/Label";
4
+ /**
5
+ * Label + control + helper/error stack for consistent form density.
6
+ */
7
+ export function FormField({ id, label, description, error, children, className, required, }) {
8
+ return (_jsxs("div", { "data-slot": "form-field", className: cn("space-y-2", className), children: [_jsxs(Label, { htmlFor: id, children: [label, required ? _jsx("span", { className: "text-destructive", children: "*" }) : null] }), children, description && !error ? (_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: description })) : null, error ? (_jsx("p", { className: "text-xs font-semibold text-destructive", role: "alert", children: error })) : null] }));
9
+ }
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const glassCardVariants: (props?: ({
4
+ variant?: "default" | "menu" | "header" | "panel" | "subtle" | "app" | null | undefined;
5
+ padding?: "none" | "sm" | "lg" | "md" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export type GlassCardProps = React.ComponentProps<"div"> & VariantProps<typeof glassCardVariants>;
8
+ /**
9
+ * Opinionated glass surface — prefer this over raw `.glass*` class strings.
10
+ */
11
+ declare const GlassCard: React.ForwardRefExoticComponent<Omit<GlassCardProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
12
+ export { GlassCard, glassCardVariants };
13
+ //# sourceMappingURL=GlassCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlassCard.d.ts","sourceRoot":"","sources":["../../src/composites/GlassCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlE,QAAA,MAAM,iBAAiB;;;8EAqBrB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GACtD,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEzC;;GAEG;AACH,QAAA,MAAM,SAAS,oGAUd,CAAC;AAGF,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../lib/cn";
5
+ const glassCardVariants = cva("relative overflow-hidden", {
6
+ variants: {
7
+ variant: {
8
+ default: "glass rounded-[var(--radius-card)]",
9
+ panel: "glass-panel rounded-[var(--radius-card)]",
10
+ subtle: "glass-subtle rounded-[var(--radius-card)]",
11
+ header: "glass-header",
12
+ menu: "glass-menu rounded-[var(--radius-card)]",
13
+ app: "app-card",
14
+ },
15
+ padding: {
16
+ none: "p-0",
17
+ sm: "p-3 sm:p-4",
18
+ md: "p-4 sm:p-5",
19
+ lg: "p-5 sm:p-6",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ padding: "md",
25
+ },
26
+ });
27
+ /**
28
+ * Opinionated glass surface — prefer this over raw `.glass*` class strings.
29
+ */
30
+ const GlassCard = React.forwardRef(({ className, variant, padding, ...props }, ref) => (_jsx("div", { ref: ref, "data-slot": "glass-card", "data-variant": variant ?? "default", className: cn(glassCardVariants({ variant, padding }), className), ...props })));
31
+ GlassCard.displayName = "GlassCard";
32
+ export { GlassCard, glassCardVariants };
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ export type HeaderIconButtonProps = React.ComponentProps<"button"> & {
3
+ active?: boolean;
4
+ /** Unread / status count shown as a pill */
5
+ badge?: number | string | boolean;
6
+ size?: "sm" | "md";
7
+ };
8
+ /**
9
+ * Square frosted toolbar control used in sticky headers and app chrome.
10
+ * Fully customizable via className; children typically an icon.
11
+ */
12
+ export declare function HeaderIconButton({ className, active, badge, size, children, ...props }: HeaderIconButtonProps): React.JSX.Element;
13
+ //# sourceMappingURL=HeaderIconButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeaderIconButton.d.ts","sourceRoot":"","sources":["../../src/composites/HeaderIconButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,MAAM,EACN,KAAK,EACL,IAAW,EACX,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,qBA0BvB"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ import { Button } from "../primitives/Button";
4
+ /**
5
+ * Square frosted toolbar control used in sticky headers and app chrome.
6
+ * Fully customizable via className; children typically an icon.
7
+ */
8
+ export function HeaderIconButton({ className, active, badge, size = "md", children, ...props }) {
9
+ const dim = size === "sm" ? "h-8 w-8" : "h-9 w-9";
10
+ const showBadge = badge === true || (typeof badge === "number" && badge > 0) || typeof badge === "string";
11
+ return (_jsxs(Button, { type: "button", variant: "ghost", "data-slot": "header-icon-button", "data-active": active ? "true" : "false", className: cn(dim, "relative rounded-xl border border-transparent text-muted-foreground hover:bg-muted/40 hover:text-foreground", active && "border-primary/30 bg-primary/10 text-primary", className), ...props, children: [children, showBadge ? (_jsx("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", children: badge === true ? "" : badge })) : null] }));
12
+ }
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function ListRow({ active, className, children, trailing, onClick, }: {
3
+ active?: boolean;
4
+ className?: string;
5
+ children: ReactNode;
6
+ trailing?: ReactNode;
7
+ onClick?: () => void;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=ListRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListRow.d.ts","sourceRoot":"","sources":["../../src/composites/ListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,wBAAgB,OAAO,CAAC,EACtB,MAAc,EACd,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,OAAO,GACR,EAAE;IACD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,+BAYA"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ import { listRowToggleClass } from "../lib/chips";
4
+ export function ListRow({ active = false, className, children, trailing, onClick, }) {
5
+ return (_jsxs("button", { type: "button", "data-slot": "list-row", className: cn(listRowToggleClass(active), className), onClick: onClick, children: [_jsx("span", { className: "min-w-0 flex-1 text-left", children: children }), trailing] }));
6
+ }
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ export type MediaEntryCardProps = {
3
+ title: string;
4
+ body?: string;
5
+ imageUrl?: string;
6
+ hideImage?: boolean;
7
+ /** Soft accent stripe when no image */
8
+ accentClassName?: string;
9
+ chips?: ReactNode;
10
+ meta?: ReactNode;
11
+ footerLeft?: ReactNode;
12
+ footerRight?: ReactNode;
13
+ menu?: ReactNode;
14
+ highlighted?: boolean;
15
+ onClick?: () => void;
16
+ className?: string;
17
+ /** Override media width on sm+ */
18
+ mediaClassName?: string;
19
+ };
20
+ /**
21
+ * Long horizontal entry card (story / activity style) with optional side media.
22
+ * All content regions are slots for full customization.
23
+ */
24
+ export declare function MediaEntryCard({ title, body, imageUrl, hideImage, accentClassName, chips, meta, footerLeft, footerRight, menu, highlighted, onClick, className, mediaClassName, }: MediaEntryCardProps): import("react").JSX.Element;
25
+ //# sourceMappingURL=MediaEntryCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaEntryCard.d.ts","sourceRoot":"","sources":["../../src/composites/MediaEntryCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,eAA8B,EAC9B,KAAK,EACL,IAAI,EACJ,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,OAAO,EACP,SAAS,EACT,cAAc,GACf,EAAE,mBAAmB,+BAqDrB"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ /**
4
+ * Long horizontal entry card (story / activity style) with optional side media.
5
+ * All content regions are slots for full customization.
6
+ */
7
+ export function MediaEntryCard({ title, body, imageUrl, hideImage, accentClassName = "bg-primary", chips, meta, footerLeft, footerRight, menu, highlighted, onClick, className, mediaClassName, }) {
8
+ const Comp = onClick ? "button" : "div";
9
+ return (_jsxs(Comp, { type: onClick ? "button" : undefined, "data-slot": "media-entry-card", onClick: onClick, className: cn("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", "hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-lg", highlighted && "border-primary/50 shadow-[0_0_0_3px_color-mix(in_srgb,var(--primary)_12%,transparent)]", className), children: [!hideImage && imageUrl ? (_jsxs("div", { className: cn("relative hidden w-[38%] shrink-0 sm:block", mediaClassName), children: [_jsx("img", { src: imageUrl, alt: "", className: "absolute inset-0 size-full object-cover" }), _jsx("div", { className: "absolute inset-y-0 right-0 w-16 bg-gradient-to-l from-card/80 to-transparent" })] })) : (_jsx("div", { className: cn("w-1 shrink-0 self-stretch opacity-80", accentClassName) })), _jsxs("div", { className: "flex min-h-[160px] min-w-0 flex-1 flex-col gap-3 p-4 sm:p-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-base font-black tracking-tight text-foreground", children: title }), chips ? _jsx("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: chips }) : null] }), menu] }), meta ? (_jsx("div", { className: "flex flex-wrap items-center gap-2 text-xs font-medium text-muted-foreground", children: meta })) : null, body ? (_jsx("p", { className: "line-clamp-3 text-sm leading-relaxed text-muted-foreground", children: body })) : null, (footerLeft || footerRight) && (_jsxs("div", { className: "mt-auto flex items-center justify-between gap-2 border-t border-border/20 pt-2", children: [_jsx("div", { className: "min-w-0 flex flex-wrap gap-1.5", children: footerLeft }), _jsx("div", { className: "shrink-0", children: footerRight })] }))] })] }));
10
+ }
@@ -0,0 +1,33 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ export type MobileTabItem = {
4
+ id: string;
5
+ label: string;
6
+ icon: LucideIcon;
7
+ active?: boolean;
8
+ onClick?: () => void;
9
+ href?: string;
10
+ badge?: ReactNode;
11
+ };
12
+ /** Main-content padding that clears the floating bar until the desktop sidebar takes over. */
13
+ export declare const APP_TABBAR_CLEARANCE_CLASS = "app-tabbar-clearance xl:pb-6";
14
+ export type MobileTabBarProps = {
15
+ items: MobileTabItem[];
16
+ className?: string;
17
+ /** Extra trailing control (e.g. More) */
18
+ trailing?: ReactNode;
19
+ ariaLabel?: string;
20
+ /** Slide the bar away (e.g. while a text field is focused on iOS). */
21
+ hidden?: boolean;
22
+ /**
23
+ * Position inside a parent instead of the viewport, and keep the bar visible
24
+ * on large screens (docs previews, device frames).
25
+ */
26
+ contained?: boolean;
27
+ };
28
+ /**
29
+ * iOS 26-style floating liquid-glass tab bar.
30
+ * Sits above the home indicator as a capsule (not a full-bleed dock).
31
+ */
32
+ export declare function MobileTabBar({ items, className, trailing, ariaLabel, hidden, contained, }: MobileTabBarProps): import("react").JSX.Element;
33
+ //# sourceMappingURL=MobileTabBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileTabBar.d.ts","sourceRoot":"","sources":["../../src/composites/MobileTabBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,iCAAiC,CAAC;AAEzE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,SAAS,EACT,QAAQ,EACR,SAAqB,EACrB,MAAc,EACd,SAAiB,GAClB,EAAE,iBAAiB,+BAsFnB"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ /** Main-content padding that clears the floating bar until the desktop sidebar takes over. */
4
+ export const APP_TABBAR_CLEARANCE_CLASS = "app-tabbar-clearance xl:pb-6";
5
+ /**
6
+ * iOS 26-style floating liquid-glass tab bar.
7
+ * Sits above the home indicator as a capsule (not a full-bleed dock).
8
+ */
9
+ export function MobileTabBar({ items, className, trailing, ariaLabel = "Primary", hidden = false, contained = false, }) {
10
+ return (_jsx("div", { "data-slot": "mobile-tab-bar", "aria-hidden": hidden || undefined, "data-mobile-tabbar-hidden": hidden ? "true" : undefined, className: cn("pointer-events-none z-50 flex justify-center", contained ? "absolute inset-x-0 bottom-0" : "fixed inset-x-0 bottom-0 xl:hidden", "transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none", hidden &&
11
+ "translate-y-[calc(100%+var(--app-tabbar-inset-y)+env(safe-area-inset-bottom,0px))] opacity-0", className), children: _jsx("div", { "data-app-tabbar-shell": true, className: cn("rounded-full", contained
12
+ ? "mb-1.5 w-[calc(100%-1rem)] max-w-[26.5rem]"
13
+ : "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]", hidden ? "pointer-events-none" : "pointer-events-auto"), children: _jsxs("nav", { "data-app-tabbar": true, "aria-label": ariaLabel, className: "glass-tabbar flex h-[var(--app-tabbar-height)] items-stretch gap-0.5 px-1.5", children: [items.map((item) => {
14
+ const Icon = item.icon;
15
+ const classNameItem = cn("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", "touch-manipulation select-none transition-[color,transform] duration-150 active:scale-[0.96]", item.active ? "text-primary" : "text-muted-foreground hover:text-foreground");
16
+ const body = (_jsxs(_Fragment, { children: [item.active ? (_jsx("span", { className: "absolute inset-[3px] rounded-full bg-white/50 shadow-[inset_0_1px_0_var(--glass-highlight)] dark:bg-white/[0.14]", "aria-hidden": true })) : null, _jsxs("span", { className: "relative", children: [_jsx(Icon, { className: "relative h-5 w-5 shrink-0", strokeWidth: item.active ? 2.25 : 1.9, fill: item.active ? "currentColor" : "none", fillOpacity: item.active ? 0.18 : 0, "aria-hidden": true }), item.badge] }), _jsx("span", { className: cn("relative max-w-full truncate text-[10px] leading-none tracking-tight", item.active ? "font-semibold text-primary" : "font-medium text-muted-foreground"), children: item.label })] }));
17
+ return item.href ? (_jsx("a", { href: item.href, className: classNameItem, onClick: item.onClick, "aria-current": item.active ? "page" : undefined, children: body }, item.id)) : (_jsx("button", { type: "button", className: classNameItem, onClick: item.onClick, children: body }, item.id));
18
+ }), trailing] }) }) }));
19
+ }
@@ -0,0 +1,28 @@
1
+ import { type ReactNode } from "react";
2
+ export type NotificationItem = {
3
+ id: string;
4
+ title: string;
5
+ body?: string;
6
+ createdAtLabel?: string;
7
+ isRead?: boolean;
8
+ icon?: ReactNode;
9
+ onOpen?: () => void;
10
+ onDismiss?: () => void;
11
+ };
12
+ export type NotificationBellProps = {
13
+ unreadCount?: number;
14
+ items?: NotificationItem[];
15
+ open: boolean;
16
+ onOpenChange: (open: boolean) => void;
17
+ onMarkAll?: () => void;
18
+ emptyState?: ReactNode;
19
+ className?: string;
20
+ panelClassName?: string;
21
+ title?: string;
22
+ };
23
+ /**
24
+ * Notification trigger + glass panel list. Customize items and actions fully.
25
+ * Panel uses an opaque surface so sticky page chrome (e.g. "+ Record") cannot show through.
26
+ */
27
+ export declare function NotificationBell({ unreadCount, items, open, onOpenChange, onMarkAll, emptyState, className, panelClassName, title, }: NotificationBellProps): import("react").JSX.Element;
28
+ //# sourceMappingURL=NotificationBell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationBell.d.ts","sourceRoot":"","sources":["../../src/composites/NotificationBell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMlD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,WAAe,EACf,KAAU,EACV,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,SAAS,EACT,cAAc,EACd,KAAuB,GACxB,EAAE,qBAAqB,+BAwHvB"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect } from "react";
3
+ import { Bell } from "lucide-react";
4
+ import { cn } from "../lib/cn";
5
+ import { Button } from "../primitives/Button";
6
+ import { HeaderIconButton } from "./HeaderIconButton";
7
+ /**
8
+ * Notification trigger + glass panel list. Customize items and actions fully.
9
+ * Panel uses an opaque surface so sticky page chrome (e.g. "+ Record") cannot show through.
10
+ */
11
+ export function NotificationBell({ unreadCount = 0, items = [], open, onOpenChange, onMarkAll, emptyState, className, panelClassName, title = "Notifications", }) {
12
+ useEffect(() => {
13
+ if (!open)
14
+ return;
15
+ const onKey = (e) => {
16
+ if (e.key === "Escape")
17
+ onOpenChange(false);
18
+ };
19
+ window.addEventListener("keydown", onKey);
20
+ return () => window.removeEventListener("keydown", onKey);
21
+ }, [open, onOpenChange]);
22
+ return (_jsxs("div", { "data-slot": "notification-bell", className: cn("relative z-20", className), children: [_jsx(HeaderIconButton, { badge: unreadCount > 0 ? (unreadCount > 99 ? "99+" : unreadCount) : undefined, active: open, "aria-label": title, "aria-expanded": open, onClick: () => onOpenChange(!open), children: _jsx(Bell, { className: cn("h-4 w-4", unreadCount > 0 && "text-primary") }) }), open ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "fixed inset-0 z-[90]", "aria-label": "Close notifications", onClick: () => onOpenChange(false) }), _jsxs("div", { role: "dialog", "aria-label": title, className: cn("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)]", panelClassName), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-4 py-3.5", children: [_jsx("p", { className: "text-[10px] font-black uppercase tracking-[0.2em] text-muted-foreground/60", children: title }), onMarkAll && unreadCount > 0 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onMarkAll, children: "Mark all" })) : null] }), _jsx("div", { className: "mx-4 h-px bg-border/40" }), _jsx("div", { className: "max-h-80 overflow-y-auto py-2", children: items.length === 0
23
+ ? emptyState ?? (_jsx("p", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: "You\u2019re all caught up." }))
24
+ : items.map((item) => (_jsxs("div", { className: cn("flex gap-3 px-4 py-3 transition-colors hover:bg-muted/30", !item.isRead && "bg-primary/[0.035]"), children: [_jsx("div", { className: "mt-0.5 shrink-0 text-muted-foreground", children: item.icon ?? _jsx(Bell, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("p", { className: cn("min-w-0 flex-1 text-sm", item.isRead ? "font-medium" : "font-bold"), children: item.title }), !item.isRead ? (_jsx("span", { className: "mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-primary" })) : null] }), item.body ? (_jsx("p", { className: "mt-0.5 line-clamp-2 text-xs text-muted-foreground", children: item.body })) : null, _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: [item.createdAtLabel ? (_jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70", children: item.createdAtLabel })) : null, item.onOpen ? (_jsx("button", { type: "button", className: "text-[10px] font-black uppercase tracking-widest text-primary", onClick: item.onOpen, children: "Open" })) : null, item.onDismiss ? (_jsx("button", { type: "button", className: "text-[10px] font-black uppercase tracking-widest text-muted-foreground", onClick: item.onDismiss, children: "Dismiss" })) : null] })] })] }, item.id))) })] })] })) : null] }));
25
+ }
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ export type PageHeaderProps = {
3
+ kicker?: string;
4
+ title: string;
5
+ description?: string;
6
+ actions?: ReactNode;
7
+ className?: string;
8
+ };
9
+ /** Page title block with optional kicker and action slot. */
10
+ export declare function PageHeader({ kicker, title, description, actions, className }: PageHeaderProps): import("react").JSX.Element;
11
+ //# sourceMappingURL=PageHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageHeader.d.ts","sourceRoot":"","sources":["../../src/composites/PageHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,eAAe,+BAc7F"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ /** Page title block with optional kicker and action slot. */
4
+ export function PageHeader({ kicker, title, description, actions, className }) {
5
+ return (_jsxs("div", { "data-slot": "page-header", className: cn("flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", className), children: [_jsxs("div", { className: "min-w-0", children: [kicker ? _jsx("p", { className: "app-section-kicker", children: kicker }) : null, _jsx("h1", { className: "app-title", children: title }), description ? _jsx("p", { className: "app-subtitle", children: description }) : null] }), actions ? _jsx("div", { className: "flex shrink-0 flex-wrap gap-2", children: actions }) : null] }));
6
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from "react";
2
+ export type PageShellProps = {
3
+ children: ReactNode;
4
+ className?: string;
5
+ /** Include .app-shell horizontal padding. */
6
+ padded?: boolean;
7
+ };
8
+ /**
9
+ * Full-page backdrop with theme accent washes (.app-page).
10
+ */
11
+ export declare function PageShell({ children, className, padded }: PageShellProps): import("react").JSX.Element;
12
+ //# sourceMappingURL=PageShell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageShell.d.ts","sourceRoot":"","sources":["../../src/composites/PageShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAa,EAAE,EAAE,cAAc,+BAM/E"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ /**
4
+ * Full-page backdrop with theme accent washes (.app-page).
5
+ */
6
+ export function PageShell({ children, className, padded = true }) {
7
+ return (_jsx("div", { "data-slot": "page-shell", className: cn("app-page", className), children: _jsx("div", { className: cn(padded && "app-shell"), children: children }) }));
8
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import type { ReactNode } from "react";
3
+ export type PageStickyHeaderProps = React.ComponentProps<"div"> & {
4
+ /** Offset below a fixed app bar (defaults to top-16 → --app-chrome-offset). */
5
+ offsetClassName?: string;
6
+ title?: ReactNode;
7
+ subtitle?: ReactNode;
8
+ count?: ReactNode;
9
+ leading?: ReactNode;
10
+ icon?: ReactNode;
11
+ search?: ReactNode;
12
+ actions?: ReactNode;
13
+ /** Full-width row under the main toolbar (mobile search / secondary actions). */
14
+ below?: ReactNode;
15
+ belowClassName?: string;
16
+ showBack?: boolean;
17
+ onBack?: () => void;
18
+ titleClassName?: string;
19
+ subtitleClassName?: string;
20
+ density?: "compact" | "comfortable";
21
+ mobileSearch?: ReactNode;
22
+ };
23
+ export declare function PageHeaderBadge({ children, className, }: {
24
+ children: ReactNode;
25
+ className?: string;
26
+ }): React.JSX.Element;
27
+ /**
28
+ * Sticky glass page chrome — title, icon badge, search & action slots.
29
+ * Matches journalD PageHeader (density, count, below row, page-header-* type).
30
+ */
31
+ declare const PageStickyHeader: React.ForwardRefExoticComponent<Omit<PageStickyHeaderProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
32
+ export { PageStickyHeader };
33
+ //# sourceMappingURL=PageStickyHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageStickyHeader.d.ts","sourceRoot":"","sources":["../../src/composites/PageStickyHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAChE,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,iFAAiF;IACjF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,qBAYA;AAED;;;GAGG;AACH,QAAA,MAAM,gBAAgB,2GAsGrB,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { ArrowLeft } from "lucide-react";
4
+ import { cn } from "../lib/cn";
5
+ import { Button } from "../primitives/Button";
6
+ export function PageHeaderBadge({ children, className, }) {
7
+ return (_jsx("div", { className: cn("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", className), "aria-hidden": true, children: children }));
8
+ }
9
+ /**
10
+ * Sticky glass page chrome — title, icon badge, search & action slots.
11
+ * Matches journalD PageHeader (density, count, below row, page-header-* type).
12
+ */
13
+ const PageStickyHeader = React.forwardRef(({ className, offsetClassName = "top-16", title, subtitle, count, leading, icon, search, actions, below, belowClassName, showBack, onBack, titleClassName, subtitleClassName, density = "compact", mobileSearch, children, ...props }, ref) => {
14
+ const shouldShowBack = showBack ?? Boolean(onBack);
15
+ const belowRow = below ?? mobileSearch;
16
+ return (_jsx("div", { ref: ref, "data-slot": "page-sticky-header", className: cn("glass-header sticky z-30 shrink-0 border-b border-border/50", offsetClassName, className), ...props, children: children ?? (_jsxs("div", { className: cn("app-frame-pad", density === "comfortable" ? "py-2.5 sm:py-3" : "py-1.5 sm:py-2"), children: [_jsxs("div", { className: cn("flex w-full min-w-0 items-center", density === "comfortable" ? "gap-2.5 sm:gap-3" : "gap-2"), children: [shouldShowBack ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0 rounded-xl", onClick: onBack, "aria-label": "Go back", children: _jsx(ArrowLeft, { className: "h-4 w-4" }) })) : null, leading, icon ? _jsx(PageHeaderBadge, { children: icon }) : null, (title || subtitle || count != null) && (_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [title ? (_jsx("h1", { className: cn("page-header-title", titleClassName ?? "truncate"), children: title })) : null, count != null && count !== false ? (typeof count === "string" || typeof count === "number" ? (_jsx("span", { className: "page-header-count", children: count })) : (count)) : null] }), subtitle ? (_jsx("div", { className: cn("page-header-subtitle mt-0.5 truncate", subtitleClassName), children: subtitle })) : null] })), search ? (_jsx("div", { className: "hidden min-w-0 flex-1 md:block md:max-w-xs lg:max-w-sm", children: search })) : null, actions ? (_jsx("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: actions })) : null] }), belowRow ? _jsx("div", { className: cn("mt-2", belowClassName), children: belowRow }) : null] })) }));
17
+ });
18
+ PageStickyHeader.displayName = "PageStickyHeader";
19
+ export { PageStickyHeader };
@@ -0,0 +1,7 @@
1
+ export declare function Pagination({ page, pageCount, onPageChange, className, }: {
2
+ page: number;
3
+ pageCount: number;
4
+ onPageChange: (page: number) => void;
5
+ className?: string;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/composites/Pagination.tsx"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BA2BA"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn";
3
+ import { Button } from "../primitives/Button";
4
+ export function Pagination({ page, pageCount, onPageChange, className, }) {
5
+ return (_jsxs("div", { "data-slot": "pagination", className: cn("flex items-center justify-center gap-2", className), children: [_jsx(Button, { variant: "outline", size: "sm", disabled: page <= 1, onClick: () => onPageChange(page - 1), children: "Prev" }), _jsxs("span", { className: "min-w-16 text-center text-xs font-bold text-muted-foreground", children: [page, " / ", pageCount] }), _jsx(Button, { variant: "outline", size: "sm", disabled: page >= pageCount, onClick: () => onPageChange(page + 1), children: "Next" })] }));
6
+ }
@@ -0,0 +1,18 @@
1
+ export type PhotoViewerItem = {
2
+ src: string;
3
+ alt?: string;
4
+ caption?: string;
5
+ };
6
+ export type PhotoViewerProps = {
7
+ open: boolean;
8
+ onOpenChange: (open: boolean) => void;
9
+ items: PhotoViewerItem[];
10
+ index?: number;
11
+ onIndexChange?: (index: number) => void;
12
+ className?: string;
13
+ };
14
+ /**
15
+ * Full-screen photo viewer / lightbox with zoom and prev/next.
16
+ */
17
+ export declare function PhotoViewer({ open, onOpenChange, items, index: controlledIndex, onIndexChange, className, }: PhotoViewerProps): import("react").JSX.Element | null;
18
+ //# sourceMappingURL=PhotoViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhotoViewer.d.ts","sourceRoot":"","sources":["../../src/composites/PhotoViewer.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,KAAK,EAAE,eAAe,EACtB,aAAa,EACb,SAAS,GACV,EAAE,gBAAgB,sCA+GlB"}
@@ -0,0 +1,37 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { ChevronLeft, ChevronRight, X, ZoomIn, ZoomOut } from "lucide-react";
4
+ import { cn } from "../lib/cn";
5
+ import { Button } from "../primitives/Button";
6
+ /**
7
+ * Full-screen photo viewer / lightbox with zoom and prev/next.
8
+ */
9
+ export function PhotoViewer({ open, onOpenChange, items, index: controlledIndex, onIndexChange, className, }) {
10
+ const [internalIndex, setInternalIndex] = useState(0);
11
+ const [zoom, setZoom] = useState(1);
12
+ const index = controlledIndex ?? internalIndex;
13
+ const setIndex = useCallback((next) => {
14
+ const clamped = Math.max(0, Math.min(items.length - 1, next));
15
+ setInternalIndex(clamped);
16
+ onIndexChange?.(clamped);
17
+ setZoom(1);
18
+ }, [items.length, onIndexChange]);
19
+ useEffect(() => {
20
+ if (!open)
21
+ return;
22
+ const onKey = (e) => {
23
+ if (e.key === "Escape")
24
+ onOpenChange(false);
25
+ if (e.key === "ArrowLeft")
26
+ setIndex(index - 1);
27
+ if (e.key === "ArrowRight")
28
+ setIndex(index + 1);
29
+ };
30
+ window.addEventListener("keydown", onKey);
31
+ return () => window.removeEventListener("keydown", onKey);
32
+ }, [open, index, onOpenChange, setIndex]);
33
+ if (!open || items.length === 0)
34
+ return null;
35
+ const item = items[index];
36
+ return (_jsxs("div", { "data-slot": "photo-viewer", role: "dialog", "aria-modal": "true", "aria-label": item?.alt || "Photo viewer", className: cn("fixed inset-0 z-[60] flex flex-col bg-black/90", className), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 text-white", children: [_jsxs("p", { className: "truncate text-sm font-bold", children: [index + 1, " / ", items.length, item?.caption ? ` · ${item.caption}` : ""] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "text-white hover:bg-white/10", onClick: () => setZoom((z) => Math.max(1, z - 0.25)), "aria-label": "Zoom out", children: _jsx(ZoomOut, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "text-white hover:bg-white/10", onClick: () => setZoom((z) => Math.min(3, z + 0.25)), "aria-label": "Zoom in", children: _jsx(ZoomIn, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "text-white hover:bg-white/10", onClick: () => onOpenChange(false), "aria-label": "Close", children: _jsx(X, { className: "size-4" }) })] })] }), _jsxs("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden px-12", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "absolute left-2 text-white hover:bg-white/10", disabled: index <= 0, onClick: () => setIndex(index - 1), "aria-label": "Previous", children: _jsx(ChevronLeft, { className: "size-6" }) }), item ? (_jsx("img", { src: item.src, alt: item.alt || "", className: "max-h-full max-w-full object-contain transition-transform duration-200", style: { transform: `scale(${zoom})` }, draggable: false })) : null, _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "absolute right-2 text-white hover:bg-white/10", disabled: index >= items.length - 1, onClick: () => setIndex(index + 1), "aria-label": "Next", children: _jsx(ChevronRight, { className: "size-6" }) })] })] }));
37
+ }