@eifi1/ui-kit 0.11.0 → 0.12.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 (342) hide show
  1. package/README.md +57 -32
  2. package/dist/chart.d.ts +3 -1
  3. package/dist/chart.js +2 -0
  4. package/dist/chart.js.map +1 -1
  5. package/dist/components/account-settings-labels.d.ts +105 -0
  6. package/dist/components/account-settings-labels.js +81 -0
  7. package/dist/components/account-settings-labels.js.map +1 -0
  8. package/dist/components/account-settings.d.ts +36 -52
  9. package/dist/components/account-settings.js +44 -8
  10. package/dist/components/account-settings.js.map +1 -1
  11. package/dist/components/amount-input.d.ts +24 -4
  12. package/dist/components/amount-input.js +5 -2
  13. package/dist/components/amount-input.js.map +1 -1
  14. package/dist/components/authed-image.d.ts +68 -0
  15. package/dist/components/authed-image.js +117 -0
  16. package/dist/components/authed-image.js.map +1 -0
  17. package/dist/components/breadcrumbs.d.ts +2 -1
  18. package/dist/components/breadcrumbs.js +5 -2
  19. package/dist/components/breadcrumbs.js.map +1 -1
  20. package/dist/components/button-group.d.ts +51 -2
  21. package/dist/components/button-group.js +5 -1
  22. package/dist/components/button-group.js.map +1 -1
  23. package/dist/components/calculator.d.ts +15 -4
  24. package/dist/components/calendar-heatmap.d.ts +41 -3
  25. package/dist/components/calendar-heatmap.js +48 -7
  26. package/dist/components/calendar-heatmap.js.map +1 -1
  27. package/dist/components/chip.d.ts +2 -1
  28. package/dist/components/chip.js +6 -3
  29. package/dist/components/chip.js.map +1 -1
  30. package/dist/components/choice-card.d.ts +35 -3
  31. package/dist/components/choice-card.js +83 -25
  32. package/dist/components/choice-card.js.map +1 -1
  33. package/dist/components/combobox-core.js +2 -0
  34. package/dist/components/combobox-core.js.map +1 -1
  35. package/dist/components/combobox.d.ts +2 -2
  36. package/dist/components/combobox.js +31 -9
  37. package/dist/components/combobox.js.map +1 -1
  38. package/dist/components/copy-button.d.ts +51 -70
  39. package/dist/components/copy-button.js +1 -1
  40. package/dist/components/copy-button.js.map +1 -1
  41. package/dist/components/data-table-cells.d.ts +63 -0
  42. package/dist/components/data-table-cells.js +64 -0
  43. package/dist/components/data-table-cells.js.map +1 -0
  44. package/dist/components/data-table-filter-popover.d.ts +1 -1
  45. package/dist/components/data-table-filters.d.ts +1 -1
  46. package/dist/components/data-table-filters.js +31 -1
  47. package/dist/components/data-table-filters.js.map +1 -1
  48. package/dist/components/data-table-labels.d.ts +13 -0
  49. package/dist/components/data-table-labels.js +8 -1
  50. package/dist/components/data-table-labels.js.map +1 -1
  51. package/dist/components/data-table.d.ts +1 -1
  52. package/dist/components/data-table.js +102 -12
  53. package/dist/components/data-table.js.map +1 -1
  54. package/dist/components/date-picker.d.ts +3 -1
  55. package/dist/components/date-picker.js +15 -2
  56. package/dist/components/date-picker.js.map +1 -1
  57. package/dist/components/description-list.d.ts +23 -3
  58. package/dist/components/description-list.js +23 -3
  59. package/dist/components/description-list.js.map +1 -1
  60. package/dist/components/dialog-frame.d.ts +1 -0
  61. package/dist/components/entity-combobox.d.ts +1 -1
  62. package/dist/components/entity-combobox.js +18 -3
  63. package/dist/components/entity-combobox.js.map +1 -1
  64. package/dist/components/error-boundary.d.ts +92 -0
  65. package/dist/components/error-boundary.js +111 -0
  66. package/dist/components/error-boundary.js.map +1 -0
  67. package/dist/components/field.d.ts +30 -4
  68. package/dist/components/field.js +40 -17
  69. package/dist/components/field.js.map +1 -1
  70. package/dist/components/file-button.d.ts +50 -208
  71. package/dist/components/file-dropzone.d.ts +49 -2
  72. package/dist/components/floating-panel.d.ts +13 -4
  73. package/dist/components/floating-panel.js +7 -3
  74. package/dist/components/floating-panel.js.map +1 -1
  75. package/dist/components/form-actions.d.ts +53 -0
  76. package/dist/components/form-actions.js +98 -0
  77. package/dist/components/form-actions.js.map +1 -0
  78. package/dist/components/image-grid.d.ts +67 -0
  79. package/dist/components/image-grid.js +112 -0
  80. package/dist/components/image-grid.js.map +1 -0
  81. package/dist/components/lightbox.d.ts +104 -0
  82. package/dist/components/lightbox.js +229 -0
  83. package/dist/components/lightbox.js.map +1 -0
  84. package/dist/components/line-items.d.ts +114 -0
  85. package/dist/components/line-items.js +238 -0
  86. package/dist/components/line-items.js.map +1 -0
  87. package/dist/components/list.d.ts +23 -2
  88. package/dist/components/list.js +32 -13
  89. package/dist/components/list.js.map +1 -1
  90. package/dist/components/loading-state.d.ts +24 -0
  91. package/dist/components/loading-state.js +32 -0
  92. package/dist/components/loading-state.js.map +1 -0
  93. package/dist/components/menu-item.d.ts +2 -1
  94. package/dist/components/menu-item.js +5 -1
  95. package/dist/components/menu-item.js.map +1 -1
  96. package/dist/components/mini-calendar.d.ts +13 -1
  97. package/dist/components/mini-calendar.js +46 -2
  98. package/dist/components/mini-calendar.js.map +1 -1
  99. package/dist/components/modal.d.ts +20 -1
  100. package/dist/components/modal.js +26 -2
  101. package/dist/components/modal.js.map +1 -1
  102. package/dist/components/month-picker.d.ts +21 -1
  103. package/dist/components/month-picker.js +139 -26
  104. package/dist/components/month-picker.js.map +1 -1
  105. package/dist/components/multi-entity-combobox.d.ts +1 -1
  106. package/dist/components/multi-entity-combobox.js +19 -3
  107. package/dist/components/multi-entity-combobox.js.map +1 -1
  108. package/dist/components/nav-pills.d.ts +2 -1
  109. package/dist/components/nav-pills.js +5 -1
  110. package/dist/components/nav-pills.js.map +1 -1
  111. package/dist/components/number-field.d.ts +24 -5
  112. package/dist/components/number-field.js +5 -7
  113. package/dist/components/number-field.js.map +1 -1
  114. package/dist/components/number-input.d.ts +25 -5
  115. package/dist/components/number-input.js +7 -1
  116. package/dist/components/number-input.js.map +1 -1
  117. package/dist/components/numpad-sheet.d.ts +15 -4
  118. package/dist/components/passkeys-setting.d.ts +67 -0
  119. package/dist/components/passkeys-setting.js +214 -0
  120. package/dist/components/passkeys-setting.js.map +1 -0
  121. package/dist/components/picker-sheet.js +2 -1
  122. package/dist/components/picker-sheet.js.map +1 -1
  123. package/dist/components/pie-chart-labels.d.ts +20 -0
  124. package/dist/components/pie-chart-labels.js +12 -0
  125. package/dist/components/pie-chart-labels.js.map +1 -0
  126. package/dist/components/pie-chart.d.ts +101 -0
  127. package/dist/components/pie-chart.js +283 -0
  128. package/dist/components/pie-chart.js.map +1 -0
  129. package/dist/components/progress-bar.d.ts +44 -3
  130. package/dist/components/progress-bar.js +58 -22
  131. package/dist/components/progress-bar.js.map +1 -1
  132. package/dist/components/qr-code.d.ts +34 -0
  133. package/dist/components/qr-code.js +57 -0
  134. package/dist/components/qr-code.js.map +1 -0
  135. package/dist/components/search-field.d.ts +15 -3
  136. package/dist/components/search-field.js +5 -1
  137. package/dist/components/search-field.js.map +1 -1
  138. package/dist/components/settings-fields.d.ts +49 -1
  139. package/dist/components/signed-amount.d.ts +121 -0
  140. package/dist/components/signed-amount.js +152 -0
  141. package/dist/components/signed-amount.js.map +1 -0
  142. package/dist/components/skeleton.d.ts +10 -3
  143. package/dist/components/skeleton.js +12 -2
  144. package/dist/components/skeleton.js.map +1 -1
  145. package/dist/components/stat-tile.d.ts +2 -1
  146. package/dist/components/stat-tile.js +5 -2
  147. package/dist/components/stat-tile.js.map +1 -1
  148. package/dist/components/table.d.ts +39 -5
  149. package/dist/components/table.js +45 -11
  150. package/dist/components/table.js.map +1 -1
  151. package/dist/components/text-link.d.ts +176 -0
  152. package/dist/components/text-link.js +109 -0
  153. package/dist/components/text-link.js.map +1 -0
  154. package/dist/components/time-input.d.ts +49 -1
  155. package/dist/components/toggle-group.d.ts +13 -0
  156. package/dist/components/toggle-group.js +35 -6
  157. package/dist/components/toggle-group.js.map +1 -1
  158. package/dist/components/toggle-legend.d.ts +36 -3
  159. package/dist/components/toggle-legend.js +49 -0
  160. package/dist/components/toggle-legend.js.map +1 -1
  161. package/dist/components/ui.d.ts +51 -779
  162. package/dist/components/ui.js +215 -23
  163. package/dist/components/ui.js.map +1 -1
  164. package/dist/components/use-table-state.d.ts +3 -52
  165. package/dist/components/use-table-state.js +128 -30
  166. package/dist/components/use-table-state.js.map +1 -1
  167. package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
  168. package/dist/data-table.d.ts +2 -1
  169. package/dist/data-table.js +5 -1
  170. package/dist/data-table.js.map +1 -1
  171. package/dist/feedback/feedback-dialog.d.ts +26 -3
  172. package/dist/feedback/feedback-dialog.js +16 -2
  173. package/dist/feedback/feedback-dialog.js.map +1 -1
  174. package/dist/feedback/feedback-thread.d.ts +133 -0
  175. package/dist/feedback/feedback-thread.js +237 -0
  176. package/dist/feedback/feedback-thread.js.map +1 -0
  177. package/dist/feedback.d.ts +2 -1
  178. package/dist/feedback.js +1 -0
  179. package/dist/feedback.js.map +1 -1
  180. package/dist/hooks/use-authed-src.d.ts +71 -0
  181. package/dist/hooks/use-authed-src.js +63 -0
  182. package/dist/hooks/use-authed-src.js.map +1 -0
  183. package/dist/hooks/use-file-drop.d.ts +49 -2
  184. package/dist/hooks/use-hotkey.d.ts +59 -0
  185. package/dist/hooks/use-hotkey.js +105 -0
  186. package/dist/hooks/use-hotkey.js.map +1 -0
  187. package/dist/hooks/use-search-param-state.d.ts +83 -0
  188. package/dist/hooks/use-search-param-state.js +94 -0
  189. package/dist/hooks/use-search-param-state.js.map +1 -0
  190. package/dist/i18n/defaults.d.ts +15 -4
  191. package/dist/i18n/defaults.js +31 -1
  192. package/dist/i18n/defaults.js.map +1 -1
  193. package/dist/i18n/kit-labels.d.ts +46 -312
  194. package/dist/i18n/kit-labels.js +20 -5
  195. package/dist/i18n/kit-labels.js.map +1 -1
  196. package/dist/i18n/locales/de-CH-informal.d.ts +15 -4
  197. package/dist/i18n/locales/de-CH.d.ts +15 -4
  198. package/dist/i18n/locales/de-informal.d.ts +15 -4
  199. package/dist/i18n/locales/de.d.ts +15 -4
  200. package/dist/i18n/locales/de.js +156 -3
  201. package/dist/i18n/locales/de.js.map +1 -1
  202. package/dist/i18n/locales/es.d.ts +15 -4
  203. package/dist/i18n/locales/es.js +153 -3
  204. package/dist/i18n/locales/es.js.map +1 -1
  205. package/dist/i18n/locales/fr.d.ts +15 -4
  206. package/dist/i18n/locales/fr.js +153 -3
  207. package/dist/i18n/locales/fr.js.map +1 -1
  208. package/dist/i18n/locales/hu.d.ts +15 -4
  209. package/dist/i18n/locales/hu.js +153 -3
  210. package/dist/i18n/locales/hu.js.map +1 -1
  211. package/dist/i18n/locales/it.d.ts +15 -4
  212. package/dist/i18n/locales/it.js +153 -3
  213. package/dist/i18n/locales/it.js.map +1 -1
  214. package/dist/i18n/locales/zh.d.ts +15 -4
  215. package/dist/i18n/locales/zh.js +153 -3
  216. package/dist/i18n/locales/zh.js.map +1 -1
  217. package/dist/index.d.ts +37 -17
  218. package/dist/index.js +40 -1
  219. package/dist/index.js.map +1 -1
  220. package/dist/kit-labels-v3biUF1L.d.ts +1645 -0
  221. package/dist/lib/format.d.ts +144 -0
  222. package/dist/lib/format.js +149 -0
  223. package/dist/lib/format.js.map +1 -0
  224. package/dist/lib/qr-encode.d.ts +44 -0
  225. package/dist/lib/qr-encode.js +338 -0
  226. package/dist/lib/qr-encode.js.map +1 -0
  227. package/dist/rhf/fields.d.ts +241 -0
  228. package/dist/rhf/fields.js +601 -0
  229. package/dist/rhf/fields.js.map +1 -0
  230. package/dist/rhf/form.d.ts +49 -1
  231. package/dist/rhf/line-items.d.ts +31 -0
  232. package/dist/rhf/line-items.js +59 -0
  233. package/dist/rhf/line-items.js.map +1 -0
  234. package/dist/rhf.d.ts +61 -1
  235. package/dist/rhf.js +2 -0
  236. package/dist/rhf.js.map +1 -1
  237. package/dist/shell/app-shell.d.ts +99 -2
  238. package/dist/shell/app-shell.js +58 -3
  239. package/dist/shell/app-shell.js.map +1 -1
  240. package/dist/shell/auth-layout.d.ts +74 -0
  241. package/dist/shell/auth-layout.js +97 -0
  242. package/dist/shell/auth-layout.js.map +1 -0
  243. package/dist/shell/top-bar-brand.d.ts +81 -0
  244. package/dist/shell/top-bar-brand.js +47 -0
  245. package/dist/shell/top-bar-brand.js.map +1 -0
  246. package/dist/shell/topbar-action-menu.d.ts +12 -1
  247. package/dist/shell/topbar-action-menu.js +16 -1
  248. package/dist/shell/topbar-action-menu.js.map +1 -1
  249. package/dist/shell.d.ts +56 -1
  250. package/dist/shell.js +2 -0
  251. package/dist/shell.js.map +1 -1
  252. package/dist/wizard/stepper-nav.d.ts +48 -1
  253. package/dist/wizard/wizard-context.d.ts +24 -1
  254. package/dist/wizard/wizard-context.js +19 -2
  255. package/dist/wizard/wizard-context.js.map +1 -1
  256. package/dist/wizard/wizard-step.d.ts +20 -5
  257. package/dist/wizard/wizard-step.js +16 -3
  258. package/dist/wizard/wizard-step.js.map +1 -1
  259. package/dist/wizard.d.ts +50 -3
  260. package/package.json +1 -1
  261. package/src/chart.ts +4 -0
  262. package/src/components/account-settings-labels.ts +190 -0
  263. package/src/components/account-settings.tsx +110 -63
  264. package/src/components/amount-input.tsx +13 -1
  265. package/src/components/authed-image.tsx +190 -0
  266. package/src/components/breadcrumbs.tsx +8 -2
  267. package/src/components/button-group.tsx +7 -2
  268. package/src/components/calendar-heatmap.tsx +105 -10
  269. package/src/components/chip.tsx +8 -4
  270. package/src/components/choice-card.tsx +136 -25
  271. package/src/components/combobox-core.tsx +5 -0
  272. package/src/components/combobox.tsx +31 -9
  273. package/src/components/copy-button.tsx +3 -2
  274. package/src/components/data-table-cells.tsx +119 -0
  275. package/src/components/data-table-filters.ts +77 -0
  276. package/src/components/data-table-labels.ts +23 -0
  277. package/src/components/data-table.tsx +224 -13
  278. package/src/components/date-picker.tsx +19 -3
  279. package/src/components/description-list.tsx +52 -3
  280. package/src/components/entity-combobox.tsx +18 -3
  281. package/src/components/error-boundary.tsx +216 -0
  282. package/src/components/field.tsx +56 -5
  283. package/src/components/floating-panel.tsx +20 -7
  284. package/src/components/form-actions.tsx +191 -0
  285. package/src/components/image-grid.tsx +181 -0
  286. package/src/components/lightbox.tsx +374 -0
  287. package/src/components/line-items.tsx +385 -0
  288. package/src/components/list.tsx +65 -16
  289. package/src/components/loading-state.tsx +47 -0
  290. package/src/components/menu-item.tsx +7 -2
  291. package/src/components/mini-calendar.tsx +72 -3
  292. package/src/components/modal.tsx +61 -3
  293. package/src/components/month-picker.tsx +181 -26
  294. package/src/components/multi-entity-combobox.tsx +19 -3
  295. package/src/components/nav-pills.tsx +8 -1
  296. package/src/components/number-field.tsx +15 -10
  297. package/src/components/number-input.tsx +16 -1
  298. package/src/components/passkeys-setting.tsx +331 -0
  299. package/src/components/picker-sheet.tsx +7 -1
  300. package/src/components/pie-chart-labels.ts +32 -0
  301. package/src/components/pie-chart.tsx +479 -0
  302. package/src/components/progress-bar.tsx +129 -19
  303. package/src/components/qr-code.tsx +83 -0
  304. package/src/components/search-field.tsx +20 -4
  305. package/src/components/signed-amount.tsx +286 -0
  306. package/src/components/skeleton.tsx +21 -3
  307. package/src/components/stat-tile.tsx +8 -2
  308. package/src/components/table.tsx +102 -13
  309. package/src/components/text-link.tsx +248 -0
  310. package/src/components/toggle-group.tsx +58 -10
  311. package/src/components/toggle-legend.tsx +87 -2
  312. package/src/components/ui.tsx +426 -36
  313. package/src/components/use-table-state.ts +265 -33
  314. package/src/data-table.ts +10 -0
  315. package/src/feedback/feedback-dialog.tsx +42 -3
  316. package/src/feedback/feedback-thread.tsx +434 -0
  317. package/src/feedback.ts +1 -0
  318. package/src/hooks/use-authed-src.ts +161 -0
  319. package/src/hooks/use-hotkey.ts +172 -0
  320. package/src/hooks/use-search-param-state.ts +187 -0
  321. package/src/i18n/defaults.ts +30 -0
  322. package/src/i18n/kit-labels.tsx +102 -8
  323. package/src/i18n/locales/de.ts +153 -0
  324. package/src/i18n/locales/es.ts +150 -0
  325. package/src/i18n/locales/fr.ts +153 -0
  326. package/src/i18n/locales/hu.ts +151 -0
  327. package/src/i18n/locales/it.ts +151 -0
  328. package/src/i18n/locales/zh.ts +150 -0
  329. package/src/index.ts +59 -0
  330. package/src/lib/format.ts +325 -0
  331. package/src/lib/qr-encode.ts +420 -0
  332. package/src/rhf/fields.tsx +1054 -0
  333. package/src/rhf/line-items.tsx +130 -0
  334. package/src/rhf.ts +6 -0
  335. package/src/shell/app-shell.tsx +147 -2
  336. package/src/shell/auth-layout.tsx +190 -0
  337. package/src/shell/top-bar-brand.tsx +76 -0
  338. package/src/shell/topbar-action-menu.tsx +21 -0
  339. package/src/shell.ts +2 -0
  340. package/src/wizard/wizard-context.tsx +41 -1
  341. package/src/wizard/wizard-step.tsx +43 -6
  342. package/tokens.css +12 -0
@@ -6,7 +6,8 @@ import { cn } from "../lib/cn.js";
6
6
  import { horizontalStep } from "../lib/direction.js";
7
7
  import { useMediaQuery } from "../hooks/use-media-query.js";
8
8
  import { Tooltip } from "./tooltip.js";
9
- import { DEFAULT_COMMON_LABELS, useKitLabels } from "../i18n/kit-labels.js";
9
+ import { DEFAULT_COMMON_LABELS, useKitLabels, useKitLink } from "../i18n/kit-labels.js";
10
+ import { pickLinkRenderer } from "./text-link.js";
10
11
  const BUTTON_BASE = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed";
11
12
  const BUTTON_SIZES = {
12
13
  md: "gap-2 px-3 py-2 text-sm",
@@ -32,6 +33,10 @@ const BUTTON_TONES = {
32
33
  danger: "text-[var(--text-secondary)] hover:text-[var(--danger)] focus:ring-[var(--danger-border)] disabled:hover:text-[var(--text-secondary)]"
33
34
  };
34
35
  const TONED_VARIANTS = /* @__PURE__ */ new Set(["link", "ghost"]);
36
+ const BUTTON_BOXED_DANGER = {
37
+ secondary: "border-[var(--danger-border)] bg-transparent text-[var(--danger)] hover:bg-[var(--danger-bg)] focus:ring-[var(--danger-border)]",
38
+ primary: "border-[var(--danger-border)] bg-[var(--danger-bg)] text-[var(--danger)] hover:border-[var(--danger)] hover:bg-[var(--danger)] hover:text-[var(--danger-contrast)] focus:ring-[var(--danger-border)] disabled:hover:border-[var(--danger-border)] disabled:hover:bg-[var(--danger-bg)] disabled:hover:text-[var(--danger)]"
39
+ };
35
40
  const BUTTON_LINK_PRESSED = "font-medium text-[var(--brand)] hover:text-[var(--brand)] disabled:hover:text-[var(--brand)]";
36
41
  function buttonClasses(variant = "primary", classNameOrOptions) {
37
42
  const { size = "md", className } = typeof classNameOrOptions === "object" ? classNameOrOptions : { className: classNameOrOptions };
@@ -40,49 +45,151 @@ function buttonClasses(variant = "primary", classNameOrOptions) {
40
45
  function hasContent(node) {
41
46
  return node !== void 0 && node !== null && node !== false && node !== "";
42
47
  }
43
- function Button({
48
+ function buttonLook(variant, size, stretch, tone) {
49
+ return cn(
50
+ BUTTON_BASE,
51
+ BUTTON_SIZES[size],
52
+ // In a flex row next to a taller labelled field, `stretch` makes the button
53
+ // fill the field's height so the two line up (self-stretch overrides the row's
54
+ // align-items). No effect outside a flex row / when it's already the tallest.
55
+ stretch && "self-stretch",
56
+ buttonVariantClasses[variant],
57
+ tone !== "default" && TONED_VARIANTS.has(variant) && BUTTON_TONES[tone],
58
+ tone === "danger" && BUTTON_BOXED_DANGER[variant]
59
+ );
60
+ }
61
+ function Button(props) {
62
+ if (props.href !== void 0) return /* @__PURE__ */ jsx(ButtonLink, { ...props });
63
+ return /* @__PURE__ */ jsx(ButtonElement, { ...props });
64
+ }
65
+ function RenderedButtonLink({ render, ...props }) {
66
+ return render(props);
67
+ }
68
+ function ButtonLink({
69
+ href,
70
+ renderLink,
71
+ external,
72
+ disabled,
73
+ variant = "primary",
74
+ size = "md",
75
+ stretch,
76
+ tone = "default",
77
+ className,
78
+ children,
79
+ target,
80
+ rel,
81
+ onClick,
82
+ ...rest
83
+ }) {
84
+ const kitLink = useKitLink();
85
+ const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
86
+ const Link = pickLinkRenderer(renderLink, kitLink, href);
87
+ const newTab = !disabled && (external || target === "_blank");
88
+ const cls = cn(
89
+ buttonLook(variant, size, stretch, tone),
90
+ // `relative` holds the sr-only note (see sr-only-containment.test).
91
+ newTab && "relative",
92
+ // BUTTON_BASE's `disabled:` classes never match an `<a>`; no pointer events also
93
+ // keeps the hover fill from lighting up on something that refuses the click.
94
+ disabled && "pointer-events-none cursor-not-allowed opacity-50",
95
+ className
96
+ );
97
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
98
+ children,
99
+ newTab && " ",
100
+ newTab && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
101
+ "(",
102
+ common.opensInNewTab,
103
+ ")"
104
+ ] })
105
+ ] });
106
+ if (disabled) {
107
+ return /* @__PURE__ */ jsx("a", { ...rest, role: "link", "aria-disabled": "true", className: cls, children: body });
108
+ }
109
+ if (external || !Link) {
110
+ return /* @__PURE__ */ jsx(
111
+ "a",
112
+ {
113
+ ...rest,
114
+ href,
115
+ target: external ? "_blank" : target,
116
+ rel: external ? rel ?? "noopener noreferrer" : rel,
117
+ onClick,
118
+ className: cls,
119
+ children: body
120
+ }
121
+ );
122
+ }
123
+ return /* @__PURE__ */ jsx(
124
+ RenderedButtonLink,
125
+ {
126
+ render: Link,
127
+ ...rest,
128
+ href,
129
+ target,
130
+ rel,
131
+ onClick,
132
+ className: cls,
133
+ children: body
134
+ }
135
+ );
136
+ }
137
+ function ButtonElement({
44
138
  variant = "primary",
45
139
  size = "md",
46
140
  stretch,
47
141
  tone = "default",
48
142
  pressed,
49
143
  disabledReason,
144
+ pending,
50
145
  className,
51
146
  onClick,
147
+ children,
148
+ href: _href,
149
+ renderLink: _renderLink,
150
+ external: _external,
52
151
  ...rest
53
152
  }) {
54
153
  const reasonId = useId();
55
154
  const locked = hasContent(disabledReason);
155
+ const inert = locked || Boolean(pending);
56
156
  const ownDescribedBy = rest["aria-describedby"];
57
157
  const button = /* @__PURE__ */ jsx(
58
158
  "button",
59
159
  {
60
160
  ...rest,
61
- disabled: locked ? void 0 : rest.disabled,
62
- "aria-disabled": locked || rest["aria-disabled"],
161
+ disabled: inert ? void 0 : rest.disabled,
162
+ "aria-disabled": inert || rest["aria-disabled"],
163
+ "aria-busy": pending || rest["aria-busy"],
63
164
  "aria-describedby": locked ? ownDescribedBy ? `${ownDescribedBy} ${reasonId}` : reasonId : ownDescribedBy,
64
165
  "aria-pressed": pressed ?? rest["aria-pressed"],
65
166
  onClick: (e) => {
66
- if (locked) {
167
+ if (inert) {
67
168
  e.preventDefault();
68
169
  return;
69
170
  }
70
171
  onClick?.(e);
71
172
  },
72
173
  className: cn(
73
- BUTTON_BASE,
74
- BUTTON_SIZES[size],
75
- // In a flex row next to a taller labelled field, `stretch` makes the button
76
- // fill the field's height so the two line up (self-stretch overrides the row's
77
- // align-items). No effect outside a flex row / when it's already the tallest.
78
- stretch && "self-stretch",
79
- buttonVariantClasses[variant],
80
- tone !== "default" && TONED_VARIANTS.has(variant) && BUTTON_TONES[tone],
174
+ buttonLook(variant, size, stretch, tone),
81
175
  pressed && (variant === "link" ? BUTTON_LINK_PRESSED : ICON_BUTTON_PRESSED),
82
176
  // The disabled look for the focusable kind of disabled.
83
177
  locked && "cursor-not-allowed opacity-50",
178
+ // No dimming while pending: the spinner IS the state, and at half opacity it
179
+ // would be the faintest thing on the button. `relative` anchors it.
180
+ pending && "relative cursor-progress",
84
181
  className
85
- )
182
+ ),
183
+ children: pending ? /* @__PURE__ */ jsxs(Fragment, { children: [
184
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center gap-[inherit] opacity-0", children }),
185
+ /* @__PURE__ */ jsx(
186
+ Spinner,
187
+ {
188
+ label: null,
189
+ className: "absolute inset-0 m-auto size-4 border-current/30 border-t-current"
190
+ }
191
+ )
192
+ ] }) : children
86
193
  }
87
194
  );
88
195
  if (!locked) return button;
@@ -99,6 +206,9 @@ function Button({
99
206
  }
100
207
  const ICON_BUTTON_BASE = "inline-flex items-center justify-center rounded-md transition-colors focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed";
101
208
  const ICON_BUTTON_SIZES = {
209
+ // 64px with a 28px glyph: a camera's shutter (keksdose invoices/camera-capture.tsx:248,
210
+ // `size-16` by hand) — the one control of a full-screen capture view, thumb-sized.
211
+ "2xl": "size-16 [&_svg]:size-7",
102
212
  // 48px with a 24px glyph: the one primary action of a phone screen or sheet, a step
103
213
  // past the 44px minimum rather than on it.
104
214
  xl: "size-12 [&_svg]:size-6",
@@ -111,6 +221,7 @@ const ICON_BUTTON_SIZES = {
111
221
  "2xs": "size-6 rounded [&_svg]:size-3.5"
112
222
  };
113
223
  const ICON_BUTTON_STRETCH_SIZES = {
224
+ "2xl": "w-16 min-h-16 self-stretch [&_svg]:size-7",
114
225
  xl: "w-12 min-h-12 self-stretch [&_svg]:size-6",
115
226
  lg: "w-11 min-h-11 self-stretch [&_svg]:size-5",
116
227
  md: "w-9 min-h-9 self-stretch [&_svg]:size-5",
@@ -165,11 +276,21 @@ const ICON_BUTTON_DISABLED_REST = {
165
276
  danger: "disabled:hover:bg-[var(--danger)]",
166
277
  brand: "disabled:hover:bg-[var(--brand)]",
167
278
  link: "disabled:hover:bg-transparent disabled:hover:no-underline",
168
- overlay: "disabled:hover:bg-[color-mix(in_srgb,var(--bg-inverse)_60%,transparent)]"
279
+ overlay: "disabled:hover:bg-[color-mix(in_srgb,var(--bg-inverse)_60%,transparent)]",
280
+ shutter: "disabled:hover:bg-[var(--media-scrim)]"
169
281
  };
170
282
  const ICON_BUTTON_QUIET_DISABLED_REST = "disabled:hover:text-[var(--text-placeholder)]";
171
283
  const ICON_BUTTON_PRESSED = "bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)] hover:text-[var(--brand)] disabled:hover:bg-[var(--brand-bg)] disabled:hover:text-[var(--brand)]";
172
284
  const ICON_BUTTON_OVERLAY = "rounded-full bg-[color-mix(in_srgb,var(--bg-inverse)_60%,transparent)] text-[var(--text-inverse)] backdrop-blur-sm hover:bg-[color-mix(in_srgb,var(--bg-inverse)_75%,transparent)] focus:ring-[var(--text-inverse)]";
285
+ const ICON_BUTTON_SHUTTER = "rounded-full border-4 border-[var(--media-ink)] bg-[var(--media-scrim)] text-[var(--media-ink)] backdrop-blur-sm hover:bg-[var(--media-scrim-hover)] focus:ring-[var(--media-ink)] focus:ring-offset-2 focus:ring-offset-[var(--media-scrim-hover)]";
286
+ const ICON_BUTTON_GLYPH_SIZES = {
287
+ 12: "[&_svg]:size-3",
288
+ 14: "[&_svg]:size-3.5",
289
+ 16: "[&_svg]:size-4",
290
+ 20: "[&_svg]:size-5",
291
+ 24: "[&_svg]:size-6",
292
+ 28: "[&_svg]:size-7"
293
+ };
173
294
  const IconButton = forwardRef(function IconButton2({
174
295
  variant = "ghost",
175
296
  size = "md",
@@ -185,6 +306,9 @@ const IconButton = forwardRef(function IconButton2({
185
306
  tooltipSide,
186
307
  tooltipPortal,
187
308
  disabledReason,
309
+ glyphSize,
310
+ badge,
311
+ disabledStyle = "dim",
188
312
  className,
189
313
  style,
190
314
  onClick,
@@ -192,6 +316,7 @@ const IconButton = forwardRef(function IconButton2({
192
316
  ...rest
193
317
  }, ref) {
194
318
  const tone = toneProp ?? (toneColor !== void 0 ? "custom" : "default");
319
+ const keep = disabledStyle === "keep";
195
320
  const reasonId = useId();
196
321
  const locked = hasContent(disabledReason);
197
322
  const ownDescribedBy = rest["aria-describedby"];
@@ -221,18 +346,32 @@ const IconButton = forwardRef(function IconButton2({
221
346
  className: cn(
222
347
  ICON_BUTTON_BASE,
223
348
  stretch ? ICON_BUTTON_STRETCH_SIZES[size] : ICON_BUTTON_SIZES[size],
349
+ glyphSize !== void 0 && ICON_BUTTON_GLYPH_SIZES[glyphSize],
224
350
  // After the size, so the overlay's `rounded-full` beats the small sizes' `rounded`.
225
- variant === "overlay" ? ICON_BUTTON_OVERLAY : buttonVariantClasses[variant],
351
+ variant === "overlay" ? ICON_BUTTON_OVERLAY : variant === "shutter" ? ICON_BUTTON_SHUTTER : buttonVariantClasses[variant],
226
352
  shape === "round" && "rounded-full",
227
353
  ICON_BUTTON_DISABLED_REST[variant],
228
354
  iconButtonToneClass(tone, quiet),
229
355
  pressed && ICON_BUTTON_PRESSED,
230
356
  // The disabled look for the focusable kind of disabled, as on Button.
231
- locked && "cursor-not-allowed opacity-50",
357
+ locked && (keep ? "cursor-default" : "cursor-not-allowed opacity-50"),
358
+ keep && "disabled:cursor-default disabled:opacity-100",
232
359
  className
233
360
  )
234
361
  }
235
362
  );
363
+ const control = hasContent(badge) ? /* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex shrink-0", stretch && "self-stretch"), children: [
364
+ button,
365
+ /* @__PURE__ */ jsx(
366
+ "span",
367
+ {
368
+ "aria-hidden": true,
369
+ "data-slot": "icon-button-badge",
370
+ className: "pointer-events-none absolute -end-0.5 -bottom-0.5 inline-flex rounded-full bg-[var(--bg-surface)] [&_svg]:size-3",
371
+ children: badge
372
+ }
373
+ )
374
+ ] }) : button;
236
375
  if (locked) {
237
376
  return (
238
377
  // Button's shape: the button and the hidden reason in a FRAGMENT, so Tooltip does
@@ -245,18 +384,18 @@ const IconButton = forwardRef(function IconButton2({
245
384
  portal: tooltipPortal,
246
385
  className: stretch ? "self-stretch" : void 0,
247
386
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
248
- button,
387
+ control,
249
388
  /* @__PURE__ */ jsx("span", { id: reasonId, hidden: true, children: disabledReason })
250
389
  ] })
251
390
  }
252
391
  )
253
392
  );
254
393
  }
255
- if (!tooltip || label === void 0 || label === "") return button;
394
+ if (!tooltip || label === void 0 || label === "") return control;
256
395
  return (
257
396
  // A fragment, so Tooltip leaves the button's description alone: the bubble says
258
397
  // exactly what `aria-label` already does.
259
- /* @__PURE__ */ jsx(Tooltip, { label, side: tooltipSide, portal: tooltipPortal, className: stretch ? "self-stretch" : void 0, children: /* @__PURE__ */ jsx(Fragment, { children: button }) })
398
+ /* @__PURE__ */ jsx(Tooltip, { label, side: tooltipSide, portal: tooltipPortal, className: stretch ? "self-stretch" : void 0, children: /* @__PURE__ */ jsx(Fragment, { children: control }) })
260
399
  );
261
400
  });
262
401
  IconButton.displayName = "IconButton";
@@ -627,19 +766,55 @@ const Textarea = forwardRef(function Textarea2({ className, label, id, placehold
627
766
  ) }) });
628
767
  });
629
768
  Textarea.displayName = "Textarea";
630
- function Card({ className, children, flush, variant = "default", ...rest }) {
769
+ const CARD_TONES = {
770
+ warning: {
771
+ border: "border-[var(--warning-border)]",
772
+ fill: "bg-[var(--warning-bg)]",
773
+ title: "[&_[data-slot=card-title]]:text-[var(--warning)]"
774
+ },
775
+ danger: {
776
+ border: "border-[var(--danger-border)]",
777
+ fill: "bg-[var(--danger-bg)]",
778
+ title: "[&_[data-slot=card-title]]:text-[var(--danger)]"
779
+ },
780
+ info: {
781
+ border: "border-[var(--info-border)]",
782
+ fill: "bg-[var(--info-bg)]",
783
+ title: "[&_[data-slot=card-title]]:text-[var(--info)]"
784
+ },
785
+ success: {
786
+ border: "border-[var(--success-border)]",
787
+ fill: "bg-[var(--success-bg)]",
788
+ title: "[&_[data-slot=card-title]]:text-[var(--success)]"
789
+ }
790
+ };
791
+ const CARD_PADDING = {
792
+ none: "p-0",
793
+ sm: "p-3",
794
+ md: "p-4"
795
+ };
796
+ function Card({ className, children, flush, variant = "default", tone, padding, ...rest }) {
797
+ const toned = tone ? CARD_TONES[tone] : void 0;
798
+ const bordered = variant !== "inset";
631
799
  return /* @__PURE__ */ jsx(
632
800
  "div",
633
801
  {
802
+ "data-tone": tone,
634
803
  ...rest,
635
804
  className: cn(
636
- variant === "inset" ? "rounded-md bg-[var(--bg-surface-2)] p-3" : cn(
805
+ variant === "inset" ? "rounded-md bg-[var(--bg-surface-2)] p-3" : variant === "outline" ? (
806
+ // No shadow and no `flush`: it sits on a surface that already has both.
807
+ "rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4"
808
+ ) : cn(
637
809
  // Surface + border are theme tokens so the palette switcher (feedback
638
810
  // #307) can re-skin every card; a caller's own bg-*/border-* override
639
811
  // still wins via tailwind-merge.
640
812
  "bg-[var(--bg-surface)]",
641
813
  flush ? "border-y border-[var(--border)] md:rounded-lg md:border md:shadow-sm" : "rounded-lg border border-[var(--border)] shadow-sm"
642
814
  ),
815
+ toned && (bordered ? toned.border : toned.fill),
816
+ toned?.title,
817
+ padding && CARD_PADDING[padding],
643
818
  className
644
819
  ),
645
820
  children
@@ -704,7 +879,7 @@ function CardFooter({ className, ...props }) {
704
879
  }
705
880
  );
706
881
  }
707
- function Spinner({ className, label, ...rest }) {
882
+ function Spinner({ className, label, showLabel, labelPosition = "end", ...rest }) {
708
883
  const common = useKitLabels("common", DEFAULT_COMMON_LABELS, {
709
884
  loading: label ?? void 0
710
885
  });
@@ -713,6 +888,23 @@ function Spinner({ className, label, ...rest }) {
713
888
  className
714
889
  );
715
890
  if (label === null) return /* @__PURE__ */ jsx("span", { "aria-hidden": true, ...rest, className: ring });
891
+ if (showLabel) {
892
+ return /* @__PURE__ */ jsxs(
893
+ "span",
894
+ {
895
+ role: "status",
896
+ ...rest,
897
+ className: cn(
898
+ "inline-flex items-center text-sm text-[var(--text-secondary)]",
899
+ labelPosition === "below" ? "flex-col gap-2 text-center" : "gap-2"
900
+ ),
901
+ children: [
902
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: ring }),
903
+ /* @__PURE__ */ jsx("span", { children: common.loading })
904
+ ]
905
+ }
906
+ );
907
+ }
716
908
  return /* @__PURE__ */ jsx("span", { role: "status", ...rest, className: ring, children: /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
717
909
  common.loading,
718
910
  " "