@eifi1/ui-kit 0.32.2 → 0.33.1

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 (400) hide show
  1. package/README.md +17 -9
  2. package/dist/account/delete-account-setting.d.ts +11 -10
  3. package/dist/account/delete-account-setting.js +2 -1
  4. package/dist/account/delete-account-setting.js.map +1 -1
  5. package/dist/account/email-change-setting.d.ts +5 -0
  6. package/dist/account/email-change-setting.js +4 -3
  7. package/dist/account/email-change-setting.js.map +1 -1
  8. package/dist/account/sessions-setting.d.ts +4 -0
  9. package/dist/account/sessions-setting.js +3 -2
  10. package/dist/account/sessions-setting.js.map +1 -1
  11. package/dist/admin/admin-action-confirm.d.ts +11 -10
  12. package/dist/admin/admin-action-confirm.js.map +1 -1
  13. package/dist/admin/invitations-panel.d.ts +11 -10
  14. package/dist/admin/invitations-panel.js +4 -3
  15. package/dist/admin/invitations-panel.js.map +1 -1
  16. package/dist/admin/plan-change.d.ts +101 -0
  17. package/dist/admin/plan-change.js +210 -0
  18. package/dist/admin/plan-change.js.map +1 -0
  19. package/dist/admin/reviewer-scope-editor.d.ts +11 -10
  20. package/dist/admin/reviewer-scope-editor.js.map +1 -1
  21. package/dist/admin/role-select.d.ts +8 -7
  22. package/dist/admin/role-select.js.map +1 -1
  23. package/dist/admin/transfer-ownership-dialog.d.ts +11 -10
  24. package/dist/admin/user-roster.d.ts +8 -7
  25. package/dist/admin/user-roster.js +2 -2
  26. package/dist/admin/user-roster.js.map +1 -1
  27. package/dist/auth/auth-errors.d.ts +13 -2
  28. package/dist/auth/auth-errors.js +5 -1
  29. package/dist/auth/auth-errors.js.map +1 -1
  30. package/dist/auth/complete-name-dialog.d.ts +4 -1
  31. package/dist/auth/complete-name-dialog.js +2 -1
  32. package/dist/auth/complete-name-dialog.js.map +1 -1
  33. package/dist/auth/register-form.d.ts +11 -10
  34. package/dist/billing/billing-banner.d.ts +14 -11
  35. package/dist/billing/billing-banner.js.map +1 -1
  36. package/dist/billing/billing-labels.d.ts +11 -10
  37. package/dist/billing/billing-labels.js +10 -1
  38. package/dist/billing/billing-labels.js.map +1 -1
  39. package/dist/billing/billing-lock.d.ts +40 -12
  40. package/dist/billing/billing-lock.js +9 -3
  41. package/dist/billing/billing-lock.js.map +1 -1
  42. package/dist/billing/billing-overview.d.ts +155 -0
  43. package/dist/billing/billing-overview.js +2 -0
  44. package/dist/billing/billing-overview.js.map +1 -0
  45. package/dist/billing/billing-standing.d.ts +11 -10
  46. package/dist/billing/checkout-processing.d.ts +218 -0
  47. package/dist/billing/checkout-processing.js +198 -0
  48. package/dist/billing/checkout-processing.js.map +1 -0
  49. package/dist/billing/checkout-return.d.ts +51 -0
  50. package/dist/billing/checkout-return.js +43 -0
  51. package/dist/billing/checkout-return.js.map +1 -0
  52. package/dist/billing/pay-page.d.ts +215 -0
  53. package/dist/billing/pay-page.js +113 -0
  54. package/dist/billing/pay-page.js.map +1 -0
  55. package/dist/billing/plan-card.d.ts +11 -10
  56. package/dist/billing/plan-limit-notice.d.ts +11 -10
  57. package/dist/billing/plan-limit-toast.d.ts +144 -0
  58. package/dist/billing/plan-limit-toast.js +40 -0
  59. package/dist/billing/plan-limit-toast.js.map +1 -0
  60. package/dist/billing/subscription-actions.d.ts +39 -15
  61. package/dist/billing/subscription-actions.js +9 -2
  62. package/dist/billing/subscription-actions.js.map +1 -1
  63. package/dist/billing/subscription-status-chip.d.ts +10 -9
  64. package/dist/bin/eifi1-pay-page.mjs +166 -0
  65. package/dist/chart.d.ts +11 -10
  66. package/dist/components/account-chips.d.ts +6 -1
  67. package/dist/components/account-settings.d.ts +4 -1
  68. package/dist/components/account-settings.js +2 -1
  69. package/dist/components/account-settings.js.map +1 -1
  70. package/dist/components/amount-input.d.ts +11 -10
  71. package/dist/components/autocomplete.d.ts +2 -1
  72. package/dist/components/autocomplete.js +5 -3
  73. package/dist/components/autocomplete.js.map +1 -1
  74. package/dist/components/button-group.d.ts +11 -10
  75. package/dist/components/button-group.js +1 -1
  76. package/dist/components/button-group.js.map +1 -1
  77. package/dist/components/calculator.d.ts +11 -10
  78. package/dist/components/calculator.js +3 -3
  79. package/dist/components/calculator.js.map +1 -1
  80. package/dist/components/checkbox-group.d.ts +2 -1
  81. package/dist/components/checkbox-group.js.map +1 -1
  82. package/dist/components/checkbox.d.ts +2 -1
  83. package/dist/components/checkbox.js.map +1 -1
  84. package/dist/components/chip.d.ts +2 -1
  85. package/dist/components/chip.js +19 -17
  86. package/dist/components/chip.js.map +1 -1
  87. package/dist/components/choice-card.d.ts +3 -1
  88. package/dist/components/choice-card.js +1 -1
  89. package/dist/components/choice-card.js.map +1 -1
  90. package/dist/components/combobox-core.js +2 -1
  91. package/dist/components/combobox-core.js.map +1 -1
  92. package/dist/components/combobox.d.ts +4 -3
  93. package/dist/components/combobox.js +2 -2
  94. package/dist/components/combobox.js.map +1 -1
  95. package/dist/components/confirm-dialog.d.ts +11 -10
  96. package/dist/components/confirm-dialog.js +6 -3
  97. package/dist/components/confirm-dialog.js.map +1 -1
  98. package/dist/components/copy-button.d.ts +12 -11
  99. package/dist/components/country-select.d.ts +2 -1
  100. package/dist/components/country-select.js.map +1 -1
  101. package/dist/components/danger-confirm.d.ts +11 -10
  102. package/dist/components/danger-confirm.js.map +1 -1
  103. package/dist/components/data-table-cells.d.ts +12 -1
  104. package/dist/components/data-table-filter-popover.d.ts +12 -1
  105. package/dist/components/data-table-filters.d.ts +12 -1
  106. package/dist/components/data-table-labels.d.ts +12 -1
  107. package/dist/components/data-table-pagination.d.ts +12 -1
  108. package/dist/components/data-table.d.ts +12 -1
  109. package/dist/components/data-table.js +3 -3
  110. package/dist/components/data-table.js.map +1 -1
  111. package/dist/components/date-picker.js +6 -4
  112. package/dist/components/date-picker.js.map +1 -1
  113. package/dist/components/dropdown.js +9 -1
  114. package/dist/components/dropdown.js.map +1 -1
  115. package/dist/components/entity-combobox.d.ts +3 -2
  116. package/dist/components/entity-combobox.js.map +1 -1
  117. package/dist/components/facing-pair.d.ts +11 -10
  118. package/dist/components/field-parts.d.ts +32 -4
  119. package/dist/components/field-parts.js +54 -19
  120. package/dist/components/field-parts.js.map +1 -1
  121. package/dist/components/file-button.d.ts +11 -10
  122. package/dist/components/file-button.js +3 -2
  123. package/dist/components/file-button.js.map +1 -1
  124. package/dist/components/file-dropzone.d.ts +12 -11
  125. package/dist/components/file-dropzone.js.map +1 -1
  126. package/dist/components/floating-panel.js +5 -5
  127. package/dist/components/floating-panel.js.map +1 -1
  128. package/dist/components/form-actions.d.ts +10 -9
  129. package/dist/components/form-actions.js +3 -3
  130. package/dist/components/form-actions.js.map +1 -1
  131. package/dist/components/full-bleed-dialog.js.map +1 -1
  132. package/dist/components/iban-input.d.ts +11 -10
  133. package/dist/components/inline-edit-field.d.ts +2 -1
  134. package/dist/components/inline-edit-field.js +6 -6
  135. package/dist/components/inline-edit-field.js.map +1 -1
  136. package/dist/components/language-select.d.ts +11 -10
  137. package/dist/components/legal-page.d.ts +11 -10
  138. package/dist/components/legal.d.ts +11 -10
  139. package/dist/components/list.d.ts +2 -1
  140. package/dist/components/list.js +4 -5
  141. package/dist/components/list.js.map +1 -1
  142. package/dist/components/modal.js.map +1 -1
  143. package/dist/components/money-field.d.ts +11 -10
  144. package/dist/components/month-picker.js +54 -44
  145. package/dist/components/month-picker.js.map +1 -1
  146. package/dist/components/multi-entity-combobox.d.ts +3 -2
  147. package/dist/components/multi-entity-combobox.js.map +1 -1
  148. package/dist/components/number-field.d.ts +18 -10
  149. package/dist/components/number-field.js.map +1 -1
  150. package/dist/components/number-input.d.ts +37 -10
  151. package/dist/components/number-input.js +27 -12
  152. package/dist/components/number-input.js.map +1 -1
  153. package/dist/components/numpad-sheet.d.ts +11 -10
  154. package/dist/components/numpad-sheet.js +2 -2
  155. package/dist/components/numpad-sheet.js.map +1 -1
  156. package/dist/components/phone-input.d.ts +11 -10
  157. package/dist/components/popover.js +6 -2
  158. package/dist/components/popover.js.map +1 -1
  159. package/dist/components/row-actions.d.ts +6 -5
  160. package/dist/components/row-actions.js +130 -32
  161. package/dist/components/row-actions.js.map +1 -1
  162. package/dist/components/series-chart.d.ts +11 -10
  163. package/dist/components/settings-fields.d.ts +11 -10
  164. package/dist/components/share-card.d.ts +8 -7
  165. package/dist/components/share-card.js +26 -10
  166. package/dist/components/share-card.js.map +1 -1
  167. package/dist/components/skeleton.d.ts +10 -2
  168. package/dist/components/skeleton.js.map +1 -1
  169. package/dist/components/status-dot.d.ts +1 -0
  170. package/dist/components/status-dot.js +2 -2
  171. package/dist/components/status-dot.js.map +1 -1
  172. package/dist/components/swatch-picker.d.ts +2 -1
  173. package/dist/components/swatch-picker.js.map +1 -1
  174. package/dist/components/swipeable-row.d.ts +88 -17
  175. package/dist/components/swipeable-row.js +46 -9
  176. package/dist/components/swipeable-row.js.map +1 -1
  177. package/dist/components/switch.d.ts +2 -1
  178. package/dist/components/switch.js.map +1 -1
  179. package/dist/components/table.d.ts +2 -1
  180. package/dist/components/table.js +6 -0
  181. package/dist/components/table.js.map +1 -1
  182. package/dist/components/text-link.d.ts +10 -9
  183. package/dist/components/time-input.d.ts +11 -10
  184. package/dist/components/toggle-group.d.ts +2 -1
  185. package/dist/components/toggle-group.js +1 -1
  186. package/dist/components/toggle-group.js.map +1 -1
  187. package/dist/components/translation-review-editor.d.ts +4 -0
  188. package/dist/components/translation-review-editor.js +4 -3
  189. package/dist/components/translation-review-editor.js.map +1 -1
  190. package/dist/components/translation-review-parts.d.ts +1 -0
  191. package/dist/components/translation-review.d.ts +1 -0
  192. package/dist/components/translation-review.js +15 -18
  193. package/dist/components/translation-review.js.map +1 -1
  194. package/dist/components/ui.d.ts +10 -9
  195. package/dist/components/ui.js +47 -27
  196. package/dist/components/ui.js.map +1 -1
  197. package/dist/components/use-table-state.d.ts +12 -1
  198. package/dist/components/user-avatar.d.ts +1 -0
  199. package/dist/components/write-lock.d.ts +101 -13
  200. package/dist/components/write-lock.js +37 -13
  201. package/dist/components/write-lock.js.map +1 -1
  202. package/dist/{data-table-labels-kpAAVi6g.d.ts → data-table-labels-BJ5JE_X2.d.ts} +17 -0
  203. package/dist/data-table.d.ts +12 -1
  204. package/dist/{delete-account-setting-Dy4kJgUm.d.ts → delete-account-setting-DAgGBrbn.d.ts} +393 -42
  205. package/dist/feedback/feedback-attachment.d.ts +11 -10
  206. package/dist/feedback/feedback-attachment.js +7 -7
  207. package/dist/feedback/feedback-attachment.js.map +1 -1
  208. package/dist/feedback/feedback-context.d.ts +11 -10
  209. package/dist/feedback/feedback-crash.d.ts +11 -10
  210. package/dist/feedback/feedback-dialog.d.ts +11 -10
  211. package/dist/feedback/feedback-inbox.d.ts +10 -9
  212. package/dist/feedback/feedback-inbox.js +7 -3
  213. package/dist/feedback/feedback-inbox.js.map +1 -1
  214. package/dist/feedback/feedback-labels.d.ts +11 -10
  215. package/dist/feedback/feedback-menu.d.ts +4 -3
  216. package/dist/feedback/feedback-record.d.ts +11 -10
  217. package/dist/feedback/feedback-row-detail.d.ts +11 -10
  218. package/dist/feedback/feedback-row-detail.js.map +1 -1
  219. package/dist/feedback/feedback-status-undo.d.ts +11 -10
  220. package/dist/feedback/feedback-submit.d.ts +11 -10
  221. package/dist/feedback/feedback-swipe.d.ts +11 -10
  222. package/dist/feedback/feedback-swipe.js +3 -6
  223. package/dist/feedback/feedback-swipe.js.map +1 -1
  224. package/dist/feedback/feedback-table.d.ts +11 -10
  225. package/dist/feedback/feedback-table.js.map +1 -1
  226. package/dist/feedback/feedback-thread.d.ts +11 -10
  227. package/dist/feedback/feedback-thread.js +5 -1
  228. package/dist/feedback/feedback-thread.js.map +1 -1
  229. package/dist/feedback.d.ts +11 -10
  230. package/dist/hooks/use-anchored-panel.d.ts +37 -7
  231. package/dist/hooks/use-anchored-panel.js +6 -7
  232. package/dist/hooks/use-anchored-panel.js.map +1 -1
  233. package/dist/hooks/use-file-drop.d.ts +11 -10
  234. package/dist/hooks/use-focus-trap.d.ts +8 -0
  235. package/dist/hooks/use-focus-trap.js +17 -11
  236. package/dist/hooks/use-focus-trap.js.map +1 -1
  237. package/dist/i18n/defaults.d.ts +11 -10
  238. package/dist/i18n/defaults.js +2 -0
  239. package/dist/i18n/defaults.js.map +1 -1
  240. package/dist/i18n/german.d.ts +11 -10
  241. package/dist/i18n/german.js +37 -1
  242. package/dist/i18n/german.js.map +1 -1
  243. package/dist/i18n/kit-labels.d.ts +5 -4
  244. package/dist/i18n/kit-labels.js.map +1 -1
  245. package/dist/i18n/languages.d.ts +11 -10
  246. package/dist/i18n/locales/de-CH.d.ts +11 -10
  247. package/dist/i18n/locales/en.d.ts +11 -10
  248. package/dist/i18n/locales/es.d.ts +11 -10
  249. package/dist/i18n/locales/es.js +35 -1
  250. package/dist/i18n/locales/es.js.map +1 -1
  251. package/dist/i18n/locales/fr.d.ts +11 -10
  252. package/dist/i18n/locales/fr.js +37 -1
  253. package/dist/i18n/locales/fr.js.map +1 -1
  254. package/dist/i18n/locales/hu.d.ts +11 -10
  255. package/dist/i18n/locales/hu.js +38 -1
  256. package/dist/i18n/locales/hu.js.map +1 -1
  257. package/dist/i18n/locales/it.d.ts +11 -10
  258. package/dist/i18n/locales/it.js +35 -1
  259. package/dist/i18n/locales/it.js.map +1 -1
  260. package/dist/i18n/locales/zh.d.ts +11 -10
  261. package/dist/i18n/locales/zh.js +35 -1
  262. package/dist/i18n/locales/zh.js.map +1 -1
  263. package/dist/i18n/plurals.d.ts +20 -1
  264. package/dist/i18n/plurals.js +11 -0
  265. package/dist/i18n/plurals.js.map +1 -1
  266. package/dist/i18n/review.d.ts +11 -10
  267. package/dist/i18n/review.js +8 -1
  268. package/dist/i18n/review.js.map +1 -1
  269. package/dist/index.d.ts +11 -6
  270. package/dist/index.js +40 -2
  271. package/dist/index.js.map +1 -1
  272. package/dist/landing/landing-actions.d.ts +11 -10
  273. package/dist/landing/landing-sections.d.ts +11 -10
  274. package/dist/landing/public-header.d.ts +11 -10
  275. package/dist/landing/routing.d.ts +11 -10
  276. package/dist/pay/.well-known/apple-developer-merchantid-domain-association +1 -0
  277. package/dist/pay/index.html +30 -0
  278. package/dist/pay/pay.css +126 -0
  279. package/dist/pay/pay.js +198 -0
  280. package/dist/rhf/fields.d.ts +11 -10
  281. package/dist/rhf/form.d.ts +11 -10
  282. package/dist/rhf.d.ts +11 -10
  283. package/dist/settings/settings-section.d.ts +11 -10
  284. package/dist/shell/app-shell.d.ts +10 -9
  285. package/dist/shell/top-bar-brand.d.ts +11 -10
  286. package/dist/shell/topbar-action-menu.d.ts +1 -0
  287. package/dist/shell.d.ts +11 -10
  288. package/dist/testing/css-variables.d.ts +74 -0
  289. package/dist/testing/css-variables.generated.d.ts +7 -0
  290. package/dist/testing/css-variables.generated.js +8 -0
  291. package/dist/testing/css-variables.generated.js.map +1 -0
  292. package/dist/testing/css-variables.js +93 -0
  293. package/dist/testing/css-variables.js.map +1 -0
  294. package/dist/testing/index.d.ts +1 -0
  295. package/dist/testing/index.js +12 -0
  296. package/dist/testing/index.js.map +1 -0
  297. package/dist/theme/contrast-tokens.d.ts +44 -1
  298. package/dist/theme/contrast-tokens.js +21 -0
  299. package/dist/theme/contrast-tokens.js.map +1 -1
  300. package/dist/theme/contrast.d.ts +2 -2
  301. package/dist/theme/palette-derive.js +60 -11
  302. package/dist/theme/palette-derive.js.map +1 -1
  303. package/dist/theme/palette-presets.js +45 -43
  304. package/dist/theme/palette-presets.js.map +1 -1
  305. package/dist/wizard/stepper-nav.d.ts +12 -11
  306. package/dist/wizard/stepper-nav.js.map +1 -1
  307. package/dist/wizard.d.ts +11 -10
  308. package/package.json +10 -2
  309. package/src/account/delete-account-setting.tsx +6 -1
  310. package/src/account/email-change-setting.tsx +9 -3
  311. package/src/account/sessions-setting.tsx +7 -2
  312. package/src/admin/admin-action-confirm.tsx +2 -1
  313. package/src/admin/invitations-panel.tsx +9 -3
  314. package/src/admin/plan-change.tsx +487 -0
  315. package/src/admin/reviewer-scope-editor.tsx +2 -1
  316. package/src/admin/role-select.tsx +2 -1
  317. package/src/admin/user-roster.tsx +5 -3
  318. package/src/auth/auth-errors.ts +25 -2
  319. package/src/auth/complete-name-dialog.tsx +6 -2
  320. package/src/billing/billing-banner.tsx +3 -1
  321. package/src/billing/billing-labels.ts +45 -0
  322. package/src/billing/billing-lock.ts +36 -3
  323. package/src/billing/billing-overview.ts +58 -0
  324. package/src/billing/checkout-processing.ts +391 -0
  325. package/src/billing/checkout-return.ts +90 -0
  326. package/src/billing/pay-page.ts +246 -0
  327. package/src/billing/plan-limit-toast.tsx +91 -0
  328. package/src/billing/subscription-actions.tsx +41 -8
  329. package/src/components/account-settings.tsx +6 -2
  330. package/src/components/autocomplete.tsx +10 -4
  331. package/src/components/button-group.tsx +4 -1
  332. package/src/components/calculator.tsx +10 -6
  333. package/src/components/checkbox-group.tsx +2 -2
  334. package/src/components/checkbox.tsx +2 -1
  335. package/src/components/chip.tsx +37 -32
  336. package/src/components/choice-card.tsx +4 -2
  337. package/src/components/combobox-core.tsx +5 -2
  338. package/src/components/combobox.tsx +8 -6
  339. package/src/components/confirm-dialog.tsx +13 -6
  340. package/src/components/country-select.tsx +2 -2
  341. package/src/components/danger-confirm.tsx +2 -2
  342. package/src/components/data-table.tsx +20 -9
  343. package/src/components/date-picker.tsx +10 -7
  344. package/src/components/dropdown.tsx +12 -2
  345. package/src/components/entity-combobox.tsx +3 -3
  346. package/src/components/field-parts.tsx +61 -14
  347. package/src/components/file-button.tsx +5 -4
  348. package/src/components/file-dropzone.tsx +2 -2
  349. package/src/components/floating-panel.tsx +7 -5
  350. package/src/components/form-actions.tsx +12 -5
  351. package/src/components/full-bleed-dialog.tsx +6 -0
  352. package/src/components/inline-edit-field.tsx +13 -13
  353. package/src/components/list.tsx +10 -9
  354. package/src/components/modal.tsx +5 -0
  355. package/src/components/month-picker.tsx +8 -2
  356. package/src/components/multi-entity-combobox.tsx +3 -3
  357. package/src/components/number-field.tsx +7 -0
  358. package/src/components/number-input.tsx +71 -11
  359. package/src/components/numpad-sheet.tsx +8 -4
  360. package/src/components/popover.tsx +6 -2
  361. package/src/components/row-actions.tsx +208 -58
  362. package/src/components/share-card.tsx +67 -13
  363. package/src/components/skeleton.tsx +10 -2
  364. package/src/components/status-dot.tsx +7 -7
  365. package/src/components/swatch-picker.tsx +2 -2
  366. package/src/components/swipeable-row.tsx +138 -27
  367. package/src/components/switch.tsx +2 -1
  368. package/src/components/table.tsx +8 -1
  369. package/src/components/toggle-group.tsx +5 -3
  370. package/src/components/translation-review-editor.tsx +8 -3
  371. package/src/components/translation-review.tsx +18 -22
  372. package/src/components/ui.tsx +92 -43
  373. package/src/components/write-lock.tsx +150 -24
  374. package/src/feedback/feedback-attachment.tsx +13 -9
  375. package/src/feedback/feedback-inbox.tsx +10 -6
  376. package/src/feedback/feedback-row-detail.tsx +3 -2
  377. package/src/feedback/feedback-swipe.tsx +6 -11
  378. package/src/feedback/feedback-table.tsx +2 -1
  379. package/src/feedback/feedback-thread.tsx +20 -1
  380. package/src/hooks/use-anchored-panel.ts +60 -14
  381. package/src/hooks/use-focus-trap.ts +55 -12
  382. package/src/i18n/defaults.ts +2 -0
  383. package/src/i18n/german.ts +41 -0
  384. package/src/i18n/kit-labels.tsx +4 -0
  385. package/src/i18n/locales/es.ts +35 -0
  386. package/src/i18n/locales/fr.ts +41 -0
  387. package/src/i18n/locales/hu.ts +39 -0
  388. package/src/i18n/locales/it.ts +36 -0
  389. package/src/i18n/locales/zh.ts +34 -0
  390. package/src/i18n/plurals.ts +29 -0
  391. package/src/i18n/review.ts +7 -0
  392. package/src/index.ts +51 -5
  393. package/src/testing/css-variables.generated.ts +112 -0
  394. package/src/testing/css-variables.ts +191 -0
  395. package/src/testing/index.ts +13 -0
  396. package/src/theme/contrast-tokens.ts +53 -2
  397. package/src/theme/palette-derive.ts +87 -10
  398. package/src/theme/palette-presets.ts +45 -43
  399. package/src/wizard/stepper-nav.tsx +2 -1
  400. package/tokens.css +324 -46
@@ -37,6 +37,7 @@ import {
37
37
  import type { FeedbackContext, FeedbackRecord } from "./feedback-record";
38
38
  import { useFeedbackStatusUndo } from "./feedback-status-undo";
39
39
  import type { FeedbackStatusChange } from "./feedback-status-undo";
40
+ import type { CommitScope } from "../components/write-lock";
40
41
 
41
42
  /**
42
43
  * One report, opened — the panel a feedback table unfolds under its row (§4.4 of
@@ -410,7 +411,7 @@ export interface FeedbackReworkSectionProps {
410
411
  * `feedbackToast` refusals, the `feedbackAttachment` words. */
411
412
  attachment?: Partial<FeedbackNoteAttachment>;
412
413
  /** The send COMMITS — see `FeedbackNoteEditor`'s `commit`. */
413
- commit?: boolean;
414
+ commit?: CommitScope;
414
415
  labels?: Partial<FeedbackDetailLabels>;
415
416
  }
416
417
 
@@ -569,7 +570,7 @@ export interface FeedbackRowDetailProps {
569
570
  * reaches `onUpdate`. Off by default — keksdose's shell lock is a read-only demo
570
571
  * BUDGET, and a person viewing one still owns their own reports.
571
572
  */
572
- commit?: boolean;
573
+ commit?: CommitScope;
573
574
  /**
574
575
  * Open with the subject: the category badge and the title (feedback contract §8.1,
575
576
  * keksdose live #395). On by default; `false` for a page that already shows the title
@@ -79,11 +79,9 @@ function targetOf(id: string, status: FeedbackStatus): FeedbackStatus | null {
79
79
  }
80
80
  }
81
81
 
82
- /** One action's panel. Each fill carries its text colour (0.26): the fills turn pastel
83
- * in dark mode, where SwipeableRow's default white text was unreadable. `--info` has no
84
- * contrast token, so `advance` writes in the surface colour — white on sky in light
85
- * mode, the dark page on the pale sky in dark (the translation list's reset does the
86
- * same on its grey). */
82
+ /** One action's panel, by tone (0.33): SwipeableRow paints the tone's soft wash idle,
83
+ * and its fill under that fill's own foreground once armed — the same pairs in light
84
+ * and dark, where the fills turn pastel. */
87
85
  function panel(
88
86
  id: FeedbackSwipeAction,
89
87
  target: FeedbackStatus,
@@ -97,24 +95,21 @@ function panel(
97
95
  onCommit,
98
96
  label: labels[target],
99
97
  icon: <ChevronsRight className="size-4" aria-hidden />,
100
- className: "bg-[var(--info)] text-[var(--info-contrast)]",
101
- armedClassName: "bg-[var(--info)] text-[var(--info-contrast)]",
98
+ tone: "info",
102
99
  };
103
100
  case "done":
104
101
  return {
105
102
  onCommit,
106
103
  label: labels.DONE,
107
104
  icon: <Check className="size-4" aria-hidden />,
108
- className: "bg-[var(--success)] text-[var(--success-contrast)]",
109
- armedClassName: "bg-[var(--success)] text-[var(--success-contrast)]",
105
+ tone: "success",
110
106
  };
111
107
  case "wont_do":
112
108
  return {
113
109
  onCommit,
114
110
  label: labels.WONT_DO,
115
111
  icon: <Ban className="size-4" aria-hidden />,
116
- className: "bg-[var(--danger)] text-[var(--danger-contrast)]",
117
- armedClassName: "bg-[var(--danger-hover)] text-[var(--danger-contrast)]",
112
+ tone: "danger",
118
113
  };
119
114
  }
120
115
  }
@@ -31,6 +31,7 @@ import {
31
31
  import { FEEDBACK_AWAITING_STATUSES, reworkCount, type FeedbackContext, type FeedbackRecord } from "./feedback-record";
32
32
  import type { FeedbackStatusChange } from "./feedback-status-undo";
33
33
  import { feedbackPageHref, feedbackPagePath } from "./feedback-row-detail";
34
+ import type { CommitScope } from "../components/write-lock";
34
35
 
35
36
  /**
36
37
  * The two feedback pages' TABLE — `/feedback` (the admin inbox) and `/my-feedback` (the
@@ -399,7 +400,7 @@ export interface FeedbackColumnsOptions {
399
400
  /** The status buttons SAVE when pressed: pass `true` to put them under the page's
400
401
  * `WriteLockProvider` (FeedbackStatusTransitions' `commit`). Off by default — a
401
402
  * feedback inbox is rarely what an app's lock is about. */
402
- commit?: boolean;
403
+ commit?: CommitScope;
403
404
  labels: FeedbackPageLabels;
404
405
  statusLabels: FeedbackStatusLabels;
405
406
  categoryLabels: FeedbackCategoryLabels;
@@ -7,6 +7,8 @@ import { Button, EmptyState, Spinner, Textarea } from "../components/ui";
7
7
  import type { CharacterCountLabels } from "../components/ui";
8
8
  import { Skeleton } from "../components/skeleton";
9
9
  import { Tooltip } from "../components/tooltip";
10
+ import { useWriteLock } from "../components/write-lock";
11
+ import type { CommitScope } from "../components/write-lock";
10
12
  import { useKitFileLabels, useKitLabels, useKitLocale } from "../i18n/kit-labels";
11
13
  import { FeedbackAttachmentField } from "./feedback-attachment";
12
14
  import type { FeedbackAttachmentFieldSingleProps } from "./feedback-attachment";
@@ -565,6 +567,18 @@ export interface FeedbackComposerProps {
565
567
  /** Rendered INSTEAD of the composer — the thread is closed, or read-only for this
566
568
  * reader. */
567
569
  disabledReason?: ReactNode;
570
+ /**
571
+ * Send COMMITS (0.33.1, kastlan's assistant): under a locked {@link WriteLockProvider}
572
+ * Send is disabled the `disabledReason` way — focusable, the lock's reason in its
573
+ * tooltip and description, as on {@link Button}'s `commit` — and neither Enter nor
574
+ * Ctrl/⌘+Enter sends. The box stays editable and the draft stays: nothing in it
575
+ * reaches the server until Send, and the lock may lift. No provider, or an unlocked
576
+ * one: no effect. A {@link CommitScope}: kastlan's assistant answers under the demo
577
+ * and not under a lapsed plan (its backend's 402), so it passes
578
+ * `commit={{ except: ["demo"] }}` where it read `useWriteLock` and fed `canSend` and a
579
+ * reason by hand. Not the prop above: that one replaces the box, this one keeps it.
580
+ */
581
+ commit?: CommitScope;
568
582
  rows?: number;
569
583
  /**
570
584
  * The longest draft the box takes, in UTF-16 code units as the browser counts — the
@@ -671,6 +685,7 @@ export function FeedbackComposer({
671
685
  canSend: canSendProp,
672
686
  sendOn = "mod-enter",
673
687
  disabledReason,
688
+ commit,
674
689
  rows = 3,
675
690
  maxLength,
676
691
  showCount,
@@ -686,6 +701,9 @@ export function FeedbackComposer({
686
701
  ...rest
687
702
  }: FeedbackComposerProps) {
688
703
  const labels = useKitLabels("feedbackComposer", DEFAULT_FEEDBACK_COMPOSER_LABELS, labelsProp);
704
+ // The lock as Send sees it, with its scope (§12.36). Send's Button reads the same
705
+ // through its own `commit`; this holds the keyboard's send back with it.
706
+ const lock = useWriteLock(commit ?? false);
689
707
  const rootAttributes = { id, ...dataAttributes(rest) };
690
708
  const hintId = useId();
691
709
  const [ownDraft, setOwnDraft] = useState("");
@@ -752,7 +770,7 @@ export function FeedbackComposer({
752
770
  // Longer than `maxLength` only through `insertText` or a controlled value (typing
753
771
  // stops at it): the server would refuse it, so it does not go.
754
772
  const tooLong = maxLength !== undefined && draft.length > maxLength;
755
- const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong;
773
+ const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong && !lock.locked;
756
774
  const send = () => {
757
775
  if (!canSend) return;
758
776
  const sentDraft = draft;
@@ -844,6 +862,7 @@ export function FeedbackComposer({
844
862
  <Button
845
863
  onClick={send}
846
864
  disabled={!canSend}
865
+ commit={commit}
847
866
  aria-busy={pending || undefined}
848
867
  size={size === "sm" ? "sm" : undefined}
849
868
  >
@@ -21,6 +21,13 @@ import { useTextSize } from "../theme/text-size";
21
21
  export interface ViewportBox {
22
22
  top: number;
23
23
  height: number;
24
+ /**
25
+ * The height of the LAYOUT viewport itself, which `position: fixed`'s `bottom` is
26
+ * measured from. Only {@link anchoredPanelPlacement} reads it, for a flipped panel's
27
+ * `bottom`. Defaults to `top + height`: the two are the same box whenever no keyboard
28
+ * and no zoom shrink the visible region.
29
+ */
30
+ layoutHeight?: number;
24
31
  }
25
32
 
26
33
  /**
@@ -71,9 +78,21 @@ export function readKeyboardInset({ minimum = MIN_KEYBOARD_PX }: KeyboardInsetOp
71
78
  function readViewport(): ViewportBox {
72
79
  if (typeof window === "undefined") return { top: 0, height: 0 };
73
80
  const vv = window.visualViewport;
81
+ // The layout viewport's height is `innerHeight`, the reference `readKeyboardInset`
82
+ // measures the keyboard against. The keyboard leaves it alone (it shrinks only the
83
+ // VISUAL viewport; under `interactive-widget=resizes-content` it shrinks both, the
84
+ // fixed box with them), and it follows the mobile URL bar as it slides away, as a
85
+ // fixed box's `bottom` does. Not `documentElement.clientHeight`: on mobile Chrome and
86
+ // Safari that stays at the small, URL-bar-shown height, so with the bar scrolled away
87
+ // a flipped panel would sit that much too low, over its own field. `innerHeight`'s one
88
+ // error is a desktop page with a horizontal scrollbar, which it counts and the fixed
89
+ // box does not: the gap widens by the bar's thickness, and never closes.
90
+ const layoutHeight = window.innerHeight;
74
91
  // offsetTop, not 0: with the keyboard up the user can also pan the visual viewport
75
92
  // within the layout viewport, which shifts where "visible" starts.
76
- return vv ? { top: vv.offsetTop, height: vv.height } : { top: 0, height: window.innerHeight };
93
+ return vv
94
+ ? { top: vv.offsetTop, height: vv.height, layoutHeight }
95
+ : { top: 0, height: window.innerHeight, layoutHeight };
77
96
  }
78
97
 
79
98
  export interface AnchoredPanelOptions {
@@ -90,11 +109,24 @@ export interface AnchoredPanelOptions {
90
109
  minHeight?: number;
91
110
  }
92
111
 
112
+ /**
113
+ * `top` and `bottom` are the two edges of a panel exactly `maxHeight` tall. A panel is
114
+ * sized by its content and `maxHeight` only caps it, so apply the edge on the trigger's
115
+ * side, the one that holds whatever the height: `top` while the panel is below,
116
+ * `bottom` (and no `top`) once it is flipped `above`.
117
+ */
93
118
  export interface AnchoredPanel {
94
119
  /** The trigger's rect, or null while closed/unmeasured. */
95
120
  rect: AnchorRect | null;
96
- /** Viewport-space `top` for the panel. */
121
+ /** Viewport-space `top` for the panel — the one to apply while it is below. Flipped,
122
+ * it is where a FULL-height panel's top would be, and a three-row list placed by it
123
+ * floated far above its field (keksdose K1, live #401); place that one by `bottom`. */
97
124
  top: number;
125
+ /** CSS `bottom` for a `position: fixed` panel: the distance from the layout
126
+ * viewport's bottom edge up to the panel's. The one to apply once it is flipped
127
+ * `above`: it keeps the panel's foot `gap` above the trigger however short its
128
+ * content, and while a filtered list shrinks or grows under the user's typing. */
129
+ bottom: number;
98
130
  /** Cap that keeps the panel inside the VISIBLE viewport — apply as `maxHeight`. */
99
131
  maxHeight: number;
100
132
  /** Whether the panel was flipped above the trigger. */
@@ -122,6 +154,14 @@ const FLIP_GAIN = 48;
122
154
  * returned `maxHeight` is what makes the flip sufficient rather than merely different:
123
155
  * without it a tall panel placed above just runs off the top instead of the bottom.
124
156
  *
157
+ * A flipped panel hangs from its BOTTOM edge, `gap` above the trigger, and is placed by
158
+ * `bottom` (keksdose K1, live #401). It used to be placed by a `top` worked out as
159
+ * though the panel were `maxHeight` tall, and a panel is only as tall as its content:
160
+ * a three-row address list under a 256 px cap sat ~176 px above its field, on a phone
161
+ * with the keyboard up. Only when even the {@link FLOOR} does not fit above does that
162
+ * edge give way, as `top` always did, so the panel's head (a search box, often) stays
163
+ * on screen rather than above it.
164
+ *
125
165
  * Split out so the geometry is testable without a DOM and a fake keyboard — @hb/ui has
126
166
  * no test runner of its own, so this is exercised from the consuming app.
127
167
  */
@@ -140,19 +180,17 @@ export function anchoredPanelPlacement(
140
180
  (spaceBelow < preferredHeight && (spaceAbove >= preferredHeight || spaceAbove - spaceBelow >= FLIP_GAIN)));
141
181
  const space = Math.max(FLOOR, above ? spaceAbove : spaceBelow);
142
182
  const maxHeight = Math.min(preferredHeight, space);
143
- return {
144
- top: above ? Math.max(viewTop, rect.top - gap - maxHeight) : rect.bottom + gap,
145
- maxHeight,
146
- above,
147
- };
183
+ // Unchanged, for the apps that call the hook and apply it: the edge to use below, and
184
+ // a full-height panel's top above. In layout-viewport space, like the rect.
185
+ const top = above ? Math.max(viewTop, rect.top - gap - maxHeight) : rect.bottom + gap;
186
+ // `bottom` counts up from the LAYOUT viewport's bottom edge, not the visible region's:
187
+ // `position: fixed` is laid out in the layout viewport, which runs on behind the
188
+ // keyboard, exactly as the rect is measured in it. The keyboard, the pan and the zoom
189
+ // are already in `maxHeight`, which keeps the panel's top inside what is on screen.
190
+ const layoutHeight = viewport.layoutHeight ?? viewport.top + viewport.height;
191
+ return { top, bottom: layoutHeight - (top + maxHeight), maxHeight, above };
148
192
  }
149
193
 
150
- /**
151
- * Where to put a panel anchored under `ref`, tracking the visible viewport.
152
- *
153
- * Callers own horizontal placement — the pickers align to the trigger's left edge, the
154
- * popovers to its right — and must apply `maxHeight` with an internal `overflow-y`.
155
- */
156
194
  /**
157
195
  * The region actually on screen, tracked while `active` (Keksdose live #328).
158
196
  *
@@ -204,6 +242,14 @@ export function useVisualViewport(active: boolean): ViewportBox | null {
204
242
  return box;
205
243
  }
206
244
 
245
+ /**
246
+ * Where to put a panel anchored under `ref`, tracking the visible viewport.
247
+ *
248
+ * Callers own horizontal placement — the pickers align to the trigger's left edge, the
249
+ * popovers to its right — and must apply `maxHeight` with an internal `overflow-y`.
250
+ * Vertically, apply `top` while the panel is below and `bottom` (with no `top`) once it
251
+ * is `above`; see {@link AnchoredPanel}.
252
+ */
207
253
  export function useAnchoredPanel<T extends HTMLElement>(
208
254
  ref: RefObject<T | null>,
209
255
  open: boolean,
@@ -236,7 +282,7 @@ export function useAnchoredPanel<T extends HTMLElement>(
236
282
  }, [open]);
237
283
 
238
284
  if (!rect) {
239
- return { rect: null, top: 0, maxHeight: preferredHeight, above: false };
285
+ return { rect: null, top: 0, bottom: 0, maxHeight: preferredHeight, above: false };
240
286
  }
241
287
  return { rect, ...anchoredPanelPlacement(rect, viewport, { ...options, preferredHeight, minHeight }) };
242
288
  }
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
2
2
 
3
3
  /**
4
4
  * Keep keyboard focus inside an open overlay, and give it back when it closes.
@@ -23,6 +23,10 @@ import { useEffect, useRef } from "react";
23
23
  * `<body>`, which for a keyboard user means losing their place in the page
24
24
  * entirely. When the target is gone, focus falls back to the nearest thing that
25
25
  * still exists rather than to nothing.
26
+ * - **Leaves an app's own autofocus alone** (0.33.1). React honours a child's
27
+ * `autoFocus` while it commits, and this hook's effect runs AFTER that commit, so
28
+ * focusing the container there took focus straight back off the field the app had
29
+ * asked for. See `initialFocus`.
26
30
  * - **Nests.** An overlay can open another — a picker inside a dialog. Only the
27
31
  * innermost trap handles a keystroke, and closing it hands control back to the one
28
32
  * underneath. Innermost is decided by DOM containment rather than by mount order;
@@ -66,29 +70,61 @@ export interface FocusTrapOptions {
66
70
  * what `Modal` does and why: focusing the first FIELD pops the software keyboard on
67
71
  * a phone the moment a dialog opens, before the user has asked to type.
68
72
  * The container needs `tabIndex={-1}` for this to work.
73
+ *
74
+ * `"container"` and `"first"` both step aside when focus is ALREADY inside the
75
+ * container, on something other than the container itself. That is a child the app
76
+ * focused on purpose — an `autoFocus`, which React honours before this hook's effect
77
+ * runs — and the keyboard it brings up is the one the user was promised. Moving focus
78
+ * off it lost keksdose's create card its amount field the instant it got it, and on a
79
+ * phone the numpad sheet with it, which shows only while the field is focused (live
80
+ * #402). A function is the caller's explicit choice and is always followed.
69
81
  */
70
82
  initialFocus?: "container" | "first" | (() => HTMLElement | null);
71
83
  }
72
84
 
85
+ /** Whatever has focus right now; nothing on the server, where there is no document. */
86
+ function focusedElement(): Element | null {
87
+ return typeof document === "undefined" ? null : document.activeElement;
88
+ }
89
+
73
90
  export function useFocusTrap(
74
91
  containerRef: React.RefObject<HTMLElement | null>,
75
92
  { active = true, restoreFocus = true, initialFocus = "container" }: FocusTrapOptions = {},
76
93
  ): void {
77
94
  // Latest-ref so a caller passing an inline `initialFocus` thunk — which every caller
78
95
  // will — does not tear the trap down and re-engage it on each render, stealing focus
79
- // back to the top of the panel mid-interaction.
80
- const optsRef = useRef({ restoreFocus, initialFocus });
81
- useEffect(() => {
82
- optsRef.current = { restoreFocus, initialFocus };
96
+ // back to the top of the panel mid-interaction. Written in a LAYOUT effect so that
97
+ // the trap's cleanup, which runs after the layout effects of the same commit, reads
98
+ // this render's `active` rather than the last one's — see the cleanup for why.
99
+ const optsRef = useRef({ active, restoreFocus, initialFocus });
100
+ useLayoutEffect(() => {
101
+ optsRef.current = { active, restoreFocus, initialFocus };
83
102
  });
84
103
 
104
+ // Where focus was BEFORE the trap engaged: the element it is given back to on close.
105
+ //
106
+ // Read DURING the render that turns the trap on, and that timing is the point. React
107
+ // honours a child's `autoFocus` while it commits that render, before any effect runs,
108
+ // so the effect below asking `document.activeElement` would get the child, inside the
109
+ // overlay. Restoring to that on close aimed focus at the dialog that was closing —
110
+ // already out of the DOM, so focus fell to <body> rather than going back to the
111
+ // button that opened it.
112
+ //
113
+ // State adjusted during render (the pattern `Modal` uses for `open`) rather than a
114
+ // ref written there: set once per opening and cleared on release, so a re-render
115
+ // while open cannot replace it with something inside, and StrictMode's second render
116
+ // and second effect both see the same element. `undefined` means "not captured";
117
+ // `null` is a capture that found nothing.
118
+ const [opener, setOpener] = useState<Element | null | undefined>(undefined);
119
+ if (active && opener === undefined) setOpener(focusedElement());
120
+ else if (!active && opener !== undefined) setOpener(undefined);
121
+
85
122
  useEffect(() => {
86
123
  if (!active) return;
87
124
  const container = containerRef.current;
88
125
  if (!container) return;
89
126
 
90
127
  container.setAttribute(TRAP_ATTR, "");
91
- const previouslyFocused = document.activeElement as HTMLElement | null;
92
128
 
93
129
  /** Is `node` inside a trap nested within this one? */
94
130
  const inNestedTrap = (node: Element | null) => {
@@ -106,12 +142,13 @@ export function useFocusTrap(
106
142
  );
107
143
 
108
144
  const { initialFocus: how } = optsRef.current;
145
+ const focused = document.activeElement;
146
+ // A child that already has focus was given it on purpose — see `initialFocus`.
147
+ const childHasFocus = focused !== container && container.contains(focused);
109
148
  if (typeof how === "function") {
110
149
  (how() ?? container).focus();
111
- } else if (how === "first") {
112
- (tabbables()[0] ?? container).focus();
113
- } else {
114
- container.focus();
150
+ } else if (!childHasFocus) {
151
+ (how === "first" ? (tabbables()[0] ?? container) : container).focus();
115
152
  }
116
153
 
117
154
  const onKeyDown = (e: KeyboardEvent) => {
@@ -149,12 +186,18 @@ export function useFocusTrap(
149
186
  container.removeEventListener("keydown", onKeyDown);
150
187
  container.removeAttribute(TRAP_ATTR);
151
188
 
189
+ // Only when the trap is really being released: the container has left the page,
190
+ // or `active` has turned false. StrictMode runs this cleanup once more right after
191
+ // mounting, with the overlay still up, and then the effect again — and React does
192
+ // not re-apply `autoFocus` for that second run. Restoring here would pull focus
193
+ // out of the autofocused field for good: live #402 again, in development.
194
+ if (container.isConnected && optsRef.current.active) return;
152
195
  if (!optsRef.current.restoreFocus) return;
153
196
  // `isConnected` is the whole point: restoring to a node that has been removed
154
197
  // sends focus to <body> and the user loses their place entirely. When the
155
198
  // original is gone, leave focus where the browser put it rather than moving it
156
199
  // somewhere arbitrary — a wrong guess is worse than no move.
157
- if (previouslyFocused?.isConnected) previouslyFocused.focus?.();
200
+ if (opener?.isConnected) (opener as HTMLElement).focus?.();
158
201
  };
159
- }, [active, containerRef]);
202
+ }, [active, containerRef, opener]);
160
203
  }
@@ -85,6 +85,7 @@ import { DEFAULT_SETTINGS_LABELS } from "../settings/settings-labels";
85
85
  import { DEFAULT_LANDING_LABELS } from "../landing/landing-labels";
86
86
  import { DEFAULT_DEMO_LABELS } from "../demo/demo-labels";
87
87
  import { DEFAULT_BILLING_LABELS } from "../billing/billing-labels";
88
+ import { DEFAULT_PLAN_CHANGE_LABELS } from "../admin/plan-change";
88
89
  import { DEFAULT_APPEARANCE_LABELS } from "../components/appearance-labels";
89
90
  import { DEFAULT_ROW_ACTIONS_LABELS } from "../components/row-actions";
90
91
  import { DEFAULT_APP_SHELL_MORE_LABELS } from "../shell/app-shell";
@@ -219,6 +220,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
219
220
  landing: DEFAULT_LANDING_LABELS,
220
221
  demo: DEFAULT_DEMO_LABELS,
221
222
  billing: DEFAULT_BILLING_LABELS,
223
+ planChange: DEFAULT_PLAN_CHANGE_LABELS,
222
224
  appearance: DEFAULT_APPEARANCE_LABELS,
223
225
  rowActions: DEFAULT_ROW_ACTIONS_LABELS,
224
226
  appShellMore: DEFAULT_APP_SHELL_MORE_LABELS,
@@ -1525,9 +1525,22 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1525
1525
  // Both figures arrive formatted.
1526
1526
  usage: (used, limit) => `${used} von ${limit}`,
1527
1527
  contactSubject: (dimension) => `Tariflimit: ${dimension}`,
1528
+ // The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12).
1529
+ limitUsageLine: (dimension, used, limit) => `${dimension}: ${used} von ${limit}`,
1528
1530
  manage: "Zahlung und Rechnungen",
1529
1531
  // §312k BGB: the button that leads to the cancellation says exactly this (§12.26).
1530
1532
  cancel: "Verträge hier kündigen",
1533
+ // §14.5: no customer at the provider yet, so no portal to open.
1534
+ notAtProvider:
1535
+ "Noch nichts zu verwalten. Zahlungen und Rechnungen erscheinen hier, sobald Sie einen kostenpflichtigen Tarif haben.",
1536
+ // §14.4: the pay page's lines, built into its bundle. "Bezahlvorgang", Paddle's own
1537
+ // German for its checkout.
1538
+ payOpening: "Der sichere Bezahlvorgang wird geöffnet…",
1539
+ payNothing: "Hier ist nichts zu bezahlen. Um einen Tarif zu wählen, kehren Sie zur App zurück.",
1540
+ payFailed:
1541
+ "Der Bezahlvorgang konnte nicht geöffnet werden. Bitte kehren Sie zurück und versuchen Sie es gleich noch einmal.",
1542
+ payBack:
1543
+ "Der Bezahlvorgang wurde geschlossen. Sie können zurückkehren oder diese Seite neu laden, um ihn wieder zu öffnen.",
1531
1544
  waitingChanges: (count) =>
1532
1545
  count === 1
1533
1546
  ? `${n(count)} Änderung wartet auf einen Tarif`
@@ -1536,6 +1549,34 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1536
1549
  discardWaiting: "Wartende Änderungen verwerfen",
1537
1550
  notConfigured: "Zahlungen sind noch nicht eingerichtet. Bitte versuchen Sie es später noch einmal.",
1538
1551
  disabled: "Abonnements sind hier nicht verfügbar.",
1552
+ // §14.2: server-kit 0.7's refusals. "Zahlung und Rechnungen", as `manage`.
1553
+ providerUnavailable:
1554
+ "Der Zahlungsanbieter ist gerade nicht erreichbar. Bitte versuchen Sie es gleich noch einmal.",
1555
+ alreadySubscribed: "Sie haben bereits ein Abonnement. Ihren Tarif ändern Sie unter „Zahlung und Rechnungen“.",
1556
+ planNotSold:
1557
+ "Dieser Tarif ist für diesen Abrechnungszeitraum oder diese Währung nicht erhältlich. Bitte wählen Sie einen anderen.",
1558
+ },
1559
+ // 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "Tarif" as
1560
+ // in `billing`; keksdose's reviewed admin wording where it had one.
1561
+ planChange: {
1562
+ column: "Tarif",
1563
+ title: "Tarif ändern",
1564
+ confirm: "Tarif ändern",
1565
+ plan: "Tarif",
1566
+ current: (plan) => `Aktueller Tarif: ${plan}`,
1567
+ keepsItems: "Ein kleinerer Tarif sperrt nur das Anlegen weiterer Einträge; gelöscht wird nichts.",
1568
+ until: "Kostenlos bis",
1569
+ untilHint: "Leer lassen für kein Ende. Eine laufende Beta behält ihr eigenes Ende.",
1570
+ needsChange: "Wählen Sie einen anderen Tarif oder ein Enddatum.",
1571
+ changed: (from, to) => `Tarif von ${from} auf ${to} geändert.`,
1572
+ set: (to) => `Tarif auf ${to} gesetzt.`,
1573
+ keptBeta: "Die Beta behält ihr Ende; nur der Tarif wurde geändert.",
1574
+ untilDone: (date) => `Kostenlos bis ${date}.`,
1575
+ overLimit: "Über dem Limit des neuen Tarifs: Nichts wird entfernt, neue Einträge sind gesperrt.",
1576
+ // 0.33.1: the app's word heads the line. After "von" it would want the dative
1577
+ // plural ("von 5 Objekten"), which no app's `one`/`other` form carries; as a
1578
+ // heading it is the nominative, in the form for the limit ("Objekt: 1 von 1").
1579
+ usageOf: (used, limit, dimension) => `${dimension}: ${used} von ${limit}`,
1539
1580
  },
1540
1581
  // 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
1541
1582
  // the "Darstellung" group.
@@ -81,6 +81,7 @@ import type { SettingsLabels } from "../settings/settings-labels";
81
81
  import type { LandingLabels } from "../landing/landing-labels";
82
82
  import type { DemoLabels } from "../demo/demo-labels";
83
83
  import type { BillingLabels } from "../billing/billing-labels";
84
+ import type { PlanChangeLabels } from "../admin/plan-change";
84
85
  import type { AppearanceLabels } from "../components/appearance-labels";
85
86
  import type { RowActionsLabels } from "../components/row-actions";
86
87
  import type { AppShellMoreLabels } from "../shell/app-shell";
@@ -398,6 +399,9 @@ export interface UiKitLabels {
398
399
  demo: DemoLabels;
399
400
  /** 0.32.0: plans, standing, banners and the limit notice (docs/billing-harmonization.md §7). */
400
401
  billing: BillingLabels;
402
+ /** 0.33.0: the operator's plan parts — `planColumn`, `PlanChangeConfirm` and the
403
+ * lines of `usePlanChangeResult` (docs/billing-harmonization.md §14.12). */
404
+ planChange: PlanChangeLabels;
401
405
  /** 0.32.0: the text-size and contrast settings (docs/text-size-harmonization.md §6). */
402
406
  appearance: AppearanceLabels;
403
407
  /** 0.32.0: a row's actions folded into a "⋯" menu at Large (text size §10.8). */
@@ -1485,13 +1485,48 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1485
1485
  // Both figures arrive formatted; "de", as `characterCount.count`.
1486
1486
  usage: (used, limit) => `${used} de ${limit}`,
1487
1487
  contactSubject: (dimension) => `Límite del plan: ${dimension}`,
1488
+ // The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12).
1489
+ limitUsageLine: (dimension, used, limit) => `${dimension}: ${used} de ${limit}`,
1488
1490
  manage: "Pagos y facturas",
1489
1491
  cancel: "Cancelar la suscripción",
1492
+ // §14.5: no customer at the provider yet, so no portal to open.
1493
+ notAtProvider:
1494
+ "Todavía no hay nada que gestionar. Los pagos y las facturas aparecerán aquí cuando tenga un plan de pago.",
1495
+ // §14.4: the pay page's lines, built into its bundle. "Usted".
1496
+ payOpening: "Abriendo el pago seguro…",
1497
+ payNothing: "Aquí no hay nada que pagar. Para elegir un plan, vuelva a la aplicación.",
1498
+ payFailed: "No se ha podido abrir el pago. Vuelva atrás e inténtelo de nuevo en un momento.",
1499
+ payBack: "El pago se ha cerrado. Puede volver atrás o recargar esta página para abrirlo de nuevo.",
1490
1500
  waitingChanges: (count) => `${n(count)} ${plural(count, "cambio", "cambios")} a la espera de un plan`,
1491
1501
  // "Descartar", as `wizard.cancelConfirmLabel`.
1492
1502
  discardWaiting: "Descartar los cambios en espera",
1493
1503
  notConfigured: "Los pagos aún no están configurados. Inténtelo de nuevo más tarde.",
1494
1504
  disabled: "Las suscripciones no están disponibles aquí.",
1505
+ // §14.2: server-kit 0.7's refusals. "Pagos y facturas", as `manage`. "Usted".
1506
+ providerUnavailable: "El proveedor de pagos no está disponible en este momento. Inténtelo de nuevo en un momento.",
1507
+ alreadySubscribed: "Ya tiene una suscripción. Para cambiar de plan, vaya a «Pagos y facturas».",
1508
+ planNotSold: "Este plan no está disponible para ese periodo de facturación o esa moneda. Elija otro.",
1509
+ },
1510
+ // 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "plan" as
1511
+ // in `billing`. "Usted".
1512
+ planChange: {
1513
+ column: "Plan",
1514
+ title: "Cambiar de plan",
1515
+ confirm: "Cambiar de plan",
1516
+ plan: "Plan",
1517
+ current: (plan) => `Plan actual: ${plan}`,
1518
+ keepsItems: "Un plan más pequeño solo impide crear más; no se elimina nada.",
1519
+ until: "Gratis hasta el",
1520
+ untilHint: "Déjelo vacío para que no tenga fin. Una beta en curso conserva su propio fin.",
1521
+ needsChange: "Elija otro plan o una fecha de fin.",
1522
+ changed: (from, to) => `Plan cambiado de ${from} a ${to}.`,
1523
+ set: (to) => `Plan establecido en ${to}.`,
1524
+ keptBeta: "La beta conserva su fin; solo ha cambiado el plan.",
1525
+ untilDone: (date) => `Gratis hasta el ${date}.`,
1526
+ overLimit: "Por encima del límite del nuevo plan: no se elimina nada, los elementos nuevos quedan bloqueados.",
1527
+ // Figures formatted; "de", as `billing.usage`. The app's word follows the limit,
1528
+ // in its form (0.33.1): "1 de 1 presupuesto", "1 de 5 presupuestos".
1529
+ usageOf: (used, limit, dimension) => `${used} de ${limit} ${dimension}`,
1495
1530
  },
1496
1531
  // 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
1497
1532
  // the "Apariencia" group.
@@ -1516,14 +1516,55 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1516
1516
  // Both figures arrive formatted; "sur", as `characterCount.count`.
1517
1517
  usage: (used, limit) => `${used} sur ${limit}`,
1518
1518
  contactSubject: (dimension) => `Limite de la formule\u00a0: ${dimension}`,
1519
+ // The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12):
1520
+ // a key of its own for the space before the colon.
1521
+ limitUsageLine: (dimension, used, limit) => `${dimension}\u00a0: ${used} sur ${limit}`,
1519
1522
  manage: "Paiement et factures",
1520
1523
  cancel: "Résilier l’abonnement",
1524
+ // §14.5: no customer at the provider yet, so no portal to open.
1525
+ notAtProvider:
1526
+ "Rien à gérer pour l’instant. Les paiements et les factures apparaîtront ici dès que vous aurez une formule payante.",
1527
+ // §14.4: the pay page's lines, built into its bundle.
1528
+ payOpening: "Ouverture du paiement sécurisé…",
1529
+ payNothing: "Il n’y a rien à payer ici. Pour choisir une formule, revenez à l’application.",
1530
+ payFailed: "Le paiement n’a pas pu s’ouvrir. Veuillez revenir en arrière et réessayer dans un instant.",
1531
+ payBack: "Le paiement a été fermé. Vous pouvez revenir en arrière, ou recharger cette page pour le rouvrir.",
1521
1532
  waitingChanges: (count) =>
1522
1533
  `${n(count)} ${plural(count, "modification en attente", "modifications en attente")} d’une formule`,
1523
1534
  // "Abandonner", as `wizard.cancelConfirmLabel`.
1524
1535
  discardWaiting: "Abandonner les modifications en attente",
1525
1536
  notConfigured: "Les paiements ne sont pas encore configurés. Veuillez réessayer plus tard.",
1526
1537
  disabled: "Les abonnements ne sont pas disponibles ici.",
1538
+ // §14.2: server-kit 0.7's refusals. "Paiement et factures", as `manage`.
1539
+ providerUnavailable:
1540
+ "Le prestataire de paiement est injoignable pour le moment. Veuillez réessayer dans un instant.",
1541
+ alreadySubscribed:
1542
+ "Vous avez déjà un abonnement. Pour changer de formule, allez dans «\u202fPaiement et factures\u202f».",
1543
+ planNotSold:
1544
+ "Cette formule n’est pas proposée pour cette période de facturation ou cette devise. Veuillez en choisir une autre.",
1545
+ },
1546
+ // 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "formule"
1547
+ // as in `billing`. "Vous".
1548
+ planChange: {
1549
+ column: "Formule",
1550
+ title: "Changer de formule",
1551
+ confirm: "Changer de formule",
1552
+ plan: "Formule",
1553
+ current: (plan) => `Formule actuelle\u00a0: ${plan}`,
1554
+ keepsItems: "Une formule plus petite bloque seulement les nouvelles créations\u202f; rien n’est supprimé.",
1555
+ until: "Gratuit jusqu’au",
1556
+ untilHint: "Laissez vide pour aucune fin. Une bêta en cours garde sa propre fin.",
1557
+ needsChange: "Choisissez une autre formule ou une date de fin.",
1558
+ changed: (from, to) => `Formule changée de ${from} à ${to}.`,
1559
+ set: (to) => `Formule définie sur ${to}.`,
1560
+ keptBeta: "La bêta garde sa fin\u202f; seule la formule a changé.",
1561
+ untilDone: (date) => `Gratuit jusqu’au ${date}.`,
1562
+ overLimit:
1563
+ "Au-delà de la limite de la nouvelle formule\u00a0: rien n’est supprimé, les nouveaux éléments sont bloqués.",
1564
+ // Figures formatted; "sur", as `billing.usage`. The app's word follows the limit,
1565
+ // in its form (0.33.1): "1 sur 1 budget", "1 sur 5 budgets" — and French reads 0
1566
+ // as singular, which the forms' `one` covers.
1567
+ usageOf: (used, limit, dimension) => `${used} sur ${limit} ${dimension}`,
1527
1568
  },
1528
1569
  // 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
1529
1570
  // the "Apparence" group. The steps agree with "taille", the modes with "contraste".