@eifi1/ui-kit 0.34.0 → 0.36.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 (446) hide show
  1. package/README.md +115 -41
  2. package/dist/admin/role-select.d.ts +3 -2
  3. package/dist/admin/role-select.js +2 -0
  4. package/dist/admin/role-select.js.map +1 -1
  5. package/dist/billing/billing-banner.d.ts +5 -2
  6. package/dist/billing/billing-banner.js.map +1 -1
  7. package/dist/billing/plan-limit-notice.d.ts +5 -2
  8. package/dist/billing/plan-limit-notice.js.map +1 -1
  9. package/dist/chart.js.map +1 -1
  10. package/dist/components/amount-input.d.ts +2 -5
  11. package/dist/components/amount-input.js +2 -2
  12. package/dist/components/amount-input.js.map +1 -1
  13. package/dist/components/autocomplete.d.ts +2 -2
  14. package/dist/components/autocomplete.js +2 -1
  15. package/dist/components/autocomplete.js.map +1 -1
  16. package/dist/components/button-look.d.ts +1 -1
  17. package/dist/components/button-look.js +9 -4
  18. package/dist/components/button-look.js.map +1 -1
  19. package/dist/components/calculator.d.ts +1 -4
  20. package/dist/components/calculator.js +1 -3
  21. package/dist/components/calculator.js.map +1 -1
  22. package/dist/components/chart-zoom.d.ts +0 -37
  23. package/dist/components/chart-zoom.js +1 -28
  24. package/dist/components/chart-zoom.js.map +1 -1
  25. package/dist/components/checkbox-group.d.ts +5 -2
  26. package/dist/components/checkbox-group.js +15 -4
  27. package/dist/components/checkbox-group.js.map +1 -1
  28. package/dist/components/chip-input.d.ts +0 -4
  29. package/dist/components/chip-input.js +1 -5
  30. package/dist/components/chip-input.js.map +1 -1
  31. package/dist/components/column-mapper.d.ts +5 -3
  32. package/dist/components/column-mapper.js +2 -0
  33. package/dist/components/column-mapper.js.map +1 -1
  34. package/dist/components/combobox.d.ts +3 -3
  35. package/dist/components/combobox.js +2 -1
  36. package/dist/components/combobox.js.map +1 -1
  37. package/dist/components/copy-button.d.ts +5 -1
  38. package/dist/components/copy-button.js.map +1 -1
  39. package/dist/components/country-select.d.ts +3 -3
  40. package/dist/components/country-select.js +3 -0
  41. package/dist/components/country-select.js.map +1 -1
  42. package/dist/components/currency-option-list.d.ts +3 -8
  43. package/dist/components/currency-option-list.js +1 -29
  44. package/dist/components/currency-option-list.js.map +1 -1
  45. package/dist/components/currency-select.d.ts +3 -2
  46. package/dist/components/currency-select.js +10 -1
  47. package/dist/components/currency-select.js.map +1 -1
  48. package/dist/components/danger-confirm.d.ts +3 -3
  49. package/dist/components/danger-confirm.js.map +1 -1
  50. package/dist/components/data-table-filter-popover.d.ts +1 -1
  51. package/dist/components/data-table-filter-popover.js +1 -1
  52. package/dist/components/data-table-filter-popover.js.map +1 -1
  53. package/dist/components/data-table-labels.d.ts +1 -42
  54. package/dist/components/data-table-labels.js +2 -31
  55. package/dist/components/data-table-labels.js.map +1 -1
  56. package/dist/components/data-table-shared.d.ts +17 -1
  57. package/dist/components/data-table-shared.js +22 -0
  58. package/dist/components/data-table-shared.js.map +1 -1
  59. package/dist/components/data-table.d.ts +12 -2
  60. package/dist/components/data-table.js +18 -14
  61. package/dist/components/data-table.js.map +1 -1
  62. package/dist/components/date-field.d.ts +2 -1
  63. package/dist/components/date-field.js.map +1 -1
  64. package/dist/components/date-picker.js +3 -0
  65. package/dist/components/date-picker.js.map +1 -1
  66. package/dist/components/date-range-picker.js +3 -0
  67. package/dist/components/date-range-picker.js.map +1 -1
  68. package/dist/components/dialog-frame.d.ts +3 -1
  69. package/dist/components/dialog-frame.js.map +1 -1
  70. package/dist/components/dropdown.d.ts +18 -15
  71. package/dist/components/dropdown.js +11 -3
  72. package/dist/components/dropdown.js.map +1 -1
  73. package/dist/components/entity-combobox.d.ts +3 -3
  74. package/dist/components/entity-combobox.js +2 -1
  75. package/dist/components/entity-combobox.js.map +1 -1
  76. package/dist/components/field-base.d.ts +0 -5
  77. package/dist/components/field-base.js +0 -5
  78. package/dist/components/field-base.js.map +1 -1
  79. package/dist/components/field-parts.d.ts +69 -22
  80. package/dist/components/field-parts.js +33 -19
  81. package/dist/components/field-parts.js.map +1 -1
  82. package/dist/components/field-strip.d.ts +9 -23
  83. package/dist/components/field-strip.js +26 -23
  84. package/dist/components/field-strip.js.map +1 -1
  85. package/dist/components/field-sync.d.ts +0 -4
  86. package/dist/components/field-sync.js +0 -4
  87. package/dist/components/field-sync.js.map +1 -1
  88. package/dist/components/field.d.ts +7 -6
  89. package/dist/components/field.js +24 -15
  90. package/dist/components/field.js.map +1 -1
  91. package/dist/components/full-bleed-dialog.js +2 -2
  92. package/dist/components/full-bleed-dialog.js.map +1 -1
  93. package/dist/components/help-popover.d.ts +9 -7
  94. package/dist/components/help-popover.js.map +1 -1
  95. package/dist/components/hover-menu.d.ts +6 -13
  96. package/dist/components/hover-menu.js +3 -3
  97. package/dist/components/hover-menu.js.map +1 -1
  98. package/dist/components/icon-picker.d.ts +3 -2
  99. package/dist/components/icon-picker.js +3 -0
  100. package/dist/components/icon-picker.js.map +1 -1
  101. package/dist/components/inline-entity-combobox.d.ts +3 -3
  102. package/dist/components/inline-entity-combobox.js +2 -1
  103. package/dist/components/inline-entity-combobox.js.map +1 -1
  104. package/dist/components/input.d.ts +2 -2
  105. package/dist/components/input.js +2 -1
  106. package/dist/components/input.js.map +1 -1
  107. package/dist/components/modal.d.ts +9 -1
  108. package/dist/components/modal.js +7 -4
  109. package/dist/components/modal.js.map +1 -1
  110. package/dist/components/month-picker.d.ts +2 -1
  111. package/dist/components/month-picker.js +8 -1
  112. package/dist/components/month-picker.js.map +1 -1
  113. package/dist/components/multi-entity-combobox.d.ts +2 -2
  114. package/dist/components/multi-entity-combobox.js +2 -1
  115. package/dist/components/multi-entity-combobox.js.map +1 -1
  116. package/dist/components/multi-select.d.ts +7 -18
  117. package/dist/components/multi-select.js +8 -10
  118. package/dist/components/multi-select.js.map +1 -1
  119. package/dist/components/number-input.d.ts +2 -5
  120. package/dist/components/number-input.js +2 -4
  121. package/dist/components/number-input.js.map +1 -1
  122. package/dist/components/numeric-field.d.ts +5 -2
  123. package/dist/components/numeric-field.js +4 -1
  124. package/dist/components/numeric-field.js.map +1 -1
  125. package/dist/components/one-time-code-input.d.ts +2 -1
  126. package/dist/components/one-time-code-input.js +2 -1
  127. package/dist/components/one-time-code-input.js.map +1 -1
  128. package/dist/components/phone-input.d.ts +1 -1
  129. package/dist/components/phone-input.js +4 -0
  130. package/dist/components/phone-input.js.map +1 -1
  131. package/dist/components/popover.js +3 -3
  132. package/dist/components/popover.js.map +1 -1
  133. package/dist/components/select.d.ts +4 -3
  134. package/dist/components/select.js +2 -1
  135. package/dist/components/select.js.map +1 -1
  136. package/dist/components/series-chart.js.map +1 -1
  137. package/dist/components/server-wake.d.ts +0 -6
  138. package/dist/components/server-wake.js.map +1 -1
  139. package/dist/components/settings-fields.d.ts +4 -2
  140. package/dist/components/settings-fields.js.map +1 -1
  141. package/dist/components/slider.d.ts +3 -2
  142. package/dist/components/slider.js +7 -1
  143. package/dist/components/slider.js.map +1 -1
  144. package/dist/components/stat-tile.d.ts +8 -2
  145. package/dist/components/stat-tile.js +2 -11
  146. package/dist/components/stat-tile.js.map +1 -1
  147. package/dist/components/swatch-picker.d.ts +3 -2
  148. package/dist/components/swatch-picker.js +3 -0
  149. package/dist/components/swatch-picker.js.map +1 -1
  150. package/dist/components/swipeable-row.d.ts +2 -17
  151. package/dist/components/swipeable-row.js +11 -16
  152. package/dist/components/swipeable-row.js.map +1 -1
  153. package/dist/components/text-link.js +3 -1
  154. package/dist/components/text-link.js.map +1 -1
  155. package/dist/components/textarea.d.ts +2 -1
  156. package/dist/components/textarea.js +9 -3
  157. package/dist/components/textarea.js.map +1 -1
  158. package/dist/components/toggle-group.d.ts +2 -2
  159. package/dist/components/toggle-group.js +7 -3
  160. package/dist/components/toggle-group.js.map +1 -1
  161. package/dist/components/tooltip.d.ts +9 -1
  162. package/dist/components/tooltip.js +20 -5
  163. package/dist/components/tooltip.js.map +1 -1
  164. package/dist/components/translation-review.d.ts +10 -2
  165. package/dist/components/translation-review.js +11 -3
  166. package/dist/components/translation-review.js.map +1 -1
  167. package/dist/components/use-table-state.d.ts +31 -3
  168. package/dist/components/use-table-state.js +53 -8
  169. package/dist/components/use-table-state.js.map +1 -1
  170. package/dist/components/user-avatar.d.ts +2 -2
  171. package/dist/components/user-avatar.js.map +1 -1
  172. package/dist/components/wizard-stepper.d.ts +1 -4
  173. package/dist/components/wizard-stepper.js +2 -3
  174. package/dist/components/wizard-stepper.js.map +1 -1
  175. package/dist/currencies.d.ts +23 -0
  176. package/dist/currencies.js +38 -0
  177. package/dist/currencies.js.map +1 -0
  178. package/dist/demo/demo-banner.d.ts +7 -2
  179. package/dist/demo/demo-banner.js.map +1 -1
  180. package/dist/eslint/index.d.ts +83 -0
  181. package/dist/eslint/index.js +69 -0
  182. package/dist/eslint/index.js.map +1 -0
  183. package/dist/eslint/role-utilities.d.ts +48 -0
  184. package/dist/eslint/role-utilities.generated.d.ts +2 -0
  185. package/dist/eslint/role-utilities.generated.js +143 -0
  186. package/dist/eslint/role-utilities.generated.js.map +1 -0
  187. package/dist/eslint/role-utilities.js +43 -0
  188. package/dist/eslint/role-utilities.js.map +1 -0
  189. package/dist/feedback/feedback-menu.d.ts +2 -2
  190. package/dist/feedback/feedback-menu.js.map +1 -1
  191. package/dist/feedback/feedback-thread.d.ts +3 -8
  192. package/dist/feedback/feedback-thread.js +0 -2
  193. package/dist/feedback/feedback-thread.js.map +1 -1
  194. package/dist/hooks/use-dismiss.d.ts +16 -3
  195. package/dist/hooks/use-dismiss.js +14 -2
  196. package/dist/hooks/use-dismiss.js.map +1 -1
  197. package/dist/i18n/defaults.js +5 -1
  198. package/dist/i18n/defaults.js.map +1 -1
  199. package/dist/i18n/german.js +97 -1
  200. package/dist/i18n/german.js.map +1 -1
  201. package/dist/i18n/kit-labels.d.ts +29 -4
  202. package/dist/i18n/kit-labels.js +21 -1
  203. package/dist/i18n/kit-labels.js.map +1 -1
  204. package/dist/i18n/locales/en.js +104 -3
  205. package/dist/i18n/locales/en.js.map +3 -3
  206. package/dist/i18n/locales/es.js +95 -1
  207. package/dist/i18n/locales/es.js.map +1 -1
  208. package/dist/i18n/locales/fr.js +99 -3
  209. package/dist/i18n/locales/fr.js.map +1 -1
  210. package/dist/i18n/locales/hu.js +95 -1
  211. package/dist/i18n/locales/hu.js.map +1 -1
  212. package/dist/i18n/locales/it.js +99 -3
  213. package/dist/i18n/locales/it.js.map +1 -1
  214. package/dist/i18n/locales/zh.js +95 -1
  215. package/dist/i18n/locales/zh.js.map +1 -1
  216. package/dist/i18n/review.js +13 -0
  217. package/dist/i18n/review.js.map +1 -1
  218. package/dist/i18n/shared-labels.js +3 -1
  219. package/dist/i18n/shared-labels.js.map +1 -1
  220. package/dist/index.d.ts +23 -1
  221. package/dist/index.js +40 -2
  222. package/dist/index.js.map +1 -1
  223. package/dist/lib/device-settings.d.ts +72 -0
  224. package/dist/lib/device-settings.js +64 -0
  225. package/dist/lib/device-settings.js.map +1 -0
  226. package/dist/lib/plain-text.d.ts +11 -0
  227. package/dist/lib/plain-text.js +16 -0
  228. package/dist/lib/plain-text.js.map +1 -0
  229. package/dist/lib/refs.d.ts +13 -0
  230. package/dist/lib/refs.js +15 -0
  231. package/dist/lib/refs.js.map +1 -0
  232. package/dist/lib/safe-storage.d.ts +6 -0
  233. package/dist/lib/safe-storage.js +11 -0
  234. package/dist/lib/safe-storage.js.map +1 -1
  235. package/dist/notifications/notification-parts.d.ts +39 -0
  236. package/dist/notifications/notification-parts.js +47 -0
  237. package/dist/notifications/notification-parts.js.map +1 -0
  238. package/dist/notifications/notification-types-setting.d.ts +62 -0
  239. package/dist/notifications/notification-types-setting.js +192 -0
  240. package/dist/notifications/notification-types-setting.js.map +1 -0
  241. package/dist/notifications/notification-types.d.ts +75 -0
  242. package/dist/notifications/notification-types.js +2 -0
  243. package/dist/notifications/notification-types.js.map +1 -0
  244. package/dist/notifications/notifications-labels.d.ts +131 -0
  245. package/dist/notifications/notifications-labels.js +83 -0
  246. package/dist/notifications/notifications-labels.js.map +1 -0
  247. package/dist/notifications/push-device-setting.d.ts +37 -0
  248. package/dist/notifications/push-device-setting.js +164 -0
  249. package/dist/notifications/push-device-setting.js.map +1 -0
  250. package/dist/notifications/push-devices-setting.d.ts +44 -0
  251. package/dist/notifications/push-devices-setting.js +203 -0
  252. package/dist/notifications/push-devices-setting.js.map +1 -0
  253. package/dist/notifications/quiet-hours-setting.d.ts +47 -0
  254. package/dist/notifications/quiet-hours-setting.js +131 -0
  255. package/dist/notifications/quiet-hours-setting.js.map +1 -0
  256. package/dist/notifications/time-zone-select.d.ts +50 -0
  257. package/dist/notifications/time-zone-select.js +76 -0
  258. package/dist/notifications/time-zone-select.js.map +1 -0
  259. package/dist/notifications/unsubscribe-panel.d.ts +53 -0
  260. package/dist/notifications/unsubscribe-panel.js +139 -0
  261. package/dist/notifications/unsubscribe-panel.js.map +1 -0
  262. package/dist/notifications/use-push-device.d.ts +122 -0
  263. package/dist/notifications/use-push-device.js +230 -0
  264. package/dist/notifications/use-push-device.js.map +1 -0
  265. package/dist/rhf/fields.d.ts +36 -29
  266. package/dist/rhf/fields.js +51 -22
  267. package/dist/rhf/fields.js.map +1 -1
  268. package/dist/rhf/form-core.d.ts +5 -4
  269. package/dist/rhf/form-core.js.map +1 -1
  270. package/dist/rhf/form.d.ts +7 -36
  271. package/dist/rhf/form.js +1 -16
  272. package/dist/rhf/form.js.map +1 -1
  273. package/dist/rhf/line-items.d.ts +1 -1
  274. package/dist/rhf/line-items.js.map +1 -1
  275. package/dist/rhf/use-rhf-wizard-step.js.map +1 -1
  276. package/dist/rhf.js.map +1 -1
  277. package/dist/settings/device-reset-setting-labels.d.ts +3 -0
  278. package/dist/settings/device-reset-setting-labels.js +15 -0
  279. package/dist/settings/device-reset-setting-labels.js.map +1 -0
  280. package/dist/settings/device-reset-setting.d.ts +58 -0
  281. package/dist/settings/device-reset-setting.js +51 -0
  282. package/dist/settings/device-reset-setting.js.map +1 -0
  283. package/dist/shell/app-shell.js +5 -0
  284. package/dist/shell/app-shell.js.map +1 -1
  285. package/dist/shell/option-switcher-menu.d.ts +5 -17
  286. package/dist/shell/option-switcher-menu.js +1 -3
  287. package/dist/shell/option-switcher-menu.js.map +1 -1
  288. package/dist/shell/role-switcher.d.ts +1 -4
  289. package/dist/shell/role-switcher.js +2 -3
  290. package/dist/shell/role-switcher.js.map +1 -1
  291. package/dist/shell/topbar-action-menu.d.ts +7 -19
  292. package/dist/shell/topbar-action-menu.js +1 -3
  293. package/dist/shell/topbar-action-menu.js.map +1 -1
  294. package/dist/shell/topbar-controls.d.ts +3 -12
  295. package/dist/shell/topbar-controls.js +6 -9
  296. package/dist/shell/topbar-controls.js.map +1 -1
  297. package/dist/testing/index.d.ts +1 -1
  298. package/dist/testing/index.js +2 -0
  299. package/dist/testing/index.js.map +1 -1
  300. package/dist/testing/viewport.d.ts +25 -2
  301. package/dist/testing/viewport.js +61 -10
  302. package/dist/testing/viewport.js.map +1 -1
  303. package/dist/theme/contrast-tokens.d.ts +42 -4
  304. package/dist/theme/contrast-tokens.js +56 -9
  305. package/dist/theme/contrast-tokens.js.map +1 -1
  306. package/dist/theme/contrast.d.ts +4 -2
  307. package/dist/theme/contrast.js +13 -0
  308. package/dist/theme/contrast.js.map +1 -1
  309. package/dist/theme/palette-derive.d.ts +15 -1
  310. package/dist/theme/palette-derive.js +37 -8
  311. package/dist/theme/palette-derive.js.map +1 -1
  312. package/dist/theme/palette-presets.d.ts +2 -8
  313. package/dist/theme/palette-presets.js +5 -284
  314. package/dist/theme/palette-presets.js.map +1 -1
  315. package/dist/theme/palette-store.js +6 -0
  316. package/dist/theme/palette-store.js.map +1 -1
  317. package/dist/theme/text-size.js +6 -0
  318. package/dist/theme/text-size.js.map +1 -1
  319. package/dist/theme/theme-store.js +6 -0
  320. package/dist/theme/theme-store.js.map +1 -1
  321. package/dist/wizard/types.d.ts +0 -5
  322. package/dist/wizard/types.js +1 -5
  323. package/dist/wizard/types.js.map +1 -1
  324. package/package.json +9 -1
  325. package/src/admin/role-select.tsx +4 -1
  326. package/src/billing/billing-banner.tsx +5 -2
  327. package/src/billing/plan-limit-notice.tsx +5 -2
  328. package/src/chart.ts +5 -3
  329. package/src/components/amount-input.tsx +4 -7
  330. package/src/components/autocomplete.tsx +5 -2
  331. package/src/components/button-look.ts +15 -5
  332. package/src/components/calculator.tsx +2 -7
  333. package/src/components/chart-zoom.tsx +4 -47
  334. package/src/components/checkbox-group.tsx +22 -4
  335. package/src/components/chip-input.tsx +0 -7
  336. package/src/components/column-mapper.tsx +6 -2
  337. package/src/components/combobox.tsx +6 -3
  338. package/src/components/copy-button.tsx +5 -1
  339. package/src/components/country-select.tsx +6 -1
  340. package/src/components/currency-option-list.tsx +8 -42
  341. package/src/components/currency-select.tsx +12 -3
  342. package/src/components/danger-confirm.tsx +3 -3
  343. package/src/components/data-table-filter-popover.tsx +2 -1
  344. package/src/components/data-table-labels.ts +2 -84
  345. package/src/components/data-table-shared.tsx +55 -2
  346. package/src/components/data-table.tsx +29 -17
  347. package/src/components/date-field.tsx +2 -2
  348. package/src/components/date-picker.tsx +3 -0
  349. package/src/components/date-range-picker.tsx +3 -0
  350. package/src/components/dialog-frame.tsx +3 -1
  351. package/src/components/dropdown.tsx +42 -18
  352. package/src/components/entity-combobox.tsx +6 -3
  353. package/src/components/field-base.tsx +0 -11
  354. package/src/components/field-parts.tsx +112 -38
  355. package/src/components/field-strip.tsx +12 -27
  356. package/src/components/field-sync.tsx +0 -7
  357. package/src/components/field.tsx +32 -19
  358. package/src/components/full-bleed-dialog.tsx +4 -3
  359. package/src/components/help-popover.tsx +9 -7
  360. package/src/components/hover-menu.tsx +12 -16
  361. package/src/components/icon-picker.tsx +8 -3
  362. package/src/components/inline-entity-combobox.tsx +6 -3
  363. package/src/components/input.tsx +4 -2
  364. package/src/components/modal.tsx +20 -4
  365. package/src/components/month-picker.tsx +10 -3
  366. package/src/components/multi-entity-combobox.tsx +5 -2
  367. package/src/components/multi-select.tsx +19 -31
  368. package/src/components/number-input.tsx +5 -10
  369. package/src/components/numeric-field.ts +10 -2
  370. package/src/components/one-time-code-input.tsx +5 -3
  371. package/src/components/phone-input.tsx +4 -0
  372. package/src/components/popover.tsx +8 -4
  373. package/src/components/select.tsx +6 -3
  374. package/src/components/series-chart.tsx +1 -1
  375. package/src/components/server-wake.tsx +0 -6
  376. package/src/components/settings-fields.tsx +4 -2
  377. package/src/components/slider.tsx +10 -3
  378. package/src/components/stat-tile.tsx +11 -20
  379. package/src/components/swatch-picker.tsx +9 -4
  380. package/src/components/swipeable-row.tsx +15 -37
  381. package/src/components/text-link.tsx +3 -1
  382. package/src/components/textarea.tsx +11 -4
  383. package/src/components/toggle-group.tsx +21 -5
  384. package/src/components/tooltip.tsx +37 -6
  385. package/src/components/translation-review.tsx +25 -7
  386. package/src/components/use-table-state.ts +91 -10
  387. package/src/components/user-avatar.tsx +2 -2
  388. package/src/components/wizard-stepper.tsx +2 -6
  389. package/src/currencies.ts +70 -0
  390. package/src/demo/demo-banner.tsx +7 -2
  391. package/src/eslint/index.ts +161 -0
  392. package/src/eslint/role-utilities.generated.ts +146 -0
  393. package/src/eslint/role-utilities.ts +99 -0
  394. package/src/feedback/feedback-menu.tsx +2 -2
  395. package/src/feedback/feedback-thread.tsx +3 -8
  396. package/src/hooks/use-dismiss.ts +39 -4
  397. package/src/i18n/defaults.ts +4 -0
  398. package/src/i18n/german.ts +113 -0
  399. package/src/i18n/kit-labels.tsx +66 -6
  400. package/src/i18n/locales/en.ts +6 -0
  401. package/src/i18n/locales/es.ts +106 -0
  402. package/src/i18n/locales/fr.ts +109 -2
  403. package/src/i18n/locales/hu.ts +104 -0
  404. package/src/i18n/locales/it.ts +108 -2
  405. package/src/i18n/locales/zh.ts +96 -0
  406. package/src/i18n/review.ts +13 -0
  407. package/src/i18n/shared-labels.ts +2 -0
  408. package/src/index.ts +53 -1
  409. package/src/lib/device-settings.ts +146 -0
  410. package/src/lib/plain-text.ts +22 -0
  411. package/src/lib/refs.ts +22 -0
  412. package/src/lib/safe-storage.ts +16 -0
  413. package/src/notifications/notification-parts.tsx +83 -0
  414. package/src/notifications/notification-types-setting.tsx +309 -0
  415. package/src/notifications/notification-types.ts +80 -0
  416. package/src/notifications/notifications-labels.ts +238 -0
  417. package/src/notifications/push-device-setting.tsx +224 -0
  418. package/src/notifications/push-devices-setting.tsx +306 -0
  419. package/src/notifications/quiet-hours-setting.tsx +185 -0
  420. package/src/notifications/time-zone-select.tsx +126 -0
  421. package/src/notifications/unsubscribe-panel.tsx +203 -0
  422. package/src/notifications/use-push-device.ts +392 -0
  423. package/src/rhf/fields.tsx +87 -39
  424. package/src/rhf/form-core.tsx +5 -4
  425. package/src/rhf/form.tsx +7 -43
  426. package/src/rhf/line-items.tsx +1 -1
  427. package/src/rhf/use-rhf-wizard-step.ts +4 -4
  428. package/src/rhf.ts +3 -2
  429. package/src/settings/device-reset-setting-labels.ts +20 -0
  430. package/src/settings/device-reset-setting.tsx +119 -0
  431. package/src/shell/app-shell.tsx +8 -0
  432. package/src/shell/option-switcher-menu.tsx +5 -22
  433. package/src/shell/role-switcher.tsx +2 -6
  434. package/src/shell/topbar-action-menu.tsx +7 -24
  435. package/src/shell/topbar-controls.tsx +6 -18
  436. package/src/testing/index.ts +3 -1
  437. package/src/testing/viewport.ts +125 -19
  438. package/src/theme/contrast-tokens.ts +106 -12
  439. package/src/theme/contrast.ts +30 -4
  440. package/src/theme/palette-derive.ts +68 -7
  441. package/src/theme/palette-presets.ts +16 -299
  442. package/src/theme/palette-store.ts +12 -0
  443. package/src/theme/text-size.ts +8 -0
  444. package/src/theme/theme-store.ts +10 -0
  445. package/src/wizard/types.ts +0 -8
  446. package/tokens.css +38 -4
@@ -5,16 +5,12 @@ import { cn } from "../lib/cn";
5
5
  export function WizardStepper({
6
6
  steps,
7
7
  current,
8
- "aria-label": ariaLabelAttr,
9
- ariaLabel,
8
+ "aria-label": ariaLabel,
10
9
  }: {
11
10
  steps: { key: string; label: string }[];
12
11
  current: string;
13
12
  /** Names the list of steps ("Import steps"). */
14
13
  "aria-label"?: string;
15
- /** @deprecated 0.34 — use `aria-label`; removed in 0.35 (refactor audit 2026-10, API-1).
16
- * Names the steps only when `aria-label` is absent. */
17
- ariaLabel?: string;
18
14
  }) {
19
15
  const currentIndex = steps.findIndex((s) => s.key === current);
20
16
  return (
@@ -22,7 +18,7 @@ export function WizardStepper({
22
18
  // (0.32.1, the 360 px Extra-large sweep; docs/text-size-harmonization.md §4).
23
19
  // "Upload — Map columns — Confirm" ran 22 px past a 360 px phone at Extra large.
24
20
  // Where the steps fit, they stay one row.
25
- <ol className="flex flex-wrap items-center gap-1 text-xs" aria-label={ariaLabelAttr ?? ariaLabel}>
21
+ <ol className="flex flex-wrap items-center gap-1 text-xs" aria-label={ariaLabel}>
26
22
  {steps.map((step, i) => (
27
23
  <li key={step.key} className="flex items-center gap-1">
28
24
  {i > 0 && <span className="w-4 border-t border-subtle" />}
@@ -0,0 +1,70 @@
1
+ // `@eifi1/ui-kit/currencies` — the currencies the kit's pickers offer, as plain data
2
+ // (0.35, keksdose's 0.34 report).
3
+ //
4
+ // This module imports NOTHING: no React, no stylesheet, no other kit module. That is the
5
+ // entry's whole promise, because an app's BUILD reads it in Node — keksdose's Vite plugin
6
+ // builds its flag-icons subset from these country codes while Vite loads its config. Until
7
+ // 0.35 that plugin parsed the `country` literals out of dist/components/currency-select.js;
8
+ // 0.34 moved the list into the picker's option list and 91 of its test files failed at
9
+ // load. A file inside dist/ is not something the kit promises; an entry is.
10
+ // packaging-contract.test.ts pins the purity, scripts/verify-package.mjs the load in Node.
11
+ //
12
+ // CurrencySelect, AmountInput's currency chip and NumberInput's code detection read this
13
+ // same array (components/currency-option-list re-exports it, and currency-select as it
14
+ // always has), so the list the pickers show and the flags an app ships cannot drift.
15
+
16
+ export interface CurrencyOption {
17
+ code: string;
18
+ name: string;
19
+ symbol: string;
20
+ /** ISO 3166-1 alpha-2 country code (lowercase) used by flag-icons. */
21
+ country: string;
22
+ }
23
+
24
+ export const CURRENCIES: CurrencyOption[] = [
25
+ { code: "EUR", name: "Euro", symbol: "€", country: "eu" },
26
+ { code: "USD", name: "US Dollar", symbol: "$", country: "us" },
27
+ { code: "GBP", name: "British Pound", symbol: "£", country: "gb" },
28
+ { code: "CHF", name: "Swiss Franc", symbol: "CHF", country: "ch" },
29
+ { code: "JPY", name: "Japanese Yen", symbol: "¥", country: "jp" },
30
+ { code: "CAD", name: "Canadian Dollar", symbol: "CA$", country: "ca" },
31
+ { code: "AUD", name: "Australian Dollar", symbol: "A$", country: "au" },
32
+ { code: "NZD", name: "New Zealand Dollar", symbol: "NZ$", country: "nz" },
33
+ { code: "SEK", name: "Swedish Krona", symbol: "kr", country: "se" },
34
+ { code: "NOK", name: "Norwegian Krone", symbol: "kr", country: "no" },
35
+ { code: "DKK", name: "Danish Krone", symbol: "kr", country: "dk" },
36
+ { code: "PLN", name: "Polish Zloty", symbol: "zł", country: "pl" },
37
+ { code: "CZK", name: "Czech Koruna", symbol: "Kč", country: "cz" },
38
+ { code: "HUF", name: "Hungarian Forint", symbol: "Ft", country: "hu" },
39
+ { code: "RON", name: "Romanian Leu", symbol: "lei", country: "ro" },
40
+ { code: "BGN", name: "Bulgarian Lev", symbol: "лв", country: "bg" },
41
+ { code: "TRY", name: "Turkish Lira", symbol: "₺", country: "tr" },
42
+ { code: "RUB", name: "Russian Ruble", symbol: "₽", country: "ru" },
43
+ { code: "CNY", name: "Chinese Yuan", symbol: "¥", country: "cn" },
44
+ { code: "HKD", name: "Hong Kong Dollar", symbol: "HK$", country: "hk" },
45
+ { code: "SGD", name: "Singapore Dollar", symbol: "S$", country: "sg" },
46
+ { code: "INR", name: "Indian Rupee", symbol: "₹", country: "in" },
47
+ { code: "BRL", name: "Brazilian Real", symbol: "R$", country: "br" },
48
+ { code: "MXN", name: "Mexican Peso", symbol: "MX$", country: "mx" },
49
+ { code: "ZAR", name: "South African Rand", symbol: "R", country: "za" },
50
+ { code: "ILS", name: "Israeli Shekel", symbol: "₪", country: "il" },
51
+ { code: "AED", name: "UAE Dirham", symbol: "د.إ", country: "ae" },
52
+ ];
53
+
54
+ /**
55
+ * Each currency code → the flag the kit paints for it: the lowercase ISO 3166-1 alpha-2
56
+ * code flag-icons names it by (`fi-ch`, `flags/4x3/ch.svg`). `CHF` → `"ch"`, `EUR` →
57
+ * `"eu"` (flag-icons has the EU's flag under that code).
58
+ *
59
+ * Derived from {@link CURRENCIES}, so a currency the pickers gain is a flag here too. An
60
+ * app that ships only the flags it renders builds its subset from these values:
61
+ *
62
+ * ```ts
63
+ * // vite.config.ts — plain Node, no DOM
64
+ * import { CURRENCY_COUNTRIES } from "@eifi1/ui-kit/currencies";
65
+ * const flags = new Set(Object.values(CURRENCY_COUNTRIES));
66
+ * ```
67
+ */
68
+ export const CURRENCY_COUNTRIES: Record<string, string> = Object.fromEntries(
69
+ CURRENCIES.map((c) => [c.code, c.country]),
70
+ );
@@ -1,5 +1,5 @@
1
1
  import { useState } from "react";
2
- import type { ComponentPropsWithoutRef } from "react";
2
+ import type { ComponentPropsWithRef } from "react";
3
3
  import { ChevronDown, ChevronUp, Timer } from "lucide-react";
4
4
 
5
5
  import { cn } from "../lib/cn";
@@ -30,7 +30,12 @@ function timeText(countdown: DemoCountdown, labels: DemoLabels): string {
30
30
  : labels.hoursLeft(countdown.hours, countdown.minutes);
31
31
  }
32
32
 
33
- export interface DemoBannerProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "onClick"> {
33
+ /**
34
+ * A `div`'s props, `ref` included since 0.35 (round 0.35 R4, the follow-up to API-2). They
35
+ * go on to the {@link AlertBanner} strip, so the ref is the strip's own `<div>` — never a
36
+ * link or a button: the banner takes no `href` or `onClick` of its own.
37
+ */
38
+ export interface DemoBannerProps extends Omit<ComponentPropsWithRef<"div">, "children" | "onClick"> {
34
39
  /** The demo's end: `expires_at` from the `TokenResponse`, or `demo_expires_at` from
35
40
  * `/auth/me` (§5.3). */
36
41
  expiresAt: DemoExpiry | null | undefined;
@@ -0,0 +1,161 @@
1
+ // `@eifi1/ui-kit/eslint` — the kit's role-utility rule for an app's own markup (round 0.35
2
+ // R5, Kurvenschmiede's Q6).
3
+ //
4
+ // The kit holds its markup to "write `text-muted`, not `text-[var(--text-muted)]`" with
5
+ // `npm run check:tokens`, at zero since 0.34. The apps are asked to follow the same rule
6
+ // (README "How to write a colour"), but nothing in their builds objected when a
7
+ // `[var(--role)]` class came back. This is that check as an ESLint plugin, so an app's
8
+ // `eslint.config.js` turns it on with one line:
9
+ //
10
+ // import uiKit from "@eifi1/ui-kit/eslint";
11
+ // export default [..., uiKit.configs.recommended];
12
+ //
13
+ // It reads every string literal and template-literal quasi — a JSX `className`, a `cn()` or
14
+ // `clsx()` argument, a class constant — and reports a `[var(--x)]` class where the property
15
+ // has a role utility for that token, naming it. One utility is an autofix that keeps the
16
+ // variants and the opacity: `hover:text-[var(--text-muted)]/70` → `hover:text-muted/70`.
17
+ //
18
+ // Plain Node, like the rest of an ESLint config: no React, no stylesheet, and no import of
19
+ // `eslint` itself, which is the app's (packaging-contract.test.ts refuses an undeclared
20
+ // package in src/), so the few types it needs are written out below.
21
+ import { findRoleClasses } from "./role-utilities";
22
+ import { ROLE_UTILITIES } from "./role-utilities.generated";
23
+
24
+ interface Position {
25
+ line: number;
26
+ column: number;
27
+ }
28
+
29
+ /** The part of an ESTree string node the rule reads. */
30
+ interface StringNode {
31
+ type: string;
32
+ value?: unknown;
33
+ range?: [number, number];
34
+ loc?: { start: Position; end: Position } | null;
35
+ }
36
+
37
+ interface Fix {
38
+ range: [number, number];
39
+ text: string;
40
+ }
41
+
42
+ /**
43
+ * The part of ESLint's rule context the rule uses — `sourceCode.text` and `report`, which
44
+ * every ESLint since flat config has — so that ESLint's own RuleContext, and @eslint/core's
45
+ * that `defineConfig` checks a plugin against, are assignable to it. The source is only an
46
+ * `object` here because a language's source may be binary, with no `text` at all.
47
+ */
48
+ interface RoleUtilitiesContext {
49
+ sourceCode: object;
50
+ report(descriptor: {
51
+ loc: { start: Position; end: Position };
52
+ messageId: string;
53
+ data: Record<string, string>;
54
+ fix: ((fixer: { replaceTextRange(range: [number, number], text: string): Fix }) => Fix) | null;
55
+ }): void;
56
+ }
57
+
58
+ interface RoleUtilitiesRule {
59
+ meta: {
60
+ type: "suggestion";
61
+ docs: { description: string; url: string };
62
+ fixable: "code";
63
+ schema: [];
64
+ messages: { useRoleUtility: string };
65
+ };
66
+ create(context: RoleUtilitiesContext): {
67
+ Literal(node: StringNode): void;
68
+ TemplateElement(node: StringNode): void;
69
+ };
70
+ }
71
+
72
+ const roleUtilities: RoleUtilitiesRule = {
73
+ meta: {
74
+ type: "suggestion",
75
+ docs: {
76
+ description: "Write a colour role's utility (text-muted) rather than its [var(--…)] class",
77
+ url: "https://github.com/Eifi1/ui-kit#lint-the-role-utilities-in-an-app",
78
+ },
79
+ fixable: "code",
80
+ schema: [],
81
+ messages: {
82
+ useRoleUtility:
83
+ "Write the role utility `{{utilities}}`, not `{{found}}`: variants and an opacity work on it the same (tokens.css, @theme inline).",
84
+ },
85
+ },
86
+ create(context) {
87
+ const source = context.sourceCode as { text?: unknown };
88
+ const text = typeof source.text === "string" ? source.text : "";
89
+ const check = (node: StringNode) => {
90
+ if (!node.range || !node.loc) return;
91
+ const [start] = node.range;
92
+ const { line, column } = node.loc.start;
93
+ // An offset in the node's source text — quotes included, so it is an offset in the
94
+ // file — as a position. A template literal may span lines.
95
+ const at = (offset: number): Position => {
96
+ const lines = text.slice(start, start + offset).split("\n");
97
+ const last = lines.length - 1;
98
+ return { line: line + last, column: last ? lines[last].length : column + offset };
99
+ };
100
+ for (const hit of findRoleClasses(text.slice(start, node.range[1]), ROLE_UTILITIES)) {
101
+ const end = hit.index + hit.found.length;
102
+ const range: [number, number] = [start + hit.index, start + end];
103
+ const [only] = hit.utilities;
104
+ context.report({
105
+ loc: { start: at(hit.index), end: at(end) },
106
+ messageId: "useRoleUtility",
107
+ data: { found: hit.found, utilities: hit.utilities.join("` or `") },
108
+ // Several names for one token are a choice, not a fix.
109
+ fix: hit.utilities.length === 1 ? (fixer) => fixer.replaceTextRange(range, only) : null,
110
+ });
111
+ }
112
+ };
113
+ return {
114
+ Literal(node) {
115
+ if (typeof node.value === "string") check(node);
116
+ },
117
+ TemplateElement: check,
118
+ };
119
+ },
120
+ };
121
+
122
+ // The kit's version, baked in by tsup's `define` (tsup.config.ts). ESLint's cache keys a
123
+ // file's result on each plugin's name@version, so without it an app's `eslint --cache`
124
+ // would keep a file's old verdict after a kit release that gave a role a utility. Unset
125
+ // when the source runs (the tests), where nothing is cached.
126
+ declare const __UI_KIT_VERSION__: string | undefined;
127
+ const VERSION = typeof __UI_KIT_VERSION__ === "string" ? __UI_KIT_VERSION__ : "0.0.0-source";
128
+
129
+ /** Spelled out, self-reference included, so a typed config (`defineConfig`, `tseslint.config`) takes it. */
130
+ interface UiKitEslintPlugin {
131
+ meta: { name: string; version: string; namespace: string };
132
+ rules: { "role-utilities": RoleUtilitiesRule };
133
+ configs: {
134
+ /** The rule as an error, on every file the app's other config objects lint but its tests. */
135
+ recommended: {
136
+ name: string;
137
+ ignores: string[];
138
+ plugins: { "@eifi1/ui-kit": UiKitEslintPlugin };
139
+ rules: { "@eifi1/ui-kit/role-utilities": "error" };
140
+ };
141
+ };
142
+ }
143
+
144
+ const recommended: UiKitEslintPlugin["configs"]["recommended"] = {
145
+ name: "@eifi1/ui-kit/recommended",
146
+ // Not the tests, as check:tokens skips the kit's: a test names the old class to assert it
147
+ // is gone (keksdose's cleared-status-toggle.test.tsx does, three times).
148
+ ignores: ["**/__tests__/**", "**/*.test.*", "**/*.spec.*"],
149
+ plugins: {} as UiKitEslintPlugin["configs"]["recommended"]["plugins"],
150
+ rules: { "@eifi1/ui-kit/role-utilities": "error" },
151
+ };
152
+
153
+ /** The flat-config plugin: add `configs.recommended`, or register it under `@eifi1/ui-kit`. */
154
+ const plugin: UiKitEslintPlugin = {
155
+ meta: { name: "@eifi1/ui-kit", version: VERSION, namespace: "@eifi1/ui-kit" },
156
+ rules: { "role-utilities": roleUtilities },
157
+ configs: { recommended },
158
+ };
159
+ recommended.plugins["@eifi1/ui-kit"] = plugin;
160
+
161
+ export default plugin;
@@ -0,0 +1,146 @@
1
+ // GENERATED by scripts/gen-role-utilities.mjs from tokens.css's `@theme inline` block — do
2
+ // not edit. `npm run check:tokens` fails while it is stale; re-run the script.
3
+ //
4
+ // Per property, each token that has a role utility → the utility's name after the
5
+ // property's prefix (`border`: `--border` → `subtle`, i.e. `border-subtle`). Read by
6
+ // `@eifi1/ui-kit/eslint` and scripts/check-token-discipline.mjs (round 0.35 R5).
7
+ import type { RoleUtilityTable } from "./role-utilities";
8
+
9
+ export const ROLE_UTILITIES: RoleUtilityTable = {
10
+ "text": {
11
+ "--text-primary": ["primary"],
12
+ "--text-secondary": ["secondary"],
13
+ "--text-muted": ["muted"],
14
+ "--text-placeholder": ["placeholder"],
15
+ "--text-inverse": ["inverse"],
16
+ "--brand": ["brand"],
17
+ "--brand-muted": ["brand-muted"],
18
+ "--danger": ["danger"],
19
+ "--warning": ["warning"],
20
+ "--success": ["success"],
21
+ "--info": ["info"],
22
+ "--money-income": ["money-income"],
23
+ "--money-expense": ["money-expense"],
24
+ "--money-net": ["money-net"],
25
+ "--money-neutral": ["money-neutral"],
26
+ "--hue-blue": ["hue-blue"],
27
+ "--hue-indigo": ["hue-indigo"],
28
+ "--hue-purple": ["hue-purple"],
29
+ "--hue-teal": ["hue-teal"],
30
+ "--hue-orange": ["hue-orange"],
31
+ "--brand-contrast": ["brand-contrast"],
32
+ "--danger-contrast": ["danger-contrast"],
33
+ "--warning-contrast": ["warning-contrast"],
34
+ "--success-contrast": ["success-contrast"],
35
+ "--info-contrast": ["info-contrast"],
36
+ "--neutral-contrast": ["neutral-contrast"],
37
+ "--money-income-contrast": ["money-income-contrast"],
38
+ "--money-expense-contrast": ["money-expense-contrast"],
39
+ "--money-net-contrast": ["money-net-contrast"],
40
+ "--money-neutral-contrast": ["money-neutral-contrast"],
41
+ "--hue-blue-contrast": ["hue-blue-contrast"],
42
+ "--hue-indigo-contrast": ["hue-indigo-contrast"],
43
+ "--hue-purple-contrast": ["hue-purple-contrast"],
44
+ "--hue-teal-contrast": ["hue-teal-contrast"],
45
+ "--hue-orange-contrast": ["hue-orange-contrast"],
46
+ "--media-ink": ["media-ink"],
47
+ },
48
+ "bg": {
49
+ "--bg-page": ["page"],
50
+ "--bg-surface": ["surface"],
51
+ "--bg-surface-2": ["surface-2"],
52
+ "--bg-hover": ["hover"],
53
+ "--bg-active": ["active"],
54
+ "--bg-inverse": ["inverse"],
55
+ "--brand": ["brand"],
56
+ "--brand-hover": ["brand-hover"],
57
+ "--brand-bg": ["brand-soft"],
58
+ "--brand-bg-hover": ["brand-soft-hover"],
59
+ "--danger": ["danger"],
60
+ "--danger-hover": ["danger-hover"],
61
+ "--warning": ["warning"],
62
+ "--success": ["success"],
63
+ "--info": ["info"],
64
+ "--neutral": ["neutral"],
65
+ "--money-income": ["money-income"],
66
+ "--money-expense": ["money-expense"],
67
+ "--money-net": ["money-net"],
68
+ "--money-neutral": ["money-neutral"],
69
+ "--danger-bg": ["danger-soft"],
70
+ "--warning-bg": ["warning-soft"],
71
+ "--success-bg": ["success-soft"],
72
+ "--info-bg": ["info-soft"],
73
+ "--hue-blue": ["hue-blue"],
74
+ "--hue-indigo": ["hue-indigo"],
75
+ "--hue-purple": ["hue-purple"],
76
+ "--hue-teal": ["hue-teal"],
77
+ "--hue-orange": ["hue-orange"],
78
+ "--hue-blue-bg": ["hue-blue-soft"],
79
+ "--hue-indigo-bg": ["hue-indigo-soft"],
80
+ "--hue-purple-bg": ["hue-purple-soft"],
81
+ "--hue-teal-bg": ["hue-teal-soft"],
82
+ "--hue-orange-bg": ["hue-orange-soft"],
83
+ "--media-scrim": ["media-scrim"],
84
+ "--media-scrim-hover": ["media-scrim-hover"],
85
+ },
86
+ "border": {
87
+ "--border": ["subtle"],
88
+ "--border-strong": ["strong"],
89
+ "--brand": ["brand"],
90
+ "--danger-border": ["danger"],
91
+ "--warning-border": ["warning"],
92
+ "--success-border": ["success"],
93
+ "--info-border": ["info"],
94
+ "--danger-border-strong": ["danger-strong"],
95
+ "--warning-border-strong": ["warning-strong"],
96
+ "--hue-blue-border": ["hue-blue"],
97
+ "--hue-indigo-border": ["hue-indigo"],
98
+ "--hue-purple-border": ["hue-purple"],
99
+ "--hue-teal-border": ["hue-teal"],
100
+ "--hue-orange-border": ["hue-orange"],
101
+ "--media-ink": ["media-ink"],
102
+ },
103
+ "ring": {
104
+ "--border": ["subtle"],
105
+ "--border-strong": ["strong"],
106
+ "--brand": ["brand"],
107
+ "--brand-contrast": ["brand-contrast"],
108
+ "--danger-border-strong": ["danger-strong"],
109
+ "--warning-border-strong": ["warning-strong"],
110
+ "--media-ink": ["media-ink"],
111
+ },
112
+ "ring-offset": {
113
+ "--bg-page": ["page"],
114
+ "--bg-surface": ["surface"],
115
+ "--media-scrim-hover": ["media-scrim-hover"],
116
+ },
117
+ "outline": {
118
+ "--border": ["subtle"],
119
+ "--border-strong": ["strong"],
120
+ "--brand": ["brand"],
121
+ "--danger-border-strong": ["danger-strong"],
122
+ "--warning-border-strong": ["warning-strong"],
123
+ },
124
+ "fill": {
125
+ "--text-primary": ["primary"],
126
+ "--text-secondary": ["secondary"],
127
+ "--text-muted": ["muted"],
128
+ "--bg-surface": ["surface"],
129
+ "--border": ["subtle"],
130
+ "--border-strong": ["strong"],
131
+ },
132
+ "stroke": {
133
+ "--text-primary": ["primary"],
134
+ "--text-secondary": ["secondary"],
135
+ "--text-muted": ["muted"],
136
+ "--bg-surface": ["surface"],
137
+ "--border": ["subtle"],
138
+ "--border-strong": ["strong"],
139
+ },
140
+ "ring-width": {
141
+ "--focus-ring-width": ["focus"],
142
+ },
143
+ "outline-width": {
144
+ "--focus-ring-width": ["focus"],
145
+ },
146
+ };
@@ -0,0 +1,99 @@
1
+ // Which `[var(--token)]` classes have a role utility, and what it is called — the one
2
+ // definition `@eifi1/ui-kit/eslint`'s `role-utilities` rule and `npm run check:tokens`
3
+ // (scripts/check-token-discipline.mjs) both read (round 0.35 R5, Kurvenschmiede's Q6).
4
+ //
5
+ // The table itself is role-utilities.generated.ts, re-rendered from tokens.css's
6
+ // `@theme inline` block by scripts/gen-role-utilities.mjs with the parser below; the
7
+ // check fails while it is stale. So tokens.css stays the source: a role that gains a
8
+ // utility there is flagged in the kit and in the apps from the next release on.
9
+ //
10
+ // This module imports NOTHING, and its TypeScript is erasable only (no enum, no
11
+ // namespace, no parameter property): the check imports it from source under Node's type
12
+ // stripping, before any build, and the plugin ships it to an app's ESLint, which is
13
+ // plain Node with no React and no stylesheet.
14
+
15
+ /**
16
+ * The properties a role utility exists for, each with the Tailwind theme namespace it is
17
+ * declared in. `ring-width` and `outline-width` are a width typed `length:` —
18
+ * `ring-[length:var(--focus-ring-width)]` is `ring-focus`. `divide-*` and the side borders
19
+ * (`border-t-*`, `border-x-*` …) read `border`'s, as Tailwind does.
20
+ */
21
+ export const ROLE_NAMESPACES = {
22
+ text: "text-color",
23
+ bg: "background-color",
24
+ border: "border-color",
25
+ ring: "ring-color",
26
+ "ring-offset": "ring-offset-color",
27
+ outline: "outline-color",
28
+ fill: "fill",
29
+ stroke: "stroke",
30
+ "ring-width": "ring-width",
31
+ "outline-width": "outline-width",
32
+ } as const;
33
+
34
+ export type RoleProperty = keyof typeof ROLE_NAMESPACES;
35
+
36
+ /**
37
+ * Per property, each token that has a role utility → the utility's name after the
38
+ * property's prefix: `border: { "--border": ["subtle"] }` is `border-subtle`. A list,
39
+ * because nothing stops two names from reading one token; one name is a fix, several a
40
+ * choice.
41
+ */
42
+ export type RoleUtilityTable = {
43
+ readonly [P in RoleProperty]: Readonly<Record<string, readonly string[]>>;
44
+ };
45
+
46
+ /**
47
+ * The table `tokens.css` declares: every `--<namespace>-<name>: var(--<token>)` in its
48
+ * `@theme inline` block, in block order. `null` when there is no such block — the role
49
+ * utilities are gone, or this parser is.
50
+ */
51
+ export function roleUtilitiesFromCss(css: string): RoleUtilityTable | null {
52
+ const theme = /@theme inline\s*\{([^}]*)\}/.exec(css.replace(/\/\*[\s\S]*?\*\//g, ""))?.[1];
53
+ if (theme === undefined) return null;
54
+ const table = {} as Record<RoleProperty, Record<string, string[]>>;
55
+ for (const prop of Object.keys(ROLE_NAMESPACES) as RoleProperty[]) {
56
+ const names: Record<string, string[]> = {};
57
+ const declared = new RegExp(`--${ROLE_NAMESPACES[prop]}-([a-z0-9-]+)\\s*:\\s*var\\((--[a-z0-9-]+)\\)`, "g");
58
+ for (const [, name, token] of theme.matchAll(declared)) (names[token] ??= []).push(name);
59
+ table[prop] = names;
60
+ }
61
+ return table;
62
+ }
63
+
64
+ /**
65
+ * `text-[var(--text-muted)]`, `hover:bg-[var(--bg-hover)]`, `border-t-[var(--border)]`,
66
+ * `focus-visible:ring-[var(--brand)]/40`, `ring-offset-[var(--bg-surface)]`,
67
+ * `ring-[length:var(--focus-ring-width)]`. Preceded by a start, a space, a quote or a
68
+ * variant's colon, which the match does not include — nor the variants before it or an
69
+ * opacity or `!` after it — so replacing the match keeps all of them.
70
+ */
71
+ const ROLE_CLASS =
72
+ /(?<=^|[\s"'`:])(text|bg|border(?:-[xytrblse])?|divide|ring-offset|ring|outline|fill|stroke)-\[(length:)?var\((--[a-zA-Z0-9-]+)\)\]/g;
73
+
74
+ export interface RoleClassHit {
75
+ /** Where the class starts in the scanned text, after its variants. */
76
+ index: number;
77
+ /** The class as written, without variants or opacity: `border-t-[var(--border)]`. */
78
+ found: string;
79
+ /** The role utilities that paint the same: `["border-t-subtle"]`. */
80
+ utilities: string[];
81
+ }
82
+
83
+ /**
84
+ * Every `[var(--token)]` class in `text` whose token has a role utility on that property.
85
+ * A token with no utility there (`text-[var(--chart-1)]`, `--status-*`,
86
+ * `bg-[var(--border)]` as a divider's fill) is not a hit: `[var(--…)]` is right for it.
87
+ */
88
+ export function findRoleClasses(text: string, table: RoleUtilityTable): RoleClassHit[] {
89
+ if (!text.includes("var(--")) return [];
90
+ const hits: RoleClassHit[] = [];
91
+ for (const m of text.matchAll(ROLE_CLASS)) {
92
+ const [found, prop, length, token] = m;
93
+ // `border-t` and `divide` read `border`'s names; a `length:` hint reads the widths.
94
+ const key = length ? `${prop}-width` : prop.startsWith("border-") || prop === "divide" ? "border" : prop;
95
+ const names = (table as Partial<Record<string, Readonly<Record<string, readonly string[]>>>>)[key]?.[token];
96
+ if (names?.length) hits.push({ index: m.index, found, utilities: names.map((name) => `${prop}-${name}`) });
97
+ }
98
+ return hits;
99
+ }
@@ -56,8 +56,8 @@ export interface FeedbackMenuProps {
56
56
  iconBadge?: UserAvatarBadge | null;
57
57
  /** Prop > `<UiKitProvider labels={{ feedbackMenu }}>` > English. */
58
58
  labels?: LabelOverride<FeedbackMenuLabels>;
59
- /** Which edge of the trigger the panel lines up with (see `HoverMenu`, whose type this
60
- * is: `left`/`right` are deprecated 0.34). Default `end`. */
59
+ /** Which edge of the trigger the panel lines up with, `start` or `end` in the reading
60
+ * direction (see `HoverMenu`, whose type this is). Default `end`. */
61
61
  align?: HoverMenuProps["align"];
62
62
  }
63
63
 
@@ -646,7 +646,7 @@ export interface FeedbackComposerProps {
646
646
  }
647
647
 
648
648
  /**
649
- * What a {@link FeedbackComposer} `ref` holds.
649
+ * What a {@link ChatComposer} `ref` holds.
650
650
  *
651
651
  * keksdose F9: the support admin answers the same five questions all day and wants
652
652
  * chips above the box ("Thanks, fixed in the next release") that drop their text in
@@ -679,7 +679,8 @@ const isApple = () =>
679
679
  * Named for what it is since 0.22 (keksdose K17): the component was never
680
680
  * feedback-specific — a textarea, an optional picture or a host's upload slot, Send,
681
681
  * `sendOn="enter"` for the chat way — and keksdose's assistant and support chat are
682
- * chats, not reports. `FeedbackComposer` is its old name, deprecated in 0.34.
682
+ * chats, not reports. `FeedbackComposer`, its old name, was removed in 0.35 (refactor
683
+ * audit 2026-10, API-6d; deprecated in 0.34); its types keep theirs.
683
684
  */
684
685
  export function ChatComposer({
685
686
  onSend,
@@ -892,12 +893,6 @@ function dataAttributes(props: object): Record<string, unknown> {
892
893
  return out;
893
894
  }
894
895
 
895
- /**
896
- * @deprecated 0.34 — `ChatComposer`, the same function under the name that is not about
897
- * feedback; removed in 0.35 (refactor audit 2026-10, API-6d). keksdose's support thread
898
- * is the one app site.
899
- */
900
- export const FeedbackComposer = ChatComposer;
901
896
  /** {@link FeedbackComposerProps}, for {@link ChatComposer}. */
902
897
  export type ChatComposerProps = FeedbackComposerProps;
903
898
  /** {@link FeedbackComposerHandle}, for {@link ChatComposer}. */
@@ -47,6 +47,12 @@ import { isComposing } from "../lib/ime";
47
47
  * anywhere else claims nothing: a search box keeping Chromium from clearing itself
48
48
  * must not hold a dialog open (FullBleedDialog's 0.8.0 fix, which Modal never had).
49
49
  *
50
+ * The owner's handler hears Escape only while its own layer is on top (0.35, KS). It
51
+ * runs before the stack is asked, so with a tooltip, a hover menu or a list open over
52
+ * the dialog and focus on the panel, the gear wizard asked "Discard this gear?" and the
53
+ * tooltip stayed. Now the layer above takes that press, and the next one is the
54
+ * owner's. Every other key reaches the handler as before.
55
+ *
50
56
  * A handler that STOPS the key before it reaches a layer element has taken it too —
51
57
  * that is the DOM, and it is what the kit's cell editors and confirm buttons do.
52
58
  */
@@ -56,6 +62,10 @@ interface Layer {
56
62
  escape: (e: KeyboardEvent) => void;
57
63
  }
58
64
 
65
+ /** What {@link useDismissLayer} hands back: its layer while open, for
66
+ * {@link runOwnKeyDown} to tell whether that layer is the one on top. */
67
+ export type DismissLayer = RefObject<Layer | null>;
68
+
59
69
  /** Open layers, oldest first. Module-level, because the order is ACROSS instances. */
60
70
  const layers: Layer[] = [];
61
71
 
@@ -123,10 +133,11 @@ function pop(layer: Layer): void {
123
133
  * Where focus can be INSIDE the layer, its element's `onKeyDown` calls
124
134
  * {@link takeEscape} too, so the key stops there — see the module note. `onEscape` is
125
135
  * read live, so it need not be memoised; the layer is pushed and popped only when
126
- * `open` flips.
136
+ * `open` flips. The layer it returns is for the owner's {@link runOwnKeyDown}.
127
137
  */
128
- export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => void): void {
138
+ export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => void): DismissLayer {
129
139
  const handlerRef = useRef(onEscape);
140
+ const layerRef = useRef<Layer | null>(null);
130
141
  // Written in an effect, not during render, so it satisfies react-hooks/refs: a
131
142
  // render that React starts and then discards — a transition, a Suspense retry —
132
143
  // would otherwise leave this holding a handler from a tree that never committed,
@@ -138,9 +149,14 @@ export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => v
138
149
  useEffect(() => {
139
150
  if (!open || typeof document === "undefined") return;
140
151
  const layer: Layer = { escape: (e) => handlerRef.current(e) };
152
+ layerRef.current = layer;
141
153
  push(layer);
142
- return () => pop(layer);
154
+ return () => {
155
+ pop(layer);
156
+ layerRef.current = null;
157
+ };
143
158
  }, [open]);
159
+ return layerRef;
144
160
  }
145
161
 
146
162
  /**
@@ -167,14 +183,33 @@ export function claimEscape(e: KeyboardEvent | ReactKeyboardEvent): void {
167
183
  * Runs a layer owner's own key handler — a dialog's `onKeyDown` prop — and turns a
168
184
  * `preventDefault()` it makes on Escape into a claim: the one way, beside
169
185
  * {@link claimEscape}, to keep a layer open.
186
+ *
187
+ * Given the owner's `layer` (what {@link useDismissLayer} returned), an Escape that went
188
+ * down while another layer was above that one skips the handler: the press is the
189
+ * layer's on top, and the owner hears the next (0.35, KS). Without it, or while the
190
+ * owner's layer is not open, the handler sees every key, as before.
170
191
  */
171
- export function runOwnKeyDown<E extends ReactKeyboardEvent>(e: E, handler: ((e: E) => void) | undefined): void {
192
+ export function runOwnKeyDown<E extends ReactKeyboardEvent>(
193
+ e: E,
194
+ handler: ((e: E) => void) | undefined,
195
+ layer?: DismissLayer,
196
+ ): void {
172
197
  if (!handler) return;
198
+ if (layer && aboveOwner(e, layer.current)) return;
173
199
  const before = e.defaultPrevented;
174
200
  handler(e);
175
201
  if (e.key === "Escape" && !before && e.defaultPrevented) claimed.add(nativeOf(e));
176
202
  }
177
203
 
204
+ /** Whether this Escape went down with a layer above `owner` — the topmost one then was
205
+ * another, and `owner` is on the stack. An Escape the stack did not note (one inside an
206
+ * IME composition) is nobody else's. */
207
+ function aboveOwner(e: ReactKeyboardEvent, owner: Layer | null): boolean {
208
+ const flight = inFlight;
209
+ if (e.key !== "Escape" || !owner || !flight || flight.event !== nativeOf(e)) return false;
210
+ return flight.layer !== owner && layers.includes(owner);
211
+ }
212
+
178
213
  /**
179
214
  * Calls `handler` on an Escape while `enabled` — as an Escape layer. Since 0.34 this is
180
215
  * {@link useDismissLayer} with its arguments the other way round: it hears the key only