@hoardodile/ui 0.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 (356) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +70 -0
  3. package/dist/components/alert.d.ts +13 -0
  4. package/dist/components/alert.js +97 -0
  5. package/dist/components/alert.js.map +1 -0
  6. package/dist/components/app-dialog.d.ts +62 -0
  7. package/dist/components/app-dialog.js +656 -0
  8. package/dist/components/app-dialog.js.map +1 -0
  9. package/dist/components/badge.d.ts +11 -0
  10. package/dist/components/badge.js +70 -0
  11. package/dist/components/badge.js.map +1 -0
  12. package/dist/components/breadcrumb.d.ts +12 -0
  13. package/dist/components/breadcrumb.js +617 -0
  14. package/dist/components/breadcrumb.js.map +1 -0
  15. package/dist/components/button.d.ts +17 -0
  16. package/dist/components/button.js +78 -0
  17. package/dist/components/button.js.map +1 -0
  18. package/dist/components/caption-bar.d.ts +44 -0
  19. package/dist/components/caption-bar.js +827 -0
  20. package/dist/components/caption-bar.js.map +1 -0
  21. package/dist/components/chart-card.d.ts +15 -0
  22. package/dist/components/chart-card.js +27 -0
  23. package/dist/components/chart-card.js.map +1 -0
  24. package/dist/components/checkbox.d.ts +6 -0
  25. package/dist/components/checkbox.js +76 -0
  26. package/dist/components/checkbox.js.map +1 -0
  27. package/dist/components/chip-button.d.ts +28 -0
  28. package/dist/components/chip-button.js +87 -0
  29. package/dist/components/chip-button.js.map +1 -0
  30. package/dist/components/close-confirm-dialog.d.ts +19 -0
  31. package/dist/components/close-confirm-dialog.js +743 -0
  32. package/dist/components/close-confirm-dialog.js.map +1 -0
  33. package/dist/components/color-picker.d.ts +32 -0
  34. package/dist/components/color-picker.js +667 -0
  35. package/dist/components/color-picker.js.map +1 -0
  36. package/dist/components/combobox.d.ts +32 -0
  37. package/dist/components/combobox.js +1211 -0
  38. package/dist/components/combobox.js.map +1 -0
  39. package/dist/components/confirm-by-typing-dialog.d.ts +48 -0
  40. package/dist/components/confirm-by-typing-dialog.js +759 -0
  41. package/dist/components/confirm-by-typing-dialog.js.map +1 -0
  42. package/dist/components/confirm-dialog.d.ts +34 -0
  43. package/dist/components/confirm-dialog.js +710 -0
  44. package/dist/components/confirm-dialog.js.map +1 -0
  45. package/dist/components/count-badge.d.ts +13 -0
  46. package/dist/components/count-badge.js +39 -0
  47. package/dist/components/count-badge.js.map +1 -0
  48. package/dist/components/dialog.d.ts +40 -0
  49. package/dist/components/dialog.js +571 -0
  50. package/dist/components/dialog.js.map +1 -0
  51. package/dist/components/dropdown-menu.d.ts +45 -0
  52. package/dist/components/dropdown-menu.js +1003 -0
  53. package/dist/components/dropdown-menu.js.map +1 -0
  54. package/dist/components/dropdown-select.d.ts +29 -0
  55. package/dist/components/dropdown-select.js +1036 -0
  56. package/dist/components/dropdown-select.js.map +1 -0
  57. package/dist/components/empty.d.ts +15 -0
  58. package/dist/components/empty.js +119 -0
  59. package/dist/components/empty.js.map +1 -0
  60. package/dist/components/filter-rail.d.ts +48 -0
  61. package/dist/components/filter-rail.js +231 -0
  62. package/dist/components/filter-rail.js.map +1 -0
  63. package/dist/components/fixed-action-bar.d.ts +24 -0
  64. package/dist/components/fixed-action-bar.js +48 -0
  65. package/dist/components/fixed-action-bar.js.map +1 -0
  66. package/dist/components/font-picker.d.ts +45 -0
  67. package/dist/components/font-picker.js +759 -0
  68. package/dist/components/font-picker.js.map +1 -0
  69. package/dist/components/form.d.ts +17 -0
  70. package/dist/components/form.js +150 -0
  71. package/dist/components/form.js.map +1 -0
  72. package/dist/components/group-label.d.ts +12 -0
  73. package/dist/components/group-label.js +10 -0
  74. package/dist/components/group-label.js.map +1 -0
  75. package/dist/components/icon-tile.d.ts +16 -0
  76. package/dist/components/icon-tile.js +67 -0
  77. package/dist/components/icon-tile.js.map +1 -0
  78. package/dist/components/icon-toggle.d.ts +24 -0
  79. package/dist/components/icon-toggle.js +81 -0
  80. package/dist/components/icon-toggle.js.map +1 -0
  81. package/dist/components/icon.d.ts +2 -0
  82. package/dist/components/icon.js +49 -0
  83. package/dist/components/icon.js.map +1 -0
  84. package/dist/components/image-crop-panel.d.ts +80 -0
  85. package/dist/components/image-crop-panel.js +1104 -0
  86. package/dist/components/image-crop-panel.js.map +1 -0
  87. package/dist/components/image-cropper.d.ts +63 -0
  88. package/dist/components/image-cropper.js +166 -0
  89. package/dist/components/image-cropper.js.map +1 -0
  90. package/dist/components/input-group.d.ts +22 -0
  91. package/dist/components/input-group.js +270 -0
  92. package/dist/components/input-group.js.map +1 -0
  93. package/dist/components/input.d.ts +16 -0
  94. package/dist/components/input.js +60 -0
  95. package/dist/components/input.js.map +1 -0
  96. package/dist/components/label.d.ts +5 -0
  97. package/dist/components/label.js +40 -0
  98. package/dist/components/label.js.map +1 -0
  99. package/dist/components/list-empty-row.d.ts +16 -0
  100. package/dist/components/list-empty-row.js +40 -0
  101. package/dist/components/list-empty-row.js.map +1 -0
  102. package/dist/components/management-empty.d.ts +14 -0
  103. package/dist/components/management-empty.js +40 -0
  104. package/dist/components/management-empty.js.map +1 -0
  105. package/dist/components/management-skeleton.d.ts +11 -0
  106. package/dist/components/management-skeleton.js +72 -0
  107. package/dist/components/management-skeleton.js.map +1 -0
  108. package/dist/components/meta-chip.d.ts +17 -0
  109. package/dist/components/meta-chip.js +49 -0
  110. package/dist/components/meta-chip.js.map +1 -0
  111. package/dist/components/mobile-drawer.d.ts +29 -0
  112. package/dist/components/mobile-drawer.js +331 -0
  113. package/dist/components/mobile-drawer.js.map +1 -0
  114. package/dist/components/page-header.d.ts +20 -0
  115. package/dist/components/page-header.js +44 -0
  116. package/dist/components/page-header.js.map +1 -0
  117. package/dist/components/page-scaffold.d.ts +34 -0
  118. package/dist/components/page-scaffold.js +157 -0
  119. package/dist/components/page-scaffold.js.map +1 -0
  120. package/dist/components/pagination-bar.d.ts +22 -0
  121. package/dist/components/pagination-bar.js +703 -0
  122. package/dist/components/pagination-bar.js.map +1 -0
  123. package/dist/components/pagination.d.ts +22 -0
  124. package/dist/components/pagination.js +680 -0
  125. package/dist/components/pagination.js.map +1 -0
  126. package/dist/components/panel-toolbar.d.ts +37 -0
  127. package/dist/components/panel-toolbar.js +781 -0
  128. package/dist/components/panel-toolbar.js.map +1 -0
  129. package/dist/components/pill-tabs.d.ts +33 -0
  130. package/dist/components/pill-tabs.js +66 -0
  131. package/dist/components/pill-tabs.js.map +1 -0
  132. package/dist/components/plugin-download-consent.d.ts +52 -0
  133. package/dist/components/plugin-download-consent.js +780 -0
  134. package/dist/components/plugin-download-consent.js.map +1 -0
  135. package/dist/components/popover.d.ts +26 -0
  136. package/dist/components/popover.js +343 -0
  137. package/dist/components/popover.js.map +1 -0
  138. package/dist/components/progress.d.ts +10 -0
  139. package/dist/components/progress.js +103 -0
  140. package/dist/components/progress.js.map +1 -0
  141. package/dist/components/query-state-view.d.ts +36 -0
  142. package/dist/components/query-state-view.js +17 -0
  143. package/dist/components/query-state-view.js.map +1 -0
  144. package/dist/components/radio-group.d.ts +8 -0
  145. package/dist/components/radio-group.js +60 -0
  146. package/dist/components/radio-group.js.map +1 -0
  147. package/dist/components/scroll-area.d.ts +7 -0
  148. package/dist/components/scroll-area.js +80 -0
  149. package/dist/components/scroll-area.js.map +1 -0
  150. package/dist/components/search-field.d.ts +49 -0
  151. package/dist/components/search-field.js +586 -0
  152. package/dist/components/search-field.js.map +1 -0
  153. package/dist/components/secondary-stat.d.ts +13 -0
  154. package/dist/components/secondary-stat.js +56 -0
  155. package/dist/components/secondary-stat.js.map +1 -0
  156. package/dist/components/section-header.d.ts +23 -0
  157. package/dist/components/section-header.js +76 -0
  158. package/dist/components/section-header.js.map +1 -0
  159. package/dist/components/section-label.d.ts +20 -0
  160. package/dist/components/section-label.js +46 -0
  161. package/dist/components/section-label.js.map +1 -0
  162. package/dist/components/section-tabs.d.ts +47 -0
  163. package/dist/components/section-tabs.js +145 -0
  164. package/dist/components/section-tabs.js.map +1 -0
  165. package/dist/components/separator.d.ts +14 -0
  166. package/dist/components/separator.js +49 -0
  167. package/dist/components/separator.js.map +1 -0
  168. package/dist/components/sheet.d.ts +22 -0
  169. package/dist/components/sheet.js +469 -0
  170. package/dist/components/sheet.js.map +1 -0
  171. package/dist/components/sidebar.d.ts +69 -0
  172. package/dist/components/sidebar.js +1645 -0
  173. package/dist/components/sidebar.js.map +1 -0
  174. package/dist/components/skeleton.d.ts +8 -0
  175. package/dist/components/skeleton.js +41 -0
  176. package/dist/components/skeleton.js.map +1 -0
  177. package/dist/components/slider.d.ts +6 -0
  178. package/dist/components/slider.js +75 -0
  179. package/dist/components/slider.js.map +1 -0
  180. package/dist/components/sortable-chip-list.d.ts +26 -0
  181. package/dist/components/sortable-chip-list.js +59 -0
  182. package/dist/components/sortable-chip-list.js.map +1 -0
  183. package/dist/components/spinner.d.ts +5 -0
  184. package/dist/components/spinner.js +65 -0
  185. package/dist/components/spinner.js.map +1 -0
  186. package/dist/components/surface.d.ts +13 -0
  187. package/dist/components/surface.js +51 -0
  188. package/dist/components/surface.js.map +1 -0
  189. package/dist/components/switch.d.ts +8 -0
  190. package/dist/components/switch.js +53 -0
  191. package/dist/components/switch.js.map +1 -0
  192. package/dist/components/table.d.ts +12 -0
  193. package/dist/components/table.js +129 -0
  194. package/dist/components/table.js.map +1 -0
  195. package/dist/components/textarea.d.ts +15 -0
  196. package/dist/components/textarea.js +58 -0
  197. package/dist/components/textarea.js.map +1 -0
  198. package/dist/components/toast.d.ts +19 -0
  199. package/dist/components/toast.js +805 -0
  200. package/dist/components/toast.js.map +1 -0
  201. package/dist/components/toggle.d.ts +12 -0
  202. package/dist/components/toggle.js +63 -0
  203. package/dist/components/toggle.js.map +1 -0
  204. package/dist/components/tooltip.d.ts +9 -0
  205. package/dist/components/tooltip.js +82 -0
  206. package/dist/components/tooltip.js.map +1 -0
  207. package/dist/hooks/use-dialog-footer-actions.d.ts +18 -0
  208. package/dist/hooks/use-dialog-footer-actions.js +11 -0
  209. package/dist/hooks/use-dialog-footer-actions.js.map +1 -0
  210. package/dist/hooks/use-form-field.d.ts +33 -0
  211. package/dist/hooks/use-form-field.js +36 -0
  212. package/dist/hooks/use-form-field.js.map +1 -0
  213. package/dist/hooks/use-mobile.d.ts +34 -0
  214. package/dist/hooks/use-mobile.js +29 -0
  215. package/dist/hooks/use-mobile.js.map +1 -0
  216. package/dist/hooks/use-pinch-pan.d.ts +56 -0
  217. package/dist/hooks/use-pinch-pan.js +223 -0
  218. package/dist/hooks/use-pinch-pan.js.map +1 -0
  219. package/dist/hooks/useMobileBackToClose.d.ts +61 -0
  220. package/dist/hooks/useMobileBackToClose.js +234 -0
  221. package/dist/hooks/useMobileBackToClose.js.map +1 -0
  222. package/dist/icon-BOgL5bpL.d.ts +85 -0
  223. package/dist/icons/actions.d.ts +4 -0
  224. package/dist/icons/actions.js +520 -0
  225. package/dist/icons/actions.js.map +1 -0
  226. package/dist/icons/icon-style.d.ts +2 -0
  227. package/dist/icons/icon-style.js +51 -0
  228. package/dist/icons/icon-style.js.map +1 -0
  229. package/dist/icons/marks.d.ts +15 -0
  230. package/dist/icons/marks.js +48 -0
  231. package/dist/icons/marks.js.map +1 -0
  232. package/dist/icons/registry.d.ts +177 -0
  233. package/dist/icons/registry.js +1180 -0
  234. package/dist/icons/registry.js.map +1 -0
  235. package/dist/index.d.ts +6 -0
  236. package/dist/index.js +653 -0
  237. package/dist/index.js.map +1 -0
  238. package/dist/lib/merge-config.d.ts +15 -0
  239. package/dist/lib/merge-config.js +20 -0
  240. package/dist/lib/merge-config.js.map +1 -0
  241. package/dist/lib/pagination.d.ts +19 -0
  242. package/dist/lib/pagination.js +26 -0
  243. package/dist/lib/pagination.js.map +1 -0
  244. package/dist/lib/utils.d.ts +5 -0
  245. package/dist/lib/utils.js +26 -0
  246. package/dist/lib/utils.js.map +1 -0
  247. package/dist/styles/theme.css +480 -0
  248. package/dist/styles/theme.css.map +1 -0
  249. package/dist/styles/theme.d.ts +2 -0
  250. package/dist/viewport.d.ts +64 -0
  251. package/dist/viewport.js +13 -0
  252. package/dist/viewport.js.map +1 -0
  253. package/package.json +122 -0
  254. package/src/components/alert.tsx +76 -0
  255. package/src/components/app-dialog.tsx +187 -0
  256. package/src/components/badge.tsx +52 -0
  257. package/src/components/breadcrumb.tsx +129 -0
  258. package/src/components/button.tsx +70 -0
  259. package/src/components/caption-bar.test.tsx +151 -0
  260. package/src/components/caption-bar.tsx +392 -0
  261. package/src/components/chart-card.tsx +31 -0
  262. package/src/components/checkbox.tsx +28 -0
  263. package/src/components/chip-button.tsx +69 -0
  264. package/src/components/close-confirm-dialog.tsx +99 -0
  265. package/src/components/color-picker.tsx +186 -0
  266. package/src/components/combobox.tsx +335 -0
  267. package/src/components/confirm-by-typing-dialog.tsx +130 -0
  268. package/src/components/confirm-dialog.test.tsx +39 -0
  269. package/src/components/confirm-dialog.tsx +98 -0
  270. package/src/components/count-badge.tsx +23 -0
  271. package/src/components/dialog.tsx +315 -0
  272. package/src/components/dropdown-menu.tsx +311 -0
  273. package/src/components/dropdown-select.tsx +211 -0
  274. package/src/components/empty.tsx +104 -0
  275. package/src/components/filter-rail.tsx +139 -0
  276. package/src/components/fixed-action-bar.tsx +46 -0
  277. package/src/components/font-picker.tsx +257 -0
  278. package/src/components/form.tsx +127 -0
  279. package/src/components/group-label.tsx +13 -0
  280. package/src/components/icon-tile.tsx +35 -0
  281. package/src/components/icon-toggle.tsx +57 -0
  282. package/src/components/icon.tsx +66 -0
  283. package/src/components/image-crop-panel.test.tsx +167 -0
  284. package/src/components/image-crop-panel.tsx +465 -0
  285. package/src/components/image-cropper.test.tsx +144 -0
  286. package/src/components/image-cropper.tsx +265 -0
  287. package/src/components/input-group.tsx +156 -0
  288. package/src/components/input.tsx +45 -0
  289. package/src/components/label.tsx +20 -0
  290. package/src/components/list-empty-row.tsx +27 -0
  291. package/src/components/management-empty.tsx +25 -0
  292. package/src/components/management-skeleton.tsx +23 -0
  293. package/src/components/meta-chip.tsx +40 -0
  294. package/src/components/mobile-drawer.test.tsx +52 -0
  295. package/src/components/mobile-drawer.tsx +98 -0
  296. package/src/components/page-header.tsx +47 -0
  297. package/src/components/page-scaffold.test.tsx +24 -0
  298. package/src/components/page-scaffold.tsx +131 -0
  299. package/src/components/pagination-bar.test.tsx +87 -0
  300. package/src/components/pagination-bar.tsx +139 -0
  301. package/src/components/pagination.tsx +139 -0
  302. package/src/components/panel-toolbar.tsx +99 -0
  303. package/src/components/pill-tabs.test.tsx +84 -0
  304. package/src/components/pill-tabs.tsx +77 -0
  305. package/src/components/plugin-download-consent.test.tsx +90 -0
  306. package/src/components/plugin-download-consent.tsx +183 -0
  307. package/src/components/popover.tsx +133 -0
  308. package/src/components/progress.tsx +88 -0
  309. package/src/components/query-state-view.tsx +47 -0
  310. package/src/components/radio-group.tsx +36 -0
  311. package/src/components/scroll-area.tsx +52 -0
  312. package/src/components/search-field.test.tsx +86 -0
  313. package/src/components/search-field.tsx +119 -0
  314. package/src/components/secondary-stat.tsx +24 -0
  315. package/src/components/section-header.tsx +54 -0
  316. package/src/components/section-label.tsx +40 -0
  317. package/src/components/section-tabs.test.tsx +154 -0
  318. package/src/components/section-tabs.tsx +190 -0
  319. package/src/components/separator.tsx +38 -0
  320. package/src/components/sheet.tsx +170 -0
  321. package/src/components/sidebar.tsx +725 -0
  322. package/src/components/skeleton.tsx +18 -0
  323. package/src/components/slider.tsx +52 -0
  324. package/src/components/sortable-chip-list.tsx +56 -0
  325. package/src/components/spinner.tsx +35 -0
  326. package/src/components/surface.tsx +37 -0
  327. package/src/components/switch.tsx +30 -0
  328. package/src/components/table.tsx +116 -0
  329. package/src/components/textarea.tsx +41 -0
  330. package/src/components/toast.tsx +239 -0
  331. package/src/components/toggle.tsx +45 -0
  332. package/src/components/tooltip.tsx +64 -0
  333. package/src/hooks/use-dialog-footer-actions.ts +23 -0
  334. package/src/hooks/use-form-field.ts +62 -0
  335. package/src/hooks/use-mobile.ts +54 -0
  336. package/src/hooks/use-pinch-pan.test.tsx +42 -0
  337. package/src/hooks/use-pinch-pan.ts +340 -0
  338. package/src/hooks/useMobileBackToClose.test.tsx +140 -0
  339. package/src/hooks/useMobileBackToClose.ts +508 -0
  340. package/src/icons/actions.ts +10 -0
  341. package/src/icons/icon-style.test.tsx +69 -0
  342. package/src/icons/icon-style.ts +104 -0
  343. package/src/icons/marks.tsx +72 -0
  344. package/src/icons/registry.test.ts +65 -0
  345. package/src/icons/registry.ts +1163 -0
  346. package/src/index.ts +22 -0
  347. package/src/lib/merge-config.test.ts +109 -0
  348. package/src/lib/merge-config.ts +22 -0
  349. package/src/lib/pagination.test.ts +55 -0
  350. package/src/lib/pagination.ts +42 -0
  351. package/src/lib/utils.ts +6 -0
  352. package/src/styles/scan-contract.test.ts +74 -0
  353. package/src/styles/theme.css +798 -0
  354. package/src/test/i18n.tsx +22 -0
  355. package/src/test/setup.ts +104 -0
  356. package/src/viewport.ts +69 -0
@@ -0,0 +1,18 @@
1
+ import { cn } from "@hoardodile/ui/lib/utils"
2
+ import type { ElementType } from "react"
3
+
4
+ function Skeleton({
5
+ className,
6
+ element: Element = "div",
7
+ ...props
8
+ }: React.ComponentProps<"div"> & { readonly element?: ElementType }) {
9
+ return (
10
+ <Element
11
+ data-slot="skeleton"
12
+ className={cn("animate-pulse rounded-md bg-muted", className)}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ export { Skeleton }
@@ -0,0 +1,52 @@
1
+ import { Slider as SliderPrimitive } from "@base-ui/react/slider"
2
+
3
+ import { cn } from "@hoardodile/ui/lib/utils"
4
+
5
+ function Slider({
6
+ className,
7
+ defaultValue,
8
+ value,
9
+ min = 0,
10
+ max = 100,
11
+ ...props
12
+ }: SliderPrimitive.Root.Props) {
13
+ const _values = Array.isArray(value)
14
+ ? value
15
+ : Array.isArray(defaultValue)
16
+ ? defaultValue
17
+ : [min, max]
18
+
19
+ return (
20
+ <SliderPrimitive.Root
21
+ className={cn("data-horizontal:w-full data-vertical:h-full", className)}
22
+ data-slot="slider"
23
+ defaultValue={defaultValue}
24
+ value={value}
25
+ min={min}
26
+ max={max}
27
+ thumbAlignment="edge"
28
+ {...props}
29
+ >
30
+ <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
31
+ <SliderPrimitive.Track
32
+ data-slot="slider-track"
33
+ className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5"
34
+ >
35
+ <SliderPrimitive.Indicator
36
+ data-slot="slider-range"
37
+ className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
38
+ />
39
+ </SliderPrimitive.Track>
40
+ {Array.from({ length: _values.length }, (_, index) => (
41
+ <SliderPrimitive.Thumb
42
+ data-slot="slider-thumb"
43
+ key={index}
44
+ className="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
45
+ />
46
+ ))}
47
+ </SliderPrimitive.Control>
48
+ </SliderPrimitive.Root>
49
+ )
50
+ }
51
+
52
+ export { Slider }
@@ -0,0 +1,56 @@
1
+ import { closestCorners, DndContext } from "@dnd-kit/core"
2
+ import {
3
+ rectSortingStrategy,
4
+ SortableContext,
5
+ type SortingStrategy,
6
+ } from "@dnd-kit/sortable"
7
+ import { cn } from "@hoardodile/ui/lib/utils"
8
+ import type { ReactNode } from "react"
9
+
10
+ export type SortableChipListProps<T extends { readonly id: string }> = {
11
+ readonly items: readonly T[]
12
+ readonly renderItem: (item: T) => ReactNode
13
+ readonly sensors: ReturnType<typeof import("@dnd-kit/core").useSensors>
14
+ readonly onDragEnd: (event: import("@dnd-kit/core").DragEndEvent) => void
15
+ readonly strategy?: SortingStrategy
16
+ readonly empty?: ReactNode
17
+ readonly className?: string
18
+ readonly listClassName?: string
19
+ }
20
+
21
+ /**
22
+ * Encapsulates the canonical DnD chip list used by management panels:
23
+ * `DndContext` + `SortableContext` with `closestCorners` and
24
+ * `rectSortingStrategy`.
25
+ */
26
+ export function SortableChipList<T extends { readonly id: string }>(
27
+ props: SortableChipListProps<T>,
28
+ ): ReactNode {
29
+ if (props.items.length === 0) {
30
+ return props.empty ?? null
31
+ }
32
+
33
+ return (
34
+ <div className={props.className}>
35
+ <DndContext
36
+ sensors={props.sensors}
37
+ collisionDetection={closestCorners}
38
+ onDragEnd={props.onDragEnd}
39
+ >
40
+ <SortableContext
41
+ items={props.items.map((item) => item.id)}
42
+ strategy={props.strategy ?? rectSortingStrategy}
43
+ >
44
+ <div
45
+ className={cn(
46
+ "flex flex-wrap items-center gap-1.5",
47
+ props.listClassName,
48
+ )}
49
+ >
50
+ {props.items.map((item) => props.renderItem(item))}
51
+ </div>
52
+ </SortableContext>
53
+ </DndContext>
54
+ </div>
55
+ )
56
+ }
@@ -0,0 +1,35 @@
1
+ import { useTranslation } from "react-i18next"
2
+
3
+ import { cn } from "@hoardodile/ui/lib/utils"
4
+
5
+ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
6
+ const { t } = useTranslation("ui", { useSuspense: false })
7
+ return (
8
+ <svg
9
+ data-slot="spinner"
10
+ role="status"
11
+ aria-label={t("aria.loading")}
12
+ viewBox="0 0 24 24"
13
+ fill="none"
14
+ className={cn("size-4 animate-spin", className)}
15
+ {...props}
16
+ >
17
+ <circle
18
+ className="opacity-25"
19
+ cx="12"
20
+ cy="12"
21
+ r="10"
22
+ stroke="currentColor"
23
+ strokeWidth="4"
24
+ />
25
+ <path
26
+ d="M22 12a10 10 0 0 0-10-10"
27
+ stroke="currentColor"
28
+ strokeWidth="4"
29
+ strokeLinecap="round"
30
+ />
31
+ </svg>
32
+ )
33
+ }
34
+
35
+ export { Spinner }
@@ -0,0 +1,37 @@
1
+ import { cn } from "@hoardodile/ui/lib/utils"
2
+ import type * as React from "react"
3
+
4
+ type SurfaceProps<C extends React.ElementType = "div"> = {
5
+ as?: C
6
+ size?: "default" | "compact"
7
+ /**
8
+ * Visual tone. `flat` (default) is the hairline card
9
+ */
10
+ tone?: "flat"
11
+ } & Omit<React.ComponentPropsWithoutRef<C>, "as" | "size" | "tone">
12
+
13
+ function Surface<C extends React.ElementType = "div">({
14
+ as,
15
+ size = "default",
16
+ tone = "flat",
17
+ className,
18
+ ...props
19
+ }: SurfaceProps<C>) {
20
+ const Component = as || "div"
21
+ return (
22
+ <Component
23
+ data-slot="surface"
24
+ data-size={size}
25
+ data-tone={tone}
26
+ className={cn(
27
+ "rounded-md border bg-card text-card-foreground",
28
+ size === "default" && "p-4",
29
+ size === "compact" && "p-2",
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ export { Surface }
@@ -0,0 +1,30 @@
1
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
2
+
3
+ import { cn } from "@hoardodile/ui/lib/utils"
4
+
5
+ function Switch({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }: SwitchPrimitive.Root.Props & {
10
+ size?: "sm" | "default"
11
+ }) {
12
+ return (
13
+ <SwitchPrimitive.Root
14
+ data-slot="switch"
15
+ data-size={size}
16
+ className={cn(
17
+ "peer group/switch relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-5 data-[size=default]:w-9 data-[size=default]:p-[3px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ <SwitchPrimitive.Thumb
23
+ data-slot="switch-thumb"
24
+ className="pointer-events-none block rounded-full bg-background ring-1 ring-foreground/10 transition-transform group-data-[size=default]/switch:size-3.5 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%+2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
25
+ />
26
+ </SwitchPrimitive.Root>
27
+ )
28
+ }
29
+
30
+ export { Switch }
@@ -0,0 +1,116 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "@hoardodile/ui/lib/utils"
6
+
7
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
8
+ return (
9
+ <div
10
+ data-slot="table-container"
11
+ className="relative w-full overflow-x-auto"
12
+ >
13
+ <table
14
+ data-slot="table"
15
+ className={cn("w-full caption-bottom text-sm", className)}
16
+ {...props}
17
+ />
18
+ </div>
19
+ )
20
+ }
21
+
22
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
23
+ return (
24
+ <thead
25
+ data-slot="table-header"
26
+ className={cn("[&_tr]:border-b", className)}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
33
+ return (
34
+ <tbody
35
+ data-slot="table-body"
36
+ className={cn("[&_tr:last-child]:border-0", className)}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
43
+ return (
44
+ <tfoot
45
+ data-slot="table-footer"
46
+ className={cn(
47
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
56
+ return (
57
+ <tr
58
+ data-slot="table-row"
59
+ className={cn(
60
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ )
66
+ }
67
+
68
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
69
+ return (
70
+ <th
71
+ data-slot="table-head"
72
+ className={cn(
73
+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
74
+ className
75
+ )}
76
+ {...props}
77
+ />
78
+ )
79
+ }
80
+
81
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
82
+ return (
83
+ <td
84
+ data-slot="table-cell"
85
+ className={cn(
86
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
87
+ className
88
+ )}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ function TableCaption({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<"caption">) {
98
+ return (
99
+ <caption
100
+ data-slot="table-caption"
101
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
102
+ {...props}
103
+ />
104
+ )
105
+ }
106
+
107
+ export {
108
+ Table,
109
+ TableHeader,
110
+ TableBody,
111
+ TableFooter,
112
+ TableHead,
113
+ TableRow,
114
+ TableCell,
115
+ TableCaption,
116
+ }
@@ -0,0 +1,41 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+ import * as React from "react"
3
+
4
+ import { cn } from "@hoardodile/ui/lib/utils"
5
+
6
+ /**
7
+ * Muted-fill textarea (the forms Textarea anatomy): fill background,
8
+ * `rounded-lg`, no border, same anatomy as the inputs. The `size` tiers
9
+ * only shift padding and text; the height stays row-driven.
10
+ */
11
+ const textareaVariants = cva(
12
+ "flex field-sizing-content min-h-16 w-full resize-none rounded-lg bg-muted px-3 py-2.5 text-ui text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-[3px] aria-invalid:ring-destructive/20",
13
+ {
14
+ variants: {
15
+ size: {
16
+ sm: "px-2.5 py-2 text-xs",
17
+ md: "px-3 py-2.5",
18
+ lg: "px-3.5 py-3",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ size: "md",
23
+ },
24
+ }
25
+ )
26
+
27
+ function Textarea({
28
+ className,
29
+ size,
30
+ ...props
31
+ }: React.ComponentProps<"textarea"> & VariantProps<typeof textareaVariants>) {
32
+ return (
33
+ <textarea
34
+ data-slot="textarea"
35
+ className={cn(textareaVariants({ size }), className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ export { Textarea, textareaVariants }
@@ -0,0 +1,239 @@
1
+ import { CheckCircle } from "@hoardodile/ui/icons/registry"
2
+ import { DangerTriangle } from "@hoardodile/ui/icons/registry"
3
+ import { ForbiddenCircle } from "@hoardodile/ui/icons/registry"
4
+ import { InfoCircle } from "@hoardodile/ui/icons/registry"
5
+ import * as React from "react"
6
+ import { Toast as ToastPrimitive } from "@base-ui/react/toast"
7
+ import { useTranslation } from "react-i18next"
8
+
9
+ import { cn } from "@hoardodile/ui/lib/utils"
10
+ import { Button } from "@hoardodile/ui/components/button"
11
+ import { Cross } from "@hoardodile/ui/icons/marks"
12
+ import { Spinner } from "@hoardodile/ui/components/spinner"
13
+
14
+ const toast = ToastPrimitive.createToastManager()
15
+
16
+ function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
17
+ return <ToastPrimitive.Provider {...props} />
18
+ }
19
+
20
+ function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
21
+ return <ToastPrimitive.Portal data-slot="toast-portal" {...props} />
22
+ }
23
+
24
+ function ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {
25
+ return (
26
+ <ToastPrimitive.Viewport
27
+ data-slot="toast-viewport"
28
+ className={cn(
29
+ "pointer-events-none fixed inset-x-4 bottom-4 z-50 mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full",
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function Toast({ className, ...props }: ToastPrimitive.Root.Props) {
38
+ return (
39
+ <ToastPrimitive.Root
40
+ data-slot="toast"
41
+ className={cn(
42
+ "group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-2xl border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
43
+ "[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]",
44
+ "h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]",
45
+ "after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']",
46
+ "data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]",
47
+ "data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]",
48
+ "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]",
49
+ "data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
50
+ "data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
51
+ "data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
52
+ "data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
53
+ "data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
54
+ "data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
55
+ "data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
56
+ "data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {
65
+ return (
66
+ <ToastPrimitive.Content
67
+ data-slot="toast-content"
68
+ className={cn(
69
+ "flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100",
70
+ className
71
+ )}
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ function ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {
78
+ return (
79
+ <ToastPrimitive.Title
80
+ data-slot="toast-title"
81
+ className={cn("text-sm font-medium", className)}
82
+ {...props}
83
+ />
84
+ )
85
+ }
86
+
87
+ function ToastDescription({
88
+ className,
89
+ ...props
90
+ }: ToastPrimitive.Description.Props) {
91
+ return (
92
+ <ToastPrimitive.Description
93
+ data-slot="toast-description"
94
+ className={cn("text-sm text-muted-foreground", className)}
95
+ {...props}
96
+ />
97
+ )
98
+ }
99
+
100
+ function ToastAction({
101
+ className,
102
+ render = <Button variant="outline" size="sm" />,
103
+ ...props
104
+ }: ToastPrimitive.Action.Props) {
105
+ return (
106
+ <ToastPrimitive.Action
107
+ data-slot="toast-action"
108
+ render={render}
109
+ className={cn("shrink-0", className)}
110
+ {...props}
111
+ />
112
+ )
113
+ }
114
+
115
+ function ToastClose({
116
+ className,
117
+ children,
118
+ render = <Button variant="ghost" size="icon-sm" />,
119
+ ...props
120
+ }: ToastPrimitive.Close.Props) {
121
+ const { t } = useTranslation("ui", { useSuspense: false })
122
+ return (
123
+ <ToastPrimitive.Close
124
+ data-slot="toast-close"
125
+ aria-label={t("aria.closeToast")}
126
+ render={render}
127
+ className={cn(
128
+ "relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground",
129
+ className
130
+ )}
131
+ {...props}
132
+ >
133
+ {children ?? (
134
+ <Cross aria-hidden="true" />
135
+ )}
136
+ </ToastPrimitive.Close>
137
+ )
138
+ }
139
+
140
+ function ToastIcon({ type }: { type: string | undefined }) {
141
+ let icon: React.ReactNode = null
142
+
143
+ if (type === "success") {
144
+ icon = (
145
+ <CheckCircle className="text-emerald-500" aria-hidden="true" />
146
+ )
147
+ }
148
+
149
+ if (type === "info") {
150
+ icon = (
151
+ <InfoCircle aria-hidden="true" />
152
+ )
153
+ }
154
+
155
+ if (type === "warning") {
156
+ icon = (
157
+ <DangerTriangle className="text-amber-500" aria-hidden="true" />
158
+ )
159
+ }
160
+
161
+ if (type === "error") {
162
+ icon = (
163
+ <ForbiddenCircle className="text-destructive" aria-hidden="true" />
164
+ )
165
+ }
166
+
167
+ if (type === "loading") {
168
+ icon = (
169
+ <Spinner className="size-4" aria-hidden="true" />
170
+ )
171
+ }
172
+
173
+ if (!icon) {
174
+ return null
175
+ }
176
+
177
+ return (
178
+ <span
179
+ data-slot="toast-icon"
180
+ className="shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"
181
+ >
182
+ {icon}
183
+ </span>
184
+ )
185
+ }
186
+
187
+ function ToastList() {
188
+ const { toasts } = ToastPrimitive.useToastManager()
189
+
190
+ return toasts.map((toastItem) => (
191
+ <Toast key={toastItem.id} toast={toastItem}>
192
+ <ToastContent>
193
+ <ToastIcon type={toastItem.type} />
194
+ <div className="flex min-w-0 flex-1 flex-col gap-1">
195
+ <ToastTitle />
196
+ <ToastDescription />
197
+ </div>
198
+ <ToastAction />
199
+ <ToastClose />
200
+ </ToastContent>
201
+ </Toast>
202
+ ))
203
+ }
204
+
205
+ function Toaster({
206
+ children,
207
+ toastManager = toast,
208
+ ...props
209
+ }: ToastPrimitive.Provider.Props) {
210
+ return (
211
+ <ToastProvider toastManager={toastManager} {...props}>
212
+ {children}
213
+ <ToastPortal>
214
+ <ToastViewport>
215
+ <ToastList />
216
+ </ToastViewport>
217
+ </ToastPortal>
218
+ </ToastProvider>
219
+ )
220
+ }
221
+
222
+ const createToastManager = ToastPrimitive.createToastManager
223
+ const useToastManager = ToastPrimitive.useToastManager
224
+
225
+ export {
226
+ Toaster,
227
+ Toast,
228
+ ToastAction,
229
+ ToastClose,
230
+ ToastContent,
231
+ ToastDescription,
232
+ ToastPortal,
233
+ ToastProvider,
234
+ ToastTitle,
235
+ ToastViewport,
236
+ createToastManager,
237
+ toast,
238
+ useToastManager,
239
+ }
@@ -0,0 +1,45 @@
1
+ "use client"
2
+
3
+ import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+
6
+ import { cn } from "@hoardodile/ui/lib/utils"
7
+
8
+ const toggleVariants = cva(
9
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "bg-transparent",
14
+ outline: "border border-input bg-transparent shadow-xs hover:bg-muted",
15
+ },
16
+ size: {
17
+ default:
18
+ "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
19
+ sm: "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
20
+ lg: "h-10 min-w-10 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ size: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Toggle({
31
+ className,
32
+ variant = "default",
33
+ size = "default",
34
+ ...props
35
+ }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
36
+ return (
37
+ <TogglePrimitive
38
+ data-slot="toggle"
39
+ className={cn(toggleVariants({ variant, size, className }))}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export { Toggle, toggleVariants }