@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
package/package.json ADDED
@@ -0,0 +1,122 @@
1
+ {
2
+ "name": "@hoardodile/ui",
3
+ "version": "0.0.0",
4
+ "license": "MIT",
5
+ "description": "shadcn/ui component library for hoardodile plugin iframes.",
6
+ "keywords": [
7
+ "hoardodile",
8
+ "plugin",
9
+ "ui",
10
+ "shadcn",
11
+ "react"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/hoardodile/hoardodile.git",
16
+ "directory": "packages/ui"
17
+ },
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "development": "./src/index.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./theme.css": {
26
+ "development": "./src/styles/theme.css",
27
+ "default": "./dist/styles/theme.css"
28
+ },
29
+ "./lib/*": {
30
+ "types": "./dist/lib/*.d.ts",
31
+ "development": "./src/lib/*",
32
+ "default": "./dist/lib/*.js"
33
+ },
34
+ "./components/*": {
35
+ "types": "./dist/components/*.d.ts",
36
+ "development": "./src/components/*",
37
+ "default": "./dist/components/*.js"
38
+ },
39
+ "./icons/*": {
40
+ "types": "./dist/icons/*.d.ts",
41
+ "development": "./src/icons/*",
42
+ "default": "./dist/icons/*.js"
43
+ },
44
+ "./hooks/*": {
45
+ "types": "./dist/hooks/*.d.ts",
46
+ "development": "./src/hooks/*",
47
+ "default": "./dist/hooks/*.js"
48
+ },
49
+ "./viewport": {
50
+ "types": "./dist/viewport.d.ts",
51
+ "development": "./src/viewport.ts",
52
+ "default": "./dist/viewport.js"
53
+ }
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "src"
58
+ ],
59
+ "sideEffects": [
60
+ "*.css"
61
+ ],
62
+ "engines": {
63
+ "node": ">=24"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "dependencies": {
69
+ "@base-ui/react": "^1.7.0",
70
+ "@dnd-kit/core": "^6.3.1",
71
+ "@dnd-kit/sortable": "^10.0.0",
72
+ "@dnd-kit/utilities": "^3.2.2",
73
+ "@solar-icons/react": "^2.1.0",
74
+ "class-variance-authority": "^0.7.1",
75
+ "clsx": "^2.1.1",
76
+ "react-hook-form": "^7.86.0",
77
+ "react-image-crop": "^11.1.2",
78
+ "react-use": "^17.6.1",
79
+ "tailwind-merge": "^3.6.0",
80
+ "@hoardodile/i18n": "0.0.0"
81
+ },
82
+ "peerDependencies": {
83
+ "i18next": "^26.4.0",
84
+ "react": "^19.2.8",
85
+ "react-dom": "^19.2.8",
86
+ "react-i18next": "^17.0.12",
87
+ "shadcn": "^4.16.1",
88
+ "tailwindcss": "^4.3.3",
89
+ "tw-animate-css": "^1.4.0"
90
+ },
91
+ "peerDependenciesMeta": {
92
+ "shadcn": {
93
+ "optional": true
94
+ },
95
+ "tailwindcss": {
96
+ "optional": true
97
+ },
98
+ "tw-animate-css": {
99
+ "optional": true
100
+ }
101
+ },
102
+ "devDependencies": {
103
+ "@tailwindcss/node": "^4.3.3",
104
+ "@tailwindcss/oxide": "^4.3.3",
105
+ "@testing-library/jest-dom": "^7.0.1",
106
+ "@testing-library/react": "^16.3.2",
107
+ "@testing-library/user-event": "^14.6.6",
108
+ "@types/node": "^26.2.0",
109
+ "@types/react": "^19.2.18",
110
+ "@types/react-dom": "^19.2.4",
111
+ "shadcn": "^4.19.0",
112
+ "tailwindcss": "^4.3.3",
113
+ "tsup": "^8.5.1",
114
+ "tw-animate-css": "^1.4.0",
115
+ "typescript": "5.9.3"
116
+ },
117
+ "scripts": {
118
+ "build": "tsup",
119
+ "lint": "tsc --noEmit",
120
+ "test": "vitest run"
121
+ }
122
+ }
@@ -0,0 +1,76 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@hoardodile/ui/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-card text-card-foreground",
12
+ destructive:
13
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ function Alert({
23
+ className,
24
+ variant,
25
+ ...props
26
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
27
+ return (
28
+ <div
29
+ data-slot="alert"
30
+ role="alert"
31
+ className={cn(alertVariants({ variant }), className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38
+ return (
39
+ <div
40
+ data-slot="alert-title"
41
+ className={cn(
42
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
43
+ className
44
+ )}
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ function AlertDescription({
51
+ className,
52
+ ...props
53
+ }: React.ComponentProps<"div">) {
54
+ return (
55
+ <div
56
+ data-slot="alert-description"
57
+ className={cn(
58
+ "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
59
+ className
60
+ )}
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
67
+ return (
68
+ <div
69
+ data-slot="alert-action"
70
+ className={cn("absolute top-2.5 right-3", className)}
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ export { Alert, AlertTitle, AlertDescription, AlertAction }
@@ -0,0 +1,187 @@
1
+ import {
2
+ Dialog,
3
+ DialogBody,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogFooter,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ } from "./dialog"
10
+ import {
11
+ DialogFooterActionsContext,
12
+ useDialogFooterActions,
13
+ } from "@hoardodile/ui/hooks/use-dialog-footer-actions"
14
+ import { cn } from "@hoardodile/ui/lib/utils"
15
+ import {
16
+ type ReactNode,
17
+ useEffect,
18
+ useMemo,
19
+ useRef,
20
+ useState,
21
+ } from "react"
22
+
23
+ /** Dialog width tiers: the 320–480 narrow slot plus
24
+ the wide working tiers — edit hubs, pickers and selectors get
25
+ real estate; confirmations never leave the narrow slot. */
26
+ export type AppDialogSize = "sm" | "md" | "lg" | "xl" | "2xl"
27
+
28
+ const sizeClasses: Record<AppDialogSize, string> = {
29
+ sm: "sm:max-w-sm",
30
+ md: "sm:max-w-md",
31
+ lg: "sm:max-w-2xl",
32
+ xl: "sm:max-w-3xl",
33
+ "2xl": "sm:max-w-4xl",
34
+ }
35
+
36
+ export { useDialogFooterActions }
37
+
38
+ /**
39
+ * Declarative footer action: renders nothing in the body and contributes
40
+ * the node to the surrounding dialog's footer (appended after the
41
+ * dialog's own `footer` prop). Panels embed their primary action this way
42
+ * so action buttons live in the footer, never in the body. Outside a
43
+ * dialog (sheets, pickers) it renders nothing — callers keep their inline
44
+ * action bar as the fallback.
45
+ */
46
+ export function DialogFooterActions({ children }: { children: ReactNode }) {
47
+ const slot = useDialogFooterActions()
48
+ useEffect(() => {
49
+ if (slot === null) return
50
+ slot.setFooterActions(children)
51
+ return () => slot.setFooterActions(null)
52
+ }, [slot, children])
53
+ return null
54
+ }
55
+
56
+ export type AppDialogProps = Readonly<{
57
+ open: boolean
58
+ onOpenChange: (open: boolean) => void
59
+ title: ReactNode
60
+ /** Uppercase section marker above the title (feature dialogs). */
61
+ eyebrow?: ReactNode
62
+ /** Leading icon tile in the title row (feature dialogs). */
63
+ icon?: ReactNode
64
+ /** Danger register — the title ink takes the destructive hue; the
65
+ danger itself stays in copy and the confirm button. */
66
+ danger?: boolean
67
+ /** Width tier; defaults to `md` (the narrow form slot). */
68
+ size?: AppDialogSize
69
+ /**
70
+ * Drops the body padding for workspace bodies that own their layout
71
+ * edge to edge (the two-pane editors); the header keeps its padding
72
+ * and the footer becomes a plain bottom bar.
73
+ */
74
+ flush?: boolean
75
+ description?: ReactNode
76
+ children: ReactNode
77
+ /** Leading footer actions; body panels may append theirs via
78
+ {@link DialogFooterActions}. */
79
+ footer?: ReactNode
80
+ contentClassName?: string
81
+ /** Passed to dialog overlay (e.g. disable backdrop blur over WebGL). */
82
+ overlayClassName?: string
83
+ /** Fade-only motion — lighter than default slide/zoom when over heavy surfaces. */
84
+ contentMotion?: "default" | "minimal"
85
+ /** Defaults to true. Set to false to honour native auto-focus (rare). */
86
+ suppressAutoFocus?: boolean
87
+ contentTestId?: string
88
+ }>
89
+
90
+ /**
91
+ * Standard dialog shell. Wires up `Dialog`/`DialogContent`/`DialogHeader`/
92
+ * `DialogTitle` and an optional eyebrow, icon, description and footer so
93
+ * callers stop re-stating the boilerplate. Use {@link ConfirmDialog} for
94
+ * the common "cancel + primary action" pattern.
95
+ */
96
+ export function AppDialog(props: AppDialogProps) {
97
+ const {
98
+ open,
99
+ onOpenChange,
100
+ title,
101
+ eyebrow,
102
+ icon,
103
+ danger,
104
+ size,
105
+ flush,
106
+ description,
107
+ children,
108
+ footer,
109
+ contentClassName,
110
+ overlayClassName,
111
+ contentMotion,
112
+ suppressAutoFocus = true,
113
+ contentTestId,
114
+ } = props
115
+ // Focusing the first focusable element on open would steal the caret
116
+ // from the page underneath, jump scroll position, and on iOS
117
+ // occasionally pop the soft keyboard before the user even reaches an
118
+ // input. Routing focus to the dialog container itself keeps the
119
+ // dialog accessible without those side effects.
120
+ const contentRef = useRef<HTMLDivElement | null>(null)
121
+
122
+ // Footer actions contributed by body panels (DialogFooterActions):
123
+ // appended after the dialog's own footer, reset when the dialog closes.
124
+ const [footerActions, setFooterActions] = useState<ReactNode>(null)
125
+ const actionsValue = useMemo(
126
+ () => ({ setFooterActions }),
127
+ [setFooterActions],
128
+ )
129
+
130
+ return (
131
+ <Dialog open={open} onOpenChange={onOpenChange}>
132
+ <DialogContent
133
+ ref={contentRef}
134
+ className={cn(
135
+ size !== undefined && sizeClasses[size],
136
+ flush && "p-0",
137
+ contentClassName,
138
+ )}
139
+ overlayClassName={overlayClassName}
140
+ contentMotion={contentMotion}
141
+ initialFocus={suppressAutoFocus ? contentRef : undefined}
142
+ data-testid={contentTestId}
143
+ >
144
+ <DialogHeader className={flush ? "gap-4" : undefined}>
145
+ {eyebrow !== undefined ? (
146
+ <span
147
+ className={cn(
148
+ "text-tiny font-semibold tracking-label uppercase",
149
+ danger ? "text-destructive" : "text-muted-foreground",
150
+ )}
151
+ >
152
+ {eyebrow}
153
+ </span>
154
+ ) : null}
155
+ <div className="flex min-w-0 items-center gap-3 pr-6">
156
+ {icon}
157
+ <DialogTitle
158
+ className={cn("truncate", danger && "text-destructive")}
159
+ >
160
+ {title}
161
+ </DialogTitle>
162
+ </div>
163
+ {description !== undefined ? (
164
+ <DialogDescription>{description}</DialogDescription>
165
+ ) : null}
166
+ </DialogHeader>
167
+ {/* The header, body and footer must stay direct children of
168
+ DialogContent — its arrangement logic recognises them by
169
+ type, and a wrapper in between would re-wrap the body in a
170
+ second DialogBody. The footer-actions context therefore
171
+ wraps only the body's own content. */}
172
+ <DialogBody className={flush ? "p-0" : undefined}>
173
+ <DialogFooterActionsContext.Provider value={actionsValue}>
174
+ {children}
175
+ </DialogFooterActionsContext.Provider>
176
+ </DialogBody>
177
+ {(footer !== undefined && footer !== null) ||
178
+ footerActions !== null ? (
179
+ <DialogFooter flush={flush}>
180
+ {footer}
181
+ {footerActions}
182
+ </DialogFooter>
183
+ ) : null}
184
+ </DialogContent>
185
+ </Dialog>
186
+ )
187
+ }
@@ -0,0 +1,52 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@hoardodile/ui/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ secondary:
14
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
15
+ destructive:
16
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
17
+ outline:
18
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
19
+ ghost:
20
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Badge({
31
+ className,
32
+ variant = "default",
33
+ render,
34
+ ...props
35
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
36
+ return useRender({
37
+ defaultTagName: "span",
38
+ props: mergeProps<"span">(
39
+ {
40
+ className: cn(badgeVariants({ variant }), className),
41
+ },
42
+ props
43
+ ),
44
+ render,
45
+ state: {
46
+ slot: "badge",
47
+ variant,
48
+ },
49
+ })
50
+ }
51
+
52
+ export { Badge, badgeVariants }
@@ -0,0 +1,129 @@
1
+ import * as React from "react"
2
+ import { mergeProps } from "@base-ui/react/merge-props"
3
+ import { useRender } from "@base-ui/react/use-render"
4
+ import { useTranslation } from "react-i18next"
5
+
6
+ import { cn } from "@hoardodile/ui/lib/utils"
7
+ import { AltArrowRight } from "@hoardodile/ui/icons/registry"
8
+ import { MenuDots } from "@hoardodile/ui/icons/registry"
9
+
10
+ function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
11
+ const { t } = useTranslation("ui", { useSuspense: false })
12
+ return (
13
+ <nav
14
+ aria-label={t("aria.breadcrumb")}
15
+ data-slot="breadcrumb"
16
+ className={cn(className)}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
23
+ return (
24
+ <ol
25
+ data-slot="breadcrumb-list"
26
+ className={cn(
27
+ "flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground sm:gap-2.5",
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
36
+ return (
37
+ <li
38
+ data-slot="breadcrumb-item"
39
+ className={cn("inline-flex items-center gap-1.5", className)}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ function BreadcrumbLink({
46
+ className,
47
+ render,
48
+ ...props
49
+ }: useRender.ComponentProps<"a">) {
50
+ return useRender({
51
+ defaultTagName: "a",
52
+ props: mergeProps<"a">(
53
+ {
54
+ className: cn("transition-colors hover:text-foreground", className),
55
+ },
56
+ props
57
+ ),
58
+ render,
59
+ state: {
60
+ slot: "breadcrumb-link",
61
+ },
62
+ })
63
+ }
64
+
65
+ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
66
+ return (
67
+ <span
68
+ data-slot="breadcrumb-page"
69
+ role="link"
70
+ aria-disabled="true"
71
+ aria-current="page"
72
+ className={cn("font-normal text-foreground", className)}
73
+ {...props}
74
+ />
75
+ )
76
+ }
77
+
78
+ function BreadcrumbSeparator({
79
+ children,
80
+ className,
81
+ ...props
82
+ }: React.ComponentProps<"li">) {
83
+ return (
84
+ <li
85
+ data-slot="breadcrumb-separator"
86
+ role="presentation"
87
+ aria-hidden="true"
88
+ className={cn("[&>svg]:size-3.5", className)}
89
+ {...props}
90
+ >
91
+ {children ?? (
92
+ <AltArrowRight />
93
+ )}
94
+ </li>
95
+ )
96
+ }
97
+
98
+ function BreadcrumbEllipsis({
99
+ className,
100
+ ...props
101
+ }: React.ComponentProps<"span">) {
102
+ const { t } = useTranslation("ui", { useSuspense: false })
103
+ return (
104
+ <span
105
+ data-slot="breadcrumb-ellipsis"
106
+ role="presentation"
107
+ aria-hidden="true"
108
+ className={cn(
109
+ "flex size-5 items-center justify-center [&>svg]:size-4",
110
+ className
111
+ )}
112
+ {...props}
113
+ >
114
+ <MenuDots
115
+ />
116
+ <span className="sr-only">{t("aria.more")}</span>
117
+ </span>
118
+ )
119
+ }
120
+
121
+ export {
122
+ Breadcrumb,
123
+ BreadcrumbList,
124
+ BreadcrumbItem,
125
+ BreadcrumbLink,
126
+ BreadcrumbPage,
127
+ BreadcrumbSeparator,
128
+ BreadcrumbEllipsis,
129
+ }
@@ -0,0 +1,70 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@hoardodile/ui/lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg text-ui font-medium whitespace-nowrap outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary px-4! text-primary-foreground hover:bg-primary/80",
12
+ outline:
13
+ "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
14
+ secondary:
15
+ "bg-secondary text-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-foreground",
16
+ ghost:
17
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
18
+ destructive:
19
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
20
+ danger:
21
+ "bg-[color-mix(in_oklab,var(--destructive),black_12%)] text-white hover:bg-[color-mix(in_oklab,var(--destructive),black_4%)] focus-visible:border-destructive/40 focus-visible:ring-destructive/20 disabled:bg-destructive/10 disabled:text-muted-foreground dark:focus-visible:ring-destructive/40",
22
+ link: "text-primary underline-offset-4 hover:underline",
23
+ },
24
+ size: {
25
+ default:
26
+ "h-control gap-2 px-3 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
27
+ xs: "h-6 gap-2 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
28
+ sm: "h-chip gap-2 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
29
+ lg: "h-10 gap-2 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
30
+ icon: "size-9",
31
+ "icon-xs":
32
+ "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
33
+ "icon-sm":
34
+ "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
35
+ "icon-lg": "size-10",
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ variant: "default",
40
+ size: "default",
41
+ },
42
+ }
43
+ )
44
+
45
+ function Button({
46
+ active,
47
+ className,
48
+ variant = "default",
49
+ size = "default",
50
+ ...props
51
+ }: ButtonPrimitive.Props &
52
+ VariantProps<typeof buttonVariants> & {
53
+ /** Latched toggle state: accent fill, hover pinned so an active
54
+ button never shifts. Callers keep choosing the matching aria
55
+ attribute (`aria-pressed` / `aria-expanded`). */
56
+ active?: boolean
57
+ }) {
58
+ return (
59
+ <ButtonPrimitive
60
+ data-slot="button"
61
+ className={cn(
62
+ buttonVariants({ variant, size, className }),
63
+ active && "bg-accent text-accent-foreground hover:bg-accent"
64
+ )}
65
+ {...props}
66
+ />
67
+ )
68
+ }
69
+
70
+ export { Button, buttonVariants }