@eifi1/ui-kit 0.48.1 → 0.49.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 (318) hide show
  1. package/README.md +28 -13
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +11 -5
  7. package/dist/admin/user-roster.js +15 -4
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.js +2 -2
  16. package/dist/auth/verify-email.js.map +1 -1
  17. package/dist/billing/plan-card.js +2 -2
  18. package/dist/billing/plan-card.js.map +1 -1
  19. package/dist/components/breadcrumbs.d.ts +19 -1
  20. package/dist/components/breadcrumbs.js +11 -4
  21. package/dist/components/breadcrumbs.js.map +1 -1
  22. package/dist/components/button-look.d.ts +37 -7
  23. package/dist/components/button-look.js +33 -6
  24. package/dist/components/button-look.js.map +1 -1
  25. package/dist/components/button.d.ts +4 -1
  26. package/dist/components/button.js +2 -2
  27. package/dist/components/button.js.map +1 -1
  28. package/dist/components/calendar-heatmap.js +2 -1
  29. package/dist/components/calendar-heatmap.js.map +1 -1
  30. package/dist/components/chart-edge.d.ts +10 -0
  31. package/dist/components/chart-edge.js +57 -0
  32. package/dist/components/chart-edge.js.map +1 -0
  33. package/dist/components/chart.js +2 -1
  34. package/dist/components/chart.js.map +1 -1
  35. package/dist/components/chip.d.ts +8 -1
  36. package/dist/components/chip.js +4 -1
  37. package/dist/components/chip.js.map +1 -1
  38. package/dist/components/confirm-context.d.ts +5 -0
  39. package/dist/components/confirm-context.js +7 -0
  40. package/dist/components/confirm-context.js.map +1 -0
  41. package/dist/components/confirm-dialog.js +3 -3
  42. package/dist/components/confirm-dialog.js.map +1 -1
  43. package/dist/components/danger-confirm.d.ts +2 -2
  44. package/dist/components/danger-confirm.js +1 -1
  45. package/dist/components/danger-confirm.js.map +1 -1
  46. package/dist/components/data-table-fit.d.ts +8 -1
  47. package/dist/components/data-table-fit.js +28 -4
  48. package/dist/components/data-table-fit.js.map +1 -1
  49. package/dist/components/data-table-groups.d.ts +3 -1
  50. package/dist/components/data-table-groups.js +11 -5
  51. package/dist/components/data-table-groups.js.map +1 -1
  52. package/dist/components/data-table-head.d.ts +8 -1
  53. package/dist/components/data-table-head.js +11 -3
  54. package/dist/components/data-table-head.js.map +1 -1
  55. package/dist/components/data-table-leading-row.d.ts +3 -1
  56. package/dist/components/data-table-leading-row.js +2 -1
  57. package/dist/components/data-table-leading-row.js.map +1 -1
  58. package/dist/components/data-table-mobile.d.ts +48 -2
  59. package/dist/components/data-table-mobile.js +87 -10
  60. package/dist/components/data-table-mobile.js.map +1 -1
  61. package/dist/components/data-table-row-actions.d.ts +2 -1
  62. package/dist/components/data-table-row-actions.js +9 -2
  63. package/dist/components/data-table-row-actions.js.map +1 -1
  64. package/dist/components/data-table-row.d.ts +84 -0
  65. package/dist/components/data-table-row.js +261 -0
  66. package/dist/components/data-table-row.js.map +1 -0
  67. package/dist/components/data-table-selection.d.ts +3 -0
  68. package/dist/components/data-table-selection.js +12 -1
  69. package/dist/components/data-table-selection.js.map +1 -1
  70. package/dist/components/data-table-virtual.d.ts +98 -0
  71. package/dist/components/data-table-virtual.js +369 -0
  72. package/dist/components/data-table-virtual.js.map +1 -0
  73. package/dist/components/data-table.d.ts +74 -2
  74. package/dist/components/data-table.js +241 -221
  75. package/dist/components/data-table.js.map +1 -1
  76. package/dist/components/error-boundary.js +2 -2
  77. package/dist/components/error-boundary.js.map +1 -1
  78. package/dist/components/floating-panel.d.ts +36 -13
  79. package/dist/components/floating-panel.js +27 -7
  80. package/dist/components/floating-panel.js.map +1 -1
  81. package/dist/components/form-actions.d.ts +1 -1
  82. package/dist/components/form-actions.js +1 -1
  83. package/dist/components/form-actions.js.map +1 -1
  84. package/dist/components/icon-button.d.ts +11 -0
  85. package/dist/components/icon-button.js +7 -5
  86. package/dist/components/icon-button.js.map +1 -1
  87. package/dist/components/nav-pills.d.ts +8 -1
  88. package/dist/components/nav-pills.js +61 -38
  89. package/dist/components/nav-pills.js.map +1 -1
  90. package/dist/components/page-header-context.d.ts +8 -0
  91. package/dist/components/page-header-context.js +7 -0
  92. package/dist/components/page-header-context.js.map +1 -0
  93. package/dist/components/page-header.d.ts +14 -2
  94. package/dist/components/page-header.js +10 -1
  95. package/dist/components/page-header.js.map +1 -1
  96. package/dist/components/pie-chart.js +2 -1
  97. package/dist/components/pie-chart.js.map +1 -1
  98. package/dist/components/progress-bar.js +5 -4
  99. package/dist/components/progress-bar.js.map +1 -1
  100. package/dist/components/reauth-dialog.js +1 -1
  101. package/dist/components/reauth-dialog.js.map +1 -1
  102. package/dist/components/row-actions-labels.js +3 -1
  103. package/dist/components/row-actions-labels.js.map +1 -1
  104. package/dist/components/row-actions.d.ts +43 -9
  105. package/dist/components/row-actions.js +78 -15
  106. package/dist/components/row-actions.js.map +1 -1
  107. package/dist/components/section-nav.d.ts +37 -0
  108. package/dist/components/section-nav.js +33 -0
  109. package/dist/components/section-nav.js.map +1 -0
  110. package/dist/components/series-chart.js +8 -3
  111. package/dist/components/series-chart.js.map +1 -1
  112. package/dist/components/share-card.js +1 -1
  113. package/dist/components/share-card.js.map +1 -1
  114. package/dist/components/signature-pad.js +1 -1
  115. package/dist/components/signature-pad.js.map +1 -1
  116. package/dist/components/sparkline.js +2 -1
  117. package/dist/components/sparkline.js.map +1 -1
  118. package/dist/components/stat-tile.js +14 -7
  119. package/dist/components/stat-tile.js.map +1 -1
  120. package/dist/components/status-dot.d.ts +5 -1
  121. package/dist/components/status-dot.js +4 -1
  122. package/dist/components/status-dot.js.map +1 -1
  123. package/dist/components/tooltip.js +1 -1
  124. package/dist/components/tooltip.js.map +1 -1
  125. package/dist/components/translation-review-editor.js +2 -2
  126. package/dist/components/translation-review-editor.js.map +1 -1
  127. package/dist/components/translation-review.js +2 -2
  128. package/dist/components/translation-review.js.map +1 -1
  129. package/dist/components/treemap.d.ts +5 -2
  130. package/dist/components/treemap.js +26 -6
  131. package/dist/components/treemap.js.map +1 -1
  132. package/dist/components/use-table-state.js +37 -6
  133. package/dist/components/use-table-state.js.map +1 -1
  134. package/dist/data-table.d.ts +1 -0
  135. package/dist/data-table.js.map +1 -1
  136. package/dist/demo/demo-banner.js +1 -1
  137. package/dist/demo/demo-banner.js.map +1 -1
  138. package/dist/demo/demo-ended.js +1 -1
  139. package/dist/demo/demo-ended.js.map +1 -1
  140. package/dist/demo/demo-start.js +1 -1
  141. package/dist/demo/demo-start.js.map +1 -1
  142. package/dist/eslint/index.js +1 -1
  143. package/dist/feedback/feedback-inbox.js +1 -1
  144. package/dist/feedback/feedback-inbox.js.map +1 -1
  145. package/dist/hooks/use-anchored-panel.js +2 -1
  146. package/dist/hooks/use-anchored-panel.js.map +1 -1
  147. package/dist/hooks/use-dismiss.d.ts +9 -1
  148. package/dist/hooks/use-dismiss.js +25 -5
  149. package/dist/hooks/use-dismiss.js.map +1 -1
  150. package/dist/hooks/use-document-title.d.ts +14 -0
  151. package/dist/hooks/use-document-title.js +22 -0
  152. package/dist/hooks/use-document-title.js.map +1 -0
  153. package/dist/i18n/german.js +6 -2
  154. package/dist/i18n/german.js.map +1 -1
  155. package/dist/i18n/kit-labels.d.ts +31 -1
  156. package/dist/i18n/kit-labels.js +43 -2
  157. package/dist/i18n/kit-labels.js.map +1 -1
  158. package/dist/i18n/locales/en.js +6 -2
  159. package/dist/i18n/locales/en.js.map +2 -2
  160. package/dist/i18n/locales/es.js +6 -2
  161. package/dist/i18n/locales/es.js.map +1 -1
  162. package/dist/i18n/locales/fr.js +6 -2
  163. package/dist/i18n/locales/fr.js.map +1 -1
  164. package/dist/i18n/locales/hu.js +6 -2
  165. package/dist/i18n/locales/hu.js.map +1 -1
  166. package/dist/i18n/locales/it.js +6 -2
  167. package/dist/i18n/locales/it.js.map +1 -1
  168. package/dist/i18n/locales/zh.js +6 -2
  169. package/dist/i18n/locales/zh.js.map +1 -1
  170. package/dist/i18n/review.js +2 -0
  171. package/dist/i18n/review.js.map +1 -1
  172. package/dist/i18n/shared-labels.js +1 -0
  173. package/dist/i18n/shared-labels.js.map +1 -1
  174. package/dist/index.d.ts +8 -1
  175. package/dist/index.js +11 -0
  176. package/dist/index.js.map +1 -1
  177. package/dist/landing/landing-actions.js +2 -2
  178. package/dist/landing/landing-actions.js.map +1 -1
  179. package/dist/landing/public-header.js +2 -2
  180. package/dist/landing/public-header.js.map +1 -1
  181. package/dist/lib/clipboard-minus.d.ts +27 -0
  182. package/dist/lib/clipboard-minus.js +63 -0
  183. package/dist/lib/clipboard-minus.js.map +1 -0
  184. package/dist/lib/document-title.d.ts +17 -0
  185. package/dist/lib/document-title.js +29 -0
  186. package/dist/lib/document-title.js.map +1 -0
  187. package/dist/lib/float-clearance.js +8 -2
  188. package/dist/lib/float-clearance.js.map +1 -1
  189. package/dist/lib/format.d.ts +35 -10
  190. package/dist/lib/format.js +55 -11
  191. package/dist/lib/format.js.map +1 -1
  192. package/dist/lib/minus.d.ts +22 -0
  193. package/dist/lib/minus.js +14 -0
  194. package/dist/lib/minus.js.map +1 -0
  195. package/dist/lib/status-tone.d.ts +30 -0
  196. package/dist/lib/status-tone.js +17 -0
  197. package/dist/lib/status-tone.js.map +1 -0
  198. package/dist/settings/settings-layout.js +3 -2
  199. package/dist/settings/settings-layout.js.map +1 -1
  200. package/dist/shell/account-menu-language.d.ts +1 -1
  201. package/dist/shell/account-menu-language.js +9 -1
  202. package/dist/shell/account-menu-language.js.map +1 -1
  203. package/dist/shell/app-shell-labels.js +2 -1
  204. package/dist/shell/app-shell-labels.js.map +1 -1
  205. package/dist/shell/app-shell.d.ts +58 -16
  206. package/dist/shell/app-shell.js +158 -44
  207. package/dist/shell/app-shell.js.map +1 -1
  208. package/dist/shell/language-code.d.ts +3 -0
  209. package/dist/shell/language-code.js +8 -0
  210. package/dist/shell/language-code.js.map +1 -0
  211. package/dist/shell/topbar-controls.d.ts +14 -6
  212. package/dist/shell/topbar-controls.js +14 -17
  213. package/dist/shell/topbar-controls.js.map +1 -1
  214. package/dist/testing/css-variables.generated.d.ts +1 -1
  215. package/dist/testing/css-variables.generated.js +1 -1
  216. package/dist/testing/css-variables.generated.js.map +1 -1
  217. package/dist/theme/chart-palette.d.ts +82 -15
  218. package/dist/theme/chart-palette.js +51 -24
  219. package/dist/theme/chart-palette.js.map +1 -1
  220. package/dist/theme/palette-presets.d.ts +9 -0
  221. package/dist/theme/palette-presets.js +77 -62
  222. package/dist/theme/palette-presets.js.map +1 -1
  223. package/dist/tour/tour.js +1 -1
  224. package/dist/tour/tour.js.map +1 -1
  225. package/dist/wizard/stepper-nav.d.ts +1 -1
  226. package/dist/wizard/stepper-nav.js +3 -3
  227. package/dist/wizard/stepper-nav.js.map +1 -1
  228. package/package.json +1 -1
  229. package/src/admin/admin-action-confirm.tsx +1 -1
  230. package/src/admin/invitations-panel.tsx +7 -3
  231. package/src/admin/user-roster.tsx +23 -6
  232. package/src/auth/accept-invitation.tsx +3 -3
  233. package/src/auth/not-found-page.tsx +1 -1
  234. package/src/auth/reset-password-form.tsx +3 -3
  235. package/src/auth/verify-email.tsx +3 -3
  236. package/src/billing/plan-card.tsx +2 -2
  237. package/src/components/breadcrumbs.tsx +32 -4
  238. package/src/components/button-look.ts +86 -15
  239. package/src/components/button.tsx +6 -3
  240. package/src/components/calendar-heatmap.tsx +2 -1
  241. package/src/components/chart-edge.ts +73 -0
  242. package/src/components/chart.tsx +3 -1
  243. package/src/components/chip.tsx +10 -1
  244. package/src/components/confirm-context.ts +7 -0
  245. package/src/components/confirm-dialog.tsx +3 -3
  246. package/src/components/danger-confirm.tsx +3 -3
  247. package/src/components/data-table-fit.ts +58 -8
  248. package/src/components/data-table-groups.tsx +15 -4
  249. package/src/components/data-table-head.tsx +25 -2
  250. package/src/components/data-table-leading-row.tsx +4 -1
  251. package/src/components/data-table-mobile.tsx +162 -11
  252. package/src/components/data-table-row-actions.tsx +12 -3
  253. package/src/components/data-table-row.tsx +485 -0
  254. package/src/components/data-table-selection.ts +17 -1
  255. package/src/components/data-table-virtual.ts +547 -0
  256. package/src/components/data-table.tsx +367 -326
  257. package/src/components/error-boundary.tsx +2 -2
  258. package/src/components/floating-panel.tsx +67 -16
  259. package/src/components/form-actions.tsx +2 -2
  260. package/src/components/icon-button.tsx +21 -8
  261. package/src/components/nav-pills.tsx +29 -2
  262. package/src/components/page-header-context.ts +8 -0
  263. package/src/components/page-header.tsx +34 -2
  264. package/src/components/pie-chart.tsx +2 -1
  265. package/src/components/progress-bar.tsx +6 -3
  266. package/src/components/reauth-dialog.tsx +1 -1
  267. package/src/components/row-actions-labels.ts +2 -0
  268. package/src/components/row-actions.tsx +154 -33
  269. package/src/components/section-nav.tsx +65 -0
  270. package/src/components/series-chart.tsx +12 -3
  271. package/src/components/share-card.tsx +1 -1
  272. package/src/components/signature-pad.tsx +1 -1
  273. package/src/components/sparkline.tsx +2 -1
  274. package/src/components/stat-tile.tsx +19 -7
  275. package/src/components/status-dot.tsx +7 -1
  276. package/src/components/tooltip.tsx +3 -1
  277. package/src/components/translation-review-editor.tsx +2 -2
  278. package/src/components/translation-review.tsx +2 -2
  279. package/src/components/treemap.tsx +34 -8
  280. package/src/components/use-table-state.ts +63 -8
  281. package/src/data-table.ts +5 -3
  282. package/src/demo/demo-banner.tsx +1 -1
  283. package/src/demo/demo-ended.tsx +1 -1
  284. package/src/demo/demo-start.tsx +1 -1
  285. package/src/feedback/feedback-inbox.tsx +1 -1
  286. package/src/hooks/use-anchored-panel.ts +9 -1
  287. package/src/hooks/use-dismiss.ts +43 -4
  288. package/src/hooks/use-document-title.ts +31 -0
  289. package/src/i18n/german.ts +4 -0
  290. package/src/i18n/kit-labels.tsx +76 -1
  291. package/src/i18n/locales/es.ts +4 -0
  292. package/src/i18n/locales/fr.ts +4 -0
  293. package/src/i18n/locales/hu.ts +4 -0
  294. package/src/i18n/locales/it.ts +4 -0
  295. package/src/i18n/locales/zh.ts +4 -0
  296. package/src/i18n/review.ts +2 -0
  297. package/src/i18n/shared-labels.ts +1 -0
  298. package/src/index.ts +13 -0
  299. package/src/landing/landing-actions.tsx +2 -2
  300. package/src/landing/public-header.tsx +2 -2
  301. package/src/lib/clipboard-minus.ts +94 -0
  302. package/src/lib/document-title.ts +41 -0
  303. package/src/lib/float-clearance.ts +19 -3
  304. package/src/lib/format.ts +115 -30
  305. package/src/lib/minus.ts +40 -0
  306. package/src/lib/status-tone.ts +43 -0
  307. package/src/settings/settings-layout.tsx +3 -2
  308. package/src/shell/account-menu-language.tsx +14 -2
  309. package/src/shell/app-shell-labels.ts +1 -0
  310. package/src/shell/app-shell.tsx +298 -69
  311. package/src/shell/language-code.ts +5 -0
  312. package/src/shell/topbar-controls.tsx +35 -15
  313. package/src/testing/css-variables.generated.ts +6 -6
  314. package/src/theme/chart-palette.ts +102 -20
  315. package/src/theme/palette-presets.ts +121 -84
  316. package/src/tour/tour.tsx +1 -1
  317. package/src/wizard/stepper-nav.tsx +4 -4
  318. package/tokens.css +28 -18
@@ -21,6 +21,11 @@ import { DEFAULT_ROW_ACTIONS_LABELS } from "./row-actions-labels.js";
21
21
  * The data is the roster's `UserRowActionList`, generalised: entries, and `false` /
22
22
  * `null` for the ones a condition left out (`row.locked || {…}`).
23
23
  *
24
+ * Destructive actions belong in the menu (0.49 KIT-12): a `tone: "danger"` action goes
25
+ * into the "⋯" menu while the others stay inline (`dangerPlacement`), and choosing it
26
+ * opens a `ConfirmDialog` before `onSelect` runs (`confirm`; `false` to opt out). A
27
+ * delete is then never a bin icon in every row, one stray tap from gone.
28
+ *
24
29
  * 0.33 (§10.16): an action carries its state at both sizes — a toggle (`pressed`), a
25
30
  * disclosure (`expanded`, `controls`), a tone, a tour anchor (`dataTour`) — so a row
26
31
  * with an "on" share button or a tour step no longer keeps hand-rolled IconButtons at
@@ -36,11 +41,21 @@ export interface RowAction {
36
41
  /** The action's name: the inline icon's name and tooltip, the menu entry's words. */
37
42
  label: string;
38
43
  /**
39
- * Runs it. The kit sends nothing — the app opens its confirmation or calls its API.
40
- * Optional only beside `href`; an action has one or the other (or both: `onSelect`
41
- * then runs as the link is followed).
44
+ * Runs it. The kit sends nothing — it calls this, after a `tone: "danger"` action's
45
+ * confirmation (see {@link confirm}). Optional only beside `href`; an action has one or
46
+ * the other (or both: `onSelect` then runs as the link is followed).
42
47
  */
43
48
  onSelect?: () => void;
49
+ /**
50
+ * 0.49 KIT-12: ask before `onSelect`. A `tone: "danger"` action asks by default — a
51
+ * `ConfirmDialog` (the provider's `useConfirm`, else one of its own) titled with the
52
+ * action and the row's name, "Delete “Ada Example”?", the consequence under it
53
+ * (`rowActions.confirmBody`), the action's label on the danger button and focus on
54
+ * Cancel. Pass the words to say more, or `false` when the app confirms in `onSelect`
55
+ * itself or offers an undo instead. Any other tone asks only when given one. Not for
56
+ * an `href` action: a link only navigates.
57
+ */
58
+ confirm?: boolean | RowActionConfirm;
44
59
  /**
45
60
  * The action NAVIGATES — an edit page, a mail to compose (0.32.1, kastlan's 0.32
46
61
  * report): a real link inline and in the menu, so open in a new tab, a middle click
@@ -121,6 +136,12 @@ export interface RowAction {
121
136
  */
122
137
  dataTour?: string;
123
138
  }
139
+ /** The words of a {@link RowAction.confirm}; each defaults as described there. */
140
+ export interface RowActionConfirm {
141
+ title?: ReactNode;
142
+ body?: ReactNode;
143
+ confirmLabel?: ReactNode;
144
+ }
124
145
  /** What a row's actions may be: entries, and `false` / `null` / `undefined` for the ones
125
146
  * a condition left out. */
126
147
  export type RowActionList = readonly (RowAction | false | null | undefined)[];
@@ -130,6 +151,11 @@ export interface RowActionsLabels {
130
151
  actions: string;
131
152
  /** The menu button's name, given the row's: "Actions for Ada Example". */
132
153
  actionsFor: (name: string) => string;
154
+ /** 0.49 KIT-12: a danger action's confirmation title, given the action's label and the
155
+ * row's name when it has one: "Delete “Ada Example”?", "Delete?". */
156
+ confirmTitle: (action: string, name?: string) => string;
157
+ /** 0.49 KIT-12: the line under it — "This cannot be undone." */
158
+ confirmBody: string;
133
159
  }
134
160
  export { DEFAULT_ROW_ACTIONS_LABELS };
135
161
  /**
@@ -148,6 +174,13 @@ export type RowActionsCollapse = "auto" | "menu" | "inline";
148
174
  export type RowActionsSize = Extract<IconButtonSize, "md" | "sm" | "xs" | "2xs">;
149
175
  export interface RowActionsProps {
150
176
  actions: RowActionList;
177
+ /**
178
+ * 0.49 KIT-12: where `tone: "danger"` actions go while the others are inline. `"menu"`
179
+ * (default): into the row's "⋯" menu, so a delete is never one stray tap in every
180
+ * row — the other actions stay icons beside it. `"inline"`: an icon like the rest.
181
+ * `collapse="inline"` keeps every action inline, `collapse="menu"` every one in the menu.
182
+ */
183
+ dangerPlacement?: "menu" | "inline";
151
184
  /** The row's name, for the menu button's name — "Actions for Ada Example". Without it
152
185
  * (or `null`, as API fields often are) the button is "Actions". */
153
186
  name?: string | null;
@@ -171,7 +204,7 @@ export interface RowActionsProps {
171
204
  * A row's actions, inline or as a "⋯" menu — see {@link RowActionsCollapse}. Every
172
205
  * control stops its click (and the Enter/Space it stands for) from reaching the row, so
173
206
  * a clickable row or a DataTable `rowLink` does not open as well; in a DataTable column
174
- * also set `noRowLink: true`, as {@link rowActionsColumn} does.
207
+ * also set `noRowLink: true` and `role: "actions"`, as {@link rowActionsColumn} does.
175
208
  *
176
209
  * Inline, the icons sit in a {@link CompactControls} region at Normal: their labels
177
210
  * stay the names and the tooltips, and a locked one keeps its reason in the tooltip — a
@@ -183,7 +216,7 @@ export interface RowActionsProps {
183
216
  * press close it, and focus goes back to the "⋯" button, which is where a confirmation
184
217
  * dialog then returns it.
185
218
  */
186
- export declare function RowActions({ actions, name, collapse, size, menuWidth, className, glyphSize, tooltipSide, labels: labelsProp, }: RowActionsProps): import("react").JSX.Element | null;
219
+ export declare function RowActions(props: RowActionsProps): import("react").JSX.Element;
187
220
  /** The actions cell's classes — `rowActionsColumn`'s `className`: the column hugs its
188
221
  * buttons, on one line, at the row's end. A hand-built table's `<TableCell>` takes them
189
222
  * around its {@link RowActions} (0.47, keksdose N17). */
@@ -238,7 +271,7 @@ export interface RowActionsColumnOptions<T> {
238
271
  */
239
272
  header?: ReactNode | false;
240
273
  labels?: LabelOverride<RowActionsLabels>;
241
- /** Anything else for the column (`mobileHidden`, `cellProps`…), merged last. */
274
+ /** Anything else for the column (`mobileHidden`, `cellProps`, another `role`…), merged last. */
242
275
  column?: Partial<DataTableColumn<T>>;
243
276
  }
244
277
  /**
@@ -249,8 +282,9 @@ export interface RowActionsColumnOptions<T> {
249
282
  *
250
283
  * The heading says "Actions" on screen (0.46.1; `header` changes or hides it), the
251
284
  * column hugs its content at the row's end, and `noRowLink` keeps a `rowHref` anchor off it, so the
252
- * buttons keep their own clicks. On a phone it is a line of the card like any column;
253
- * set `column: { mobileHidden: true }` to drop it there (with `mobileDetailsInRow` it
254
- * then shows in the opened row instead).
285
+ * buttons keep their own clicks. It is `role: "actions"` (0.49 KIT-7): DataTable's default
286
+ * phone card draws it after its three fields, at the card's end, and never counts it as
287
+ * one of them. Set `column: { mobileHidden: true }` to drop it there (with
288
+ * `mobileDetailsInRow` it then shows in the opened row instead).
255
289
  */
256
290
  export declare function rowActionsColumn<T>({ key, actions, name, collapse, size, glyphSize, tooltipSide, headerText, header, labels, column, }: RowActionsColumnOptions<T>): DataTableColumn<T>;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { isValidElement } from "react";
3
+ import { isValidElement, useContext } from "react";
4
4
  import { MoreHorizontal } from "lucide-react";
5
5
  import { cn } from "../lib/cn.js";
6
6
  import { useLargeText } from "../hooks/use-large-text.js";
@@ -12,6 +12,8 @@ import { IconButton } from "./icon-button.js";
12
12
  import { useCommitReason } from "./write-lock.js";
13
13
  import { TableHeaderCell } from "./table.js";
14
14
  import { DEFAULT_ROW_ACTIONS_LABELS } from "./row-actions-labels.js";
15
+ import { ConfirmContext } from "./confirm-context.js";
16
+ import { ConfirmProvider } from "./confirm-dialog.js";
15
17
  function visibleActions(actions) {
16
18
  return actions.filter((a) => Boolean(a) && !a.hidden);
17
19
  }
@@ -34,10 +36,41 @@ function stateAttrs(action) {
34
36
  function toneAttr(tone) {
35
37
  return { "data-tone": tone === void 0 || tone === "default" ? void 0 : tone };
36
38
  }
37
- function RowActions({
39
+ function RowActions(props) {
40
+ const outer = useContext(ConfirmContext);
41
+ if (outer === null && visibleActions(props.actions).some(asksFirst)) {
42
+ return /* @__PURE__ */ jsx(ConfirmProvider, { children: /* @__PURE__ */ jsx(RowActionsBody, { ...props }) });
43
+ }
44
+ return /* @__PURE__ */ jsx(RowActionsBody, { ...props });
45
+ }
46
+ function asksFirst(action) {
47
+ if (action.href !== void 0 || action.confirm === false) return false;
48
+ return action.confirm !== void 0 || action.tone === "danger";
49
+ }
50
+ function useRunAction(name, labels) {
51
+ const confirm = useContext(ConfirmContext);
52
+ return (action) => {
53
+ if (!asksFirst(action) || confirm === null) {
54
+ action.onSelect?.();
55
+ return;
56
+ }
57
+ const words = typeof action.confirm === "object" ? action.confirm : {};
58
+ void confirm({
59
+ title: words.title ?? labels.confirmTitle(action.label, name ?? void 0),
60
+ body: words.body ?? labels.confirmBody,
61
+ confirmLabel: words.confirmLabel ?? action.label,
62
+ tone: "danger",
63
+ commit: action.commit
64
+ }).then((ok) => {
65
+ if (ok) action.onSelect?.();
66
+ });
67
+ };
68
+ }
69
+ function RowActionsBody({
38
70
  actions,
39
71
  name,
40
72
  collapse = "auto",
73
+ dangerPlacement = "menu",
41
74
  size = "xs",
42
75
  menuWidth = "15rem",
43
76
  className,
@@ -47,23 +80,50 @@ function RowActions({
47
80
  }) {
48
81
  const labels = useKitLabels("rowActions", DEFAULT_ROW_ACTIONS_LABELS, labelsProp);
49
82
  const large = useLargeText();
83
+ const run = useRunAction(name, labels);
50
84
  const entries = visibleActions(actions);
51
85
  if (entries.length === 0) return null;
52
86
  const asMenu = collapse === "menu" || collapse === "auto" && large && entries.length > 1;
53
- if (!asMenu) {
54
- const strip = /* @__PURE__ */ jsx("div", { "data-slot": "row-actions", className: cn("inline-flex items-center gap-0.5", className), children: entries.map((action) => /* @__PURE__ */ jsx(
87
+ const toMenu = (action) => asMenu || collapse === "auto" && dangerPlacement === "menu" && action.tone === "danger";
88
+ const inline = entries.filter((action) => !toMenu(action));
89
+ const menu = entries.filter(toMenu);
90
+ const title = name ? labels.actionsFor(name) : labels.actions;
91
+ const menuButton = menu.length > 0 && /* @__PURE__ */ jsx(
92
+ RowActionsMenu,
93
+ {
94
+ entries: menu,
95
+ title,
96
+ size,
97
+ menuWidth,
98
+ run,
99
+ className: inline.length === 0 ? className : void 0
100
+ }
101
+ );
102
+ if (inline.length === 0) return menuButton || null;
103
+ const strip = /* @__PURE__ */ jsxs("div", { "data-slot": "row-actions", className: cn("inline-flex items-center gap-0.5", className), children: [
104
+ inline.map((action) => /* @__PURE__ */ jsx(
55
105
  InlineAction,
56
106
  {
57
107
  action,
58
108
  size,
59
109
  glyphSize,
60
- tooltipSide
110
+ tooltipSide,
111
+ run
61
112
  },
62
113
  action.key ?? action.label
63
- )) });
64
- return large ? strip : /* @__PURE__ */ jsx(CompactControls, { children: strip });
65
- }
66
- const title = name ? labels.actionsFor(name) : labels.actions;
114
+ )),
115
+ menuButton
116
+ ] });
117
+ return large ? strip : /* @__PURE__ */ jsx(CompactControls, { children: strip });
118
+ }
119
+ function RowActionsMenu({
120
+ entries,
121
+ title,
122
+ size,
123
+ menuWidth,
124
+ run,
125
+ className
126
+ }) {
67
127
  const busy = entries.some((action) => action.pending);
68
128
  const anchored = entries.filter((action) => action.dataTour);
69
129
  return (
@@ -94,7 +154,7 @@ function RowActions({
94
154
  ),
95
155
  onClick: stopClick,
96
156
  onKeyDown: stopActivationKeys,
97
- children: (close) => /* @__PURE__ */ jsx("ul", { "data-slot": "row-actions-menu", className: "space-y-0.5", children: entries.map((action) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MenuAction, { action, close }) }, action.key ?? action.label)) })
157
+ children: (close) => /* @__PURE__ */ jsx("ul", { "data-slot": "row-actions-menu", className: "space-y-0.5", children: entries.map((action) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(MenuAction, { action, close, run }) }, action.key ?? action.label)) })
98
158
  }
99
159
  ),
100
160
  anchored.map((action) => (
@@ -123,7 +183,7 @@ function useActionHref(action) {
123
183
  const refused = reason !== void 0 && reason !== null && reason !== false && reason !== "";
124
184
  return action.href !== void 0 && !action.pending && !refused ? action.href : void 0;
125
185
  }
126
- function MenuAction({ action, close }) {
186
+ function MenuAction({ action, close, run }) {
127
187
  const href = useActionHref(action);
128
188
  const glyph = glyphOf(action.icon);
129
189
  const glyphTone = action.tone === void 0 ? void 0 : MENU_GLYPH_TONE[action.tone];
@@ -142,7 +202,7 @@ function MenuAction({ action, close }) {
142
202
  const tone = action.tone === "danger" ? "danger" : void 0;
143
203
  const select = () => {
144
204
  close();
145
- action.onSelect?.();
205
+ run(action);
146
206
  };
147
207
  if (href !== void 0) {
148
208
  return /* @__PURE__ */ jsx(
@@ -185,7 +245,8 @@ function InlineAction({
185
245
  action,
186
246
  size,
187
247
  glyphSize,
188
- tooltipSide
248
+ tooltipSide,
249
+ run
189
250
  }) {
190
251
  const href = useActionHref(action);
191
252
  const glyph = glyphOf(action.icon);
@@ -228,7 +289,7 @@ function InlineAction({
228
289
  pending: action.pending,
229
290
  disabled: action.disabled,
230
291
  stopPropagation: true,
231
- onClick: () => action.onSelect?.(),
292
+ onClick: () => run(action),
232
293
  children: glyph
233
294
  }
234
295
  );
@@ -236,7 +297,7 @@ function InlineAction({
236
297
  const stop = {
237
298
  onClick: (e) => {
238
299
  e.stopPropagation();
239
- action.onSelect?.();
300
+ run(action);
240
301
  },
241
302
  onKeyDown: (e) => {
242
303
  if (e.key === "Enter" || e.key === " ") e.stopPropagation();
@@ -325,6 +386,8 @@ function rowActionsColumn({
325
386
  // `RowActionsHead` (0.47).
326
387
  headClassName: ROW_ACTIONS_HEAD_CLASS,
327
388
  noRowLink: true,
389
+ // After the phone card's fields, never one of them (0.49 KIT-7).
390
+ role: "actions",
328
391
  ...column
329
392
  };
330
393
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/row-actions.tsx"],"sourcesContent":["import { isValidElement } from \"react\";\nimport type { KeyboardEvent, MouseEvent, ReactElement, ReactNode } from \"react\";\nimport { MoreHorizontal } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { useLargeText } from \"../hooks/use-large-text\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { Popover } from \"./popover\";\nimport { Button } from \"./button\";\nimport { CompactControls } from \"./field-parts\";\nimport { IconButton } from \"./icon-button\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\nimport type { IconButtonGlyphSize, IconButtonSize } from \"./icon-button\";\nimport type { TooltipSide } from \"./tooltip\";\nimport type { DataTableColumn } from \"./data-table\";\nimport { TableHeaderCell, type TableHeaderCellProps } from \"./table\";\nimport { DEFAULT_ROW_ACTIONS_LABELS } from \"./row-actions-labels\";\n\n/**\n * A row's actions — inline icons at Normal, one \"⋯\" menu at Large and above\n * (docs/text-size-harmonization.md §10.8).\n *\n * At Large an IconButton shows its label as text (§4). In a dense row — a register's\n * action column, a list row's end — every label at once does not fit: Kurvenschmiede\n * has 44 IconButtons in rows, kastlan 119. So a row's actions collapse instead, into\n * the menu the user roster has used since 0.31 (`UserRowActions`, now built on this):\n * one \"⋯\" button, and a menu with each action's icon AND words, where a locked one stays\n * listed and says why.\n *\n * The data is the roster's `UserRowActionList`, generalised: entries, and `false` /\n * `null` for the ones a condition left out (`row.locked || {…}`).\n *\n * 0.33 (§10.16): an action carries its state at both sizes — a toggle (`pressed`), a\n * disclosure (`expanded`, `controls`), a tone, a tour anchor (`dataTour`) — so a row\n * with an \"on\" share button or a tour step no longer keeps hand-rolled IconButtons at\n * Normal and this only at Large (keksdose k15).\n */\n\n/** An action's colour: IconButton's tone inline; in the menu the glyph takes it and the\n * words keep the text colour, except `danger`, whose words are the danger text. */\nexport type RowActionTone = \"default\" | \"muted\" | \"info\" | \"warning\" | \"danger\";\n\n/** One action of a row. */\nexport interface RowAction {\n /** React key. Default: the label. */\n key?: string;\n /** The action's name: the inline icon's name and tooltip, the menu entry's words. */\n label: string;\n /**\n * Runs it. The kit sends nothing — the app opens its confirmation or calls its API.\n * Optional only beside `href`; an action has one or the other (or both: `onSelect`\n * then runs as the link is followed).\n */\n onSelect?: () => void;\n /**\n * The action NAVIGATES — an edit page, a mail to compose (0.32.1, kastlan's 0.32\n * report): a real link inline and in the menu, so open in a new tab, a middle click\n * and the URL on hover work, which `onSelect` + `navigate` lost. The kit's link rule\n * applies ({@link Button}'s `href`): in-app through the provider's `linkComponent`, an\n * external or `#anchor` one a plain `<a>`, `mailto:` included.\n *\n * A link cannot carry a reason or a spinner, so an action with a `disabledReason`\n * (or under a write lock, with `commit`) or `pending` stays a button and says why.\n */\n href?: string;\n /** With `href`: leaves the app — a new tab, and a screen reader is told so. */\n external?: boolean;\n /**\n * The glyph: a Lucide icon (`Pencil`), or an element (`<Pencil />`, an app's own\n * svg) — DataTable's `rowActions` hand their `icon` node through. Inline, an action\n * without one is a small text button; in the menu it is the words alone.\n */\n icon?: LucideIcon | ReactElement;\n /**\n * `danger` for delete, revoke, erase: the quiet red of IconButton's `tone=\"danger\"`\n * inline, the danger text tone in the menu. `muted` (an open or add that should not\n * compete), `info` (a notice-worthy, harmless action: keksdose's reconcile) and\n * `warning` (a flag that wants attention): IconButton's tone inline; in the menu the\n * glyph takes it and the words keep the text colour, so `muted` looks like `default`\n * there (0.33).\n */\n tone?: RowActionTone;\n /** Why it is not available for this row. It stays listed, focusable, and says so — an\n * action that silently vanished would leave the reader looking for it. */\n disabledReason?: ReactNode;\n /**\n * This action COMMITS — it saves, sends, deletes: under a locked\n * {@link WriteLockProvider} it is refused with the lock's reason, as {@link Button}'s\n * `commit` is, inline and in the menu alike. No provider, or an unlocked one: no effect.\n */\n commit?: CommitScope;\n /**\n * In flight — the request it started has not answered. Inline, the icon turns into\n * IconButton's spinner (`pending`); collapsed, the \"⋯\" button does, since the menu\n * closed when the action was chosen. Either way nothing in the row can be pressed again\n * until it settles: the \"⋯\" opens no menu while busy.\n */\n pending?: boolean;\n /**\n * Not available for a moment, with nothing to explain — another action of the list\n * is in flight (an admin panel's `busy`). The plain `disabled`; a lasting refusal is a\n * `disabledReason`, which says why.\n */\n disabled?: boolean;\n /** Leave it out for this row. */\n hidden?: boolean;\n /**\n * A toggle (0.33): `aria-pressed` and IconButton's \"on\" look inline, `aria-pressed` on\n * the menu entry. Keep the label the same in both states (\"Share budget\"): the pressed\n * state already says whether it is on. Not on an `href` action — a link is not a\n * toggle — nor with `expanded`.\n */\n pressed?: boolean;\n /**\n * Opens or closes something IN THE PAGE (0.33) — a share card under the row, an\n * add-category row: `aria-expanded`, inline and on the menu entry. Not with `pressed`:\n * a disclosure says open, not on. Not on an `href` action.\n */\n expanded?: boolean;\n /** The `id` of what `expanded` opens: `aria-controls`, inline and on the menu entry. */\n controls?: string;\n /**\n * A tour anchor (0.33, §10.18): `data-tour` on ONE element per action at a time.\n * Inline, on the action's own control (the IconButton, the small text button, the\n * link). Collapsed, on an `aria-hidden` box laid over the \"⋯\" button — `[data-tour=x]`\n * resolves to a box the size of the \"⋯\", whose `data-slot=\"row-actions\"` box holds the\n * button, two anchored actions in one row don't fight over one attribute, and a tap\n * still reaches the button. Never on the entry in the open menu: the kit's tour takes\n * the first visible match and the menu is portalled after the row, so a copy there\n * would never be found, only make the selector match twice. The kit's name for it, as\n * `AppShellNavItem.dataTour`.\n */\n dataTour?: string;\n}\n\n/** What a row's actions may be: entries, and `false` / `null` / `undefined` for the ones\n * a condition left out. */\nexport type RowActionList = readonly (RowAction | false | null | undefined)[];\n\nexport interface RowActionsLabels {\n /** The menu button's name when the row has no name of its own; also the actions\n * column's (hidden) header in {@link rowActionsColumn}. */\n actions: string;\n /** The menu button's name, given the row's: \"Actions for Ada Example\". */\n actionsFor: (name: string) => string;\n}\n\n// Defined in ./row-actions-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_ROW_ACTIONS_LABELS };\n\n/**\n * - `\"auto\"` (default): the icons inline at Normal; the \"⋯\" menu at Large and Extra\n * large once there are two or more (§10.8). A single action stays inline at every\n * size, with its label as text at Large — one label fits a row, and a menu of one is\n * a detour.\n * - `\"menu\"`: the menu at every size — the roster's choice, whose actions grow (§2.4\n * there) and do not fit a phone card as a strip.\n * - `\"inline\"`: the icons at every size — for a row the app knows has room; at Large\n * each then shows its label (IconButton's `labelVisible`).\n */\nexport type RowActionsCollapse = \"auto\" | \"menu\" | \"inline\";\n\n/** The sizes a row action comes in: IconButton's row steps. `2xs` is DataTable's\n * compact density. At Large every one is at least a 48 px target (IconButton). */\nexport type RowActionsSize = Extract<IconButtonSize, \"md\" | \"sm\" | \"xs\" | \"2xs\">;\n\nexport interface RowActionsProps {\n actions: RowActionList;\n /** The row's name, for the menu button's name — \"Actions for Ada Example\". Without it\n * (or `null`, as API fields often are) the button is \"Actions\". */\n name?: string | null;\n /** When the actions collapse into the menu — see {@link RowActionsCollapse}. */\n collapse?: RowActionsCollapse;\n /** The inline icons' and the \"⋯\" button's size. Default `xs`, a list row's action. */\n size?: RowActionsSize;\n /** The menu's width. Default `\"15rem\"`, in rem so it grows with the words (§3.2). */\n menuWidth?: `${number}rem`;\n /** On the wrapper: the inline strip, or the box round the \"⋯\" button. */\n className?: string;\n /** The inline glyphs' size, IconButton's `glyphSize` — e.g. 14 in a dense header strip\n * (keksdose's category group header). The \"⋯\" button keeps its box's own. */\n glyphSize?: IconButtonGlyphSize;\n /** The inline tooltips' side, IconButton's `tooltipSide` — `\"start\"` for a table's last\n * column, so a bubble does not run off the row's end. Per row: every inline action. */\n tooltipSide?: TooltipSide;\n labels?: LabelOverride<RowActionsLabels>;\n}\n\nfunction visibleActions(actions: RowActionList): RowAction[] {\n return actions.filter((a): a is RowAction => Boolean(a) && !(a as RowAction).hidden);\n}\n\n/** An action's glyph as a node: a component is rendered, an element is used as is. */\nfunction glyphOf(icon: RowAction[\"icon\"]): ReactNode {\n if (icon === undefined) return null;\n if (isValidElement(icon)) return icon;\n const Icon = icon as LucideIcon;\n return <Icon />;\n}\n\n/** The menu glyph's tint per tone (0.33): the words keep the text colour, so the glyph\n * carries `info` and `warning` — IconButton's toned colours. `danger` colours the words\n * through Button's tone, and `muted` is the default look in a menu. */\nconst MENU_GLYPH_TONE: Partial<Record<RowActionTone, string>> = {\n info: \"text-info\",\n warning: \"text-warning\",\n};\n\n/** The state an action's control says, inline and in the menu alike: `aria-expanded` and\n * `aria-controls` for a disclosure (a toggle's `aria-pressed` goes through the button's\n * own `pressed`, which also draws the \"on\" look). */\nfunction stateAttrs(action: RowAction) {\n return {\n \"aria-expanded\": action.expanded,\n \"aria-controls\": action.expanded === undefined ? undefined : action.controls,\n };\n}\n\n/** An action's tone on its inline control, for a caller's CSS and a test: `data-tone`,\n * left off for the default look. */\nfunction toneAttr(tone: RowActionTone | undefined) {\n return { \"data-tone\": tone === undefined || tone === \"default\" ? undefined : tone };\n}\n\n/**\n * A row's actions, inline or as a \"⋯\" menu — see {@link RowActionsCollapse}. Every\n * control stops its click (and the Enter/Space it stands for) from reaching the row, so\n * a clickable row or a DataTable `rowLink` does not open as well; in a DataTable column\n * also set `noRowLink: true`, as {@link rowActionsColumn} does.\n *\n * Inline, the icons sit in a {@link CompactControls} region at Normal: their labels\n * stay the names and the tooltips, and a locked one keeps its reason in the tooltip — a\n * line under one icon of a row would break the row. In the menu a locked action's\n * reason is a line under its words at Large and on touch (Button's\n * `disabledReasonDisplay`).\n *\n * The menu is portalled, so a table's scroller cannot clip it; Escape and an outside\n * press close it, and focus goes back to the \"⋯\" button, which is where a confirmation\n * dialog then returns it.\n */\nexport function RowActions({\n actions,\n name,\n collapse = \"auto\",\n size = \"xs\",\n menuWidth = \"15rem\",\n className,\n glyphSize,\n tooltipSide,\n labels: labelsProp,\n}: RowActionsProps) {\n const labels = useKitLabels(\"rowActions\", DEFAULT_ROW_ACTIONS_LABELS, labelsProp);\n const large = useLargeText();\n const entries = visibleActions(actions);\n if (entries.length === 0) return null;\n const asMenu = collapse === \"menu\" || (collapse === \"auto\" && large && entries.length > 1);\n if (!asMenu) {\n const strip = (\n <div data-slot=\"row-actions\" className={cn(\"inline-flex items-center gap-0.5\", className)}>\n {entries.map((action) => (\n <InlineAction\n key={action.key ?? action.label}\n action={action}\n size={size}\n glyphSize={glyphSize}\n tooltipSide={tooltipSide}\n />\n ))}\n </div>\n );\n // Compact at Normal (no room for a reason line in a row); at Large an inline action\n // is the app's choice (\"inline\") or the row's only one, and shows its words.\n return large ? strip : <CompactControls>{strip}</CompactControls>;\n }\n const title = name ? labels.actionsFor(name) : labels.actions;\n // The menu closed when the action was chosen, so what is in flight shows on \"⋯\".\n const busy = entries.some((action) => action.pending);\n const anchored = entries.filter((action) => action.dataTour);\n return (\n // The \"⋯\" in a box of its own, the inline strip's slot: the tour anchors of the\n // collapsed actions lie over the button in it (see `dataTour`), and `className`\n // places the box as it places the strip.\n <div data-slot=\"row-actions\" className={cn(\"relative inline-flex\", className)}>\n <Popover\n width={menuWidth}\n aria-label={title}\n className=\"p-1\"\n trigger={({ open, toggle, ref }) => (\n <IconButton\n ref={ref}\n size={size}\n label={title}\n // \"⋯\" is read by everyone as \"more actions\"; the menu it opens is the words.\n labelVisible={false}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n pending={busy}\n stopPropagation\n onClick={toggle}\n >\n <MoreHorizontal />\n </IconButton>\n )}\n // React carries an event out of a portal along the COMPONENT tree, so a click in\n // the menu reached a clickable row or a DataTable `onRowClick` the menu was opened\n // from, though the panel is nowhere near it in the page — only the \"⋯\" trigger\n // stopped its own (keksdose's 0.32 report). Stopped on the PANEL itself, its padding\n // and border included (0.32.2: 0.32.1 stopped it on an inner box, and a click on\n // the panel's `p-1` still went through): clicks, and the Enter / Space a row also\n // listens for. Escape goes on, to the popover's own handler that closes it.\n onClick={stopClick}\n onKeyDown={stopActivationKeys}\n >\n {(close) => (\n <ul data-slot=\"row-actions-menu\" className=\"space-y-0.5\">\n {entries.map((action) => (\n <li key={action.key ?? action.label}>\n <MenuAction action={action} close={close} />\n </li>\n ))}\n </ul>\n )}\n </Popover>\n {anchored.map((action) => (\n // The More cell's TourAnchor: decoration over the button, which keeps every tap.\n <span\n key={action.key ?? action.label}\n aria-hidden\n data-tour={action.dataTour}\n className=\"pointer-events-none absolute inset-0\"\n />\n ))}\n </div>\n );\n}\n\nfunction stopClick(e: MouseEvent) {\n e.stopPropagation();\n}\n\nfunction stopActivationKeys(e: KeyboardEvent) {\n if (e.key === \"Enter\" || e.key === \" \") e.stopPropagation();\n}\n\n/**\n * The `href` an action renders as a link with, or `undefined` for a button: a link\n * cannot carry a reason or a spinner (Button's link form has neither), so a refused\n * action — its own `disabledReason`, or a write lock on a `commit` — and a pending one\n * stay buttons and say so.\n */\nfunction useActionHref(action: RowAction): string | undefined {\n const reason = useCommitReason(action.commit, action.disabledReason);\n const refused = reason !== undefined && reason !== null && reason !== false && reason !== \"\";\n return action.href !== undefined && !action.pending && !refused ? action.href : undefined;\n}\n\n/** One entry of the \"⋯\" menu: the words, as a link for an `href`, else a button. */\nfunction MenuAction({ action, close }: { action: RowAction; close: () => void }) {\n const href = useActionHref(action);\n const glyph = glyphOf(action.icon);\n const glyphTone = action.tone === undefined ? undefined : MENU_GLYPH_TONE[action.tone];\n const words = (\n <>\n {glyph && (\n <span\n aria-hidden\n data-tone={glyphTone ? action.tone : undefined}\n className={cn(\"flex shrink-0 [&_svg]:size-4\", glyphTone)}\n >\n {glyph}\n </span>\n )}\n {action.label}\n </>\n );\n const tone = action.tone === \"danger\" ? \"danger\" : undefined;\n const select = () => {\n close();\n action.onSelect?.();\n };\n if (href !== undefined) {\n return (\n <Button\n href={href}\n external={action.external}\n variant=\"ghost\"\n size=\"sm\"\n stretch\n tone={tone}\n disabled={action.disabled}\n className=\"w-full justify-start text-start\"\n onClick={select}\n >\n {words}\n </Button>\n );\n }\n return (\n <Button\n {...stateAttrs(action)}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n stretch\n tone={tone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n className=\"w-full justify-start text-start\"\n onClick={select}\n >\n {words}\n </Button>\n );\n}\n\n/** One inline action: an IconButton with its icon, else a small text button — each a\n * link for an `href` (see {@link useActionHref}). The control carries the action's\n * state, its tone and its tour anchor. */\nfunction InlineAction({\n action,\n size,\n glyphSize,\n tooltipSide,\n}: {\n action: RowAction;\n size: RowActionsSize;\n glyphSize?: IconButtonGlyphSize;\n tooltipSide?: TooltipSide;\n}) {\n const href = useActionHref(action);\n const glyph = glyphOf(action.icon);\n const iconTone = action.tone === \"default\" ? undefined : action.tone;\n // A text button has Button's two tones; `info` and `warning` are a glyph's colours.\n const textTone = action.tone === \"danger\" || action.tone === \"muted\" ? action.tone : undefined;\n const marks = { \"data-tour\": action.dataTour, ...toneAttr(action.tone) };\n if (glyph) {\n if (href !== undefined) {\n return (\n <IconButton\n {...marks}\n href={href}\n external={action.external}\n size={size}\n glyphSize={glyphSize}\n label={action.label}\n tooltipSide={tooltipSide}\n tone={iconTone}\n disabled={action.disabled}\n stopPropagation\n onClick={() => action.onSelect?.()}\n >\n {glyph}\n </IconButton>\n );\n }\n return (\n <IconButton\n {...marks}\n {...stateAttrs(action)}\n size={size}\n glyphSize={glyphSize}\n label={action.label}\n tooltipSide={tooltipSide}\n tone={iconTone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n stopPropagation\n onClick={() => action.onSelect?.()}\n >\n {glyph}\n </IconButton>\n );\n }\n // A row listening for clicks must not open as well (IconButton's stopPropagation).\n const stop = {\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n action.onSelect?.();\n },\n onKeyDown: (e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") e.stopPropagation();\n },\n };\n if (href !== undefined) {\n return (\n <Button\n {...marks}\n href={href}\n external={action.external}\n variant=\"ghost\"\n size=\"sm\"\n tone={textTone}\n disabled={action.disabled}\n {...stop}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n {...marks}\n {...stateAttrs(action)}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n tone={textTone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n {...stop}\n >\n {action.label}\n </Button>\n );\n}\n\n/** The actions column's heading, in the provider's words: on screen since 0.46.1 (keksdose\n * dev#598 rework), a screen reader's name only with `hidden`. */\nfunction RowActionsHeader({\n text,\n labels,\n hidden,\n}: {\n text?: string;\n labels?: LabelOverride<RowActionsLabels>;\n hidden?: boolean;\n}) {\n const resolved = useKitLabels(\"rowActions\", DEFAULT_ROW_ACTIONS_LABELS, labels);\n const word = text ?? resolved.actions;\n return hidden ? <span className=\"sr-only\">{word}</span> : <>{word}</>;\n}\n\n/** The actions cell's classes — `rowActionsColumn`'s `className`: the column hugs its\n * buttons, on one line, at the row's end. A hand-built table's `<TableCell>` takes them\n * around its {@link RowActions} (0.47, keksdose N17). */\nexport const ROW_ACTIONS_CELL_CLASS = \"w-px whitespace-nowrap text-end\";\n/** The actions heading's classes — `rowActionsColumn`'s `headClassName`, which\n * {@link RowActionsHead} draws: hugging, end-aligned over the buttons (0.47, N17). */\nexport const ROW_ACTIONS_HEAD_CLASS = \"w-px text-end\";\n\nexport interface RowActionsHeadProps extends Omit<TableHeaderCellProps, \"children\"> {\n /** The heading. Default: the provider's `rowActions.actions` (\"Actions\"). */\n children?: ReactNode;\n /** Named for a screen reader only — `rowActionsColumn`'s `header: false`. */\n hideLabel?: boolean;\n labels?: LabelOverride<RowActionsLabels>;\n}\n\n/**\n * A hand-built table's actions heading (0.47, keksdose N17): the `<th>` `rowActionsColumn`\n * gives a DataTable — \"Actions\" on screen in the provider's words, end-aligned over the\n * buttons, hugging them. Pair it with `<TableCell className={ROW_ACTIONS_CELL_CLASS}>`\n * around each row's {@link RowActions}. Every other `TableHeaderCell` prop passes on\n * (`size`, the app's `className`, merged last).\n *\n * <RowActionsHead size=\"sm\" className={TH} />\n */\nexport function RowActionsHead({ children, hideLabel, labels, className, ...rest }: RowActionsHeadProps) {\n return (\n <TableHeaderCell align=\"end\" {...rest} className={cn(ROW_ACTIONS_HEAD_CLASS, className)}>\n {typeof children === \"string\" || children == null ? (\n <RowActionsHeader text={children ?? undefined} labels={labels} hidden={hideLabel} />\n ) : (\n children\n )}\n </TableHeaderCell>\n );\n}\n\nexport interface RowActionsColumnOptions<T> {\n /** The column's key. Default `\"actions\"`. */\n key?: string;\n /** The row's actions — `false` / `null` for the ones a condition leaves out. */\n actions: (row: T) => RowActionList;\n /** The row's name, for the \"⋯\" button's name (\"Actions for Ada Example\"). `null` and\n * `undefined` both mean \"Actions\". */\n name?: (row: T) => string | null | undefined;\n collapse?: RowActionsCollapse;\n /** Default `xs`. */\n size?: RowActionsSize;\n /** See {@link RowActionsProps.glyphSize}. */\n glyphSize?: IconButtonGlyphSize;\n /** See {@link RowActionsProps.tooltipSide} — `\"start\"` in a table's last column. */\n tooltipSide?: TooltipSide;\n /**\n * The column's name as plain text — the column-settings checklist and the phone\n * card's label read it (`DataTableColumn.headerText`). Pass the app's own word; left\n * out, the heading shows the provider's `rowActions.actions` and the plain-text\n * places fall back to the column's `key`.\n */\n headerText?: string;\n /**\n * The heading (0.46.1, keksdose dev#598 rework — Marcel: a VISIBLE \"Actions\" heading on\n * every table with an actions column). Default: `headerText`, else the provider's\n * `rowActions.actions` (\"Actions\"), shown, end-aligned over the buttons. A node\n * replaces it; `false` keeps the pre-0.46.1 heading, named for a screen reader only.\n */\n header?: ReactNode | false;\n labels?: LabelOverride<RowActionsLabels>;\n /** Anything else for the column (`mobileHidden`, `cellProps`…), merged last. */\n column?: Partial<DataTableColumn<T>>;\n}\n\n/**\n * A DataTable column of {@link RowActions} — an app's action column in one line\n * (§10.8): inline icons at Normal, the \"⋯\" menu at Large with two or more actions.\n *\n * columns={[...columns, rowActionsColumn({ actions: (r) => [...], name: (r) => r.name })]}\n *\n * The heading says \"Actions\" on screen (0.46.1; `header` changes or hides it), the\n * column hugs its content at the row's end, and `noRowLink` keeps a `rowHref` anchor off it, so the\n * buttons keep their own clicks. On a phone it is a line of the card like any column;\n * set `column: { mobileHidden: true }` to drop it there (with `mobileDetailsInRow` it\n * then shows in the opened row instead).\n */\nexport function rowActionsColumn<T>({\n key = \"actions\",\n actions,\n name,\n collapse,\n size,\n glyphSize,\n tooltipSide,\n headerText,\n header,\n labels,\n column,\n}: RowActionsColumnOptions<T>): DataTableColumn<T> {\n return {\n key,\n header:\n header === false || header == null ? (\n <RowActionsHeader text={headerText} labels={labels} hidden={header === false} />\n ) : (\n header\n ),\n headerText,\n cell: (row) => (\n <RowActions\n actions={actions(row)}\n name={name?.(row)}\n collapse={collapse}\n size={size}\n glyphSize={glyphSize}\n tooltipSide={tooltipSide}\n labels={labels}\n />\n ),\n className: ROW_ACTIONS_CELL_CLASS,\n // The heading over the buttons, at the row's end (0.46.1); a hand-built table's\n // `RowActionsHead` (0.47).\n headClassName: ROW_ACTIONS_HEAD_CLASS,\n noRowLink: true,\n ...column,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/row-actions.tsx"],"sourcesContent":["import { isValidElement, useContext } from \"react\";\nimport type { KeyboardEvent, MouseEvent, ReactElement, ReactNode } from \"react\";\nimport { MoreHorizontal } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { useLargeText } from \"../hooks/use-large-text\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { Popover } from \"./popover\";\nimport { Button } from \"./button\";\nimport { CompactControls } from \"./field-parts\";\nimport { IconButton } from \"./icon-button\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\nimport type { IconButtonGlyphSize, IconButtonSize } from \"./icon-button\";\nimport type { TooltipSide } from \"./tooltip\";\nimport type { DataTableColumn } from \"./data-table\";\nimport { TableHeaderCell, type TableHeaderCellProps } from \"./table\";\nimport { DEFAULT_ROW_ACTIONS_LABELS } from \"./row-actions-labels\";\nimport { ConfirmContext } from \"./confirm-context\";\nimport { ConfirmProvider } from \"./confirm-dialog\";\n\n/**\n * A row's actions — inline icons at Normal, one \"⋯\" menu at Large and above\n * (docs/text-size-harmonization.md §10.8).\n *\n * At Large an IconButton shows its label as text (§4). In a dense row — a register's\n * action column, a list row's end — every label at once does not fit: Kurvenschmiede\n * has 44 IconButtons in rows, kastlan 119. So a row's actions collapse instead, into\n * the menu the user roster has used since 0.31 (`UserRowActions`, now built on this):\n * one \"⋯\" button, and a menu with each action's icon AND words, where a locked one stays\n * listed and says why.\n *\n * The data is the roster's `UserRowActionList`, generalised: entries, and `false` /\n * `null` for the ones a condition left out (`row.locked || {…}`).\n *\n * Destructive actions belong in the menu (0.49 KIT-12): a `tone: \"danger\"` action goes\n * into the \"⋯\" menu while the others stay inline (`dangerPlacement`), and choosing it\n * opens a `ConfirmDialog` before `onSelect` runs (`confirm`; `false` to opt out). A\n * delete is then never a bin icon in every row, one stray tap from gone.\n *\n * 0.33 (§10.16): an action carries its state at both sizes — a toggle (`pressed`), a\n * disclosure (`expanded`, `controls`), a tone, a tour anchor (`dataTour`) — so a row\n * with an \"on\" share button or a tour step no longer keeps hand-rolled IconButtons at\n * Normal and this only at Large (keksdose k15).\n */\n\n/** An action's colour: IconButton's tone inline; in the menu the glyph takes it and the\n * words keep the text colour, except `danger`, whose words are the danger text. */\nexport type RowActionTone = \"default\" | \"muted\" | \"info\" | \"warning\" | \"danger\";\n\n/** One action of a row. */\nexport interface RowAction {\n /** React key. Default: the label. */\n key?: string;\n /** The action's name: the inline icon's name and tooltip, the menu entry's words. */\n label: string;\n /**\n * Runs it. The kit sends nothing — it calls this, after a `tone: \"danger\"` action's\n * confirmation (see {@link confirm}). Optional only beside `href`; an action has one or\n * the other (or both: `onSelect` then runs as the link is followed).\n */\n onSelect?: () => void;\n /**\n * 0.49 KIT-12: ask before `onSelect`. A `tone: \"danger\"` action asks by default — a\n * `ConfirmDialog` (the provider's `useConfirm`, else one of its own) titled with the\n * action and the row's name, \"Delete “Ada Example”?\", the consequence under it\n * (`rowActions.confirmBody`), the action's label on the danger button and focus on\n * Cancel. Pass the words to say more, or `false` when the app confirms in `onSelect`\n * itself or offers an undo instead. Any other tone asks only when given one. Not for\n * an `href` action: a link only navigates.\n */\n confirm?: boolean | RowActionConfirm;\n /**\n * The action NAVIGATES — an edit page, a mail to compose (0.32.1, kastlan's 0.32\n * report): a real link inline and in the menu, so open in a new tab, a middle click\n * and the URL on hover work, which `onSelect` + `navigate` lost. The kit's link rule\n * applies ({@link Button}'s `href`): in-app through the provider's `linkComponent`, an\n * external or `#anchor` one a plain `<a>`, `mailto:` included.\n *\n * A link cannot carry a reason or a spinner, so an action with a `disabledReason`\n * (or under a write lock, with `commit`) or `pending` stays a button and says why.\n */\n href?: string;\n /** With `href`: leaves the app — a new tab, and a screen reader is told so. */\n external?: boolean;\n /**\n * The glyph: a Lucide icon (`Pencil`), or an element (`<Pencil />`, an app's own\n * svg) — DataTable's `rowActions` hand their `icon` node through. Inline, an action\n * without one is a small text button; in the menu it is the words alone.\n */\n icon?: LucideIcon | ReactElement;\n /**\n * `danger` for delete, revoke, erase: the quiet red of IconButton's `tone=\"danger\"`\n * inline, the danger text tone in the menu. `muted` (an open or add that should not\n * compete), `info` (a notice-worthy, harmless action: keksdose's reconcile) and\n * `warning` (a flag that wants attention): IconButton's tone inline; in the menu the\n * glyph takes it and the words keep the text colour, so `muted` looks like `default`\n * there (0.33).\n */\n tone?: RowActionTone;\n /** Why it is not available for this row. It stays listed, focusable, and says so — an\n * action that silently vanished would leave the reader looking for it. */\n disabledReason?: ReactNode;\n /**\n * This action COMMITS — it saves, sends, deletes: under a locked\n * {@link WriteLockProvider} it is refused with the lock's reason, as {@link Button}'s\n * `commit` is, inline and in the menu alike. No provider, or an unlocked one: no effect.\n */\n commit?: CommitScope;\n /**\n * In flight — the request it started has not answered. Inline, the icon turns into\n * IconButton's spinner (`pending`); collapsed, the \"⋯\" button does, since the menu\n * closed when the action was chosen. Either way nothing in the row can be pressed again\n * until it settles: the \"⋯\" opens no menu while busy.\n */\n pending?: boolean;\n /**\n * Not available for a moment, with nothing to explain — another action of the list\n * is in flight (an admin panel's `busy`). The plain `disabled`; a lasting refusal is a\n * `disabledReason`, which says why.\n */\n disabled?: boolean;\n /** Leave it out for this row. */\n hidden?: boolean;\n /**\n * A toggle (0.33): `aria-pressed` and IconButton's \"on\" look inline, `aria-pressed` on\n * the menu entry. Keep the label the same in both states (\"Share budget\"): the pressed\n * state already says whether it is on. Not on an `href` action — a link is not a\n * toggle — nor with `expanded`.\n */\n pressed?: boolean;\n /**\n * Opens or closes something IN THE PAGE (0.33) — a share card under the row, an\n * add-category row: `aria-expanded`, inline and on the menu entry. Not with `pressed`:\n * a disclosure says open, not on. Not on an `href` action.\n */\n expanded?: boolean;\n /** The `id` of what `expanded` opens: `aria-controls`, inline and on the menu entry. */\n controls?: string;\n /**\n * A tour anchor (0.33, §10.18): `data-tour` on ONE element per action at a time.\n * Inline, on the action's own control (the IconButton, the small text button, the\n * link). Collapsed, on an `aria-hidden` box laid over the \"⋯\" button — `[data-tour=x]`\n * resolves to a box the size of the \"⋯\", whose `data-slot=\"row-actions\"` box holds the\n * button, two anchored actions in one row don't fight over one attribute, and a tap\n * still reaches the button. Never on the entry in the open menu: the kit's tour takes\n * the first visible match and the menu is portalled after the row, so a copy there\n * would never be found, only make the selector match twice. The kit's name for it, as\n * `AppShellNavItem.dataTour`.\n */\n dataTour?: string;\n}\n\n/** The words of a {@link RowAction.confirm}; each defaults as described there. */\nexport interface RowActionConfirm {\n title?: ReactNode;\n body?: ReactNode;\n confirmLabel?: ReactNode;\n}\n\n/** What a row's actions may be: entries, and `false` / `null` / `undefined` for the ones\n * a condition left out. */\nexport type RowActionList = readonly (RowAction | false | null | undefined)[];\n\nexport interface RowActionsLabels {\n /** The menu button's name when the row has no name of its own; also the actions\n * column's (hidden) header in {@link rowActionsColumn}. */\n actions: string;\n /** The menu button's name, given the row's: \"Actions for Ada Example\". */\n actionsFor: (name: string) => string;\n /** 0.49 KIT-12: a danger action's confirmation title, given the action's label and the\n * row's name when it has one: \"Delete “Ada Example”?\", \"Delete?\". */\n confirmTitle: (action: string, name?: string) => string;\n /** 0.49 KIT-12: the line under it — \"This cannot be undone.\" */\n confirmBody: string;\n}\n\n// Defined in ./row-actions-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_ROW_ACTIONS_LABELS };\n\n/**\n * - `\"auto\"` (default): the icons inline at Normal; the \"⋯\" menu at Large and Extra\n * large once there are two or more (§10.8). A single action stays inline at every\n * size, with its label as text at Large — one label fits a row, and a menu of one is\n * a detour.\n * - `\"menu\"`: the menu at every size — the roster's choice, whose actions grow (§2.4\n * there) and do not fit a phone card as a strip.\n * - `\"inline\"`: the icons at every size — for a row the app knows has room; at Large\n * each then shows its label (IconButton's `labelVisible`).\n */\nexport type RowActionsCollapse = \"auto\" | \"menu\" | \"inline\";\n\n/** The sizes a row action comes in: IconButton's row steps. `2xs` is DataTable's\n * compact density. At Large every one is at least a 48 px target (IconButton). */\nexport type RowActionsSize = Extract<IconButtonSize, \"md\" | \"sm\" | \"xs\" | \"2xs\">;\n\nexport interface RowActionsProps {\n actions: RowActionList;\n /**\n * 0.49 KIT-12: where `tone: \"danger\"` actions go while the others are inline. `\"menu\"`\n * (default): into the row's \"⋯\" menu, so a delete is never one stray tap in every\n * row — the other actions stay icons beside it. `\"inline\"`: an icon like the rest.\n * `collapse=\"inline\"` keeps every action inline, `collapse=\"menu\"` every one in the menu.\n */\n dangerPlacement?: \"menu\" | \"inline\";\n /** The row's name, for the menu button's name — \"Actions for Ada Example\". Without it\n * (or `null`, as API fields often are) the button is \"Actions\". */\n name?: string | null;\n /** When the actions collapse into the menu — see {@link RowActionsCollapse}. */\n collapse?: RowActionsCollapse;\n /** The inline icons' and the \"⋯\" button's size. Default `xs`, a list row's action. */\n size?: RowActionsSize;\n /** The menu's width. Default `\"15rem\"`, in rem so it grows with the words (§3.2). */\n menuWidth?: `${number}rem`;\n /** On the wrapper: the inline strip, or the box round the \"⋯\" button. */\n className?: string;\n /** The inline glyphs' size, IconButton's `glyphSize` — e.g. 14 in a dense header strip\n * (keksdose's category group header). The \"⋯\" button keeps its box's own. */\n glyphSize?: IconButtonGlyphSize;\n /** The inline tooltips' side, IconButton's `tooltipSide` — `\"start\"` for a table's last\n * column, so a bubble does not run off the row's end. Per row: every inline action. */\n tooltipSide?: TooltipSide;\n labels?: LabelOverride<RowActionsLabels>;\n}\n\nfunction visibleActions(actions: RowActionList): RowAction[] {\n return actions.filter((a): a is RowAction => Boolean(a) && !(a as RowAction).hidden);\n}\n\n/** An action's glyph as a node: a component is rendered, an element is used as is. */\nfunction glyphOf(icon: RowAction[\"icon\"]): ReactNode {\n if (icon === undefined) return null;\n if (isValidElement(icon)) return icon;\n const Icon = icon as LucideIcon;\n return <Icon />;\n}\n\n/** The menu glyph's tint per tone (0.33): the words keep the text colour, so the glyph\n * carries `info` and `warning` — IconButton's toned colours. `danger` colours the words\n * through Button's tone, and `muted` is the default look in a menu. */\nconst MENU_GLYPH_TONE: Partial<Record<RowActionTone, string>> = {\n info: \"text-info\",\n warning: \"text-warning\",\n};\n\n/** The state an action's control says, inline and in the menu alike: `aria-expanded` and\n * `aria-controls` for a disclosure (a toggle's `aria-pressed` goes through the button's\n * own `pressed`, which also draws the \"on\" look). */\nfunction stateAttrs(action: RowAction) {\n return {\n \"aria-expanded\": action.expanded,\n \"aria-controls\": action.expanded === undefined ? undefined : action.controls,\n };\n}\n\n/** An action's tone on its inline control, for a caller's CSS and a test: `data-tone`,\n * left off for the default look. */\nfunction toneAttr(tone: RowActionTone | undefined) {\n return { \"data-tone\": tone === undefined || tone === \"default\" ? undefined : tone };\n}\n\n/**\n * A row's actions, inline or as a \"⋯\" menu — see {@link RowActionsCollapse}. Every\n * control stops its click (and the Enter/Space it stands for) from reaching the row, so\n * a clickable row or a DataTable `rowLink` does not open as well; in a DataTable column\n * also set `noRowLink: true` and `role: \"actions\"`, as {@link rowActionsColumn} does.\n *\n * Inline, the icons sit in a {@link CompactControls} region at Normal: their labels\n * stay the names and the tooltips, and a locked one keeps its reason in the tooltip — a\n * line under one icon of a row would break the row. In the menu a locked action's\n * reason is a line under its words at Large and on touch (Button's\n * `disabledReasonDisplay`).\n *\n * The menu is portalled, so a table's scroller cannot clip it; Escape and an outside\n * press close it, and focus goes back to the \"⋯\" button, which is where a confirmation\n * dialog then returns it.\n */\nexport function RowActions(props: RowActionsProps) {\n const outer = useContext(ConfirmContext);\n // A danger action asks first (0.49 KIT-12). Under the app's ConfirmProvider through\n // it; without one, through a provider of this row's own — only when there is\n // something to ask, so a plain row adds nothing.\n if (outer === null && visibleActions(props.actions).some(asksFirst)) {\n return (\n <ConfirmProvider>\n <RowActionsBody {...props} />\n </ConfirmProvider>\n );\n }\n return <RowActionsBody {...props} />;\n}\n\n/** Whether choosing `action` opens a confirmation first: a danger action unless it says\n * `confirm: false`, any other when it passes one; never a link. */\nfunction asksFirst(action: RowAction): boolean {\n if (action.href !== undefined || action.confirm === false) return false;\n return action.confirm !== undefined || action.tone === \"danger\";\n}\n\n/** `action.onSelect`, after its confirmation when it {@link asksFirst}. */\nfunction useRunAction(name: string | null | undefined, labels: RowActionsLabels) {\n const confirm = useContext(ConfirmContext);\n return (action: RowAction) => {\n if (!asksFirst(action) || confirm === null) {\n action.onSelect?.();\n return;\n }\n const words = typeof action.confirm === \"object\" ? action.confirm : {};\n void confirm({\n title: words.title ?? labels.confirmTitle(action.label, name ?? undefined),\n body: words.body ?? labels.confirmBody,\n confirmLabel: words.confirmLabel ?? action.label,\n tone: \"danger\",\n commit: action.commit,\n }).then((ok) => {\n if (ok) action.onSelect?.();\n });\n };\n}\n\nfunction RowActionsBody({\n actions,\n name,\n collapse = \"auto\",\n dangerPlacement = \"menu\",\n size = \"xs\",\n menuWidth = \"15rem\",\n className,\n glyphSize,\n tooltipSide,\n labels: labelsProp,\n}: RowActionsProps) {\n const labels = useKitLabels(\"rowActions\", DEFAULT_ROW_ACTIONS_LABELS, labelsProp);\n const large = useLargeText();\n const run = useRunAction(name, labels);\n const entries = visibleActions(actions);\n if (entries.length === 0) return null;\n const asMenu = collapse === \"menu\" || (collapse === \"auto\" && large && entries.length > 1);\n // Inline, a danger action still goes into the \"⋯\" menu (KIT-12) unless the app keeps it\n // inline: the other actions stay icons, the menu holds the destructive ones.\n const toMenu = (action: RowAction) =>\n asMenu || (collapse === \"auto\" && dangerPlacement === \"menu\" && action.tone === \"danger\");\n const inline = entries.filter((action) => !toMenu(action));\n const menu = entries.filter(toMenu);\n const title = name ? labels.actionsFor(name) : labels.actions;\n const menuButton = menu.length > 0 && (\n <RowActionsMenu\n entries={menu}\n title={title}\n size={size}\n menuWidth={menuWidth}\n run={run}\n className={inline.length === 0 ? className : undefined}\n />\n );\n if (inline.length === 0) return menuButton || null;\n const strip = (\n <div data-slot=\"row-actions\" className={cn(\"inline-flex items-center gap-0.5\", className)}>\n {inline.map((action) => (\n <InlineAction\n key={action.key ?? action.label}\n action={action}\n size={size}\n glyphSize={glyphSize}\n tooltipSide={tooltipSide}\n run={run}\n />\n ))}\n {menuButton}\n </div>\n );\n // Compact at Normal (no room for a reason line in a row); at Large an inline action\n // is the app's choice (\"inline\") or the row's only one, and shows its words.\n return large ? strip : <CompactControls>{strip}</CompactControls>;\n}\n\n/** The \"⋯\" button and its menu of `entries`. */\nfunction RowActionsMenu({\n entries,\n title,\n size,\n menuWidth,\n run,\n className,\n}: {\n entries: RowAction[];\n title: string;\n size: RowActionsSize;\n menuWidth: `${number}rem`;\n run: (action: RowAction) => void;\n className?: string;\n}) {\n // The menu closed when the action was chosen, so what is in flight shows on \"⋯\".\n const busy = entries.some((action) => action.pending);\n const anchored = entries.filter((action) => action.dataTour);\n return (\n // The \"⋯\" in a box of its own, the inline strip's slot: the tour anchors of the\n // collapsed actions lie over the button in it (see `dataTour`), and `className`\n // places the box as it places the strip.\n <div data-slot=\"row-actions\" className={cn(\"relative inline-flex\", className)}>\n <Popover\n width={menuWidth}\n aria-label={title}\n className=\"p-1\"\n trigger={({ open, toggle, ref }) => (\n <IconButton\n ref={ref}\n size={size}\n label={title}\n // \"⋯\" is read by everyone as \"more actions\"; the menu it opens is the words.\n labelVisible={false}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n pending={busy}\n stopPropagation\n onClick={toggle}\n >\n <MoreHorizontal />\n </IconButton>\n )}\n // React carries an event out of a portal along the COMPONENT tree, so a click in\n // the menu reached a clickable row or a DataTable `onRowClick` the menu was opened\n // from, though the panel is nowhere near it in the page — only the \"⋯\" trigger\n // stopped its own (keksdose's 0.32 report). Stopped on the PANEL itself, its padding\n // and border included (0.32.2: 0.32.1 stopped it on an inner box, and a click on\n // the panel's `p-1` still went through): clicks, and the Enter / Space a row also\n // listens for. Escape goes on, to the popover's own handler that closes it.\n onClick={stopClick}\n onKeyDown={stopActivationKeys}\n >\n {(close) => (\n <ul data-slot=\"row-actions-menu\" className=\"space-y-0.5\">\n {entries.map((action) => (\n <li key={action.key ?? action.label}>\n <MenuAction action={action} close={close} run={run} />\n </li>\n ))}\n </ul>\n )}\n </Popover>\n {anchored.map((action) => (\n // The More cell's TourAnchor: decoration over the button, which keeps every tap.\n <span\n key={action.key ?? action.label}\n aria-hidden\n data-tour={action.dataTour}\n className=\"pointer-events-none absolute inset-0\"\n />\n ))}\n </div>\n );\n}\n\nfunction stopClick(e: MouseEvent) {\n e.stopPropagation();\n}\n\nfunction stopActivationKeys(e: KeyboardEvent) {\n if (e.key === \"Enter\" || e.key === \" \") e.stopPropagation();\n}\n\n/**\n * The `href` an action renders as a link with, or `undefined` for a button: a link\n * cannot carry a reason or a spinner (Button's link form has neither), so a refused\n * action — its own `disabledReason`, or a write lock on a `commit` — and a pending one\n * stay buttons and say so.\n */\nfunction useActionHref(action: RowAction): string | undefined {\n const reason = useCommitReason(action.commit, action.disabledReason);\n const refused = reason !== undefined && reason !== null && reason !== false && reason !== \"\";\n return action.href !== undefined && !action.pending && !refused ? action.href : undefined;\n}\n\n/** One entry of the \"⋯\" menu: the words, as a link for an `href`, else a button. */\nfunction MenuAction({ action, close, run }: { action: RowAction; close: () => void; run: (action: RowAction) => void }) {\n const href = useActionHref(action);\n const glyph = glyphOf(action.icon);\n const glyphTone = action.tone === undefined ? undefined : MENU_GLYPH_TONE[action.tone];\n const words = (\n <>\n {glyph && (\n <span\n aria-hidden\n data-tone={glyphTone ? action.tone : undefined}\n className={cn(\"flex shrink-0 [&_svg]:size-4\", glyphTone)}\n >\n {glyph}\n </span>\n )}\n {action.label}\n </>\n );\n const tone = action.tone === \"danger\" ? \"danger\" : undefined;\n const select = () => {\n close();\n run(action);\n };\n if (href !== undefined) {\n return (\n <Button\n href={href}\n external={action.external}\n variant=\"ghost\"\n size=\"sm\"\n stretch\n tone={tone}\n disabled={action.disabled}\n className=\"w-full justify-start text-start\"\n onClick={select}\n >\n {words}\n </Button>\n );\n }\n return (\n <Button\n {...stateAttrs(action)}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n stretch\n tone={tone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n className=\"w-full justify-start text-start\"\n onClick={select}\n >\n {words}\n </Button>\n );\n}\n\n/** One inline action: an IconButton with its icon, else a small text button — each a\n * link for an `href` (see {@link useActionHref}). The control carries the action's\n * state, its tone and its tour anchor. */\nfunction InlineAction({\n action,\n size,\n glyphSize,\n tooltipSide,\n run,\n}: {\n action: RowAction;\n size: RowActionsSize;\n glyphSize?: IconButtonGlyphSize;\n tooltipSide?: TooltipSide;\n run: (action: RowAction) => void;\n}) {\n const href = useActionHref(action);\n const glyph = glyphOf(action.icon);\n const iconTone = action.tone === \"default\" ? undefined : action.tone;\n // A text button has Button's two tones; `info` and `warning` are a glyph's colours.\n const textTone = action.tone === \"danger\" || action.tone === \"muted\" ? action.tone : undefined;\n const marks = { \"data-tour\": action.dataTour, ...toneAttr(action.tone) };\n if (glyph) {\n if (href !== undefined) {\n return (\n <IconButton\n {...marks}\n href={href}\n external={action.external}\n size={size}\n glyphSize={glyphSize}\n label={action.label}\n tooltipSide={tooltipSide}\n tone={iconTone}\n disabled={action.disabled}\n stopPropagation\n onClick={() => action.onSelect?.()}\n >\n {glyph}\n </IconButton>\n );\n }\n return (\n <IconButton\n {...marks}\n {...stateAttrs(action)}\n size={size}\n glyphSize={glyphSize}\n label={action.label}\n tooltipSide={tooltipSide}\n tone={iconTone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n stopPropagation\n onClick={() => run(action)}\n >\n {glyph}\n </IconButton>\n );\n }\n // A row listening for clicks must not open as well (IconButton's stopPropagation).\n const stop = {\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n run(action);\n },\n onKeyDown: (e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") e.stopPropagation();\n },\n };\n if (href !== undefined) {\n return (\n <Button\n {...marks}\n href={href}\n external={action.external}\n variant=\"ghost\"\n size=\"sm\"\n tone={textTone}\n disabled={action.disabled}\n {...stop}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n {...marks}\n {...stateAttrs(action)}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n tone={textTone}\n pressed={action.pressed}\n disabledReason={action.disabledReason}\n commit={action.commit}\n pending={action.pending}\n disabled={action.disabled}\n {...stop}\n >\n {action.label}\n </Button>\n );\n}\n\n/** The actions column's heading, in the provider's words: on screen since 0.46.1 (keksdose\n * dev#598 rework), a screen reader's name only with `hidden`. */\nfunction RowActionsHeader({\n text,\n labels,\n hidden,\n}: {\n text?: string;\n labels?: LabelOverride<RowActionsLabels>;\n hidden?: boolean;\n}) {\n const resolved = useKitLabels(\"rowActions\", DEFAULT_ROW_ACTIONS_LABELS, labels);\n const word = text ?? resolved.actions;\n return hidden ? <span className=\"sr-only\">{word}</span> : <>{word}</>;\n}\n\n/** The actions cell's classes — `rowActionsColumn`'s `className`: the column hugs its\n * buttons, on one line, at the row's end. A hand-built table's `<TableCell>` takes them\n * around its {@link RowActions} (0.47, keksdose N17). */\nexport const ROW_ACTIONS_CELL_CLASS = \"w-px whitespace-nowrap text-end\";\n/** The actions heading's classes — `rowActionsColumn`'s `headClassName`, which\n * {@link RowActionsHead} draws: hugging, end-aligned over the buttons (0.47, N17). */\nexport const ROW_ACTIONS_HEAD_CLASS = \"w-px text-end\";\n\nexport interface RowActionsHeadProps extends Omit<TableHeaderCellProps, \"children\"> {\n /** The heading. Default: the provider's `rowActions.actions` (\"Actions\"). */\n children?: ReactNode;\n /** Named for a screen reader only — `rowActionsColumn`'s `header: false`. */\n hideLabel?: boolean;\n labels?: LabelOverride<RowActionsLabels>;\n}\n\n/**\n * A hand-built table's actions heading (0.47, keksdose N17): the `<th>` `rowActionsColumn`\n * gives a DataTable — \"Actions\" on screen in the provider's words, end-aligned over the\n * buttons, hugging them. Pair it with `<TableCell className={ROW_ACTIONS_CELL_CLASS}>`\n * around each row's {@link RowActions}. Every other `TableHeaderCell` prop passes on\n * (`size`, the app's `className`, merged last).\n *\n * <RowActionsHead size=\"sm\" className={TH} />\n */\nexport function RowActionsHead({ children, hideLabel, labels, className, ...rest }: RowActionsHeadProps) {\n return (\n <TableHeaderCell align=\"end\" {...rest} className={cn(ROW_ACTIONS_HEAD_CLASS, className)}>\n {typeof children === \"string\" || children == null ? (\n <RowActionsHeader text={children ?? undefined} labels={labels} hidden={hideLabel} />\n ) : (\n children\n )}\n </TableHeaderCell>\n );\n}\n\nexport interface RowActionsColumnOptions<T> {\n /** The column's key. Default `\"actions\"`. */\n key?: string;\n /** The row's actions — `false` / `null` for the ones a condition leaves out. */\n actions: (row: T) => RowActionList;\n /** The row's name, for the \"⋯\" button's name (\"Actions for Ada Example\"). `null` and\n * `undefined` both mean \"Actions\". */\n name?: (row: T) => string | null | undefined;\n collapse?: RowActionsCollapse;\n /** Default `xs`. */\n size?: RowActionsSize;\n /** See {@link RowActionsProps.glyphSize}. */\n glyphSize?: IconButtonGlyphSize;\n /** See {@link RowActionsProps.tooltipSide} — `\"start\"` in a table's last column. */\n tooltipSide?: TooltipSide;\n /**\n * The column's name as plain text — the column-settings checklist and the phone\n * card's label read it (`DataTableColumn.headerText`). Pass the app's own word; left\n * out, the heading shows the provider's `rowActions.actions` and the plain-text\n * places fall back to the column's `key`.\n */\n headerText?: string;\n /**\n * The heading (0.46.1, keksdose dev#598 rework — Marcel: a VISIBLE \"Actions\" heading on\n * every table with an actions column). Default: `headerText`, else the provider's\n * `rowActions.actions` (\"Actions\"), shown, end-aligned over the buttons. A node\n * replaces it; `false` keeps the pre-0.46.1 heading, named for a screen reader only.\n */\n header?: ReactNode | false;\n labels?: LabelOverride<RowActionsLabels>;\n /** Anything else for the column (`mobileHidden`, `cellProps`, another `role`…), merged last. */\n column?: Partial<DataTableColumn<T>>;\n}\n\n/**\n * A DataTable column of {@link RowActions} — an app's action column in one line\n * (§10.8): inline icons at Normal, the \"⋯\" menu at Large with two or more actions.\n *\n * columns={[...columns, rowActionsColumn({ actions: (r) => [...], name: (r) => r.name })]}\n *\n * The heading says \"Actions\" on screen (0.46.1; `header` changes or hides it), the\n * column hugs its content at the row's end, and `noRowLink` keeps a `rowHref` anchor off it, so the\n * buttons keep their own clicks. It is `role: \"actions\"` (0.49 KIT-7): DataTable's default\n * phone card draws it after its three fields, at the card's end, and never counts it as\n * one of them. Set `column: { mobileHidden: true }` to drop it there (with\n * `mobileDetailsInRow` it then shows in the opened row instead).\n */\nexport function rowActionsColumn<T>({\n key = \"actions\",\n actions,\n name,\n collapse,\n size,\n glyphSize,\n tooltipSide,\n headerText,\n header,\n labels,\n column,\n}: RowActionsColumnOptions<T>): DataTableColumn<T> {\n return {\n key,\n header:\n header === false || header == null ? (\n <RowActionsHeader text={headerText} labels={labels} hidden={header === false} />\n ) : (\n header\n ),\n headerText,\n cell: (row) => (\n <RowActions\n actions={actions(row)}\n name={name?.(row)}\n collapse={collapse}\n size={size}\n glyphSize={glyphSize}\n tooltipSide={tooltipSide}\n labels={labels}\n />\n ),\n className: ROW_ACTIONS_CELL_CLASS,\n // The heading over the buttons, at the row's end (0.46.1); a hand-built table's\n // `RowActionsHead` (0.47).\n headClassName: ROW_ACTIONS_HEAD_CLASS,\n noRowLink: true,\n // After the phone card's fields, never one of them (0.49 KIT-7).\n role: \"actions\",\n ...column,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,37 @@
1
+ import type { NavPillItem, NavPillLinkProps } from "./nav-pills.js";
2
+ import type { ReactElement, Ref } from "react";
3
+ export interface SectionNavProps<T extends string> {
4
+ /** The section's pages, each with its `href` (a pill without one is a button that
5
+ * calls `onSelect`). Labels short: the row is one line. */
6
+ items: NavPillItem<T>[];
7
+ /** The page that is open. `null`: none of them. */
8
+ current: T | null;
9
+ /** The section's name — the landmark's accessible name ("Accounting"). */
10
+ label: string;
11
+ /** Called with a pill's value on a click, before the router follows its link. */
12
+ onSelect?: (value: T) => void;
13
+ /** Your router's link. Default: the `<UiKitProvider linkComponent>`, then `<a>`. */
14
+ renderLink?: (props: NavPillLinkProps) => ReactElement;
15
+ /** Default `true`: only below `md`, where the sidebar that lists the same pages is
16
+ * gone. `false` shows it at every width. */
17
+ phoneOnly?: boolean;
18
+ className?: string;
19
+ ref?: Ref<HTMLElement>;
20
+ }
21
+ /**
22
+ * The pages of a section on a phone, as one row of pills under the page title (0.49
23
+ * KIT-6) — kastlan's section pills, which each section page drew by hand.
24
+ *
25
+ * On a desktop the sidebar lists a group's pages; on a phone the AppShell's bar holds
26
+ * the groups, and a group's pages are a sheet away. This row puts them on the page
27
+ * itself: {@link NavPills} with `layout="scroll"` — one line that scrolls sideways, faded
28
+ * where it is cut, the open page scrolled into view (the row only, never the page). At
29
+ * most 48 px high: 44 px pills, the touch target a finger needs, and 2 px above and
30
+ * below. Each pill is a link (`aria-current="page"` on the open one) and its own tab
31
+ * stop; the row is a `<nav>` named by `label`.
32
+ *
33
+ * Place it right under the page's title (`PageHeader`); the AppShell's own row of a
34
+ * group's pages above the bar (`mobileSubNav`) is the shell-wide alternative — use one,
35
+ * not both, for the same pages.
36
+ */
37
+ export declare function SectionNav<T extends string>({ items, current, label, onSelect, renderLink, phoneOnly, className, ref, }: SectionNavProps<T>): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { cn } from "../lib/cn.js";
4
+ import { NavPills } from "./nav-pills.js";
5
+ function SectionNav({
6
+ items,
7
+ current,
8
+ label,
9
+ onSelect,
10
+ renderLink,
11
+ phoneOnly = true,
12
+ className,
13
+ ref
14
+ }) {
15
+ return /* @__PURE__ */ jsx(
16
+ NavPills,
17
+ {
18
+ ref,
19
+ "aria-label": label,
20
+ items,
21
+ current,
22
+ onSelect,
23
+ renderLink,
24
+ layout: "scroll",
25
+ itemClassName: "min-h-11 focus-visible:ring-inset",
26
+ className: cn("py-0.5 large:py-0", phoneOnly && "md:hidden", className)
27
+ }
28
+ );
29
+ }
30
+ export {
31
+ SectionNav
32
+ };
33
+ //# sourceMappingURL=section-nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/section-nav.tsx"],"sourcesContent":["import { cn } from \"../lib/cn\";\nimport { NavPills } from \"./nav-pills\";\nimport type { NavPillItem, NavPillLinkProps } from \"./nav-pills\";\nimport type { ReactElement, Ref } from \"react\";\n\nexport interface SectionNavProps<T extends string> {\n /** The section's pages, each with its `href` (a pill without one is a button that\n * calls `onSelect`). Labels short: the row is one line. */\n items: NavPillItem<T>[];\n /** The page that is open. `null`: none of them. */\n current: T | null;\n /** The section's name — the landmark's accessible name (\"Accounting\"). */\n label: string;\n /** Called with a pill's value on a click, before the router follows its link. */\n onSelect?: (value: T) => void;\n /** Your router's link. Default: the `<UiKitProvider linkComponent>`, then `<a>`. */\n renderLink?: (props: NavPillLinkProps) => ReactElement;\n /** Default `true`: only below `md`, where the sidebar that lists the same pages is\n * gone. `false` shows it at every width. */\n phoneOnly?: boolean;\n className?: string;\n ref?: Ref<HTMLElement>;\n}\n\n/**\n * The pages of a section on a phone, as one row of pills under the page title (0.49\n * KIT-6) — kastlan's section pills, which each section page drew by hand.\n *\n * On a desktop the sidebar lists a group's pages; on a phone the AppShell's bar holds\n * the groups, and a group's pages are a sheet away. This row puts them on the page\n * itself: {@link NavPills} with `layout=\"scroll\"` — one line that scrolls sideways, faded\n * where it is cut, the open page scrolled into view (the row only, never the page). At\n * most 48 px high: 44 px pills, the touch target a finger needs, and 2 px above and\n * below. Each pill is a link (`aria-current=\"page\"` on the open one) and its own tab\n * stop; the row is a `<nav>` named by `label`.\n *\n * Place it right under the page's title (`PageHeader`); the AppShell's own row of a\n * group's pages above the bar (`mobileSubNav`) is the shell-wide alternative — use one,\n * not both, for the same pages.\n */\nexport function SectionNav<T extends string>({\n items,\n current,\n label,\n onSelect,\n renderLink,\n phoneOnly = true,\n className,\n ref,\n}: SectionNavProps<T>) {\n return (\n <NavPills\n ref={ref}\n aria-label={label}\n items={items}\n current={current}\n onSelect={onSelect}\n renderLink={renderLink}\n layout=\"scroll\"\n // The ring inside the pill: the row scrolls, so it clips what leaves its box.\n itemClassName=\"min-h-11 focus-visible:ring-inset\"\n className={cn(\"py-0.5 large:py-0\", phoneOnly && \"md:hidden\", className)}\n />\n );\n}\n"],"mappings":";AAmDI;AAnDJ,SAAS,UAAU;AACnB,SAAS,gBAAgB;AAuClB,SAAS,WAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAuB;AACrB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAO;AAAA,MAEP,eAAc;AAAA,MACd,WAAW,GAAG,qBAAqB,aAAa,aAAa,SAAS;AAAA;AAAA,EACxE;AAEJ;","names":[]}
@@ -63,7 +63,9 @@ import {
63
63
  import { useBreakpoint } from "../hooks/use-breakpoint.js";
64
64
  import { useTextSize } from "../theme/text-size.js";
65
65
  import { chartHeightProps } from "./chart-height.js";
66
+ import { useChartEdge } from "./chart-edge.js";
66
67
  import { cn } from "../lib/cn.js";
68
+ import { formatWithMinus } from "../lib/minus.js";
67
69
  function resolveTooltipPlacement(own, fromProvider, phone) {
68
70
  const wanted = own ?? fromProvider ?? "cursor";
69
71
  return wanted === "auto" ? phone ? "above" : "cursor" : wanted;
@@ -113,7 +115,7 @@ function useDefaultFormat(localeProp) {
113
115
  const locale = useKitLocale(localeProp);
114
116
  return useMemo(() => {
115
117
  const format = new Intl.NumberFormat(locale, { maximumFractionDigits: 2 });
116
- return (value) => format.format(value);
118
+ return (value) => formatWithMinus(format, value);
117
119
  }, [locale]);
118
120
  }
119
121
  const ONE_UNTITLED_AXIS = [{ id: DEFAULT_Y_AXIS, title: "" }];
@@ -256,6 +258,7 @@ function SeriesPlot({
256
258
  const locale = useKitLocale(localeProp);
257
259
  const number = useDefaultFormat(localeProp);
258
260
  const { scale } = useTextSize();
261
+ const edgeFor = useChartEdge();
259
262
  const ticksAim = tickTarget(scale);
260
263
  const grow = (px) => scale === 1 ? px : Math.round(px * scale);
261
264
  const [keyStop, setKeyStop] = useState(0);
@@ -624,6 +627,7 @@ function SeriesPlot({
624
627
  const type = entry.type ?? "line";
625
628
  const yAxisId = entry.axis ?? DEFAULT_Y_AXIS;
626
629
  const color = `var(--color-${entry.key})`;
630
+ const edge = edgeFor(config[entry.key]?.color);
627
631
  const own = entry.dash ?? (entry.dashed ? 1 : 0);
628
632
  const dash = entry.step ? strokeDash(STEP_DASH) : typeof own === "string" ? own : strokeDash(own);
629
633
  const curve = entry.step ? "stepAfter" : entry.curve ?? "monotone";
@@ -638,6 +642,7 @@ function SeriesPlot({
638
642
  stackId: entry.stack,
639
643
  fill: color,
640
644
  fillOpacity: entry.fillOpacity,
645
+ ...edge ? { stroke: edge, strokeWidth: 1 } : {},
641
646
  radius: entry.stack === void 0 ? BAR_RADIUS : 0,
642
647
  ...stub ? { minPointSize: (_value, index) => finite(plotted[index]?.[entry.key]) === void 0 ? 0 : stub } : {},
643
648
  ...focusedStop?.key === entry.key ? { shape: focusedBarShape(focusedStop.index) } : {},
@@ -659,8 +664,8 @@ function SeriesPlot({
659
664
  type: curve,
660
665
  dataKey: entry.key,
661
666
  stackId: entry.stack,
662
- stroke: color,
663
- strokeWidth: width2,
667
+ stroke: edge ?? color,
668
+ strokeWidth: edge ? 1 : width2,
664
669
  strokeDasharray: dash,
665
670
  fill: color,
666
671
  fillOpacity: entry.fillOpacity ?? (entry.stack !== void 0 ? 0.55 : 0.2),