@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,359 @@
1
+ import {
2
+ useEffect,
3
+ useRef,
4
+ useState,
5
+ type CSSProperties,
6
+ type ReactNode,
7
+ } from "react";
8
+ import { ChevronLeft } from "lucide-react";
9
+ import { cn } from "../lib/cn";
10
+ import {
11
+ coverFloatingIconClass,
12
+ coverMutedClass,
13
+ coverPlateClass,
14
+ coverTitleClass,
15
+ } from "../lib/cover-chrome";
16
+ import {
17
+ getCoverPattern,
18
+ type CoverPatternKind,
19
+ } from "../lib/cover-pattern";
20
+ import { useCoverChromeBleed } from "../lib/use-cover-chrome-bleed";
21
+ import { getInitials } from "../lib/avatar-utils";
22
+ import { CoverPattern } from "./CoverPattern";
23
+
24
+ export type CoverHeroAvatar = {
25
+ name: string;
26
+ src?: string;
27
+ href?: string;
28
+ onClick?: () => void;
29
+ };
30
+
31
+ export type CoverHeroFact = {
32
+ key: string;
33
+ label: string;
34
+ value: ReactNode;
35
+ icon?: ReactNode;
36
+ wide?: boolean;
37
+ muted?: boolean;
38
+ };
39
+
40
+ export type CoverHeroProps = {
41
+ title: string;
42
+ kicker?: ReactNode;
43
+ facts?: CoverHeroFact[];
44
+ avatars?: CoverHeroAvatar[];
45
+ hiddenAvatarCount?: number;
46
+ coverUrl?: string;
47
+ hideImages?: boolean;
48
+ coverSeed?: string;
49
+ coverHue?: number;
50
+ coverKind?: CoverPatternKind | null;
51
+ dark?: boolean;
52
+ warm?: boolean;
53
+ /** Profile bleeds into a tab bar; story ends at the identity plate. */
54
+ variant?: "story" | "profile";
55
+ onBack?: () => void;
56
+ backLabel?: string;
57
+ floatingActions?: ReactNode;
58
+ compactActions?: ReactNode;
59
+ children?: ReactNode;
60
+ className?: string;
61
+ /** Offset used when the compact bar pins under app chrome. */
62
+ compactOffsetClassName?: string;
63
+ /** When false, skip writing cover-bleed attributes on `<html>` (catalog previews). */
64
+ bleed?: boolean;
65
+ };
66
+
67
+ /**
68
+ * Cover hero — generated / photo cover that bleeds into app chrome, floating
69
+ * glass controls, a notched identity plate, and a compact sticky bar once the
70
+ * hero scrolls away. Mirrors journalD StoryCoverHeader / Profile hero.
71
+ */
72
+ export function CoverHero({
73
+ title,
74
+ kicker,
75
+ facts,
76
+ avatars = [],
77
+ hiddenAvatarCount = 0,
78
+ coverUrl,
79
+ hideImages,
80
+ coverSeed,
81
+ coverHue = 210,
82
+ coverKind,
83
+ dark,
84
+ warm,
85
+ variant = "story",
86
+ onBack,
87
+ backLabel = "Go back",
88
+ floatingActions,
89
+ compactActions,
90
+ children,
91
+ className,
92
+ compactOffsetClassName = "top-16",
93
+ bleed = true,
94
+ }: CoverHeroProps) {
95
+ const seed = coverSeed ?? title;
96
+ const scene = getCoverPattern({ seed, hue: coverHue, dark, warm, kind: coverKind });
97
+ const onDarkCover = scene.headerInk === "light";
98
+ const mediaClass = variant === "profile" ? "cover-media cover-media--profile" : "cover-media cover-media--story";
99
+
100
+ const visibleAvatars = avatars.slice(0, 3);
101
+ const extra = Math.max(0, hiddenAvatarCount || avatars.length - visibleAvatars.length);
102
+ const avatarCount = visibleAvatars.length + (extra > 0 ? 1 : 0);
103
+ const hasAvatars = avatarCount > 0;
104
+
105
+ const heroRef = useRef<HTMLElement>(null);
106
+ const plateRef = useRef<HTMLDivElement>(null);
107
+ const [coverUnderChrome, setCoverUnderChrome] = useState(true);
108
+ const [heroInView, setHeroInView] = useState(true);
109
+
110
+ useEffect(() => {
111
+ let frame = 0;
112
+ const measure = () => {
113
+ frame = 0;
114
+ const hero = heroRef.current;
115
+ const plate = plateRef.current;
116
+ if (!hero || !plate) return;
117
+ const chromeHeight =
118
+ document.querySelector<HTMLElement>(".app-chrome-header")?.offsetHeight ?? 64;
119
+ const heroRect = hero.getBoundingClientRect();
120
+ if (heroRect.height === 0) return;
121
+ setCoverUnderChrome(plate.getBoundingClientRect().top > chromeHeight + 1);
122
+ setHeroInView(heroRect.bottom > chromeHeight + 1);
123
+ };
124
+ const schedule = () => {
125
+ if (!frame) frame = window.requestAnimationFrame(measure);
126
+ };
127
+ measure();
128
+ window.addEventListener("scroll", schedule, { passive: true });
129
+ window.addEventListener("resize", schedule);
130
+ return () => {
131
+ if (frame) window.cancelAnimationFrame(frame);
132
+ window.removeEventListener("scroll", schedule);
133
+ window.removeEventListener("resize", schedule);
134
+ };
135
+ }, []);
136
+
137
+ useCoverChromeBleed(bleed && coverUnderChrome, scene.chromeInk);
138
+
139
+ return (
140
+ <>
141
+ <header
142
+ ref={heroRef}
143
+ data-slot="cover-hero"
144
+ className={cn("relative z-10 w-full overflow-visible", className)}
145
+ >
146
+ <div className={cn(mediaClass, "sm:rounded-b-[1.75rem]")} aria-hidden>
147
+ {!hideImages && coverUrl ? (
148
+ <img src={coverUrl} alt="" className="absolute inset-0 size-full object-cover" />
149
+ ) : (
150
+ <CoverPattern seed={seed} hue={coverHue} dark={dark} warm={warm} kind={coverKind} />
151
+ )}
152
+ <div className="absolute inset-x-0 top-0 h-8 bg-gradient-to-b from-black/[0.04] to-transparent dark:from-black/12" />
153
+ <div className="absolute inset-x-0 bottom-0 h-[22%] bg-gradient-to-t from-black/[0.03] via-transparent to-transparent dark:from-black/10" />
154
+ </div>
155
+
156
+ <div className="relative z-10 flex flex-col">
157
+ <div className="relative min-h-[8.5rem] sm:min-h-[10.5rem] lg:min-h-[12rem]">
158
+ <div className="absolute inset-x-0 top-0 z-30 flex items-start justify-between gap-2 px-3 pt-3 sm:px-5 sm:pt-4">
159
+ {onBack ? (
160
+ <button
161
+ type="button"
162
+ onClick={onBack}
163
+ aria-label={backLabel}
164
+ data-cover-ink={scene.headerInk}
165
+ className={cn(
166
+ "cover-floating-shell flex items-center gap-1 px-3 py-1.5 text-xs font-semibold transition-colors active:scale-[0.96]",
167
+ onDarkCover ? "text-white hover:bg-black/60" : "text-zinc-900 hover:bg-white/90",
168
+ )}
169
+ >
170
+ <ChevronLeft className="h-3.5 w-3.5 shrink-0" aria-hidden />
171
+ <span className="hidden sm:inline">Back</span>
172
+ </button>
173
+ ) : (
174
+ <span />
175
+ )}
176
+ {floatingActions ? (
177
+ <div
178
+ data-cover-ink={scene.headerInk}
179
+ className="cover-floating-shell flex items-center gap-1 p-1"
180
+ >
181
+ {floatingActions}
182
+ </div>
183
+ ) : null}
184
+ </div>
185
+ </div>
186
+
187
+ <div ref={plateRef} className="relative z-20 px-3.5 pb-4 sm:px-5 sm:pb-5 lg:px-8 lg:pb-6">
188
+ <div
189
+ className="cover-identity story-identity"
190
+ style={{ "--story-avatar-count": avatarCount } as CSSProperties}
191
+ >
192
+ <div
193
+ aria-hidden
194
+ className={cn(
195
+ "pointer-events-none absolute inset-0 rounded-[1.35rem]",
196
+ hasAvatars && "cover-identity__plate story-identity__plate",
197
+ coverPlateClass(scene.headerInk),
198
+ )}
199
+ />
200
+ <div
201
+ className={cn(
202
+ "relative z-10 flex flex-col items-center px-3 pb-3 text-center sm:px-6 sm:pb-4",
203
+ hasAvatars ? "pt-0" : "pt-3 sm:pt-4",
204
+ )}
205
+ >
206
+ {hasAvatars ? (
207
+ <div className="cover-identity__avatars story-identity__avatars">
208
+ <div className="flex -space-x-5 sm:-space-x-6">
209
+ {visibleAvatars.map((avatar, index) => {
210
+ const body = (
211
+ <span
212
+ style={{
213
+ width: "var(--story-avatar-size)",
214
+ height: "var(--story-avatar-size)",
215
+ }}
216
+ className="flex items-center justify-center overflow-hidden rounded-full border-[3px] border-white bg-muted text-lg font-black text-primary shadow-[0_16px_40px_rgba(0,0,0,0.38)] dark:border-white/30"
217
+ >
218
+ {!hideImages && avatar.src ? (
219
+ <img src={avatar.src} alt="" className="size-full object-cover" />
220
+ ) : (
221
+ getInitials(avatar.name)
222
+ )}
223
+ </span>
224
+ );
225
+ return (
226
+ <span
227
+ key={avatar.name + index}
228
+ style={{ zIndex: visibleAvatars.length - index }}
229
+ className="relative"
230
+ >
231
+ {avatar.href ? (
232
+ <a
233
+ href={avatar.href}
234
+ onClick={avatar.onClick}
235
+ title={avatar.name}
236
+ aria-label={avatar.name}
237
+ className="block rounded-full outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
238
+ >
239
+ {body}
240
+ </a>
241
+ ) : (
242
+ <button
243
+ type="button"
244
+ onClick={avatar.onClick}
245
+ title={avatar.name}
246
+ aria-label={avatar.name}
247
+ className="block rounded-full outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
248
+ >
249
+ {body}
250
+ </button>
251
+ )}
252
+ </span>
253
+ );
254
+ })}
255
+ {extra > 0 ? (
256
+ <span
257
+ style={{
258
+ width: "var(--story-avatar-size)",
259
+ height: "var(--story-avatar-size)",
260
+ }}
261
+ className="relative z-0 flex items-center justify-center rounded-full border-[3px] border-white bg-zinc-900/80 text-base font-black text-white dark:border-white/30"
262
+ >
263
+ +{extra}
264
+ </span>
265
+ ) : null}
266
+ </div>
267
+ </div>
268
+ ) : null}
269
+
270
+ <div className={cn("w-full min-w-0", hasAvatars && "mt-2 sm:mt-2.5")}>
271
+ {kicker ? (
272
+ <div className="flex flex-wrap items-center justify-center gap-1.5">{kicker}</div>
273
+ ) : null}
274
+ <h1
275
+ className={cn(
276
+ "mt-2 text-balance text-[1.15rem] font-black leading-tight tracking-tight sm:text-[1.35rem] lg:text-[1.65rem]",
277
+ coverTitleClass(scene.headerInk),
278
+ )}
279
+ >
280
+ {title}
281
+ </h1>
282
+ {facts && facts.length > 0 ? (
283
+ <dl className="mt-3 grid grid-cols-2 gap-x-4 gap-y-2 sm:flex sm:flex-wrap sm:justify-center sm:gap-x-6">
284
+ {facts.map((fact) => (
285
+ <div
286
+ key={fact.key}
287
+ className={cn(
288
+ "flex min-w-0 items-center justify-center gap-2",
289
+ fact.wide && "col-span-2 sm:col-span-1",
290
+ )}
291
+ >
292
+ {fact.icon ? (
293
+ <span
294
+ className={cn(
295
+ "hidden h-7 w-7 shrink-0 items-center justify-center rounded-lg border backdrop-blur-md sm:flex",
296
+ onDarkCover
297
+ ? "border-white/20 bg-white/15 text-white/90"
298
+ : "border-zinc-950/10 bg-zinc-950/[0.06] text-zinc-700",
299
+ )}
300
+ >
301
+ {fact.icon}
302
+ </span>
303
+ ) : null}
304
+ <div className="min-w-0 text-center sm:text-left">
305
+ <dt
306
+ className={cn(
307
+ "text-[9px] font-black uppercase tracking-[0.18em]",
308
+ coverMutedClass(scene.headerInk),
309
+ )}
310
+ >
311
+ {fact.label}
312
+ </dt>
313
+ <dd
314
+ className={cn(
315
+ "truncate text-[11px] font-bold sm:text-xs",
316
+ coverTitleClass(scene.headerInk),
317
+ fact.muted && "opacity-60",
318
+ )}
319
+ >
320
+ {fact.value}
321
+ </dd>
322
+ </div>
323
+ </div>
324
+ ))}
325
+ </dl>
326
+ ) : null}
327
+ {children}
328
+ </div>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+ </header>
334
+
335
+ <div className={cn("sticky z-[60] h-0", compactOffsetClassName)}>
336
+ {!heroInView ? (
337
+ <div className="glass-header cover-chrome-bar absolute inset-x-0 top-0 border-b border-border/50 bg-background/85 backdrop-blur-md dark:bg-background/94">
338
+ <div className="app-frame-pad flex items-center gap-2 py-1.5 sm:py-2">
339
+ {onBack ? (
340
+ <button
341
+ type="button"
342
+ onClick={onBack}
343
+ aria-label={backLabel}
344
+ className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl text-foreground transition-colors hover:bg-muted"
345
+ >
346
+ <ChevronLeft className="h-4 w-4" aria-hidden />
347
+ </button>
348
+ ) : null}
349
+ <h2 className="page-header-title min-w-0 flex-1 truncate">{title}</h2>
350
+ {compactActions}
351
+ </div>
352
+ </div>
353
+ ) : null}
354
+ </div>
355
+ </>
356
+ );
357
+ }
358
+
359
+ export { coverFloatingIconClass };
@@ -0,0 +1,90 @@
1
+ import { useMemo } from "react";
2
+ import { cn } from "../lib/cn";
3
+ import {
4
+ getCoverPattern,
5
+ type CoverPatternInput,
6
+ } from "../lib/cover-pattern";
7
+
8
+ export type CoverPatternProps = CoverPatternInput & {
9
+ className?: string;
10
+ };
11
+
12
+ /**
13
+ * Seeded nature-inspired cover when no cover photo is set.
14
+ * Scene kind is unique per seed; colors follow hue + theme.
15
+ */
16
+ export function CoverPattern({
17
+ seed,
18
+ hue,
19
+ secondaryHue,
20
+ tertiaryHue,
21
+ dark,
22
+ warm,
23
+ kind,
24
+ className,
25
+ }: CoverPatternProps) {
26
+ const scene = useMemo(
27
+ () => getCoverPattern({ seed, hue, secondaryHue, tertiaryHue, dark, warm, kind }),
28
+ [seed, hue, secondaryHue, tertiaryHue, dark, warm, kind],
29
+ );
30
+
31
+ return (
32
+ <div
33
+ data-cover-pattern={scene.kind}
34
+ className={cn("absolute inset-0 overflow-hidden", className)}
35
+ aria-hidden
36
+ >
37
+ <div
38
+ className="absolute inset-0 transition-[background-color,background-image] duration-700 ease-out"
39
+ style={scene.skyStyle}
40
+ />
41
+ <svg
42
+ viewBox="0 0 1600 420"
43
+ preserveAspectRatio="xMidYMid slice"
44
+ className="absolute inset-0 size-full"
45
+ >
46
+ {scene.ellipses.map((ellipse, index) => (
47
+ <ellipse
48
+ key={`e-${scene.uid}-${index}`}
49
+ cx={ellipse.cx}
50
+ cy={ellipse.cy}
51
+ rx={ellipse.rx}
52
+ ry={ellipse.ry}
53
+ fill={ellipse.fill}
54
+ opacity={ellipse.opacity}
55
+ />
56
+ ))}
57
+ {scene.circles.map((circle, index) => (
58
+ <circle
59
+ key={`c-${scene.uid}-${index}`}
60
+ cx={circle.cx}
61
+ cy={circle.cy}
62
+ r={circle.r}
63
+ fill={circle.fill}
64
+ opacity={circle.opacity}
65
+ />
66
+ ))}
67
+ {scene.paths.map((path, index) => (
68
+ <path
69
+ key={`p-${scene.uid}-${index}`}
70
+ d={path.d}
71
+ fill={path.fill}
72
+ opacity={path.opacity}
73
+ />
74
+ ))}
75
+ {scene.polygons.map((polygon, index) => (
76
+ <polygon
77
+ key={`g-${scene.uid}-${index}`}
78
+ points={polygon.points}
79
+ fill={polygon.fill}
80
+ opacity={polygon.opacity}
81
+ />
82
+ ))}
83
+ </svg>
84
+ <div className="profile-cover-grain pointer-events-none absolute inset-0" />
85
+ </div>
86
+ );
87
+ }
88
+
89
+ /** @deprecated Use CoverPattern — kept as the journalD name. */
90
+ export const ProfileCoverPattern = CoverPattern;
@@ -0,0 +1,48 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../lib/cn";
3
+ import { SearchField } from "./SearchField";
4
+
5
+ export type DataTableToolbarProps = {
6
+ query?: string;
7
+ onQueryChange?: (value: string) => void;
8
+ searchPlaceholder?: string;
9
+ filters?: ReactNode;
10
+ actions?: ReactNode;
11
+ className?: string;
12
+ hideSearch?: boolean;
13
+ };
14
+
15
+ /** Search + filter slot + actions row above data tables. */
16
+ export function DataTableToolbar({
17
+ query = "",
18
+ onQueryChange,
19
+ searchPlaceholder = "Search…",
20
+ filters,
21
+ actions,
22
+ className,
23
+ hideSearch,
24
+ }: DataTableToolbarProps) {
25
+ return (
26
+ <div
27
+ data-slot="data-table-toolbar"
28
+ className={cn(
29
+ "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
30
+ className,
31
+ )}
32
+ >
33
+ <div className="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-center">
34
+ {!hideSearch && onQueryChange ? (
35
+ <SearchField
36
+ value={query}
37
+ onChange={(e) => onQueryChange(e.target.value)}
38
+ placeholder={searchPlaceholder}
39
+ containerClassName="w-full sm:max-w-xs"
40
+ className="h-10 rounded-xl"
41
+ />
42
+ ) : null}
43
+ {filters ? <div className="flex flex-wrap items-center gap-2">{filters}</div> : null}
44
+ </div>
45
+ {actions ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
46
+ </div>
47
+ );
48
+ }
@@ -0,0 +1,22 @@
1
+ import { cn } from "../lib/cn";
2
+
3
+ export function DividerLabel({
4
+ label,
5
+ className,
6
+ }: {
7
+ label: string;
8
+ className?: string;
9
+ }) {
10
+ return (
11
+ <div
12
+ data-slot="divider-label"
13
+ className={cn("flex items-center gap-3", className)}
14
+ >
15
+ <div className="h-px flex-1 bg-border/60" />
16
+ <span className="text-[10px] font-black uppercase tracking-widest text-muted-foreground">
17
+ {label}
18
+ </span>
19
+ <div className="h-px flex-1 bg-border/60" />
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,55 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ import { cn } from "../lib/cn";
4
+ import { Button } from "../primitives/Button";
5
+ import { GlassCard } from "./GlassCard";
6
+
7
+ export type EmptyStateProps = {
8
+ icon?: LucideIcon;
9
+ title: string;
10
+ description?: string;
11
+ actionLabel?: string;
12
+ onAction?: () => void;
13
+ children?: ReactNode;
14
+ className?: string;
15
+ };
16
+
17
+ /**
18
+ * Empty / zero-state panel for lists and dashboards.
19
+ */
20
+ export function EmptyState({
21
+ icon: Icon,
22
+ title,
23
+ description,
24
+ actionLabel,
25
+ onAction,
26
+ children,
27
+ className,
28
+ }: EmptyStateProps) {
29
+ return (
30
+ <GlassCard
31
+ variant="subtle"
32
+ padding="lg"
33
+ data-slot="empty-state"
34
+ className={cn("flex flex-col items-center text-center", className)}
35
+ >
36
+ {Icon ? (
37
+ <div className="mb-4 rounded-2xl border border-border/40 bg-background/50 p-3 text-muted-foreground">
38
+ <Icon className="h-6 w-6" />
39
+ </div>
40
+ ) : null}
41
+ <h3 className="text-base font-black tracking-tight text-foreground">{title}</h3>
42
+ {description ? (
43
+ <p className="mt-1.5 max-w-sm text-sm font-medium text-muted-foreground">
44
+ {description}
45
+ </p>
46
+ ) : null}
47
+ {actionLabel && onAction ? (
48
+ <Button className="mt-4" onClick={onAction}>
49
+ {actionLabel}
50
+ </Button>
51
+ ) : null}
52
+ {children}
53
+ </GlassCard>
54
+ );
55
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * EntityCard — quiet glass list-row / tile baseline.
3
+ *
4
+ * Use for directory rows, trash items, and other entity summaries that need
5
+ * consistent glass surface, radius, and padding without feature-specific chrome.
6
+ */
7
+ import type { ElementType, HTMLAttributes } from "react";
8
+ import { cn } from "../lib/cn";
9
+
10
+ type EntityCardElement = "div" | "li" | "article" | "section" | "button" | "a";
11
+
12
+ export type EntityCardProps = HTMLAttributes<HTMLElement> & {
13
+ /** Rendered HTML element. Defaults to `div`. */
14
+ as?: EntityCardElement;
15
+ /** Slightly denser padding for compact lists. */
16
+ density?: "default" | "compact";
17
+ /** Soft hover lift for interactive rows (navigation / selection). */
18
+ interactive?: boolean;
19
+ href?: string;
20
+ };
21
+
22
+ /**
23
+ * Glass surface wrapper for entity list items.
24
+ */
25
+ export function EntityCard({
26
+ as,
27
+ density = "default",
28
+ interactive = false,
29
+ className,
30
+ children,
31
+ href,
32
+ ...rest
33
+ }: EntityCardProps) {
34
+ const Component = (as ?? (href ? "a" : "div")) as ElementType;
35
+
36
+ return (
37
+ <Component
38
+ {...(href ? { href } : {})}
39
+ data-slot="entity-card"
40
+ className={cn(
41
+ "entity-card",
42
+ density === "compact" && "entity-card-compact",
43
+ interactive && "entity-card-interactive",
44
+ className,
45
+ )}
46
+ {...rest}
47
+ >
48
+ {children}
49
+ </Component>
50
+ );
51
+ }
52
+
53
+ export type EntityCardTitleProps = HTMLAttributes<HTMLParagraphElement>;
54
+
55
+ /** Primary title inside an EntityCard. */
56
+ export function EntityCardTitle({ className, ...props }: EntityCardTitleProps) {
57
+ return (
58
+ <p
59
+ data-slot="entity-card-title"
60
+ className={cn(
61
+ "truncate text-sm font-semibold text-foreground sm:text-[15px]",
62
+ className,
63
+ )}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ export type EntityCardMetaProps = HTMLAttributes<HTMLParagraphElement>;
70
+
71
+ /** Secondary / muted line under the title. */
72
+ export function EntityCardMeta({ className, ...props }: EntityCardMetaProps) {
73
+ return (
74
+ <p
75
+ data-slot="entity-card-meta"
76
+ className={cn(
77
+ "mt-0.5 line-clamp-2 text-xs text-muted-foreground sm:text-[13px]",
78
+ className,
79
+ )}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ /** Compact type / status chip for entity cards. */
86
+ export function EntityCardChip({
87
+ className,
88
+ ...props
89
+ }: HTMLAttributes<HTMLSpanElement>) {
90
+ return (
91
+ <span
92
+ data-slot="entity-card-chip"
93
+ className={cn(
94
+ "inline-flex shrink-0 rounded-full border border-border/50 bg-muted/40 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-muted-foreground",
95
+ className,
96
+ )}
97
+ {...props}
98
+ />
99
+ );
100
+ }