@krizaka/ui 2.2.0 → 2.4.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 (498) hide show
  1. package/README.md +33 -8
  2. package/dist/audio-player.d.ts +113 -0
  3. package/dist/audio-player.js +207 -0
  4. package/dist/audio-player.js.map +1 -0
  5. package/dist/{button-Cgfkg_3y.d.ts → button-BMAO7dKF.d.ts} +29 -2
  6. package/dist/button.d.ts +32 -5
  7. package/dist/button.js +62 -9
  8. package/dist/button.js.map +1 -1
  9. package/dist/chip-link.d.ts +53 -0
  10. package/dist/chip-link.js +32 -0
  11. package/dist/chip-link.js.map +1 -0
  12. package/dist/chunk-2USC2YT6.js +17 -0
  13. package/dist/chunk-2USC2YT6.js.map +1 -0
  14. package/dist/chunk-43PTCDTL.js +31 -0
  15. package/dist/chunk-43PTCDTL.js.map +1 -0
  16. package/dist/chunk-6ISW63JI.js +11 -0
  17. package/dist/chunk-6ISW63JI.js.map +1 -0
  18. package/dist/chunk-EUPTXKH7.js +95 -0
  19. package/dist/chunk-EUPTXKH7.js.map +1 -0
  20. package/dist/chunk-HAQWKORZ.js +117 -0
  21. package/dist/chunk-HAQWKORZ.js.map +1 -0
  22. package/dist/{chunk-FHYZSO2S.js → chunk-R6SDS7JM.js} +5 -3
  23. package/dist/chunk-R6SDS7JM.js.map +1 -0
  24. package/dist/chunk-RUPNPMP3.js +103 -0
  25. package/dist/chunk-RUPNPMP3.js.map +1 -0
  26. package/dist/chunk-RUPRYJ45.js +82 -0
  27. package/dist/chunk-RUPRYJ45.js.map +1 -0
  28. package/dist/chunk-VDR2EP6K.js +49 -0
  29. package/dist/chunk-VDR2EP6K.js.map +1 -0
  30. package/dist/chunk-WEVYKZIF.js +33 -0
  31. package/dist/chunk-WEVYKZIF.js.map +1 -0
  32. package/dist/chunk-XUMJA5ZI.js +283 -0
  33. package/dist/chunk-XUMJA5ZI.js.map +1 -0
  34. package/dist/chunk-YH4LMWRU.js +92 -0
  35. package/dist/chunk-YH4LMWRU.js.map +1 -0
  36. package/dist/chunk-YX6CNVJA.js +94 -0
  37. package/dist/chunk-YX6CNVJA.js.map +1 -0
  38. package/dist/chunk-ZIXB33X7.js +95 -0
  39. package/dist/chunk-ZIXB33X7.js.map +1 -0
  40. package/dist/combobox.d.ts +188 -0
  41. package/dist/combobox.js +16 -0
  42. package/dist/combobox.js.map +1 -0
  43. package/dist/command.d.ts +7 -7
  44. package/dist/command.js +3 -2
  45. package/dist/command.js.map +1 -1
  46. package/dist/confirm-button.d.ts +1 -1
  47. package/dist/confirm-button.js +2 -1
  48. package/dist/confirm-button.js.map +1 -1
  49. package/dist/data-table.d.ts +246 -0
  50. package/dist/data-table.js +290 -0
  51. package/dist/data-table.js.map +1 -0
  52. package/dist/date-picker.d.ts +288 -0
  53. package/dist/date-picker.js +364 -0
  54. package/dist/date-picker.js.map +1 -0
  55. package/dist/{dialog-GQ4Nvuyf.d.ts → dialog-D42QwYb_.d.ts} +11 -1
  56. package/dist/dialog.d.ts +1 -1
  57. package/dist/dialog.js +3 -2
  58. package/dist/dialog.js.map +1 -1
  59. package/dist/file-upload.d.ts +238 -0
  60. package/dist/file-upload.js +437 -0
  61. package/dist/file-upload.js.map +1 -0
  62. package/dist/filter-bar.d.ts +139 -0
  63. package/dist/filter-bar.js +155 -0
  64. package/dist/filter-bar.js.map +1 -0
  65. package/dist/glow-card.d.ts +20 -0
  66. package/dist/glow-card.js +30 -0
  67. package/dist/glow-card.js.map +1 -0
  68. package/dist/image-cropper.d.ts +115 -0
  69. package/dist/image-cropper.js +166 -0
  70. package/dist/image-cropper.js.map +1 -0
  71. package/dist/index.d.ts +2 -20
  72. package/dist/index.js +7 -64
  73. package/dist/index.js.map +1 -1
  74. package/dist/iso.d.ts +400 -0
  75. package/dist/iso.js +363 -0
  76. package/dist/iso.js.map +1 -0
  77. package/dist/loader.d.ts +206 -0
  78. package/dist/loader.js +138 -0
  79. package/dist/loader.js.map +1 -0
  80. package/dist/marquee.d.ts +33 -0
  81. package/dist/marquee.js +52 -0
  82. package/dist/marquee.js.map +1 -0
  83. package/dist/media-capture.d.ts +155 -0
  84. package/dist/media-capture.js +242 -0
  85. package/dist/media-capture.js.map +1 -0
  86. package/dist/meter.d.ts +146 -0
  87. package/dist/meter.js +107 -0
  88. package/dist/meter.js.map +1 -0
  89. package/dist/motion.css +394 -3
  90. package/dist/motion.d.ts +46 -0
  91. package/dist/motion.js +10 -0
  92. package/dist/motion.js.map +1 -0
  93. package/dist/native/media-capture.d.ts +49 -0
  94. package/dist/native/media-capture.js +113 -0
  95. package/dist/native/media-capture.js.map +1 -0
  96. package/dist/native/video-player.d.ts +50 -0
  97. package/dist/native/video-player.js +69 -0
  98. package/dist/native/video-player.js.map +1 -0
  99. package/dist/native.d.ts +66 -2
  100. package/dist/native.js +166 -68
  101. package/dist/native.js.map +1 -1
  102. package/dist/nav-bar.d.ts +178 -0
  103. package/dist/nav-bar.js +157 -0
  104. package/dist/nav-bar.js.map +1 -0
  105. package/dist/number-ticker.d.ts +32 -0
  106. package/dist/number-ticker.js +61 -0
  107. package/dist/number-ticker.js.map +1 -0
  108. package/dist/pagination.d.ts +141 -0
  109. package/dist/pagination.js +165 -0
  110. package/dist/pagination.js.map +1 -0
  111. package/dist/progress.d.ts +56 -1
  112. package/dist/progress.js +72 -25
  113. package/dist/progress.js.map +1 -1
  114. package/dist/search-field.d.ts +100 -0
  115. package/dist/search-field.js +167 -0
  116. package/dist/search-field.js.map +1 -0
  117. package/dist/skeleton.d.ts +18 -3
  118. package/dist/skeleton.js +9 -1
  119. package/dist/skeleton.js.map +1 -1
  120. package/dist/spinner.d.ts +21 -5
  121. package/dist/spinner.js +13 -12
  122. package/dist/spinner.js.map +1 -1
  123. package/dist/stepper.d.ts +112 -0
  124. package/dist/stepper.js +67 -0
  125. package/dist/stepper.js.map +1 -0
  126. package/dist/tabs.d.ts +1 -1
  127. package/dist/tabs.js +14 -8
  128. package/dist/tabs.js.map +1 -1
  129. package/dist/tag-input.d.ts +44 -0
  130. package/dist/tag-input.js +67 -0
  131. package/dist/tag-input.js.map +1 -0
  132. package/dist/theme.d.ts +1 -1
  133. package/dist/theme.js +2 -1
  134. package/dist/theme.js.map +1 -1
  135. package/dist/toast.d.ts +39 -4
  136. package/dist/toast.js +81 -7
  137. package/dist/toast.js.map +1 -1
  138. package/dist/video-player.d.ts +204 -0
  139. package/dist/video-player.js +371 -0
  140. package/dist/video-player.js.map +1 -0
  141. package/dist/video-trimmer.d.ts +142 -0
  142. package/dist/video-trimmer.js +193 -0
  143. package/dist/video-trimmer.js.map +1 -0
  144. package/package.json +375 -7
  145. package/registry/alert.json +1 -1
  146. package/registry/audio-player.json +282 -0
  147. package/registry/avatar.json +1 -1
  148. package/registry/badge.json +1 -1
  149. package/registry/button.json +232 -26
  150. package/registry/card.json +1 -1
  151. package/registry/checkbox.json +1 -1
  152. package/registry/chip-link.json +158 -0
  153. package/registry/chip.json +1 -1
  154. package/registry/combobox.json +475 -0
  155. package/registry/command.json +1 -1
  156. package/registry/confirm-button.json +91 -7
  157. package/registry/countdown.json +1 -1
  158. package/registry/data-table.json +500 -0
  159. package/registry/date-picker.json +978 -0
  160. package/registry/dialog.json +15 -7
  161. package/registry/dropdown-menu.json +1 -1
  162. package/registry/empty-state.json +1 -1
  163. package/registry/examples/audio-player/podcast.js +22 -0
  164. package/registry/examples/audio-player/podcast.tsx +19 -0
  165. package/registry/examples/audio-player/voice-note.d.ts +4 -0
  166. package/registry/examples/audio-player/voice-note.js +12 -0
  167. package/registry/examples/audio-player/voice-note.tsx +12 -0
  168. package/registry/examples/button/error.d.ts +4 -0
  169. package/registry/examples/button/error.js +11 -0
  170. package/registry/examples/button/error.tsx +10 -0
  171. package/registry/examples/button/loading.tsx +1 -1
  172. package/registry/examples/button/save-flow.d.ts +4 -0
  173. package/registry/examples/button/save-flow.js +28 -0
  174. package/registry/examples/button/save-flow.tsx +24 -0
  175. package/registry/examples/button/signature.d.ts +4 -0
  176. package/registry/examples/button/signature.js +17 -0
  177. package/registry/examples/button/signature.tsx +19 -0
  178. package/registry/examples/button/upload-progress.d.ts +4 -0
  179. package/registry/examples/button/upload-progress.js +23 -0
  180. package/registry/examples/button/upload-progress.tsx +26 -0
  181. package/registry/examples/chip-link/filters.d.ts +4 -0
  182. package/registry/examples/chip-link/filters.js +12 -0
  183. package/registry/examples/chip-link/filters.tsx +17 -0
  184. package/registry/examples/combobox/async.d.ts +4 -0
  185. package/registry/examples/combobox/async.js +23 -0
  186. package/registry/examples/combobox/async.tsx +22 -0
  187. package/registry/examples/combobox/creatable.d.ts +4 -0
  188. package/registry/examples/combobox/creatable.js +32 -0
  189. package/registry/examples/combobox/creatable.tsx +28 -0
  190. package/registry/examples/combobox/invalid.d.ts +4 -0
  191. package/registry/examples/combobox/invalid.js +23 -0
  192. package/registry/examples/combobox/invalid.tsx +17 -0
  193. package/registry/examples/combobox/multiple.d.ts +4 -0
  194. package/registry/examples/combobox/multiple.js +29 -0
  195. package/registry/examples/combobox/multiple.tsx +26 -0
  196. package/registry/examples/data-table/cards.d.ts +4 -0
  197. package/registry/examples/data-table/cards.js +21 -0
  198. package/registry/examples/data-table/cards.tsx +20 -0
  199. package/registry/examples/data-table/empty.d.ts +4 -0
  200. package/registry/examples/data-table/empty.js +26 -0
  201. package/registry/examples/data-table/empty.tsx +21 -0
  202. package/registry/examples/data-table/error.d.ts +4 -0
  203. package/registry/examples/data-table/error.js +28 -0
  204. package/registry/examples/data-table/error.tsx +29 -0
  205. package/registry/examples/data-table/jobs.d.ts +4 -0
  206. package/registry/examples/data-table/jobs.js +37 -0
  207. package/registry/examples/data-table/jobs.tsx +36 -0
  208. package/registry/examples/data-table/loading.d.ts +4 -0
  209. package/registry/examples/data-table/loading.js +16 -0
  210. package/registry/examples/data-table/loading.tsx +12 -0
  211. package/registry/examples/data-table/virtualized.d.ts +4 -0
  212. package/registry/examples/data-table/virtualized.js +21 -0
  213. package/registry/examples/data-table/virtualized.tsx +20 -0
  214. package/registry/examples/date-picker/calendar.d.ts +4 -0
  215. package/registry/examples/date-picker/calendar.js +14 -0
  216. package/registry/examples/date-picker/calendar.tsx +16 -0
  217. package/registry/examples/date-picker/french.d.ts +4 -0
  218. package/registry/examples/date-picker/french.js +20 -0
  219. package/registry/examples/date-picker/french.tsx +14 -0
  220. package/registry/examples/date-picker/open.d.ts +4 -0
  221. package/registry/examples/date-picker/open.js +11 -0
  222. package/registry/examples/date-picker/open.tsx +8 -0
  223. package/registry/examples/date-picker/range.d.ts +4 -0
  224. package/registry/examples/date-picker/range.js +29 -0
  225. package/registry/examples/date-picker/range.tsx +25 -0
  226. package/registry/examples/date-picker/schedule.d.ts +4 -0
  227. package/registry/examples/date-picker/schedule.js +26 -0
  228. package/registry/examples/date-picker/schedule.tsx +22 -0
  229. package/registry/examples/dialog/left.d.ts +4 -0
  230. package/registry/examples/dialog/left.js +22 -0
  231. package/registry/examples/dialog/left.tsx +32 -0
  232. package/registry/examples/file-upload/images.d.ts +4 -0
  233. package/registry/examples/file-upload/images.js +32 -0
  234. package/registry/examples/file-upload/images.tsx +25 -0
  235. package/registry/examples/file-upload/states.d.ts +4 -0
  236. package/registry/examples/file-upload/states.js +28 -0
  237. package/registry/examples/file-upload/states.tsx +23 -0
  238. package/registry/examples/file-upload/tus.d.ts +4 -0
  239. package/registry/examples/file-upload/tus.js +23 -0
  240. package/registry/examples/file-upload/tus.tsx +26 -0
  241. package/registry/examples/file-upload/video.d.ts +4 -0
  242. package/registry/examples/file-upload/video.js +39 -0
  243. package/registry/examples/file-upload/video.tsx +36 -0
  244. package/registry/examples/filter-bar/address.d.ts +4 -0
  245. package/registry/examples/filter-bar/address.js +29 -0
  246. package/registry/examples/filter-bar/address.tsx +27 -0
  247. package/registry/examples/filter-bar/jobs.d.ts +4 -0
  248. package/registry/examples/filter-bar/jobs.js +37 -0
  249. package/registry/examples/filter-bar/jobs.tsx +37 -0
  250. package/registry/examples/filter-bar/menu.d.ts +4 -0
  251. package/registry/examples/filter-bar/menu.js +27 -0
  252. package/registry/examples/filter-bar/menu.tsx +23 -0
  253. package/registry/examples/glow-card/link-card.d.ts +4 -0
  254. package/registry/examples/glow-card/link-card.js +14 -0
  255. package/registry/examples/glow-card/link-card.tsx +13 -0
  256. package/registry/examples/glow-card/plans.d.ts +4 -0
  257. package/registry/examples/glow-card/plans.js +25 -0
  258. package/registry/examples/glow-card/plans.tsx +26 -0
  259. package/registry/examples/glow-card/spotlight.d.ts +4 -0
  260. package/registry/examples/glow-card/spotlight.js +14 -0
  261. package/registry/examples/glow-card/spotlight.tsx +15 -0
  262. package/registry/examples/image-cropper/avatar.d.ts +4 -0
  263. package/registry/examples/image-cropper/avatar.js +13 -0
  264. package/registry/examples/image-cropper/avatar.tsx +14 -0
  265. package/registry/examples/image-cropper/banner.d.ts +4 -0
  266. package/registry/examples/image-cropper/banner.js +11 -0
  267. package/registry/examples/image-cropper/banner.tsx +10 -0
  268. package/registry/examples/image-cropper/vertical-video.d.ts +4 -0
  269. package/registry/examples/image-cropper/vertical-video.js +12 -0
  270. package/registry/examples/image-cropper/vertical-video.tsx +16 -0
  271. package/registry/examples/iso/compose.d.ts +4 -0
  272. package/registry/examples/iso/compose.js +22 -0
  273. package/registry/examples/iso/compose.tsx +19 -0
  274. package/registry/examples/iso/orazaka-hero.d.ts +4 -0
  275. package/registry/examples/iso/orazaka-hero.js +26 -0
  276. package/registry/examples/iso/orazaka-hero.tsx +21 -0
  277. package/registry/examples/iso/orochia-hero.d.ts +4 -0
  278. package/registry/examples/iso/orochia-hero.js +29 -0
  279. package/registry/examples/iso/orochia-hero.tsx +23 -0
  280. package/registry/examples/iso/still.d.ts +4 -0
  281. package/registry/examples/iso/still.js +21 -0
  282. package/registry/examples/iso/still.tsx +15 -0
  283. package/registry/examples/loader/endings.d.ts +4 -0
  284. package/registry/examples/loader/endings.js +14 -0
  285. package/registry/examples/loader/endings.tsx +11 -0
  286. package/registry/examples/loader/fetch-flow.d.ts +4 -0
  287. package/registry/examples/loader/fetch-flow.js +24 -0
  288. package/registry/examples/loader/fetch-flow.tsx +24 -0
  289. package/registry/examples/loader/minimal.d.ts +4 -0
  290. package/registry/examples/loader/minimal.js +11 -0
  291. package/registry/examples/loader/minimal.tsx +6 -0
  292. package/registry/examples/loader/overlay.d.ts +4 -0
  293. package/registry/examples/loader/overlay.js +16 -0
  294. package/registry/examples/loader/overlay.tsx +13 -0
  295. package/registry/examples/loader/region-handover.d.ts +4 -0
  296. package/registry/examples/loader/region-handover.js +42 -0
  297. package/registry/examples/loader/region-handover.tsx +29 -0
  298. package/registry/examples/loader/upload-eta.d.ts +4 -0
  299. package/registry/examples/loader/upload-eta.js +11 -0
  300. package/registry/examples/loader/upload-eta.tsx +10 -0
  301. package/registry/examples/marquee/cards.d.ts +4 -0
  302. package/registry/examples/marquee/cards.js +23 -0
  303. package/registry/examples/marquee/cards.tsx +21 -0
  304. package/registry/examples/marquee/logos.d.ts +4 -0
  305. package/registry/examples/marquee/logos.js +12 -0
  306. package/registry/examples/marquee/logos.tsx +17 -0
  307. package/registry/examples/media-capture/clip.d.ts +4 -0
  308. package/registry/examples/media-capture/clip.js +20 -0
  309. package/registry/examples/media-capture/clip.tsx +18 -0
  310. package/registry/examples/media-capture/native/photo.d.ts +4 -0
  311. package/registry/examples/media-capture/native/photo.dark.png +0 -0
  312. package/registry/examples/media-capture/native/photo.js +27 -0
  313. package/registry/examples/media-capture/native/photo.light.png +0 -0
  314. package/registry/examples/media-capture/native/photo.tsx +20 -0
  315. package/registry/examples/media-capture/profile-photo.d.ts +4 -0
  316. package/registry/examples/media-capture/profile-photo.js +24 -0
  317. package/registry/examples/media-capture/profile-photo.tsx +20 -0
  318. package/registry/examples/meter/balance.d.ts +4 -0
  319. package/registry/examples/meter/balance.js +14 -0
  320. package/registry/examples/meter/balance.tsx +15 -0
  321. package/registry/examples/meter/quota.d.ts +4 -0
  322. package/registry/examples/meter/quota.js +15 -0
  323. package/registry/examples/meter/quota.tsx +12 -0
  324. package/registry/examples/meter/split.d.ts +4 -0
  325. package/registry/examples/meter/split.js +18 -0
  326. package/registry/examples/meter/split.tsx +18 -0
  327. package/registry/examples/nav-bar/app-bar.d.ts +4 -0
  328. package/registry/examples/nav-bar/app-bar.js +22 -0
  329. package/registry/examples/nav-bar/app-bar.tsx +21 -0
  330. package/registry/examples/nav-bar/menu-open.d.ts +4 -0
  331. package/registry/examples/nav-bar/menu-open.js +26 -0
  332. package/registry/examples/nav-bar/menu-open.tsx +26 -0
  333. package/registry/examples/nav-bar/site.d.ts +4 -0
  334. package/registry/examples/nav-bar/site.js +31 -0
  335. package/registry/examples/nav-bar/site.tsx +36 -0
  336. package/registry/examples/number-ticker/gradient.d.ts +4 -0
  337. package/registry/examples/number-ticker/gradient.js +11 -0
  338. package/registry/examples/number-ticker/gradient.tsx +10 -0
  339. package/registry/examples/number-ticker/live-total.d.ts +4 -0
  340. package/registry/examples/number-ticker/live-total.js +17 -0
  341. package/registry/examples/number-ticker/live-total.tsx +21 -0
  342. package/registry/examples/number-ticker/stats.d.ts +4 -0
  343. package/registry/examples/number-ticker/stats.js +19 -0
  344. package/registry/examples/number-ticker/stats.tsx +23 -0
  345. package/registry/examples/pagination/cursor.d.ts +4 -0
  346. package/registry/examples/pagination/cursor.js +24 -0
  347. package/registry/examples/pagination/cursor.tsx +20 -0
  348. package/registry/examples/pagination/links.d.ts +4 -0
  349. package/registry/examples/pagination/links.js +11 -0
  350. package/registry/examples/pagination/links.tsx +6 -0
  351. package/registry/examples/pagination/native/cursor.d.ts +4 -0
  352. package/registry/examples/pagination/native/cursor.dark.png +0 -0
  353. package/registry/examples/pagination/native/cursor.js +19 -0
  354. package/registry/examples/pagination/native/cursor.light.png +0 -0
  355. package/registry/examples/pagination/native/cursor.tsx +13 -0
  356. package/registry/examples/pagination/native/pages.d.ts +4 -0
  357. package/registry/examples/pagination/native/pages.dark.png +0 -0
  358. package/registry/examples/pagination/native/pages.js +21 -0
  359. package/registry/examples/pagination/native/pages.light.png +0 -0
  360. package/registry/examples/pagination/native/pages.tsx +14 -0
  361. package/registry/examples/pagination/results.d.ts +4 -0
  362. package/registry/examples/pagination/results.js +23 -0
  363. package/registry/examples/pagination/results.tsx +19 -0
  364. package/registry/examples/pagination/table-footer.d.ts +4 -0
  365. package/registry/examples/pagination/table-footer.js +31 -0
  366. package/registry/examples/pagination/table-footer.tsx +27 -0
  367. package/registry/examples/progress/buffer.d.ts +4 -0
  368. package/registry/examples/progress/buffer.js +11 -0
  369. package/registry/examples/progress/buffer.tsx +10 -0
  370. package/registry/examples/progress/statuses.d.ts +4 -0
  371. package/registry/examples/progress/statuses.js +14 -0
  372. package/registry/examples/progress/statuses.tsx +11 -0
  373. package/registry/examples/progress/steps.d.ts +4 -0
  374. package/registry/examples/progress/steps.js +14 -0
  375. package/registry/examples/progress/steps.tsx +13 -0
  376. package/registry/examples/progress/upload.d.ts +4 -0
  377. package/registry/examples/progress/upload.js +11 -0
  378. package/registry/examples/progress/upload.tsx +10 -0
  379. package/registry/examples/search-field/global.d.ts +4 -0
  380. package/registry/examples/search-field/global.js +19 -0
  381. package/registry/examples/search-field/global.tsx +15 -0
  382. package/registry/examples/search-field/native/default.d.ts +4 -0
  383. package/registry/examples/search-field/native/default.dark.png +0 -0
  384. package/registry/examples/search-field/native/default.js +13 -0
  385. package/registry/examples/search-field/native/default.light.png +0 -0
  386. package/registry/examples/search-field/native/default.tsx +7 -0
  387. package/registry/examples/search-field/suggestions.d.ts +4 -0
  388. package/registry/examples/search-field/suggestions.js +30 -0
  389. package/registry/examples/search-field/suggestions.tsx +27 -0
  390. package/registry/examples/search-field/toolbar.d.ts +4 -0
  391. package/registry/examples/search-field/toolbar.js +17 -0
  392. package/registry/examples/search-field/toolbar.tsx +21 -0
  393. package/registry/examples/skeleton/paragraph.d.ts +4 -0
  394. package/registry/examples/skeleton/paragraph.js +14 -0
  395. package/registry/examples/skeleton/paragraph.tsx +12 -0
  396. package/registry/examples/skeleton/shape-of-content.d.ts +4 -0
  397. package/registry/examples/skeleton/shape-of-content.js +23 -0
  398. package/registry/examples/skeleton/shape-of-content.tsx +26 -0
  399. package/registry/examples/spinner/default.js +5 -2
  400. package/registry/examples/spinner/default.tsx +7 -1
  401. package/registry/examples/spinner/sizes.js +2 -1
  402. package/registry/examples/spinner/sizes.tsx +2 -0
  403. package/registry/examples/spinner/value.d.ts +4 -0
  404. package/registry/examples/spinner/value.js +11 -0
  405. package/registry/examples/spinner/value.tsx +12 -0
  406. package/registry/examples/stepper/checkout.d.ts +4 -0
  407. package/registry/examples/stepper/checkout.js +22 -0
  408. package/registry/examples/stepper/checkout.tsx +17 -0
  409. package/registry/examples/stepper/failed.d.ts +4 -0
  410. package/registry/examples/stepper/failed.js +26 -0
  411. package/registry/examples/stepper/failed.tsx +21 -0
  412. package/registry/examples/stepper/job.d.ts +4 -0
  413. package/registry/examples/stepper/job.js +32 -0
  414. package/registry/examples/stepper/job.tsx +28 -0
  415. package/registry/examples/tag-input/recipients.d.ts +4 -0
  416. package/registry/examples/tag-input/recipients.js +23 -0
  417. package/registry/examples/tag-input/recipients.tsx +20 -0
  418. package/registry/examples/tag-input/video-tags.d.ts +4 -0
  419. package/registry/examples/tag-input/video-tags.js +26 -0
  420. package/registry/examples/tag-input/video-tags.tsx +22 -0
  421. package/registry/examples/toast/grouped.d.ts +4 -0
  422. package/registry/examples/toast/grouped.js +21 -0
  423. package/registry/examples/toast/grouped.tsx +23 -0
  424. package/registry/examples/toast/undo.d.ts +4 -0
  425. package/registry/examples/toast/undo.js +29 -0
  426. package/registry/examples/toast/undo.tsx +30 -0
  427. package/registry/examples/toast/upload-progress.d.ts +4 -0
  428. package/registry/examples/toast/upload-progress.js +29 -0
  429. package/registry/examples/toast/upload-progress.tsx +31 -0
  430. package/registry/examples/video-player/default.d.ts +4 -0
  431. package/registry/examples/video-player/default.js +12 -0
  432. package/registry/examples/video-player/default.tsx +8 -0
  433. package/registry/examples/video-player/gated.d.ts +4 -0
  434. package/registry/examples/video-player/gated.js +26 -0
  435. package/registry/examples/video-player/gated.tsx +23 -0
  436. package/registry/examples/video-player/native/default.d.ts +4 -0
  437. package/registry/examples/video-player/native/default.dark.png +0 -0
  438. package/registry/examples/video-player/native/default.js +18 -0
  439. package/registry/examples/video-player/native/default.light.png +0 -0
  440. package/registry/examples/video-player/native/default.tsx +12 -0
  441. package/registry/examples/video-player/signed-source.d.ts +4 -0
  442. package/registry/examples/video-player/signed-source.js +16 -0
  443. package/registry/examples/video-player/signed-source.tsx +15 -0
  444. package/registry/examples/video-player/vertical.d.ts +4 -0
  445. package/registry/examples/video-player/vertical.js +12 -0
  446. package/registry/examples/video-player/vertical.tsx +12 -0
  447. package/registry/examples/video-trimmer/custom.d.ts +4 -0
  448. package/registry/examples/video-trimmer/custom.js +24 -0
  449. package/registry/examples/video-trimmer/custom.tsx +26 -0
  450. package/registry/examples/video-trimmer/filmstrip.d.ts +4 -0
  451. package/registry/examples/video-trimmer/filmstrip.js +15 -0
  452. package/registry/examples/video-trimmer/filmstrip.tsx +13 -0
  453. package/registry/examples/video-trimmer/playhead.d.ts +4 -0
  454. package/registry/examples/video-trimmer/playhead.js +15 -0
  455. package/registry/examples/video-trimmer/playhead.tsx +13 -0
  456. package/registry/field.json +1 -1
  457. package/registry/file-upload.json +471 -0
  458. package/registry/filter-bar.json +347 -0
  459. package/registry/glow-card.json +162 -0
  460. package/registry/image-cropper.json +313 -0
  461. package/registry/index.json +445 -20
  462. package/registry/iso.json +1355 -0
  463. package/registry/kbd.json +1 -1
  464. package/registry/loader.json +399 -0
  465. package/registry/marquee.json +225 -0
  466. package/registry/mascots.json +3 -3
  467. package/registry/media-capture.json +347 -0
  468. package/registry/meter.json +324 -0
  469. package/registry/nav-bar.json +480 -0
  470. package/registry/number-ticker.json +219 -0
  471. package/registry/page-header.json +1 -1
  472. package/registry/pagination.json +475 -0
  473. package/registry/popover.json +1 -1
  474. package/registry/progress.json +114 -9
  475. package/registry/radio-group.json +1 -1
  476. package/registry/search-field.json +436 -0
  477. package/registry/section-backdrop.json +1 -1
  478. package/registry/separator.json +1 -1
  479. package/registry/skeleton.json +69 -13
  480. package/registry/slider.json +1 -1
  481. package/registry/slot.json +1 -1
  482. package/registry/spinner.json +91 -27
  483. package/registry/stat.json +1 -1
  484. package/registry/stepper.json +174 -0
  485. package/registry/switch.json +1 -1
  486. package/registry/tabs.json +11 -7
  487. package/registry/tag-input.json +363 -0
  488. package/registry/theme.json +3 -3
  489. package/registry/toast.json +41 -13
  490. package/registry/tooltip.json +1 -1
  491. package/registry/video-player.json +598 -0
  492. package/registry/video-trimmer.json +385 -0
  493. package/dist/chunk-FHYZSO2S.js.map +0 -1
  494. package/dist/chunk-PESA2R4U.js +0 -51
  495. package/dist/chunk-PESA2R4U.js.map +0 -1
  496. package/registry/examples/spinner/muted.js +0 -11
  497. package/registry/examples/spinner/muted.tsx +0 -6
  498. /package/registry/examples/{spinner/muted.d.ts → audio-player/podcast.d.ts} +0 -0
@@ -0,0 +1,347 @@
1
+ {
2
+ "name": "filter-bar",
3
+ "type": "primitive",
4
+ "title": "Filter bar",
5
+ "summary": "The bar above a list: the controls that narrow it, the filters in force as removable chips, “Clear filters”, and the count of results — the state kept by the app, in the address.",
6
+ "description": "The bar above a list: the controls that narrow it, the filters in force as removable chips, “Clear filters”, and the count of results — the state kept by the app, in the address.",
7
+ "status": "beta",
8
+ "category": "forms",
9
+ "platforms": "web",
10
+ "whenToUse": [
11
+ "Above a table or a grid people narrow with several criteria at once: status, type, owner, period, a query.",
12
+ "When the filters must be visible and undoable one by one, and shared in a link."
13
+ ],
14
+ "whenNotToUse": [
15
+ {
16
+ "when": "For one choice among a few views of a list: chips in a single group.",
17
+ "use": "chip"
18
+ },
19
+ {
20
+ "when": "For a search alone: a search field.",
21
+ "use": "search-field"
22
+ }
23
+ ],
24
+ "bestPractices": [
25
+ "Keep the filters in the address: `readFilterParams` on load, `writeFilterParams` (which resets the page) on change.",
26
+ "Give each active chip the filter's name and value (“Status: Failed”), not the value alone.",
27
+ "Count the results in `results` (“42 jobs”): it is announced when it changes.",
28
+ "Use `FilterMenu` for a list of values (checkboxes, or radios with `multiple={false}`), a `DateRangePicker` for a period."
29
+ ],
30
+ "accessibility": {
31
+ "keyboard": [
32
+ {
33
+ "keys": "Enter / Space / Arrow Down",
34
+ "action": "Opens a filter's menu."
35
+ },
36
+ {
37
+ "keys": "Arrow keys",
38
+ "action": "Move between the options of an open menu; Space checks one, the menu stays open."
39
+ },
40
+ {
41
+ "keys": "Esc",
42
+ "action": "Closes the menu and returns to its button."
43
+ }
44
+ ],
45
+ "notes": [
46
+ "The bar is a `group` named by `label`; each chip's remove button is named by `removeLabel` (“Remove Status: Failed”).",
47
+ "The count of results is a polite live region: a change of filter is heard without moving the focus.",
48
+ "`FilterMenu` is a Radix menu of checkbox (or radio) items: their state is announced as they toggle."
49
+ ]
50
+ },
51
+ "related": [
52
+ "search-field",
53
+ "chip",
54
+ "dropdown-menu",
55
+ "date-picker",
56
+ "data-table"
57
+ ],
58
+ "web": {
59
+ "entry": "@krizaka/ui/filter-bar",
60
+ "import": "import { FilterBar, FilterMenu, readFilterParams, writeFilterParams } from \"@krizaka/ui/filter-bar\";",
61
+ "props": [
62
+ {
63
+ "component": "FilterBar",
64
+ "description": "The bar of filters above a list: controls, active filters as chips, clear all, the count of results.",
65
+ "props": [
66
+ {
67
+ "name": "label",
68
+ "type": "string",
69
+ "default": null,
70
+ "description": "The accessible name of the bar (\"Filter jobs\"), passed translated.",
71
+ "required": true
72
+ },
73
+ {
74
+ "name": "active",
75
+ "type": "readonly ActiveFilter[]",
76
+ "default": "[]",
77
+ "description": "The filters in force, each a removable chip.",
78
+ "required": false
79
+ },
80
+ {
81
+ "name": "children",
82
+ "type": "ReactNode",
83
+ "default": null,
84
+ "description": "The controls: `SearchField`, `FilterMenu`, `DateRangePicker`, a `Chip.Group`…",
85
+ "required": false
86
+ },
87
+ {
88
+ "name": "clearLabel",
89
+ "type": "string",
90
+ "default": "Clear filters",
91
+ "description": "The \"Clear all\" button (\"Clear filters\"), passed translated.",
92
+ "required": false
93
+ },
94
+ {
95
+ "name": "onClearAll",
96
+ "type": "(() => void)",
97
+ "default": null,
98
+ "description": "Clears every filter; the \"Clear all\" button shows when there is at least one.",
99
+ "required": false
100
+ },
101
+ {
102
+ "name": "removeLabel",
103
+ "type": "((label: string) => string)",
104
+ "default": "(text) => `Remove ${text}`",
105
+ "description": "A chip's remove button: `(label) => \"Remove \" + label`, passed translated.",
106
+ "required": false
107
+ },
108
+ {
109
+ "name": "results",
110
+ "type": "string",
111
+ "default": null,
112
+ "description": "The count of results (\"42 jobs\"), announced politely when it changes.",
113
+ "required": false
114
+ }
115
+ ]
116
+ },
117
+ {
118
+ "component": "FilterMenu",
119
+ "description": "A filter's button and its menu of options — checkboxes (several values) or radios (one) — with a count of chosen ones.",
120
+ "props": [
121
+ {
122
+ "name": "label",
123
+ "type": "string",
124
+ "default": null,
125
+ "description": "The name of the filter on its button (\"Status\"), passed translated.",
126
+ "required": true
127
+ },
128
+ {
129
+ "name": "onValueChange",
130
+ "type": "((value: string[]) => void) | ((value: string) => void)",
131
+ "default": null,
132
+ "description": "Called with the values chosen.\nCalled with the value chosen.",
133
+ "required": true
134
+ },
135
+ {
136
+ "name": "options",
137
+ "type": "readonly FilterOption[]",
138
+ "default": null,
139
+ "description": "The choices.",
140
+ "required": true
141
+ },
142
+ {
143
+ "name": "value",
144
+ "type": "string | readonly string[]",
145
+ "default": null,
146
+ "description": "The values chosen.\nThe value chosen, `\"\"` for none.",
147
+ "required": true
148
+ },
149
+ {
150
+ "name": "className",
151
+ "type": "string",
152
+ "default": null,
153
+ "description": "Classes merged on the button.",
154
+ "required": false
155
+ },
156
+ {
157
+ "name": "defaultOpen",
158
+ "type": "boolean",
159
+ "default": null,
160
+ "description": "Opens the menu at first (a story, a test).",
161
+ "required": false
162
+ },
163
+ {
164
+ "name": "multiple",
165
+ "type": "boolean",
166
+ "default": null,
167
+ "description": "Several values (checkboxes, default) or one (radios).\nOne value: the options are radios.",
168
+ "required": false
169
+ }
170
+ ]
171
+ }
172
+ ],
173
+ "examples": [
174
+ {
175
+ "name": "jobs",
176
+ "title": "Jobs",
177
+ "description": "A search, a status menu (several values), a model menu (one value), the chips and the count.",
178
+ "path": "examples/filter-bar/jobs.tsx",
179
+ "module": "@krizaka/ui/registry/examples/filter-bar/jobs",
180
+ "code": "\"use client\";\n\nimport { FilterBar, FilterMenu } from \"@krizaka/ui/filter-bar\";\nimport { SearchField } from \"@krizaka/ui/search-field\";\nimport * as React from \"react\";\n\nconst STATUS = [\n { value: \"running\", label: \"Running\", count: 3 },\n { value: \"done\", label: \"Done\", count: 128 },\n { value: \"failed\", label: \"Failed\", count: 6 },\n];\nconst MODELS = [\n { value: \"\", label: \"All models\" },\n { value: \"flux\", label: \"Flux 1.1\" },\n { value: \"whisper\", label: \"Whisper large\" },\n];\n\n// Several criteria at once; each one in force is a chip that undoes it.\nexport default function FilterBarJobs() {\n const [query, setQuery] = React.useState(\"\");\n const [status, setStatus] = React.useState([\"failed\"]);\n const [model, setModel] = React.useState(\"flux\");\n const active = [\n ...status.map((s) => ({ id: `status:${s}`, label: `Status: ${STATUS.find((o) => o.value === s)?.label}`, onRemove: () => setStatus(status.filter((x) => x !== s)) })),\n ...(model ? [{ id: \"model\", label: `Model: ${MODELS.find((m) => m.value === model)?.label}`, onRemove: () => setModel(\"\") }] : []),\n ];\n return (\n <FilterBar label=\"Filter jobs\" active={active} onClearAll={() => {\n setStatus([]);\n setModel(\"\");\n }} results=\"6 jobs\">\n <SearchField label=\"Search jobs\" placeholder=\"Search jobs\" value={query} onValueChange={setQuery} landmark={false} className=\"sm:max-w-64\" />\n <FilterMenu label=\"Status\" options={STATUS} value={status} onValueChange={setStatus} />\n <FilterMenu label=\"Model\" multiple={false} options={MODELS} value={model} onValueChange={setModel} />\n </FilterBar>\n );\n}\n"
181
+ },
182
+ {
183
+ "name": "menu",
184
+ "title": "A filter's menu",
185
+ "description": "`FilterMenu` open: checkboxes with the count each value would give.",
186
+ "path": "examples/filter-bar/menu.tsx",
187
+ "module": "@krizaka/ui/registry/examples/filter-bar/menu",
188
+ "code": "\"use client\";\n\nimport { FilterMenu } from \"@krizaka/ui/filter-bar\";\nimport * as React from \"react\";\n\n// A filter's menu: checkboxes, and what each value would give.\nexport default function FilterBarMenu({ defaultOpen }: { defaultOpen?: boolean }) {\n const [formats, setFormats] = React.useState([\"vertical\"]);\n return (\n <FilterMenu\n label=\"Format\"\n defaultOpen={defaultOpen}\n options={[\n { value: \"vertical\", label: \"Vertical\", count: 214 },\n { value: \"landscape\", label: \"Landscape\", count: 96 },\n { value: \"square\", label: \"Square\", count: 12 },\n { value: \"audio\", label: \"Audio only\", count: 0, disabled: true },\n ]}\n value={formats}\n onValueChange={setFormats}\n />\n );\n}\n"
189
+ },
190
+ {
191
+ "name": "address",
192
+ "title": "In the address",
193
+ "description": "The filters read from and written to `URLSearchParams`, the page reset on change.",
194
+ "path": "examples/filter-bar/address.tsx",
195
+ "module": "@krizaka/ui/registry/examples/filter-bar/address",
196
+ "code": "\"use client\";\n\nimport { FilterBar, FilterMenu, readFilterParams, writeFilterParams } from \"@krizaka/ui/filter-bar\";\nimport * as React from \"react\";\n\nconst ROLES = [\n { value: \"creator\", label: \"Creator\" },\n { value: \"fan\", label: \"Fan\" },\n { value: \"admin\", label: \"Admin\" },\n];\n\n// The address is the state: read it, write it back (the page goes back to 1). In Next.js: useSearchParams + router.replace.\nexport default function FilterBarAddress() {\n const [params, setParams] = React.useState(() => new URLSearchParams(\"role=creator,admin&page=3\"));\n const { role } = readFilterParams(params, [\"role\"]);\n const set = (next: string[]) => setParams(writeFilterParams(params, { role: next }));\n return (\n <FilterBar\n label=\"Filter users\"\n active={role!.map((r) => ({ id: r, label: `Role: ${ROLES.find((o) => o.value === r)?.label}`, onRemove: () => set(role!.filter((x) => x !== r)) }))}\n onClearAll={() => set([])}\n results={`?${params.toString()}`}\n >\n <FilterMenu label=\"Role\" options={ROLES} value={role!} onValueChange={set} />\n </FilterBar>\n );\n}\n"
197
+ }
198
+ ]
199
+ },
200
+ "native": null,
201
+ "files": [
202
+ {
203
+ "path": "filter-bar/filter-bar.tsx",
204
+ "content": "// Client (menus in a portal). The bar above a list: the controls that narrow it (a SearchField, FilterMenus, a\n// DateRangePicker — any child), the filters in force as removable chips, \"Clear all\", and the count of results in a\n// polite live region. It holds no state: the app keeps the filters — in the address, ideally — and the\n// `readFilterParams` / `writeFilterParams` helpers move them between a `URLSearchParams` and an object.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { Chip } from \"../chip\";\nimport { DropdownMenu, menu } from \"../dropdown-menu\";\nimport { ChevronGlyph } from \"../glyphs\";\n\nexport const filterBar = tv({\n slots: {\n root: \"flex w-full flex-col gap-3\",\n controls: \"flex w-full flex-wrap items-center gap-2 [&>*]:min-w-0\",\n active: \"flex flex-wrap items-center gap-2\",\n clear:\n \"rounded-md px-2 py-1 text-xs font-semibold text-fg-accent hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n results: \"ml-auto text-xs text-fg-secondary tabular-nums\",\n trigger:\n \"inline-flex h-11 shrink-0 items-center gap-2 rounded-lg border border-border-default bg-surface-1 px-3.5 text-sm font-semibold text-fg-secondary \" +\n \"transition-colors hover:border-border-strong hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"data-[active]:border-accent data-[active]:bg-accent-soft data-[active]:text-fg data-[state=open]:border-border-strong\",\n count: \"inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold tabular-nums text-on-accent\",\n optionCount: \"ml-auto pl-4 text-xs tabular-nums text-fg-secondary\",\n },\n});\n\nexport type ActiveFilter = {\n /** A stable key (`\"status:failed\"`). */\n id: string;\n /** What the chip says, passed translated (\"Status: Failed\"). */\n label: string;\n /** Removes this filter. */\n onRemove: () => void;\n};\n\nexport type FilterBarProps = Omit<React.ComponentProps<\"div\">, \"children\" | \"results\"> & {\n /** The accessible name of the bar (\"Filter jobs\"), passed translated. */\n label: string;\n /** The controls: `SearchField`, `FilterMenu`, `DateRangePicker`, a `Chip.Group`… */\n children?: React.ReactNode;\n /** The filters in force, each a removable chip. */\n active?: readonly ActiveFilter[];\n /** Clears every filter; the \"Clear all\" button shows when there is at least one. */\n onClearAll?: () => void;\n /** The \"Clear all\" button (\"Clear filters\"), passed translated. */\n clearLabel?: string;\n /** A chip's remove button: `(label) => \"Remove \" + label`, passed translated. */\n removeLabel?: (label: string) => string;\n /** The count of results (\"42 jobs\"), announced politely when it changes. */\n results?: string;\n};\n\n/** The bar of filters above a list: controls, active filters as chips, clear all, the count of results. */\nexport function FilterBar({\n label,\n children,\n active = [],\n onClearAll,\n clearLabel = \"Clear filters\",\n removeLabel = (text) => `Remove ${text}`,\n results,\n className,\n ...props\n}: FilterBarProps) {\n const s = filterBar();\n return (\n <div role=\"group\" aria-label={label} className={s.root({ className })} {...props}>\n {children ? <div className={s.controls()}>{children}</div> : null}\n {active.length > 0 || results !== undefined ? (\n <div className={s.active()}>\n {active.map((filter) => (\n <Chip key={filter.id} size=\"sm\" removable removeLabel={removeLabel(filter.label)} onRemove={filter.onRemove}>\n {filter.label}\n </Chip>\n ))}\n {active.length > 0 && onClearAll ? (\n <button type=\"button\" className={s.clear()} onClick={onClearAll}>\n {clearLabel}\n </button>\n ) : null}\n <p className={s.results()} aria-live=\"polite\" aria-atomic>\n {results}\n </p>\n </div>\n ) : null}\n </div>\n );\n}\n\nexport type FilterOption = {\n /** What the app stores (and puts in the address). */\n value: string;\n /** What the person reads, passed translated. */\n label: string;\n /** How many results it would give: shown on the right. */\n count?: number;\n /** Shown but not choosable. */\n disabled?: boolean;\n};\n\ntype FilterMenuBase = {\n /** The name of the filter on its button (\"Status\"), passed translated. */\n label: string;\n /** The choices. */\n options: readonly FilterOption[];\n /** Opens the menu at first (a story, a test). */\n defaultOpen?: boolean;\n /** Classes merged on the button. */\n className?: string;\n};\n\nexport type FilterMenuProps =\n | (FilterMenuBase & {\n /** Several values (checkboxes, default) or one (radios). */\n multiple?: true;\n /** The values chosen. */\n value: readonly string[];\n /** Called with the values chosen. */\n onValueChange: (value: string[]) => void;\n })\n | (FilterMenuBase & {\n /** One value: the options are radios. */\n multiple: false;\n /** The value chosen, `\"\"` for none. */\n value: string;\n /** Called with the value chosen. */\n onValueChange: (value: string) => void;\n });\n\n/** A filter's button and its menu of options — checkboxes (several values) or radios (one) — with a count of chosen ones. */\nexport function FilterMenu(props: FilterMenuProps) {\n const { label, options, defaultOpen, className } = props;\n const s = filterBar();\n const m = menu();\n const chosen = props.multiple === false ? (props.value ? [props.value] : []) : props.value;\n const single = props.multiple === false ? options.find((o) => o.value === props.value) : undefined;\n const content = (option: FilterOption) => (\n <>\n {option.label}\n {option.count !== undefined ? <span className={s.optionCount()}>{option.count}</span> : null}\n </>\n );\n return (\n <DropdownMenu.Root defaultOpen={defaultOpen} modal={false}>\n <DropdownMenu.Trigger className={s.trigger({ className })} data-active={chosen.length > 0 ? \"\" : undefined}>\n {single ? `${label}: ${single.label}` : label}\n {props.multiple !== false && chosen.length > 0 ? <span className={s.count()}>{chosen.length}</span> : null}\n <ChevronGlyph className=\"h-4 w-4\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"start\" aria-label={label}>\n {props.multiple === false ? (\n <MenuPrimitive.RadioGroup value={props.value} onValueChange={props.onValueChange}>\n {options.map((option) => (\n <MenuPrimitive.RadioItem key={option.value} value={option.value} disabled={option.disabled} className={m.item()}>\n <span className=\"flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border-strong\">\n <MenuPrimitive.ItemIndicator className=\"h-2 w-2 rounded-full bg-accent\" />\n </span>\n {content(option)}\n </MenuPrimitive.RadioItem>\n ))}\n </MenuPrimitive.RadioGroup>\n ) : (\n options.map((option) => (\n <DropdownMenu.CheckboxItem\n key={option.value}\n disabled={option.disabled}\n checked={props.value.includes(option.value)}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(checked) =>\n props.onValueChange(checked ? [...props.value, option.value] : props.value.filter((v) => v !== option.value))\n }\n >\n {content(option)}\n </DropdownMenu.CheckboxItem>\n ))\n )}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n\n/** Filters read from an address: `{ status: [\"failed\", \"running\"], q: [\"cat\"] }` for the keys asked (repeated or comma-separated values). */\nexport function readFilterParams(params: URLSearchParams, keys: readonly string[]): Record<string, string[]> {\n return Object.fromEntries(\n keys.map((key) => [key, params.getAll(key).flatMap((v) => v.split(\",\")).map((v) => v.trim()).filter(Boolean)]),\n );\n}\n\n/** Filters written into an address (a copy of `base`): empty filters removed, several values comma-separated, the page reset. */\nexport function writeFilterParams(base: URLSearchParams, filters: Record<string, string | readonly string[] | null | undefined>, resetKey = \"page\") {\n const next = new URLSearchParams(base);\n for (const [key, value] of Object.entries(filters)) {\n const list = value == null ? [] : typeof value === \"string\" ? [value] : [...value];\n if (list.filter(Boolean).length === 0) next.delete(key);\n else next.set(key, list.filter(Boolean).join(\",\"));\n }\n if (resetKey) next.delete(resetKey);\n return next;\n}\n"
205
+ },
206
+ {
207
+ "path": "filter-bar/index.ts",
208
+ "content": "/** @krizaka/ui/filter-bar — `FilterBar` (controls, active filters as chips, clear all, results), `FilterMenu`, the address helpers, `filterBar` (the slots). Client (\"use client\"). */\nexport {\n type ActiveFilter,\n FilterBar,\n filterBar,\n type FilterBarProps,\n FilterMenu,\n type FilterMenuProps,\n type FilterOption,\n readFilterParams,\n writeFilterParams,\n} from \"./filter-bar\";\n"
209
+ },
210
+ {
211
+ "path": "filter-bar/meta.ts",
212
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Filter bar\",\n summary: \"The bar above a list: the controls that narrow it, the filters in force as removable chips, “Clear filters”, and the count of results — the state kept by the app, in the address.\",\n status: \"beta\",\n category: \"forms\",\n platforms: \"web\",\n whenToUse: [\n \"Above a table or a grid people narrow with several criteria at once: status, type, owner, period, a query.\",\n \"When the filters must be visible and undoable one by one, and shared in a link.\",\n ],\n whenNotToUse: [\n { when: \"For one choice among a few views of a list: chips in a single group.\", use: \"chip\" },\n { when: \"For a search alone: a search field.\", use: \"search-field\" },\n ],\n bestPractices: [\n \"Keep the filters in the address: `readFilterParams` on load, `writeFilterParams` (which resets the page) on change.\",\n \"Give each active chip the filter's name and value (“Status: Failed”), not the value alone.\",\n \"Count the results in `results` (“42 jobs”): it is announced when it changes.\",\n \"Use `FilterMenu` for a list of values (checkboxes, or radios with `multiple={false}`), a `DateRangePicker` for a period.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Enter / Space / Arrow Down\", action: \"Opens a filter's menu.\" },\n { keys: \"Arrow keys\", action: \"Move between the options of an open menu; Space checks one, the menu stays open.\" },\n { keys: \"Esc\", action: \"Closes the menu and returns to its button.\" },\n ],\n notes: [\n \"The bar is a `group` named by `label`; each chip's remove button is named by `removeLabel` (“Remove Status: Failed”).\",\n \"The count of results is a polite live region: a change of filter is heard without moving the focus.\",\n \"`FilterMenu` is a Radix menu of checkbox (or radio) items: their state is announced as they toggle.\",\n ],\n },\n related: [\"search-field\", \"chip\", \"dropdown-menu\", \"date-picker\", \"data-table\"],\n web: {\n imports: [\"FilterBar\", \"FilterMenu\", \"readFilterParams\", \"writeFilterParams\"],\n examples: [\n { name: \"jobs\", title: \"Jobs\", description: \"A search, a status menu (several values), a model menu (one value), the chips and the count.\" },\n { name: \"menu\", title: \"A filter's menu\", description: \"`FilterMenu` open: checkboxes with the count each value would give.\" },\n { name: \"address\", title: \"In the address\", description: \"The filters read from and written to `URLSearchParams`, the page reset on change.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
213
+ },
214
+ {
215
+ "path": "glyphs.tsx",
216
+ "content": "// The small line glyphs the data and media primitives draw (play, volume, search, chevrons…): decorative, 24-unit\n// box, `currentColor`, sized by the caller's class. Shared by the entries that need them; never exported on its own —\n// a product draws its own icons with @krizaka/icons.\nimport type * as React from \"react\";\n\ntype GlyphProps = { className?: string };\n\nfunction Glyph({ className = \"h-4 w-4\", fill, children }: GlyphProps & { fill?: boolean; children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n className={className}\n fill={fill ? \"currentColor\" : \"none\"}\n stroke={fill ? \"none\" : \"currentColor\"}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport const PlayGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <path d=\"M8 5.5v13a1 1 0 0 0 1.5.86l10.4-6.5a1 1 0 0 0 0-1.72L9.5 4.64A1 1 0 0 0 8 5.5Z\" />\n </Glyph>\n);\nexport const PauseGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n </Glyph>\n);\nexport const VolumeGlyph = ({ muted, ...p }: GlyphProps & { muted?: boolean }) => (\n <Glyph {...p}>\n <path d=\"M11 5 6 9H3v6h3l5 4V5Z\" />\n {muted ? <path d=\"m22 9-6 6m0-6 6 6\" /> : <path d=\"M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13\" />}\n </Glyph>\n);\nexport const FullscreenGlyph = ({ exit, ...p }: GlyphProps & { exit?: boolean }) => (\n <Glyph {...p}>\n {exit ? <path d=\"M9 4v5H4m11-5v5h5M9 20v-5H4m11 5v-5h5\" /> : <path d=\"M4 9V4h5m6 0h5v5M4 15v5h5m11-5v5h-5\" />}\n </Glyph>\n);\nexport const CaptionsGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"M10 10.5a2 2 0 1 0 0 3M17 10.5a2 2 0 1 0 0 3\" />\n </Glyph>\n);\nexport const PipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <rect x=\"12\" y=\"12\" width=\"6\" height=\"4\" rx=\"1\" />\n </Glyph>\n);\nexport const SearchGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </Glyph>\n);\nexport const CloseGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </Glyph>\n);\nexport const ChevronGlyph = ({ direction = \"down\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" | \"up\" | \"down\" }) => (\n <Glyph {...p}>\n <path d={{ left: \"m15 6-6 6 6 6\", right: \"m9 6 6 6-6 6\", up: \"m6 15 6-6 6 6\", down: \"m6 9 6 6 6-6\" }[direction]} />\n </Glyph>\n);\nexport const CheckGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Glyph>\n);\nexport const PlusGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 5v14M5 12h14\" />\n </Glyph>\n);\nexport const CalendarGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\" />\n <path d=\"M3 10h18M8 3v4M16 3v4\" />\n </Glyph>\n);\nexport const UploadGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 16V4m0 0L7 9m5-5 5 5\" />\n <path d=\"M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\" />\n </Glyph>\n);\nexport const FileGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z\" />\n <path d=\"M14 3v5h5\" />\n </Glyph>\n);\nexport const CameraGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z\" />\n <circle cx=\"12\" cy=\"13.5\" r=\"3.5\" />\n </Glyph>\n);\nexport const FlipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 0 1 14-5.3M20 12a8 8 0 0 1-14 5.3\" />\n <path d=\"M18 3v4h-4M6 21v-4h4\" />\n </Glyph>\n);\nexport const RetryGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 1 0 2.3-5.7L4 8.5\" />\n <path d=\"M4 4v4.5h4.5\" />\n </Glyph>\n);\nexport const SortGlyph = ({ direction, ...p }: GlyphProps & { direction?: \"ascending\" | \"descending\" }) => (\n <Glyph {...p}>\n {direction === \"ascending\" ? <path d=\"m7 14 5-5 5 5\" /> : direction === \"descending\" ? <path d=\"m7 10 5 5 5-5\" /> : <path d=\"m8 9 4-4 4 4M8 15l4 4 4-4\" />}\n </Glyph>\n);\n"
217
+ },
218
+ {
219
+ "path": "meta.ts",
220
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"audio-player\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"combobox\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"data-table\"\n | \"date-picker\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"glow-card\"\n | \"image-cropper\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"media-capture\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"pagination\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"tag-input\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. `media`: video, sound, camera, files, cropping. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
221
+ }
222
+ ],
223
+ "dependencies": [
224
+ "radix-ui@^1.7.0",
225
+ "tailwind-variants@^3.3.1"
226
+ ],
227
+ "registryDependencies": [
228
+ "chip",
229
+ "dropdown-menu"
230
+ ],
231
+ "demo": {
232
+ "path": "examples/filter-bar/jobs.tsx",
233
+ "content": "\"use client\";\n\nimport { FilterBar, FilterMenu } from \"@krizaka/ui/filter-bar\";\nimport { SearchField } from \"@krizaka/ui/search-field\";\nimport * as React from \"react\";\n\nconst STATUS = [\n { value: \"running\", label: \"Running\", count: 3 },\n { value: \"done\", label: \"Done\", count: 128 },\n { value: \"failed\", label: \"Failed\", count: 6 },\n];\nconst MODELS = [\n { value: \"\", label: \"All models\" },\n { value: \"flux\", label: \"Flux 1.1\" },\n { value: \"whisper\", label: \"Whisper large\" },\n];\n\n// Several criteria at once; each one in force is a chip that undoes it.\nexport default function FilterBarJobs() {\n const [query, setQuery] = React.useState(\"\");\n const [status, setStatus] = React.useState([\"failed\"]);\n const [model, setModel] = React.useState(\"flux\");\n const active = [\n ...status.map((s) => ({ id: `status:${s}`, label: `Status: ${STATUS.find((o) => o.value === s)?.label}`, onRemove: () => setStatus(status.filter((x) => x !== s)) })),\n ...(model ? [{ id: \"model\", label: `Model: ${MODELS.find((m) => m.value === model)?.label}`, onRemove: () => setModel(\"\") }] : []),\n ];\n return (\n <FilterBar label=\"Filter jobs\" active={active} onClearAll={() => {\n setStatus([]);\n setModel(\"\");\n }} results=\"6 jobs\">\n <SearchField label=\"Search jobs\" placeholder=\"Search jobs\" value={query} onValueChange={setQuery} landmark={false} className=\"sm:max-w-64\" />\n <FilterMenu label=\"Status\" options={STATUS} value={status} onValueChange={setStatus} />\n <FilterMenu label=\"Model\" multiple={false} options={MODELS} value={model} onValueChange={setModel} />\n </FilterBar>\n );\n}\n"
234
+ },
235
+ "props": [
236
+ {
237
+ "component": "FilterBar",
238
+ "description": "The bar of filters above a list: controls, active filters as chips, clear all, the count of results.",
239
+ "props": [
240
+ {
241
+ "name": "label",
242
+ "type": "string",
243
+ "default": null,
244
+ "description": "The accessible name of the bar (\"Filter jobs\"), passed translated.",
245
+ "required": true
246
+ },
247
+ {
248
+ "name": "active",
249
+ "type": "readonly ActiveFilter[]",
250
+ "default": "[]",
251
+ "description": "The filters in force, each a removable chip.",
252
+ "required": false
253
+ },
254
+ {
255
+ "name": "children",
256
+ "type": "ReactNode",
257
+ "default": null,
258
+ "description": "The controls: `SearchField`, `FilterMenu`, `DateRangePicker`, a `Chip.Group`…",
259
+ "required": false
260
+ },
261
+ {
262
+ "name": "clearLabel",
263
+ "type": "string",
264
+ "default": "Clear filters",
265
+ "description": "The \"Clear all\" button (\"Clear filters\"), passed translated.",
266
+ "required": false
267
+ },
268
+ {
269
+ "name": "onClearAll",
270
+ "type": "(() => void)",
271
+ "default": null,
272
+ "description": "Clears every filter; the \"Clear all\" button shows when there is at least one.",
273
+ "required": false
274
+ },
275
+ {
276
+ "name": "removeLabel",
277
+ "type": "((label: string) => string)",
278
+ "default": "(text) => `Remove ${text}`",
279
+ "description": "A chip's remove button: `(label) => \"Remove \" + label`, passed translated.",
280
+ "required": false
281
+ },
282
+ {
283
+ "name": "results",
284
+ "type": "string",
285
+ "default": null,
286
+ "description": "The count of results (\"42 jobs\"), announced politely when it changes.",
287
+ "required": false
288
+ }
289
+ ]
290
+ },
291
+ {
292
+ "component": "FilterMenu",
293
+ "description": "A filter's button and its menu of options — checkboxes (several values) or radios (one) — with a count of chosen ones.",
294
+ "props": [
295
+ {
296
+ "name": "label",
297
+ "type": "string",
298
+ "default": null,
299
+ "description": "The name of the filter on its button (\"Status\"), passed translated.",
300
+ "required": true
301
+ },
302
+ {
303
+ "name": "onValueChange",
304
+ "type": "((value: string[]) => void) | ((value: string) => void)",
305
+ "default": null,
306
+ "description": "Called with the values chosen.\nCalled with the value chosen.",
307
+ "required": true
308
+ },
309
+ {
310
+ "name": "options",
311
+ "type": "readonly FilterOption[]",
312
+ "default": null,
313
+ "description": "The choices.",
314
+ "required": true
315
+ },
316
+ {
317
+ "name": "value",
318
+ "type": "string | readonly string[]",
319
+ "default": null,
320
+ "description": "The values chosen.\nThe value chosen, `\"\"` for none.",
321
+ "required": true
322
+ },
323
+ {
324
+ "name": "className",
325
+ "type": "string",
326
+ "default": null,
327
+ "description": "Classes merged on the button.",
328
+ "required": false
329
+ },
330
+ {
331
+ "name": "defaultOpen",
332
+ "type": "boolean",
333
+ "default": null,
334
+ "description": "Opens the menu at first (a story, a test).",
335
+ "required": false
336
+ },
337
+ {
338
+ "name": "multiple",
339
+ "type": "boolean",
340
+ "default": null,
341
+ "description": "Several values (checkboxes, default) or one (radios).\nOne value: the options are radios.",
342
+ "required": false
343
+ }
344
+ ]
345
+ }
346
+ ]
347
+ }
@@ -0,0 +1,162 @@
1
+ {
2
+ "name": "glow-card",
3
+ "type": "primitive",
4
+ "title": "Glow card",
5
+ "summary": "A card lit by the brand: a bloom of the accent follows the pointer across it, and its border is a ring of the brand gradient that turns and brightens under the pointer or the focus.",
6
+ "description": "A card lit by the brand: a bloom of the accent follows the pointer across it, and its border is a ring of the brand gradient that turns and brightens under the pointer or the focus.",
7
+ "status": "beta",
8
+ "category": "layout",
9
+ "platforms": "web",
10
+ "whenToUse": [
11
+ "Cards that invite a choice: plans, products, the entry points of a landing page.",
12
+ "One featured card among others (`alive`): its ring turns at rest."
13
+ ],
14
+ "whenNotToUse": [
15
+ {
16
+ "when": "For the cards of an app's lists and feeds: a calm card reads better there.",
17
+ "use": "card"
18
+ },
19
+ {
20
+ "when": "Around content the reader studies (a form, a table): light that moves distracts."
21
+ }
22
+ ],
23
+ "bestPractices": [
24
+ "One grid of glowing cards per page; `alive` on one card at most.",
25
+ "Make the whole card the link with `asChild` when it leads somewhere — the focus lights it like the pointer.",
26
+ "Colours come from the brand (`.brand-<id>`): never pass one."
27
+ ],
28
+ "accessibility": {
29
+ "keyboard": [
30
+ {
31
+ "keys": "Tab",
32
+ "action": "Focusing the card (or a control inside it) lights its ring, as the pointer does."
33
+ }
34
+ ],
35
+ "notes": [
36
+ "Decorative light only (pseudo-elements): nothing is added to the accessibility tree; the card's semantics are yours.",
37
+ "Under `prefers-reduced-motion` the ring rests at a fixed angle — still lit on hover and focus, never turning.",
38
+ "Its loops pause off screen and while the page settles (MotionObserver)."
39
+ ]
40
+ },
41
+ "related": [
42
+ "card",
43
+ "button"
44
+ ],
45
+ "web": {
46
+ "entry": "@krizaka/ui/glow-card",
47
+ "import": "import { GlowCard } from \"@krizaka/ui/glow-card\";",
48
+ "props": [
49
+ {
50
+ "component": "GlowCard",
51
+ "description": "A card lit by the brand: a bloom that follows the pointer, a gradient ring that turns. Content is yours.",
52
+ "props": [
53
+ {
54
+ "name": "alive",
55
+ "type": "boolean",
56
+ "default": "false",
57
+ "description": "The ring turns slowly even at rest (a featured card). Otherwise it rests until hovered or focused.",
58
+ "required": false
59
+ },
60
+ {
61
+ "name": "asChild",
62
+ "type": "boolean",
63
+ "default": null,
64
+ "description": "Renders your element (a link, an article) with the card's look and behaviour.",
65
+ "required": false
66
+ },
67
+ {
68
+ "name": "glow",
69
+ "type": "\"both\" | \"border\" | \"spotlight\"",
70
+ "default": "both",
71
+ "description": "What answers: `border` (the turning gradient ring), `spotlight` (the bloom under the pointer), or `both`.",
72
+ "required": false
73
+ }
74
+ ]
75
+ }
76
+ ],
77
+ "examples": [
78
+ {
79
+ "name": "plans",
80
+ "title": "Plans",
81
+ "description": "Three plans: the light follows the pointer across them; the featured one's ring turns at rest.",
82
+ "path": "examples/glow-card/plans.tsx",
83
+ "module": "@krizaka/ui/registry/examples/glow-card/plans",
84
+ "code": "import { Button } from \"@krizaka/ui/button\";\nimport { GlowCard } from \"@krizaka/ui/glow-card\";\n\nconst plans = [\n { name: \"Community\", price: \"Free\", line: \"Self-hosted, every feature.\" },\n { name: \"Team\", price: \"€19\", line: \"Shared workspaces and roles.\", featured: true },\n { name: \"Sovereign\", price: \"Custom\", line: \"Your servers, our support.\" },\n];\n\n// Plans: move the pointer across them — the light follows, the ring brightens. The featured plan's ring turns at rest.\nexport default function GlowCardPlans() {\n return (\n <div className=\"grid w-full max-w-3xl gap-4 sm:grid-cols-3\">\n {plans.map((p) => (\n <GlowCard key={p.name} alive={p.featured} className=\"flex flex-col gap-3\">\n <h3 className=\"font-display text-lg font-semibold text-fg\">{p.name}</h3>\n <p className=\"font-display text-3xl font-bold text-fg\">{p.price}</p>\n <p className=\"text-sm text-fg-secondary\">{p.line}</p>\n <Button variant={p.featured ? \"gradient\" : \"outline\"} size=\"sm\" className=\"mt-auto\">\n Choose {p.name}\n </Button>\n </GlowCard>\n ))}\n </div>\n );\n}\n"
85
+ },
86
+ {
87
+ "name": "link-card",
88
+ "title": "A card that is a link",
89
+ "description": "`asChild` on an `<a>`, `glow=\"border\"`: the focus lights the ring.",
90
+ "path": "examples/glow-card/link-card.tsx",
91
+ "module": "@krizaka/ui/registry/examples/glow-card/link-card",
92
+ "code": "import { GlowCard } from \"@krizaka/ui/glow-card\";\n\n// `asChild`: the card is the link. The focus lights the ring as the pointer does — keyboard readers get the same answer.\nexport default function GlowCardLink() {\n return (\n <GlowCard asChild glow=\"border\" className=\"block max-w-sm no-underline focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\">\n <a href=\"#docs\">\n <span className=\"block font-display text-lg font-semibold text-fg\">Read the documentation</span>\n <span className=\"mt-1 block text-sm text-fg-secondary\">Every component, its examples and its props — generated from the code.</span>\n </a>\n </GlowCard>\n );\n}\n"
93
+ },
94
+ {
95
+ "name": "spotlight",
96
+ "title": "Spotlight only",
97
+ "description": "`glow=\"spotlight\"`: the bloom alone, for a dense grid.",
98
+ "path": "examples/glow-card/spotlight.tsx",
99
+ "module": "@krizaka/ui/registry/examples/glow-card/spotlight",
100
+ "code": "import { GlowCard } from \"@krizaka/ui/glow-card\";\n\n// `glow=\"spotlight\"`: only the bloom under the pointer, for a dense grid where rings would be too much.\nexport default function GlowCardSpotlight() {\n return (\n <div className=\"grid w-full max-w-lg grid-cols-2 gap-3\">\n {[\"Tips\", \"Unlocks\", \"Auctions\", \"Challenges\"].map((name) => (\n <GlowCard key={name} glow=\"spotlight\" className=\"p-4\">\n <p className=\"font-semibold text-fg\">{name}</p>\n <p className=\"text-xs text-fg-secondary\">90 % to the creator</p>\n </GlowCard>\n ))}\n </div>\n );\n}\n"
101
+ }
102
+ ]
103
+ },
104
+ "native": null,
105
+ "files": [
106
+ {
107
+ "path": "glow-card/glow-card.tsx",
108
+ "content": "// Client (the pointer). A card that answers light: a soft bloom of the brand accent follows the pointer across it, and\n// its border is a ring of the brand gradient that turns slowly and brightens under the pointer or the focus. Composited\n// properties only (CSS variables, opacity); no animation frame — the pointer writes two variables. Under reduced motion\n// the ring rests at a fixed angle and the light stays where it is: a designed still frame, not a switched-off one.\nimport { Slot } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\n\nexport type GlowCardProps = React.ComponentProps<\"div\"> & {\n /**\n * What answers: `border` (the turning gradient ring), `spotlight` (the bloom under the pointer), or `both`.\n * @default \"both\"\n */\n glow?: \"border\" | \"spotlight\" | \"both\";\n /** Renders your element (a link, an article) with the card's look and behaviour. */\n asChild?: boolean;\n /**\n * The ring turns slowly even at rest (a featured card). Otherwise it rests until hovered or focused.\n * @default false\n */\n alive?: boolean;\n};\n\n/** A card lit by the brand: a bloom that follows the pointer, a gradient ring that turns. Content is yours. */\nexport function GlowCard({ glow = \"both\", asChild, alive, className, onPointerMove, ...props }: GlowCardProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n return (\n <Comp\n data-glow={glow}\n data-alive={alive ? \"\" : undefined}\n className={cn(\"kz-glow kz-loop relative isolate rounded-2xl border border-border-default bg-surface-1 p-6\", className)}\n onPointerMove={(e: React.PointerEvent<HTMLDivElement>) => {\n const box = e.currentTarget.getBoundingClientRect();\n e.currentTarget.style.setProperty(\"--kz-glow-x\", `${e.clientX - box.left}px`);\n e.currentTarget.style.setProperty(\"--kz-glow-y\", `${e.clientY - box.top}px`);\n onPointerMove?.(e);\n }}\n {...props}\n />\n );\n}\n"
109
+ },
110
+ {
111
+ "path": "glow-card/index.ts",
112
+ "content": "/** @krizaka/ui/glow-card — `GlowCard`: a bloom of the brand light under the pointer, a turning gradient ring. Client. */\nexport { GlowCard, type GlowCardProps } from \"./glow-card\";\n"
113
+ },
114
+ {
115
+ "path": "glow-card/meta.ts",
116
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Glow card\",\n summary:\n \"A card lit by the brand: a bloom of the accent follows the pointer across it, and its border is a ring of the brand gradient that turns and brightens under the pointer or the focus.\",\n status: \"beta\",\n category: \"layout\",\n platforms: \"web\",\n whenToUse: [\n \"Cards that invite a choice: plans, products, the entry points of a landing page.\",\n \"One featured card among others (`alive`): its ring turns at rest.\",\n ],\n whenNotToUse: [\n { when: \"For the cards of an app's lists and feeds: a calm card reads better there.\", use: \"card\" },\n { when: \"Around content the reader studies (a form, a table): light that moves distracts.\" },\n ],\n bestPractices: [\n \"One grid of glowing cards per page; `alive` on one card at most.\",\n \"Make the whole card the link with `asChild` when it leads somewhere — the focus lights it like the pointer.\",\n \"Colours come from the brand (`.brand-<id>`): never pass one.\",\n ],\n accessibility: {\n keyboard: [{ keys: \"Tab\", action: \"Focusing the card (or a control inside it) lights its ring, as the pointer does.\" }],\n notes: [\n \"Decorative light only (pseudo-elements): nothing is added to the accessibility tree; the card's semantics are yours.\",\n \"Under `prefers-reduced-motion` the ring rests at a fixed angle — still lit on hover and focus, never turning.\",\n \"Its loops pause off screen and while the page settles (MotionObserver).\",\n ],\n },\n related: [\"card\", \"button\"],\n web: {\n imports: [\"GlowCard\"],\n examples: [\n { name: \"plans\", title: \"Plans\", description: \"Three plans: the light follows the pointer across them; the featured one's ring turns at rest.\" },\n { name: \"link-card\", title: \"A card that is a link\", description: \"`asChild` on an `<a>`, `glow=\\\"border\\\"`: the focus lights the ring.\" },\n { name: \"spotlight\", title: \"Spotlight only\", description: \"`glow=\\\"spotlight\\\"`: the bloom alone, for a dense grid.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
117
+ },
118
+ {
119
+ "path": "meta.ts",
120
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"audio-player\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"combobox\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"data-table\"\n | \"date-picker\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"glow-card\"\n | \"image-cropper\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"media-capture\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"pagination\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"tag-input\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. `media`: video, sound, camera, files, cropping. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
121
+ }
122
+ ],
123
+ "dependencies": [
124
+ "radix-ui@^1.7.0"
125
+ ],
126
+ "registryDependencies": [
127
+ "cn"
128
+ ],
129
+ "demo": {
130
+ "path": "examples/glow-card/plans.tsx",
131
+ "content": "import { Button } from \"@krizaka/ui/button\";\nimport { GlowCard } from \"@krizaka/ui/glow-card\";\n\nconst plans = [\n { name: \"Community\", price: \"Free\", line: \"Self-hosted, every feature.\" },\n { name: \"Team\", price: \"€19\", line: \"Shared workspaces and roles.\", featured: true },\n { name: \"Sovereign\", price: \"Custom\", line: \"Your servers, our support.\" },\n];\n\n// Plans: move the pointer across them — the light follows, the ring brightens. The featured plan's ring turns at rest.\nexport default function GlowCardPlans() {\n return (\n <div className=\"grid w-full max-w-3xl gap-4 sm:grid-cols-3\">\n {plans.map((p) => (\n <GlowCard key={p.name} alive={p.featured} className=\"flex flex-col gap-3\">\n <h3 className=\"font-display text-lg font-semibold text-fg\">{p.name}</h3>\n <p className=\"font-display text-3xl font-bold text-fg\">{p.price}</p>\n <p className=\"text-sm text-fg-secondary\">{p.line}</p>\n <Button variant={p.featured ? \"gradient\" : \"outline\"} size=\"sm\" className=\"mt-auto\">\n Choose {p.name}\n </Button>\n </GlowCard>\n ))}\n </div>\n );\n}\n"
132
+ },
133
+ "props": [
134
+ {
135
+ "component": "GlowCard",
136
+ "description": "A card lit by the brand: a bloom that follows the pointer, a gradient ring that turns. Content is yours.",
137
+ "props": [
138
+ {
139
+ "name": "alive",
140
+ "type": "boolean",
141
+ "default": "false",
142
+ "description": "The ring turns slowly even at rest (a featured card). Otherwise it rests until hovered or focused.",
143
+ "required": false
144
+ },
145
+ {
146
+ "name": "asChild",
147
+ "type": "boolean",
148
+ "default": null,
149
+ "description": "Renders your element (a link, an article) with the card's look and behaviour.",
150
+ "required": false
151
+ },
152
+ {
153
+ "name": "glow",
154
+ "type": "\"both\" | \"border\" | \"spotlight\"",
155
+ "default": "both",
156
+ "description": "What answers: `border` (the turning gradient ring), `spotlight` (the bloom under the pointer), or `both`.",
157
+ "required": false
158
+ }
159
+ ]
160
+ }
161
+ ]
162
+ }