@eifi1/ui-kit 0.29.0 → 0.31.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 (307) hide show
  1. package/README.md +65 -6
  2. package/dist/account/account-parts.d.ts +21 -0
  3. package/dist/account/account-parts.js +40 -0
  4. package/dist/account/account-parts.js.map +1 -0
  5. package/dist/account/data-export-setting.d.ts +73 -0
  6. package/dist/account/data-export-setting.js +114 -0
  7. package/dist/account/data-export-setting.js.map +1 -0
  8. package/dist/account/delete-account-setting.d.ts +95 -0
  9. package/dist/account/delete-account-setting.js +200 -0
  10. package/dist/account/delete-account-setting.js.map +1 -0
  11. package/dist/account/email-change-setting.d.ts +136 -0
  12. package/dist/account/email-change-setting.js +278 -0
  13. package/dist/account/email-change-setting.js.map +1 -0
  14. package/dist/account/sessions-setting.d.ts +110 -0
  15. package/dist/account/sessions-setting.js +147 -0
  16. package/dist/account/sessions-setting.js.map +1 -0
  17. package/dist/admin/admin-action-confirm.d.ts +95 -0
  18. package/dist/admin/admin-action-confirm.js +276 -0
  19. package/dist/admin/admin-action-confirm.js.map +1 -0
  20. package/dist/admin/admin-action-log.d.ts +105 -0
  21. package/dist/admin/admin-action-log.js +153 -0
  22. package/dist/admin/admin-action-log.js.map +1 -0
  23. package/dist/admin/admin-parts.d.ts +40 -0
  24. package/dist/admin/admin-parts.js +45 -0
  25. package/dist/admin/admin-parts.js.map +1 -0
  26. package/dist/admin/invitations-panel.d.ts +95 -0
  27. package/dist/admin/invitations-panel.js +290 -0
  28. package/dist/admin/invitations-panel.js.map +1 -0
  29. package/dist/admin/reviewer-scope-editor.d.ts +95 -0
  30. package/dist/admin/reviewer-scope-editor.js +134 -0
  31. package/dist/admin/reviewer-scope-editor.js.map +1 -0
  32. package/dist/admin/role-select.d.ts +95 -0
  33. package/dist/admin/role-select.js +128 -0
  34. package/dist/admin/role-select.js.map +1 -0
  35. package/dist/admin/transfer-ownership-dialog.d.ts +95 -0
  36. package/dist/admin/transfer-ownership-dialog.js +83 -0
  37. package/dist/admin/transfer-ownership-dialog.js.map +1 -0
  38. package/dist/admin/user-roster.d.ts +95 -0
  39. package/dist/admin/user-roster.js +285 -0
  40. package/dist/admin/user-roster.js.map +1 -0
  41. package/dist/auth/auth-errors.d.ts +103 -6
  42. package/dist/auth/auth-errors.js +68 -2
  43. package/dist/auth/auth-errors.js.map +1 -1
  44. package/dist/auth/forgot-password-form.d.ts +10 -3
  45. package/dist/auth/forgot-password-form.js +6 -3
  46. package/dist/auth/forgot-password-form.js.map +1 -1
  47. package/dist/auth/register-form.d.ts +21 -12
  48. package/dist/auth/register-form.js +6 -4
  49. package/dist/auth/register-form.js.map +1 -1
  50. package/dist/auth/reset-password-form.d.ts +10 -3
  51. package/dist/auth/reset-password-form.js +6 -3
  52. package/dist/auth/reset-password-form.js.map +1 -1
  53. package/dist/auth/sign-in-form.d.ts +75 -9
  54. package/dist/auth/sign-in-form.js +105 -15
  55. package/dist/auth/sign-in-form.js.map +1 -1
  56. package/dist/auth/verify-email.d.ts +7 -2
  57. package/dist/auth/verify-email.js +44 -8
  58. package/dist/auth/verify-email.js.map +1 -1
  59. package/dist/chart.d.ts +20 -11
  60. package/dist/components/account-chips.d.ts +47 -7
  61. package/dist/components/account-chips.js +19 -3
  62. package/dist/components/account-chips.js.map +1 -1
  63. package/dist/components/account-settings-labels.d.ts +24 -1
  64. package/dist/components/account-settings-labels.js +6 -2
  65. package/dist/components/account-settings-labels.js.map +1 -1
  66. package/dist/components/account-settings.d.ts +69 -8
  67. package/dist/components/account-settings.js +133 -25
  68. package/dist/components/account-settings.js.map +1 -1
  69. package/dist/components/amount-input.d.ts +21 -12
  70. package/dist/components/button-group.d.ts +21 -12
  71. package/dist/components/calculator.d.ts +21 -12
  72. package/dist/components/confirm-dialog.d.ts +21 -12
  73. package/dist/components/copy-button.d.ts +18 -9
  74. package/dist/components/danger-confirm.d.ts +21 -12
  75. package/dist/components/data-table.js +8 -1
  76. package/dist/components/data-table.js.map +1 -1
  77. package/dist/components/date-picker.js +9 -4
  78. package/dist/components/date-picker.js.map +1 -1
  79. package/dist/components/facing-pair.d.ts +21 -12
  80. package/dist/components/file-button.d.ts +21 -12
  81. package/dist/components/file-dropzone.d.ts +21 -12
  82. package/dist/components/form-actions.d.ts +21 -12
  83. package/dist/components/iban-input.d.ts +21 -12
  84. package/dist/components/language-select.d.ts +21 -12
  85. package/dist/components/legal-page.d.ts +21 -12
  86. package/dist/components/legal.d.ts +21 -12
  87. package/dist/components/money-field.d.ts +21 -12
  88. package/dist/components/number-field.d.ts +21 -12
  89. package/dist/components/number-input.d.ts +21 -12
  90. package/dist/components/numpad-sheet.d.ts +21 -12
  91. package/dist/components/passkeys-setting.js +2 -1
  92. package/dist/components/passkeys-setting.js.map +1 -1
  93. package/dist/components/phone-input.d.ts +21 -12
  94. package/dist/components/series-chart.d.ts +21 -12
  95. package/dist/components/settings-fields.d.ts +55 -13
  96. package/dist/components/settings-fields.js +20 -1
  97. package/dist/components/settings-fields.js.map +1 -1
  98. package/dist/components/share-card.d.ts +20 -11
  99. package/dist/components/text-link.d.ts +21 -12
  100. package/dist/components/time-input.d.ts +21 -12
  101. package/dist/components/ui.d.ts +17 -8
  102. package/dist/{register-form-V1OeNlMr.d.ts → delete-account-setting-DCFS5Vof.d.ts} +6612 -5612
  103. package/dist/demo/demo-banner.d.ts +51 -0
  104. package/dist/demo/demo-banner.js +97 -0
  105. package/dist/demo/demo-banner.js.map +1 -0
  106. package/dist/demo/demo-countdown.d.ts +58 -0
  107. package/dist/demo/demo-countdown.js +79 -0
  108. package/dist/demo/demo-countdown.js.map +1 -0
  109. package/dist/demo/demo-ended.d.ts +45 -0
  110. package/dist/demo/demo-ended.js +42 -0
  111. package/dist/demo/demo-ended.js.map +1 -0
  112. package/dist/demo/demo-labels.d.ts +53 -0
  113. package/dist/demo/demo-labels.js +29 -0
  114. package/dist/demo/demo-labels.js.map +1 -0
  115. package/dist/demo/demo-session.d.ts +30 -0
  116. package/dist/demo/demo-session.js +8 -0
  117. package/dist/demo/demo-session.js.map +1 -0
  118. package/dist/demo/demo-start.d.ts +54 -0
  119. package/dist/demo/demo-start.js +68 -0
  120. package/dist/demo/demo-start.js.map +1 -0
  121. package/dist/feedback/feedback-attachment.d.ts +21 -12
  122. package/dist/feedback/feedback-context.d.ts +21 -12
  123. package/dist/feedback/feedback-crash.d.ts +21 -12
  124. package/dist/feedback/feedback-dialog.d.ts +21 -12
  125. package/dist/feedback/feedback-inbox.d.ts +21 -12
  126. package/dist/feedback/feedback-inbox.js +12 -2
  127. package/dist/feedback/feedback-inbox.js.map +1 -1
  128. package/dist/feedback/feedback-labels.d.ts +21 -12
  129. package/dist/feedback/feedback-labels.js +1 -0
  130. package/dist/feedback/feedback-labels.js.map +1 -1
  131. package/dist/feedback/feedback-menu.d.ts +21 -12
  132. package/dist/feedback/feedback-record.d.ts +21 -12
  133. package/dist/feedback/feedback-record.js +2 -0
  134. package/dist/feedback/feedback-record.js.map +1 -1
  135. package/dist/feedback/feedback-row-detail.d.ts +21 -12
  136. package/dist/feedback/feedback-row-detail.js +7 -0
  137. package/dist/feedback/feedback-row-detail.js.map +1 -1
  138. package/dist/feedback/feedback-status-undo.d.ts +21 -12
  139. package/dist/feedback/feedback-submit.d.ts +21 -12
  140. package/dist/feedback/feedback-swipe.d.ts +22 -13
  141. package/dist/feedback/feedback-swipe.js.map +1 -1
  142. package/dist/feedback/feedback-table.d.ts +18 -9
  143. package/dist/feedback/feedback-thread.d.ts +21 -12
  144. package/dist/feedback.d.ts +21 -12
  145. package/dist/hooks/use-anchored-panel.d.ts +6 -2
  146. package/dist/hooks/use-anchored-panel.js +2 -1
  147. package/dist/hooks/use-anchored-panel.js.map +1 -1
  148. package/dist/hooks/use-file-drop.d.ts +21 -12
  149. package/dist/i18n/defaults.d.ts +21 -12
  150. package/dist/i18n/defaults.js +28 -0
  151. package/dist/i18n/defaults.js.map +1 -1
  152. package/dist/i18n/german.d.ts +21 -12
  153. package/dist/i18n/german.js +298 -12
  154. package/dist/i18n/german.js.map +1 -1
  155. package/dist/i18n/kit-labels.d.ts +14 -5
  156. package/dist/i18n/kit-labels.js.map +1 -1
  157. package/dist/i18n/languages.d.ts +15 -6
  158. package/dist/i18n/locales/de-CH.d.ts +21 -12
  159. package/dist/i18n/locales/en.d.ts +21 -12
  160. package/dist/i18n/locales/es.d.ts +21 -12
  161. package/dist/i18n/locales/es.js +287 -10
  162. package/dist/i18n/locales/es.js.map +1 -1
  163. package/dist/i18n/locales/fr.d.ts +21 -12
  164. package/dist/i18n/locales/fr.js +290 -7
  165. package/dist/i18n/locales/fr.js.map +1 -1
  166. package/dist/i18n/locales/hu.d.ts +21 -12
  167. package/dist/i18n/locales/hu.js +288 -12
  168. package/dist/i18n/locales/hu.js.map +1 -1
  169. package/dist/i18n/locales/it.d.ts +21 -12
  170. package/dist/i18n/locales/it.js +289 -7
  171. package/dist/i18n/locales/it.js.map +1 -1
  172. package/dist/i18n/locales/zh.d.ts +21 -12
  173. package/dist/i18n/locales/zh.js +279 -7
  174. package/dist/i18n/locales/zh.js.map +1 -1
  175. package/dist/i18n/review.d.ts +21 -12
  176. package/dist/i18n/review.js +50 -1
  177. package/dist/i18n/review.js.map +1 -1
  178. package/dist/index.d.ts +31 -4
  179. package/dist/index.js +148 -0
  180. package/dist/index.js.map +1 -1
  181. package/dist/landing/access.d.ts +48 -0
  182. package/dist/landing/access.js +38 -0
  183. package/dist/landing/access.js.map +1 -0
  184. package/dist/landing/landing-actions.d.ts +138 -0
  185. package/dist/landing/landing-actions.js +39 -0
  186. package/dist/landing/landing-actions.js.map +1 -0
  187. package/dist/landing/landing-labels.d.ts +43 -0
  188. package/dist/landing/landing-labels.js +23 -0
  189. package/dist/landing/landing-labels.js.map +1 -0
  190. package/dist/landing/landing-sections.d.ts +218 -0
  191. package/dist/landing/landing-sections.js +167 -0
  192. package/dist/landing/landing-sections.js.map +1 -0
  193. package/dist/landing/page-seo.d.ts +99 -0
  194. package/dist/landing/page-seo.js +121 -0
  195. package/dist/landing/page-seo.js.map +1 -0
  196. package/dist/landing/public-header.d.ts +148 -0
  197. package/dist/landing/public-header.js +49 -0
  198. package/dist/landing/public-header.js.map +1 -0
  199. package/dist/landing/routing.d.ts +218 -0
  200. package/dist/landing/routing.js +81 -0
  201. package/dist/landing/routing.js.map +1 -0
  202. package/dist/rhf/fields.d.ts +21 -12
  203. package/dist/rhf/form.d.ts +21 -12
  204. package/dist/rhf.d.ts +21 -12
  205. package/dist/settings/settings-catalogue.d.ts +84 -0
  206. package/dist/settings/settings-catalogue.js +47 -0
  207. package/dist/settings/settings-catalogue.js.map +1 -0
  208. package/dist/settings/settings-context.d.ts +31 -0
  209. package/dist/settings/settings-context.js +11 -0
  210. package/dist/settings/settings-context.js.map +1 -0
  211. package/dist/settings/settings-heading.d.ts +50 -0
  212. package/dist/settings/settings-heading.js +27 -0
  213. package/dist/settings/settings-heading.js.map +1 -0
  214. package/dist/settings/settings-labels.d.ts +41 -0
  215. package/dist/settings/settings-labels.js +38 -0
  216. package/dist/settings/settings-labels.js.map +1 -0
  217. package/dist/settings/settings-layout.d.ts +77 -0
  218. package/dist/settings/settings-layout.js +242 -0
  219. package/dist/settings/settings-layout.js.map +1 -0
  220. package/dist/settings/settings-section.d.ts +133 -0
  221. package/dist/settings/settings-section.js +63 -0
  222. package/dist/settings/settings-section.js.map +1 -0
  223. package/dist/settings/use-account-language.d.ts +83 -0
  224. package/dist/settings/use-account-language.js +62 -0
  225. package/dist/settings/use-account-language.js.map +1 -0
  226. package/dist/settings/use-settings-focus.d.ts +34 -0
  227. package/dist/settings/use-settings-focus.js +77 -0
  228. package/dist/settings/use-settings-focus.js.map +1 -0
  229. package/dist/settings/use-settings-route.d.ts +118 -0
  230. package/dist/settings/use-settings-route.js +120 -0
  231. package/dist/settings/use-settings-route.js.map +1 -0
  232. package/dist/shell/app-shell.d.ts +27 -12
  233. package/dist/shell/app-shell.js +3 -2
  234. package/dist/shell/app-shell.js.map +1 -1
  235. package/dist/shell/top-bar-brand.d.ts +21 -12
  236. package/dist/shell.d.ts +25 -6
  237. package/dist/shell.js +56 -0
  238. package/dist/shell.js.map +1 -1
  239. package/dist/wizard/stepper-nav.d.ts +21 -12
  240. package/dist/wizard.d.ts +21 -12
  241. package/package.json +2 -1
  242. package/src/account/account-parts.ts +67 -0
  243. package/src/account/data-export-setting.tsx +219 -0
  244. package/src/account/delete-account-setting.tsx +363 -0
  245. package/src/account/email-change-setting.tsx +454 -0
  246. package/src/account/sessions-setting.tsx +284 -0
  247. package/src/admin/admin-action-confirm.tsx +495 -0
  248. package/src/admin/admin-action-log.tsx +297 -0
  249. package/src/admin/admin-parts.ts +82 -0
  250. package/src/admin/invitations-panel.tsx +523 -0
  251. package/src/admin/reviewer-scope-editor.tsx +234 -0
  252. package/src/admin/role-select.tsx +286 -0
  253. package/src/admin/transfer-ownership-dialog.tsx +172 -0
  254. package/src/admin/user-roster.tsx +573 -0
  255. package/src/auth/auth-errors.ts +196 -8
  256. package/src/auth/forgot-password-form.tsx +21 -5
  257. package/src/auth/register-form.tsx +24 -6
  258. package/src/auth/reset-password-form.tsx +19 -5
  259. package/src/auth/sign-in-form.tsx +241 -38
  260. package/src/auth/verify-email.tsx +59 -8
  261. package/src/components/account-chips.tsx +64 -7
  262. package/src/components/account-settings-labels.ts +28 -1
  263. package/src/components/account-settings.tsx +231 -34
  264. package/src/components/data-table.tsx +8 -0
  265. package/src/components/date-picker.tsx +9 -4
  266. package/src/components/passkeys-setting.tsx +2 -1
  267. package/src/components/settings-fields.tsx +50 -1
  268. package/src/demo/demo-banner.tsx +159 -0
  269. package/src/demo/demo-countdown.ts +141 -0
  270. package/src/demo/demo-ended.tsx +89 -0
  271. package/src/demo/demo-labels.ts +78 -0
  272. package/src/demo/demo-session.ts +32 -0
  273. package/src/demo/demo-start.tsx +150 -0
  274. package/src/feedback/feedback-inbox.tsx +21 -8
  275. package/src/feedback/feedback-labels.ts +2 -1
  276. package/src/feedback/feedback-record.ts +10 -7
  277. package/src/feedback/feedback-row-detail.tsx +28 -4
  278. package/src/feedback/feedback-swipe.tsx +1 -1
  279. package/src/hooks/use-anchored-panel.ts +14 -3
  280. package/src/i18n/defaults.ts +28 -0
  281. package/src/i18n/german.ts +327 -5
  282. package/src/i18n/kit-labels.tsx +42 -0
  283. package/src/i18n/locales/es.ts +313 -3
  284. package/src/i18n/locales/fr.ts +316 -0
  285. package/src/i18n/locales/hu.ts +308 -5
  286. package/src/i18n/locales/it.ts +313 -0
  287. package/src/i18n/locales/zh.ts +286 -0
  288. package/src/i18n/review.ts +49 -0
  289. package/src/index.ts +184 -0
  290. package/src/landing/access.ts +97 -0
  291. package/src/landing/landing-actions.tsx +92 -0
  292. package/src/landing/landing-labels.ts +59 -0
  293. package/src/landing/landing-sections.tsx +337 -0
  294. package/src/landing/page-seo.ts +251 -0
  295. package/src/landing/public-header.tsx +120 -0
  296. package/src/landing/routing.tsx +209 -0
  297. package/src/settings/settings-catalogue.ts +132 -0
  298. package/src/settings/settings-context.ts +33 -0
  299. package/src/settings/settings-heading.tsx +65 -0
  300. package/src/settings/settings-labels.ts +75 -0
  301. package/src/settings/settings-layout.tsx +379 -0
  302. package/src/settings/settings-section.tsx +108 -0
  303. package/src/settings/use-account-language.ts +146 -0
  304. package/src/settings/use-settings-focus.ts +120 -0
  305. package/src/settings/use-settings-route.ts +258 -0
  306. package/src/shell/app-shell.tsx +10 -2
  307. package/src/shell.ts +45 -0
@@ -0,0 +1,132 @@
1
+ import type { ReactNode } from "react";
2
+ import type { SearchEntry } from "../search/search-index";
3
+
4
+ /**
5
+ * The settings catalogue (docs/settings-harmonization.md §4.1), lifted from keksdose's
6
+ * `settings-index.ts`: the groups a settings page is split into, and one entry per
7
+ * SETTING — not per card — so a search for "quiet hours" finds the field inside the
8
+ * notifications card. One list feeds the sidebar, the phone list, the in-page search and
9
+ * ⌘K ({@link settingsSearchEntries}); keksdose's page and its palette drifted when they
10
+ * had one each.
11
+ *
12
+ * The kit resolves no words: `title`, `help` and `keywords` arrive translated (an app's
13
+ * own, or the core groups' from `DEFAULT_SETTINGS_LABELS.groups`).
14
+ */
15
+
16
+ /** One group of the page: a sidebar row on a desktop, a list row on a phone (§3.2, §3.3). */
17
+ export interface SettingsGroup<G extends string = string> {
18
+ /** The path segment, `/settings/<id>`: lowercase, a–z and "-". */
19
+ id: G;
20
+ /** The row's icon — a lucide icon at the row's size is sized by the row. */
21
+ icon: ReactNode;
22
+ /** The group's name: the sidebar row, the heading over its cards, the phone list. */
23
+ title: string;
24
+ /** One line under the heading and in the phone list, where it is cut to one line. */
25
+ help: string;
26
+ /**
27
+ * `false` hides the group: no row, no hits, and its path acts as an unknown one — it
28
+ * lands on the default group (desktop) or the list (phone), never on a 404 (§3.1).
29
+ * kastlan's `/admin/billing` while billing is off. Default: visible.
30
+ */
31
+ visible?: boolean;
32
+ }
33
+
34
+ /** One setting, and the card that holds it (§4.1). */
35
+ export interface SettingsEntry<G extends string = string> {
36
+ /** Unique within the catalogue. */
37
+ id: string;
38
+ /** The group whose page shows the card. */
39
+ group: G;
40
+ /** The card's DOM id, unique on the page — the `?focus=` target. Several entries may
41
+ * share one: a card that holds four settings has four entries. */
42
+ anchor: string;
43
+ /** What a search hit says. */
44
+ title: string;
45
+ /** Localized, space-separated words people type that the title does not contain
46
+ * ("dark night" for the theme, "2fa totp" for two-factor). Never shown. */
47
+ keywords?: string;
48
+ /**
49
+ * `false` drops the entry from the page's search and ⌘K. A group whose entries are
50
+ * ALL hidden is hidden with them (§4.1): a demo session marks every card it is refused
51
+ * (passkeys, 2FA, export, deletion …) and so never sees a page of cards that each
52
+ * answer 403. Default: visible.
53
+ */
54
+ visible?: boolean;
55
+ }
56
+
57
+ /**
58
+ * The groups that show, in their order: not `visible: false`, and — when `entries` is
59
+ * given — holding at least one visible entry (§4.1: Kurvenschmiede's notifications group
60
+ * stays hidden until it has a card). Without `entries` only the groups' own flag counts.
61
+ */
62
+ export function visibleSettingsGroups<G extends string>(
63
+ groups: readonly SettingsGroup<G>[],
64
+ entries?: readonly SettingsEntry<G>[],
65
+ ): SettingsGroup<G>[] {
66
+ if (!entries) return groups.filter((g) => g.visible !== false);
67
+ const filled = new Set<G>();
68
+ for (const e of entries) if (e.visible !== false) filled.add(e.group);
69
+ return groups.filter((g) => g.visible !== false && filled.has(g.id));
70
+ }
71
+
72
+ /** The visible entries of the visible groups — what search may find. */
73
+ export function visibleSettingsEntries<G extends string>(
74
+ groups: readonly SettingsGroup<G>[],
75
+ entries: readonly SettingsEntry<G>[],
76
+ ): SettingsEntry<G>[] {
77
+ const shown = new Set(visibleSettingsGroups(groups, entries).map((g) => g.id));
78
+ return entries.filter((e) => e.visible !== false && shown.has(e.group));
79
+ }
80
+
81
+ /** `basePath` without a trailing slash: "/settings/" and "/settings" are one page. */
82
+ export function normalizeSettingsBase(basePath: string): string {
83
+ const trimmed = basePath.replace(/\/+$/, "");
84
+ return trimmed.startsWith("/") || trimmed === "" ? trimmed : `/${trimmed}`;
85
+ }
86
+
87
+ /** `/settings/<group>`, and `?focus=<anchor>` when a card is named (§3.4). */
88
+ export function settingsHref(basePath: string, group: string, anchor?: string): string {
89
+ const path = `${normalizeSettingsBase(basePath)}/${group}`;
90
+ return anchor ? `${path}?focus=${encodeURIComponent(anchor)}` : path;
91
+ }
92
+
93
+ /** "dark night" → ["dark", "night"]. */
94
+ export function settingsKeywords(keywords: string | undefined): string[] {
95
+ return keywords ? keywords.split(/\s+/).filter(Boolean) : [];
96
+ }
97
+
98
+ export interface SettingsSearchEntriesOptions {
99
+ /** `/settings` or `/admin` — the hits link to `<basePath>/<group>?focus=<anchor>`. */
100
+ basePath: string;
101
+ }
102
+
103
+ /**
104
+ * The catalogue as {@link GlobalSearch} entries (§3.4): each visible setting, under its
105
+ * group's name as the palette's section, linking to `/settings/<group>?focus=<anchor>` —
106
+ * the same link the page's own search hit is, so ⌘K, a tour and a mail all land the
107
+ * same way (§3.5). The group's name is a keyword too, as on the page: "security" lists
108
+ * the security group's settings.
109
+ *
110
+ * Ids are prefixed with the base path ("settings:theme", "admin:users") so a palette
111
+ * that also holds pages and actions keeps them unique.
112
+ */
113
+ export function settingsSearchEntries<G extends string>(
114
+ groups: readonly SettingsGroup<G>[],
115
+ entries: readonly SettingsEntry<G>[],
116
+ options: SettingsSearchEntriesOptions,
117
+ ): SearchEntry[] {
118
+ const base = normalizeSettingsBase(options.basePath);
119
+ const prefix = base.replace(/^\//, "").replace(/\//g, ":") || "settings";
120
+ const byId = new Map(visibleSettingsGroups(groups, entries).map((g) => [g.id, g]));
121
+ return visibleSettingsEntries(groups, entries).map((e) => {
122
+ const group = byId.get(e.group)!;
123
+ return {
124
+ id: `${prefix}:${e.id}`,
125
+ title: e.title,
126
+ keywords: [group.title, ...settingsKeywords(e.keywords)],
127
+ group: group.title,
128
+ icon: group.icon,
129
+ href: settingsHref(base, e.group, e.anchor),
130
+ };
131
+ });
132
+ }
@@ -0,0 +1,33 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { SettingsHeadingTag } from "./settings-heading";
3
+
4
+ /**
5
+ * What {@link SettingsLayout} hands the cards it renders (docs/settings-harmonization.md
6
+ * §3.1, §3.7). Router-free on purpose, so `SettingsSection` and an app's cards can read
7
+ * it without importing the layout.
8
+ */
9
+ export interface SettingsLayoutContextValue {
10
+ /** `/settings` or `/admin`, without a trailing slash. */
11
+ basePath: string;
12
+ /** The group on screen; `null` on a phone's list. */
13
+ group: string | null;
14
+ /** The path segment after the group — the surface a card owns (keksdose's swipe card). */
15
+ sub: string | null;
16
+ /**
17
+ * Select the card's sub-surface: `/settings/<group>/<sub>`, or the group alone with
18
+ * `null`. A replace, keeping the query and the history marker: switching a tab inside a
19
+ * card is not a place to go back to. The card never builds the path itself (§3.1).
20
+ */
21
+ selectSub: (sub: string | null) => void;
22
+ /** The level the cards' titles are at: `h3` on a desktop, `h2` on a phone. */
23
+ headingLevel: SettingsHeadingTag;
24
+ /** Whether the catalogue has an entry with this anchor — `SettingsSection`'s check. */
25
+ hasAnchor: (anchor: string) => boolean;
26
+ }
27
+
28
+ export const SettingsLayoutContext = createContext<SettingsLayoutContextValue | null>(null);
29
+
30
+ /** The {@link SettingsLayout} around this card, or `null` outside one. */
31
+ export function useSettingsLayout(): SettingsLayoutContextValue | null {
32
+ return useContext(SettingsLayoutContext);
33
+ }
@@ -0,0 +1,65 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
3
+ import { cn } from "../lib/cn";
4
+ import { CARD_TITLE_CLASS } from "../account/account-parts";
5
+
6
+ /** The heading a settings card's title becomes. */
7
+ export type SettingsHeadingTag = "h2" | "h3" | "h4";
8
+
9
+ const SettingsHeadingLevelContext = createContext<SettingsHeadingTag | null>(null);
10
+
11
+ export interface SettingsHeadingLevelProps {
12
+ /** The element every kit setting card below titles itself with. */
13
+ level: SettingsHeadingTag;
14
+ children?: ReactNode;
15
+ }
16
+
17
+ /**
18
+ * The level the kit's setting cards title themselves at (docs/settings-harmonization.md
19
+ * §3.7). Inside {@link SettingsLayout} a card's title is a heading under the group's —
20
+ * `h3` under the desktop's `h2`, `h2` under a phone group page's `h1` — and the layout
21
+ * sets this for every card it renders. Outside one, nothing is set and the same cards
22
+ * keep the plain `div` title they always had, so a card on an app's own page does not
23
+ * suddenly put an `h3` into an outline that has no `h2`.
24
+ *
25
+ * Set it yourself only where an app lays out settings cards without the layout and
26
+ * wants them in its outline: `<SettingsHeadingLevel level="h3">…</SettingsHeadingLevel>`.
27
+ * `ProfileSetting`, `PasswordSetting`, `TwoFactorSetting`, `PasskeysSetting`,
28
+ * `EmailChangeSetting`, `SessionsSetting`, `DataExportSetting`, `DeleteAccountSetting`
29
+ * and `SettingsSection` read it.
30
+ */
31
+ export function SettingsHeadingLevel({ level, children }: SettingsHeadingLevelProps) {
32
+ return <SettingsHeadingLevelContext.Provider value={level}>{children}</SettingsHeadingLevelContext.Provider>;
33
+ }
34
+
35
+ /** The nearest {@link SettingsHeadingLevel}, or `null` outside one. */
36
+ export function useSettingsHeadingLevel(): SettingsHeadingTag | null {
37
+ return useContext(SettingsHeadingLevelContext);
38
+ }
39
+
40
+ /**
41
+ * A setting card's title at the settings type scale: a heading inside a
42
+ * {@link SettingsHeadingLevel}, the plain `div` outside one.
43
+ *
44
+ * As a heading it takes `tabIndex={-1}`: `?focus=` moves focus to the card's heading so a
45
+ * screen reader lands on the card it was sent to (§3.5), and a heading is not focusable
46
+ * otherwise. No outline of its own — the ring round the card marks it.
47
+ *
48
+ * @internal The kit's cards use it; not part of the barrel.
49
+ */
50
+ export function SettingsCardTitle({ className, ...props }: ComponentPropsWithoutRef<"div">) {
51
+ const level = useSettingsHeadingLevel();
52
+ if (!level) return <div {...props} className={cn(CARD_TITLE_CLASS, className)} />;
53
+ return <SettingsHeading as={level} tabIndex={-1} {...props} className={cn(CARD_TITLE_CLASS, "outline-none", className)} />;
54
+ }
55
+
56
+ /**
57
+ * A heading at a level chosen at run time. Its own component, taking the tag as a prop
58
+ * (as `CardTitle` does), so the element type is a prop and not a value computed in the
59
+ * caller's render.
60
+ *
61
+ * @internal
62
+ */
63
+ export function SettingsHeading({ as: Tag, ...props }: { as: SettingsHeadingTag } & ComponentPropsWithoutRef<"h2">) {
64
+ return <Tag {...props} />;
65
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The `settings` namespace (docs/settings-harmonization.md §4.3): the settings page's
3
+ * own words, and the names and help lines of the five core groups every app shares —
4
+ * appearance, account, security, notifications, data (§2.6). An app's own groups bring
5
+ * their own words; any core label may be overridden like any kit label (prop >
6
+ * `<UiKitProvider labels={{ settings }}>` > English).
7
+ */
8
+
9
+ /** A core group's two strings, as {@link SettingsGroup} takes them. */
10
+ export interface SettingsGroupLabels {
11
+ /** The group's name. */
12
+ title: string;
13
+ /** One line: under the heading, and in the phone list. */
14
+ help: string;
15
+ }
16
+
17
+ /** The five groups whose words the kit ships (§2.6, §4.2). */
18
+ export type SettingsCoreGroup = "appearance" | "account" | "security" | "notifications" | "data";
19
+
20
+ export interface SettingsLabels {
21
+ /** The page's heading, and the back link's visible text on a phone's group page. */
22
+ title: string;
23
+ /** The search field's accessible name. */
24
+ search: string;
25
+ /** The search field's placeholder. */
26
+ searchPlaceholder: string;
27
+ /** The hit list's accessible name. */
28
+ results: string;
29
+ /** No hits for the trimmed query (§3.4). */
30
+ noMatches: (query: string) => string;
31
+ /** The button beside it that empties the field. */
32
+ clearSearch: string;
33
+ /** The phone's back link's accessible name: "‹ Settings" says where, this says what. */
34
+ back: string;
35
+ /** The sidebar's accessible name (the vertical tablist). */
36
+ sections: string;
37
+ /** A sidebar row's hit badge, read in place of the bare number while searching. */
38
+ matchCount: (count: number) => string;
39
+ /** The core groups' names and help lines. */
40
+ groups: Record<SettingsCoreGroup, SettingsGroupLabels>;
41
+ }
42
+
43
+ export const DEFAULT_SETTINGS_LABELS: SettingsLabels = {
44
+ title: "Settings",
45
+ search: "Search settings",
46
+ searchPlaceholder: "Search settings…",
47
+ results: "Matching settings",
48
+ noMatches: (query) => `No settings match “${query}”.`,
49
+ clearSearch: "Clear search",
50
+ back: "Back to settings",
51
+ sections: "Settings sections",
52
+ matchCount: (count) => (count === 1 ? "1 match" : `${count} matches`),
53
+ groups: {
54
+ appearance: {
55
+ title: "Appearance",
56
+ help: "Language, theme and how the app looks on this device.",
57
+ },
58
+ account: {
59
+ title: "Account",
60
+ help: "Your profile and your email address.",
61
+ },
62
+ security: {
63
+ title: "Security",
64
+ help: "How you sign in: password, two-factor codes, passkeys and sessions.",
65
+ },
66
+ notifications: {
67
+ title: "Notifications",
68
+ help: "What you are told about, when, and on which devices.",
69
+ },
70
+ data: {
71
+ title: "Data",
72
+ help: "Export what is yours, or delete it together with your account.",
73
+ },
74
+ },
75
+ };