@eifi1/ui-kit 0.31.0 → 0.31.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.
- package/README.md +5 -5
- package/dist/account/delete-account-setting.d.ts +1 -1
- package/dist/admin/admin-action-confirm.d.ts +1 -1
- package/dist/admin/admin-parts.d.ts +8 -1
- package/dist/admin/admin-parts.js +1 -1
- package/dist/admin/admin-parts.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +1 -1
- package/dist/admin/reviewer-scope-editor.d.ts +1 -1
- package/dist/admin/role-select.d.ts +1 -1
- package/dist/admin/transfer-ownership-dialog.d.ts +1 -1
- package/dist/admin/user-roster.d.ts +1 -1
- package/dist/auth/register-form.d.ts +1 -1
- package/dist/auth/reset-password-form.d.ts +1 -1
- package/dist/auth/reset-password-form.js +2 -1
- package/dist/auth/reset-password-form.js.map +1 -1
- package/dist/auth/verify-email.d.ts +4 -0
- package/dist/auth/verify-email.js +3 -2
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/chart.d.ts +1 -1
- package/dist/components/amount-input.d.ts +1 -1
- package/dist/components/button-group.d.ts +1 -1
- package/dist/components/calculator.d.ts +1 -1
- package/dist/components/confirm-dialog.d.ts +1 -1
- package/dist/components/confirm-dialog.js +7 -1
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +1 -1
- package/dist/components/danger-confirm.d.ts +1 -1
- package/dist/components/facing-pair.d.ts +1 -1
- package/dist/components/file-button.d.ts +1 -1
- package/dist/components/file-dropzone.d.ts +1 -1
- package/dist/components/form-actions.d.ts +1 -1
- package/dist/components/iban-input.d.ts +1 -1
- package/dist/components/language-select.d.ts +1 -1
- package/dist/components/legal-page.d.ts +1 -1
- package/dist/components/legal.d.ts +1 -1
- package/dist/components/money-field.d.ts +1 -1
- package/dist/components/number-field.d.ts +1 -1
- package/dist/components/number-input.d.ts +1 -1
- package/dist/components/numpad-sheet.d.ts +1 -1
- package/dist/components/phone-input.d.ts +1 -1
- package/dist/components/series-chart.d.ts +1 -1
- package/dist/components/settings-fields.d.ts +1 -1
- package/dist/components/share-card.d.ts +1 -1
- package/dist/components/text-link.d.ts +1 -1
- package/dist/components/time-input.d.ts +1 -1
- package/dist/components/ui.d.ts +1 -1
- package/dist/{delete-account-setting-DCFS5Vof.d.ts → delete-account-setting-CpIhzxxZ.d.ts} +8 -0
- package/dist/feedback/feedback-attachment.d.ts +1 -1
- package/dist/feedback/feedback-context.d.ts +1 -1
- package/dist/feedback/feedback-crash.d.ts +1 -1
- package/dist/feedback/feedback-dialog.d.ts +1 -1
- package/dist/feedback/feedback-inbox.d.ts +1 -1
- package/dist/feedback/feedback-labels.d.ts +1 -1
- package/dist/feedback/feedback-menu.d.ts +1 -1
- package/dist/feedback/feedback-record.d.ts +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +1 -1
- package/dist/feedback/feedback-submit.d.ts +1 -1
- package/dist/feedback/feedback-swipe.d.ts +1 -1
- package/dist/feedback/feedback-table.d.ts +1 -1
- package/dist/feedback/feedback-thread.d.ts +1 -1
- package/dist/feedback.d.ts +1 -1
- package/dist/hooks/use-file-drop.d.ts +1 -1
- package/dist/i18n/defaults.d.ts +1 -1
- package/dist/i18n/german.d.ts +1 -1
- package/dist/i18n/german.js +2 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +1 -1
- package/dist/i18n/languages.d.ts +1 -1
- package/dist/i18n/locales/de-CH.d.ts +1 -1
- package/dist/i18n/locales/en.d.ts +1 -1
- package/dist/i18n/locales/es.d.ts +1 -1
- package/dist/i18n/locales/es.js +2 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +1 -1
- package/dist/i18n/locales/fr.js +2 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +1 -1
- package/dist/i18n/locales/hu.js +2 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +1 -1
- package/dist/i18n/locales/it.js +2 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +1 -1
- package/dist/i18n/locales/zh.js +2 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +1 -1
- package/dist/i18n/review.js +1 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +5 -3
- package/dist/landing/landing-actions.js +1 -1
- package/dist/landing/landing-actions.js.map +1 -1
- package/dist/landing/landing-sections.d.ts +1 -1
- package/dist/landing/public-header.d.ts +1 -1
- package/dist/landing/routing.d.ts +20 -7
- package/dist/landing/routing.js +5 -2
- package/dist/landing/routing.js.map +1 -1
- package/dist/rhf/fields.d.ts +1 -1
- package/dist/rhf/form.d.ts +1 -1
- package/dist/rhf.d.ts +1 -1
- package/dist/settings/settings-labels.d.ts +8 -1
- package/dist/settings/settings-labels.js +7 -2
- package/dist/settings/settings-labels.js.map +1 -1
- package/dist/settings/settings-section.d.ts +1 -1
- package/dist/shell/app-shell.d.ts +1 -1
- package/dist/shell/top-bar-brand.d.ts +1 -1
- package/dist/shell.d.ts +2 -2
- package/dist/shell.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +1 -1
- package/dist/wizard.d.ts +1 -1
- package/package.json +1 -1
- package/src/admin/admin-parts.ts +9 -2
- package/src/auth/reset-password-form.tsx +6 -2
- package/src/auth/verify-email.tsx +14 -2
- package/src/components/confirm-dialog.tsx +20 -1
- package/src/i18n/german.ts +2 -1
- package/src/i18n/locales/es.ts +2 -1
- package/src/i18n/locales/fr.ts +2 -1
- package/src/i18n/locales/hu.ts +2 -1
- package/src/i18n/locales/it.ts +2 -1
- package/src/i18n/locales/zh.ts +2 -1
- package/src/i18n/review.ts +1 -0
- package/src/index.ts +8 -2
- package/src/landing/landing-actions.tsx +9 -5
- package/src/landing/routing.tsx +22 -5
- package/src/settings/settings-labels.ts +13 -1
- package/src/shell.ts +7 -1
package/dist/rhf.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { RhfCheckbox, RhfCheckboxProps, RhfCombobox, RhfComboboxProps, RhfCountr
|
|
|
4
4
|
export { RhfLineItemCellContext, RhfLineItems, RhfLineItemsColumn, RhfLineItemsProps } from './rhf/line-items.js';
|
|
5
5
|
import 'react';
|
|
6
6
|
import 'react-hook-form';
|
|
7
|
-
import './delete-account-setting-
|
|
7
|
+
import './delete-account-setting-CpIhzxxZ.js';
|
|
8
8
|
import './components/tooltip.js';
|
|
9
9
|
import './hooks/use-anchored-rect.js';
|
|
10
10
|
import './lib/clipping.js';
|
|
@@ -37,5 +37,12 @@ interface SettingsLabels {
|
|
|
37
37
|
groups: Record<SettingsCoreGroup, SettingsGroupLabels>;
|
|
38
38
|
}
|
|
39
39
|
declare const DEFAULT_SETTINGS_LABELS: SettingsLabels;
|
|
40
|
+
/**
|
|
41
|
+
* 0.31.1: the `settings` namespace, resolved — English, then the provider, then `labels`.
|
|
42
|
+
* What an app reads for its catalogue's core group titles and help lines, and for the
|
|
43
|
+
* page title "<group> · Settings · <app>" (settings contract §3.1), without restating
|
|
44
|
+
* the namespace and its defaults (Kurvenschmiede's 0.31 adoption).
|
|
45
|
+
*/
|
|
46
|
+
declare function useSettingsLabels(labels?: Partial<SettingsLabels>): SettingsLabels;
|
|
40
47
|
|
|
41
|
-
export { DEFAULT_SETTINGS_LABELS, type SettingsCoreGroup, type SettingsGroupLabels, type SettingsLabels };
|
|
48
|
+
export { DEFAULT_SETTINGS_LABELS, type SettingsCoreGroup, type SettingsGroupLabels, type SettingsLabels, useSettingsLabels };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useKitLabels } from "../i18n/kit-labels.js";
|
|
2
3
|
const DEFAULT_SETTINGS_LABELS = {
|
|
3
4
|
title: "Settings",
|
|
4
5
|
search: "Search settings",
|
|
@@ -20,7 +21,7 @@ const DEFAULT_SETTINGS_LABELS = {
|
|
|
20
21
|
},
|
|
21
22
|
security: {
|
|
22
23
|
title: "Security",
|
|
23
|
-
help: "How you sign in
|
|
24
|
+
help: "How you sign in, and where you are signed in."
|
|
24
25
|
},
|
|
25
26
|
notifications: {
|
|
26
27
|
title: "Notifications",
|
|
@@ -32,7 +33,11 @@ const DEFAULT_SETTINGS_LABELS = {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
};
|
|
36
|
+
function useSettingsLabels(labels) {
|
|
37
|
+
return useKitLabels("settings", DEFAULT_SETTINGS_LABELS, labels);
|
|
38
|
+
}
|
|
35
39
|
export {
|
|
36
|
-
DEFAULT_SETTINGS_LABELS
|
|
40
|
+
DEFAULT_SETTINGS_LABELS,
|
|
41
|
+
useSettingsLabels
|
|
37
42
|
};
|
|
38
43
|
//# sourceMappingURL=settings-labels.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/settings/settings-labels.ts"],"sourcesContent":["/**\n * The `settings` namespace (docs/settings-harmonization.md §4.3): the settings page's\n * own words, and the names and help lines of the five core groups every app shares —\n * appearance, account, security, notifications, data (§2.6). An app's own groups bring\n * their own words; any core label may be overridden like any kit label (prop >\n * `<UiKitProvider labels={{ settings }}>` > English).\n */\n\n/** A core group's two strings, as {@link SettingsGroup} takes them. */\nexport interface SettingsGroupLabels {\n /** The group's name. */\n title: string;\n /** One line: under the heading, and in the phone list. */\n help: string;\n}\n\n/** The five groups whose words the kit ships (§2.6, §4.2). */\nexport type SettingsCoreGroup = \"appearance\" | \"account\" | \"security\" | \"notifications\" | \"data\";\n\nexport interface SettingsLabels {\n /** The page's heading, and the back link's visible text on a phone's group page. */\n title: string;\n /** The search field's accessible name. */\n search: string;\n /** The search field's placeholder. */\n searchPlaceholder: string;\n /** The hit list's accessible name. */\n results: string;\n /** No hits for the trimmed query (§3.4). */\n noMatches: (query: string) => string;\n /** The button beside it that empties the field. */\n clearSearch: string;\n /** The phone's back link's accessible name: \"‹ Settings\" says where, this says what. */\n back: string;\n /** The sidebar's accessible name (the vertical tablist). */\n sections: string;\n /** A sidebar row's hit badge, read in place of the bare number while searching. */\n matchCount: (count: number) => string;\n /** The core groups' names and help lines. */\n groups: Record<SettingsCoreGroup, SettingsGroupLabels>;\n}\n\nexport const DEFAULT_SETTINGS_LABELS: SettingsLabels = {\n title: \"Settings\",\n search: \"Search settings\",\n searchPlaceholder: \"Search settings…\",\n results: \"Matching settings\",\n noMatches: (query) => `No settings match “${query}”.`,\n clearSearch: \"Clear search\",\n back: \"Back to settings\",\n sections: \"Settings sections\",\n matchCount: (count) => (count === 1 ? \"1 match\" : `${count} matches`),\n groups: {\n appearance: {\n title: \"Appearance\",\n help: \"Language, theme and how the app looks on this device.\",\n },\n account: {\n title: \"Account\",\n help: \"Your profile and your email address.\",\n },\n security: {\n title: \"Security\",\n help: \"How you sign in
|
|
1
|
+
{"version":3,"sources":["../../src/settings/settings-labels.ts"],"sourcesContent":["import { useKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * The `settings` namespace (docs/settings-harmonization.md §4.3): the settings page's\n * own words, and the names and help lines of the five core groups every app shares —\n * appearance, account, security, notifications, data (§2.6). An app's own groups bring\n * their own words; any core label may be overridden like any kit label (prop >\n * `<UiKitProvider labels={{ settings }}>` > English).\n */\n\n/** A core group's two strings, as {@link SettingsGroup} takes them. */\nexport interface SettingsGroupLabels {\n /** The group's name. */\n title: string;\n /** One line: under the heading, and in the phone list. */\n help: string;\n}\n\n/** The five groups whose words the kit ships (§2.6, §4.2). */\nexport type SettingsCoreGroup = \"appearance\" | \"account\" | \"security\" | \"notifications\" | \"data\";\n\nexport interface SettingsLabels {\n /** The page's heading, and the back link's visible text on a phone's group page. */\n title: string;\n /** The search field's accessible name. */\n search: string;\n /** The search field's placeholder. */\n searchPlaceholder: string;\n /** The hit list's accessible name. */\n results: string;\n /** No hits for the trimmed query (§3.4). */\n noMatches: (query: string) => string;\n /** The button beside it that empties the field. */\n clearSearch: string;\n /** The phone's back link's accessible name: \"‹ Settings\" says where, this says what. */\n back: string;\n /** The sidebar's accessible name (the vertical tablist). */\n sections: string;\n /** A sidebar row's hit badge, read in place of the bare number while searching. */\n matchCount: (count: number) => string;\n /** The core groups' names and help lines. */\n groups: Record<SettingsCoreGroup, SettingsGroupLabels>;\n}\n\nexport const DEFAULT_SETTINGS_LABELS: SettingsLabels = {\n title: \"Settings\",\n search: \"Search settings\",\n searchPlaceholder: \"Search settings…\",\n results: \"Matching settings\",\n noMatches: (query) => `No settings match “${query}”.`,\n clearSearch: \"Clear search\",\n back: \"Back to settings\",\n sections: \"Settings sections\",\n matchCount: (count) => (count === 1 ? \"1 match\" : `${count} matches`),\n groups: {\n appearance: {\n title: \"Appearance\",\n help: \"Language, theme and how the app looks on this device.\",\n },\n account: {\n title: \"Account\",\n help: \"Your profile and your email address.\",\n },\n security: {\n title: \"Security\",\n help: \"How you sign in, and where you are signed in.\",\n },\n notifications: {\n title: \"Notifications\",\n help: \"What you are told about, when, and on which devices.\",\n },\n data: {\n title: \"Data\",\n help: \"Export what is yours, or delete it together with your account.\",\n },\n },\n};\n\n/**\n * 0.31.1: the `settings` namespace, resolved — English, then the provider, then `labels`.\n * What an app reads for its catalogue's core group titles and help lines, and for the\n * page title \"<group> · Settings · <app>\" (settings contract §3.1), without restating\n * the namespace and its defaults (Kurvenschmiede's 0.31 adoption).\n */\nexport function useSettingsLabels(labels?: Partial<SettingsLabels>): SettingsLabels {\n return useKitLabels(\"settings\", DEFAULT_SETTINGS_LABELS, labels);\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AA4CtB,MAAM,0BAA0C;AAAA,EACrD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,WAAW,CAAC,UAAU,2BAAsB,KAAK;AAAA,EACjD,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,YAAY,CAAC,UAAW,UAAU,IAAI,YAAY,GAAG,KAAK;AAAA,EAC1D,QAAQ;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,IACA,UAAU;AAAA,MACR,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,IACA,eAAe;AAAA,MACb,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAQO,SAAS,kBAAkB,QAAkD;AAClF,SAAO,aAAa,YAAY,yBAAyB,MAAM;AACjE;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
3
|
-
import { bZ as CardTone } from '../delete-account-setting-
|
|
3
|
+
import { bZ as CardTone } from '../delete-account-setting-CpIhzxxZ.js';
|
|
4
4
|
import '../components/tooltip.js';
|
|
5
5
|
import '../hooks/use-anchored-rect.js';
|
|
6
6
|
import '../lib/clipping.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
3
3
|
import { LucideIcon } from 'lucide-react';
|
|
4
|
-
import { dR as KitLinkComponent } from '../delete-account-setting-
|
|
4
|
+
import { dR as KitLinkComponent } from '../delete-account-setting-CpIhzxxZ.js';
|
|
5
5
|
import '../components/tooltip.js';
|
|
6
6
|
import '../hooks/use-anchored-rect.js';
|
|
7
7
|
import '../lib/clipping.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { dS as KitLinkProps, dR as KitLinkComponent } from '../delete-account-setting-
|
|
3
|
+
import { dS as KitLinkProps, dR as KitLinkComponent } from '../delete-account-setting-CpIhzxxZ.js';
|
|
4
4
|
import '../components/tooltip.js';
|
|
5
5
|
import '../hooks/use-anchored-rect.js';
|
|
6
6
|
import '../lib/clipping.js';
|
package/dist/shell.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { SettingsSection, SettingsSectionProps } from './settings/settings-secti
|
|
|
13
13
|
export { SettingsLayoutContextValue, useSettingsLayout } from './settings/settings-context.js';
|
|
14
14
|
export { SettingsHeadingLevel, SettingsHeadingLevelProps, SettingsHeadingTag, useSettingsHeadingLevel } from './settings/settings-heading.js';
|
|
15
15
|
export { SettingsEntry, SettingsGroup, SettingsSearchEntriesOptions, settingsHref, settingsSearchEntries, visibleSettingsEntries, visibleSettingsGroups } from './settings/settings-catalogue.js';
|
|
16
|
-
export { DEFAULT_LAST_VISITED_EXCLUDES, LastVisitedPageOptions, NEXT_PARAM, PathPattern, RedirectIfAuthed, RedirectIfAuthedProps, RootEntry, RootEntryProps, clearLastVisitedPage, readLastVisitedPage, safeNextPath, useLastVisitedPage } from './landing/routing.js';
|
|
16
|
+
export { DEFAULT_LAST_VISITED_EXCLUDES, LastVisitedPageOptions, NEXT_PARAM, PathPattern, RedirectIfAuthed, RedirectIfAuthedProps, RootEntry, RootEntryProps, RoutingSession, clearLastVisitedPage, readLastVisitedPage, safeNextPath, useLastVisitedPage } from './landing/routing.js';
|
|
17
17
|
export { PublicHeader, PublicHeaderBrand, PublicHeaderProps } from './landing/public-header.js';
|
|
18
18
|
import 'react';
|
|
19
19
|
import './theme/palette-presets.js';
|
|
@@ -22,7 +22,7 @@ import './theme/theme-store.js';
|
|
|
22
22
|
import 'zustand/middleware';
|
|
23
23
|
import 'zustand';
|
|
24
24
|
import 'lucide-react';
|
|
25
|
-
import './delete-account-setting-
|
|
25
|
+
import './delete-account-setting-CpIhzxxZ.js';
|
|
26
26
|
import './components/tooltip.js';
|
|
27
27
|
import './hooks/use-anchored-rect.js';
|
|
28
28
|
import './lib/clipping.js';
|
package/dist/shell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell.ts"],"sourcesContent":["// `@eifi1/ui-kit/shell` — the composable app chrome.\n//\n// A re-slicing of the main barrel, not a new API. This entry point requires\n// `react-router`: AppShell renders NavLink and TopBarActionMenu renders Link.\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\nexport * from \"./shell/top-bar-brand\";\nexport * from \"./shell/auth-layout\";\n// 0.31.0: the settings and admin pages' shell (docs/settings-harmonization.md §3, §5).\nexport { SettingsLayout, SETTINGS_SEARCH_THRESHOLD } from \"./settings/settings-layout\";\nexport type { SettingsLayoutProps, SettingsLayoutWidth } from \"./settings/settings-layout\";\nexport {\n useSettingsRoute,\n resolveSettingsLocation,\n settingsFromListState,\n SETTINGS_FROM_LIST_STATE,\n} from \"./settings/use-settings-route\";\nexport type {\n UseSettingsRouteOptions,\n SettingsRoute,\n SettingsLayoutMode,\n SettingsLocation,\n SettingsLocationRules,\n SettingsLocationResult,\n} from \"./settings/use-settings-route\";\nexport { useSettingsFocus, SETTINGS_FOCUS_RING, SETTINGS_FOCUS_MS } from \"./settings/use-settings-focus\";\nexport { SettingsSection } from \"./settings/settings-section\";\nexport type { SettingsSectionProps } from \"./settings/settings-section\";\nexport { useSettingsLayout } from \"./settings/settings-context\";\nexport type { SettingsLayoutContextValue } from \"./settings/settings-context\";\nexport { SettingsHeadingLevel, useSettingsHeadingLevel } from \"./settings/settings-heading\";\nexport type { SettingsHeadingLevelProps, SettingsHeadingTag } from \"./settings/settings-heading\";\nexport {\n settingsSearchEntries,\n visibleSettingsGroups,\n visibleSettingsEntries,\n settingsHref,\n} from \"./settings/settings-catalogue\";\nexport type { SettingsGroup, SettingsEntry, SettingsSearchEntriesOptions } from \"./settings/settings-catalogue\";\n// 0.31.0: the public pages' header and the resume (docs/landing-demo-harmonization.md §3, §4.1).\nexport {\n RootEntry,\n RedirectIfAuthed,\n useLastVisitedPage,\n readLastVisitedPage,\n clearLastVisitedPage,\n safeNextPath,\n NEXT_PARAM,\n DEFAULT_LAST_VISITED_EXCLUDES,\n} from \"./landing/routing\";\nexport type {
|
|
1
|
+
{"version":3,"sources":["../src/shell.ts"],"sourcesContent":["// `@eifi1/ui-kit/shell` — the composable app chrome.\n//\n// A re-slicing of the main barrel, not a new API. This entry point requires\n// `react-router`: AppShell renders NavLink and TopBarActionMenu renders Link.\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\nexport * from \"./shell/top-bar-brand\";\nexport * from \"./shell/auth-layout\";\n// 0.31.0: the settings and admin pages' shell (docs/settings-harmonization.md §3, §5).\nexport { SettingsLayout, SETTINGS_SEARCH_THRESHOLD } from \"./settings/settings-layout\";\nexport type { SettingsLayoutProps, SettingsLayoutWidth } from \"./settings/settings-layout\";\nexport {\n useSettingsRoute,\n resolveSettingsLocation,\n settingsFromListState,\n SETTINGS_FROM_LIST_STATE,\n} from \"./settings/use-settings-route\";\nexport type {\n UseSettingsRouteOptions,\n SettingsRoute,\n SettingsLayoutMode,\n SettingsLocation,\n SettingsLocationRules,\n SettingsLocationResult,\n} from \"./settings/use-settings-route\";\nexport { useSettingsFocus, SETTINGS_FOCUS_RING, SETTINGS_FOCUS_MS } from \"./settings/use-settings-focus\";\nexport { SettingsSection } from \"./settings/settings-section\";\nexport type { SettingsSectionProps } from \"./settings/settings-section\";\nexport { useSettingsLayout } from \"./settings/settings-context\";\nexport type { SettingsLayoutContextValue } from \"./settings/settings-context\";\nexport { SettingsHeadingLevel, useSettingsHeadingLevel } from \"./settings/settings-heading\";\nexport type { SettingsHeadingLevelProps, SettingsHeadingTag } from \"./settings/settings-heading\";\nexport {\n settingsSearchEntries,\n visibleSettingsGroups,\n visibleSettingsEntries,\n settingsHref,\n} from \"./settings/settings-catalogue\";\nexport type { SettingsGroup, SettingsEntry, SettingsSearchEntriesOptions } from \"./settings/settings-catalogue\";\n// 0.31.0: the public pages' header and the resume (docs/landing-demo-harmonization.md §3, §4.1).\nexport {\n RootEntry,\n RedirectIfAuthed,\n useLastVisitedPage,\n readLastVisitedPage,\n clearLastVisitedPage,\n safeNextPath,\n NEXT_PARAM,\n DEFAULT_LAST_VISITED_EXCLUDES,\n} from \"./landing/routing\";\nexport type {\n RootEntryProps,\n RedirectIfAuthedProps,\n RoutingSession,\n LastVisitedPageOptions,\n PathPattern,\n} from \"./landing/routing\";\nexport { PublicHeader } from \"./landing/public-header\";\nexport type { PublicHeaderProps, PublicHeaderBrand } from \"./landing/public-header\";\n"],"mappings":";AAIA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,SAAS,gBAAgB,iCAAiC;AAE1D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,SAAS,kBAAkB,qBAAqB,yBAAyB;AACzE,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAElC,SAAS,sBAAsB,+BAA+B;AAE9D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,SAAS,oBAAoB;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
|
-
import { bH as ButtonVariant } from '../delete-account-setting-
|
|
3
|
+
import { bH as ButtonVariant } from '../delete-account-setting-CpIhzxxZ.js';
|
|
4
4
|
import { UseWizardReturn, WizardLabels } from './types.js';
|
|
5
5
|
import '../components/tooltip.js';
|
|
6
6
|
import '../hooks/use-anchored-rect.js';
|
package/dist/wizard.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { WizardStep, WizardStepProps } from './wizard/wizard-step.js';
|
|
|
6
6
|
export { WizardSummary } from './wizard/wizard-summary.js';
|
|
7
7
|
export { StepperNav } from './wizard/stepper-nav.js';
|
|
8
8
|
import 'react';
|
|
9
|
-
import './delete-account-setting-
|
|
9
|
+
import './delete-account-setting-CpIhzxxZ.js';
|
|
10
10
|
import './components/tooltip.js';
|
|
11
11
|
import './hooks/use-anchored-rect.js';
|
|
12
12
|
import './lib/clipping.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eifi1/ui-kit",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
|
|
6
6
|
"_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
|
package/src/admin/admin-parts.ts
CHANGED
|
@@ -16,11 +16,18 @@ import { formatPersonName } from "../lib/person-name";
|
|
|
16
16
|
export type MaybePromise<T = unknown> = void | Promise<T>;
|
|
17
17
|
|
|
18
18
|
/** A person as the admin parts show one: the two name parts of the auth contract
|
|
19
|
-
* (§3.2), an older whole `name` as the fallback, and the address.
|
|
19
|
+
* (§3.2), an older whole `name` as the fallback, and the address. The name parts are
|
|
20
|
+
* read in either spelling — the kit's `first` / `last`, or server-kit's `PersonRef`
|
|
21
|
+
* as it serialises (`first_name` / `last_name`), so an app hands the server's object
|
|
22
|
+
* over as it came (keksdose's and kastlan's 0.30 adoptions mapped one to the other). */
|
|
20
23
|
export interface AdminPerson {
|
|
21
24
|
id?: string | number | null;
|
|
22
25
|
first?: string | null;
|
|
23
26
|
last?: string | null;
|
|
27
|
+
/** server-kit `PersonRef.first_name`; `first` wins when both are given. */
|
|
28
|
+
first_name?: string | null;
|
|
29
|
+
/** server-kit `PersonRef.last_name`; `last` wins when both are given. */
|
|
30
|
+
last_name?: string | null;
|
|
24
31
|
/** A whole name, for an API that still sends `display_name` only. */
|
|
25
32
|
name?: string | null;
|
|
26
33
|
email?: string | null;
|
|
@@ -39,7 +46,7 @@ export function hasMessage(node: ReactNode): boolean {
|
|
|
39
46
|
export function personLabel(person: AdminPerson | null | undefined, locale: string | undefined): string {
|
|
40
47
|
if (!person) return "";
|
|
41
48
|
return (
|
|
42
|
-
formatPersonName({ first: person.first, last: person.last }, locale) ||
|
|
49
|
+
formatPersonName({ first: person.first ?? person.first_name, last: person.last ?? person.last_name }, locale) ||
|
|
43
50
|
(person.name ?? "").trim() ||
|
|
44
51
|
(person.email ?? "").trim()
|
|
45
52
|
);
|
|
@@ -144,7 +144,7 @@ export interface ResetPasswordFormProps
|
|
|
144
144
|
forgotHref?: string;
|
|
145
145
|
/** The app's words for a failed save (the device offline), or `undefined` for the
|
|
146
146
|
* kit's: `rateLimited` for a bare `429` (0.30.0), else `failed`. Not asked for
|
|
147
|
-
* `token_invalid`, which
|
|
147
|
+
* `token_invalid` or `token_expired`, which are a state. */
|
|
148
148
|
describeError?: (error: unknown) => ReactNode | undefined;
|
|
149
149
|
/** The heading. Default `labels.title`; `null` draws none, for a page that puts it
|
|
150
150
|
* in `AuthLayout`'s `title` instead. */
|
|
@@ -299,7 +299,11 @@ export function ResetPasswordForm({
|
|
|
299
299
|
},
|
|
300
300
|
(error: unknown) => {
|
|
301
301
|
setPending(false);
|
|
302
|
-
|
|
302
|
+
// A link that died between the check and the save — used, or run out — is the
|
|
303
|
+
// dead-link state, under either code (0.31.1: keksdose mapped `token_expired`
|
|
304
|
+
// to it itself, `asDeadLink`).
|
|
305
|
+
const code = authErrorCode(error);
|
|
306
|
+
if (code === "token_invalid" || code === "token_expired") {
|
|
303
307
|
setPassword("");
|
|
304
308
|
setConfirm("");
|
|
305
309
|
setPhase({ token, phase: { kind: "invalid" } });
|
|
@@ -8,7 +8,7 @@ import { AlertBanner } from "../components/alert-banner";
|
|
|
8
8
|
import { LoadingState } from "../components/loading-state";
|
|
9
9
|
import { Button, EmptyState } from "../components/ui";
|
|
10
10
|
import { OUTCOME_CLASS, STATUS_HEADING_CLASS, isThenable, useOncePerKey } from "./status-parts";
|
|
11
|
-
import { authErrorCode } from "./auth-errors";
|
|
11
|
+
import { authErrorCode, englishRateLimited, isRateLimited, retryAfterSeconds } from "./auth-errors";
|
|
12
12
|
import type { AuthHeadingLevel } from "./status-parts";
|
|
13
13
|
|
|
14
14
|
/* ── Labels ──────────────────────────────────────────────────────────────── */
|
|
@@ -41,6 +41,10 @@ export interface VerifyEmailLabels {
|
|
|
41
41
|
resent: string;
|
|
42
42
|
/** Said (and shown) after a resend failed. */
|
|
43
43
|
resendError: string;
|
|
44
|
+
/** 0.31.1: a resend refused as throttled — HTTP 429 (`isRateLimited`) — given the
|
|
45
|
+
* `Retry-After` seconds, when the app's `describeError` has no words of its own
|
|
46
|
+
* (keksdose borrowed the sign-in namespace's). */
|
|
47
|
+
rateLimited?: (seconds?: number) => string;
|
|
44
48
|
/** The banner's message. */
|
|
45
49
|
banner: string;
|
|
46
50
|
/** The banner's ×: it defers, it does not close anything. */
|
|
@@ -62,6 +66,7 @@ export const DEFAULT_VERIFY_EMAIL_LABELS: VerifyEmailLabels = {
|
|
|
62
66
|
resendIn: (seconds) => `Send again in ${seconds} s`,
|
|
63
67
|
resent: "Confirmation email sent",
|
|
64
68
|
resendError: "Could not send the confirmation email",
|
|
69
|
+
rateLimited: englishRateLimited,
|
|
65
70
|
banner: "Please confirm your email address.",
|
|
66
71
|
dismiss: "Not now",
|
|
67
72
|
};
|
|
@@ -160,7 +165,14 @@ function ResendStatus({
|
|
|
160
165
|
labels: VerifyEmailLabels;
|
|
161
166
|
className?: string;
|
|
162
167
|
}) {
|
|
163
|
-
|
|
168
|
+
// The app's words first, then the throttle's, then the kit's catch-all.
|
|
169
|
+
const failed =
|
|
170
|
+
phase === "failed"
|
|
171
|
+
? (describeError?.(error) ??
|
|
172
|
+
(isRateLimited(error)
|
|
173
|
+
? (labels.rateLimited ?? englishRateLimited)(retryAfterSeconds(error))
|
|
174
|
+
: labels.resendError))
|
|
175
|
+
: "";
|
|
164
176
|
return (
|
|
165
177
|
<span
|
|
166
178
|
role="status"
|
|
@@ -6,6 +6,7 @@ import { TypedConfirmField, typedMatches } from "./danger-confirm";
|
|
|
6
6
|
import type { TypedMatch } from "./danger-confirm";
|
|
7
7
|
import { DialogFrame } from "./dialog-frame";
|
|
8
8
|
import { Button } from "./ui";
|
|
9
|
+
import { useWriteLock } from "./write-lock";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
|
|
@@ -76,6 +77,14 @@ export interface ConfirmOptions {
|
|
|
76
77
|
* keeps spaces too — for a phrase like "DELETE" whose contract is "exactly this".
|
|
77
78
|
*/
|
|
78
79
|
typedMatch?: TypedMatch;
|
|
80
|
+
/**
|
|
81
|
+
* 0.31.1: the confirm button COMMITS, as `Button`'s `commit` does. Under a locked
|
|
82
|
+
* `WriteLockProvider` — read where `useConfirm` was called, not where the one dialog
|
|
83
|
+
* host sits — it is `aria-disabled` with the lock's reason, so a read-only page (a
|
|
84
|
+
* demo, a viewer's budget) can't confirm a delete through a dialog. One prop for the
|
|
85
|
+
* many confirm sites an app has (kastlan's 0.31 adoption counted ~23).
|
|
86
|
+
*/
|
|
87
|
+
commit?: boolean;
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
export type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
|
|
@@ -97,12 +106,21 @@ export function useConfirm(): ConfirmFn {
|
|
|
97
106
|
if (!confirm) {
|
|
98
107
|
throw new Error("useConfirm() must be called below a <ConfirmProvider>.");
|
|
99
108
|
}
|
|
100
|
-
|
|
109
|
+
// The lock of the CALLER's place: the dialog host usually sits above the app's
|
|
110
|
+
// `WriteLockProvider`, so it could not read the lock itself.
|
|
111
|
+
const lock = useWriteLock();
|
|
112
|
+
return useCallback(
|
|
113
|
+
(options: ConfirmOptions) =>
|
|
114
|
+
confirm(options.commit && lock.locked ? ({ ...options, lockReason: lock.reason } as ConfirmOptions) : options),
|
|
115
|
+
[confirm, lock.locked, lock.reason],
|
|
116
|
+
);
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
interface Request extends ConfirmOptions {
|
|
104
120
|
id: number;
|
|
105
121
|
resolve: (answer: boolean) => void;
|
|
122
|
+
/** Set by {@link useConfirm} for a `commit` confirm under a locked provider. */
|
|
123
|
+
lockReason?: ReactNode;
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
export interface ConfirmProviderProps {
|
|
@@ -279,6 +297,7 @@ function ConfirmDialogView({
|
|
|
279
297
|
type="button"
|
|
280
298
|
variant={tone === "danger" ? "danger" : "primary"}
|
|
281
299
|
disabled={!matches}
|
|
300
|
+
disabledReason={request.lockReason}
|
|
282
301
|
onClick={() => {
|
|
283
302
|
accepted.current = true;
|
|
284
303
|
close();
|
package/src/i18n/german.ts
CHANGED
|
@@ -994,6 +994,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
994
994
|
resendIn: (seconds) => `In ${n(seconds)} s erneut senden`,
|
|
995
995
|
resent: "Bestätigungs-E-Mail gesendet",
|
|
996
996
|
resendError: "Bestätigungs-E-Mail konnte nicht gesendet werden",
|
|
997
|
+
rateLimited,
|
|
997
998
|
banner: "Bitte bestätigen Sie Ihre E-Mail-Adresse.",
|
|
998
999
|
dismiss: "Später",
|
|
999
1000
|
},
|
|
@@ -1398,7 +1399,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1398
1399
|
},
|
|
1399
1400
|
security: {
|
|
1400
1401
|
title: "Sicherheit",
|
|
1401
|
-
help: "Wie Sie sich anmelden
|
|
1402
|
+
help: "Wie Sie sich anmelden und wo Sie angemeldet sind.",
|
|
1402
1403
|
},
|
|
1403
1404
|
notifications: {
|
|
1404
1405
|
title: "Benachrichtigungen",
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -975,6 +975,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
975
975
|
resendIn: (seconds) => `Enviar de nuevo en ${n(seconds)} s`,
|
|
976
976
|
resent: "Correo de confirmación enviado",
|
|
977
977
|
resendError: "No se pudo enviar el correo de confirmación",
|
|
978
|
+
rateLimited,
|
|
978
979
|
banner: "Confirme su dirección de correo electrónico.",
|
|
979
980
|
dismiss: "Ahora no",
|
|
980
981
|
},
|
|
@@ -1363,7 +1364,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
1363
1364
|
},
|
|
1364
1365
|
security: {
|
|
1365
1366
|
title: "Seguridad",
|
|
1366
|
-
help: "Cómo inicia sesión
|
|
1367
|
+
help: "Cómo inicia sesión, y en qué dispositivos tiene la sesión abierta.",
|
|
1367
1368
|
},
|
|
1368
1369
|
notifications: {
|
|
1369
1370
|
title: "Notificaciones",
|
package/src/i18n/locales/fr.ts
CHANGED
|
@@ -997,6 +997,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
997
997
|
resendIn: (seconds) => `Envoyer à nouveau dans ${n(seconds)}\u00a0s`,
|
|
998
998
|
resent: "E-mail de confirmation envoyé",
|
|
999
999
|
resendError: "Impossible d’envoyer l’e-mail de confirmation",
|
|
1000
|
+
rateLimited,
|
|
1000
1001
|
banner: "Veuillez confirmer votre adresse e-mail.",
|
|
1001
1002
|
dismiss: "Pas maintenant",
|
|
1002
1003
|
},
|
|
@@ -1390,7 +1391,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
1390
1391
|
},
|
|
1391
1392
|
security: {
|
|
1392
1393
|
title: "Sécurité",
|
|
1393
|
-
help: "
|
|
1394
|
+
help: "Votre connexion, et les appareils où elle est ouverte.",
|
|
1394
1395
|
},
|
|
1395
1396
|
notifications: {
|
|
1396
1397
|
title: "Notifications",
|
package/src/i18n/locales/hu.ts
CHANGED
|
@@ -986,6 +986,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
986
986
|
resendIn: (seconds) => `Újraküldés ${n(seconds)} mp múlva`,
|
|
987
987
|
resent: "Megerősítő e-mail elküldve",
|
|
988
988
|
resendError: "Nem sikerült elküldeni a megerősítő e-mailt",
|
|
989
|
+
rateLimited,
|
|
989
990
|
banner: "Kérjük, erősítse meg az e-mail-címét.",
|
|
990
991
|
dismiss: "Most nem",
|
|
991
992
|
},
|
|
@@ -1365,7 +1366,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
1365
1366
|
},
|
|
1366
1367
|
security: {
|
|
1367
1368
|
title: "Biztonság",
|
|
1368
|
-
help: "Hogyan jelentkezik be
|
|
1369
|
+
help: "Hogyan jelentkezik be, és hol van bejelentkezve.",
|
|
1369
1370
|
},
|
|
1370
1371
|
notifications: {
|
|
1371
1372
|
title: "Értesítések",
|
package/src/i18n/locales/it.ts
CHANGED
|
@@ -982,6 +982,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
982
982
|
resendIn: (seconds) => `Invia di nuovo tra ${n(seconds)} s`,
|
|
983
983
|
resent: "Email di conferma inviata",
|
|
984
984
|
resendError: "Impossibile inviare l’email di conferma",
|
|
985
|
+
rateLimited,
|
|
985
986
|
banner: "Confermi il Suo indirizzo email.",
|
|
986
987
|
dismiss: "Non ora",
|
|
987
988
|
},
|
|
@@ -1374,7 +1375,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1374
1375
|
},
|
|
1375
1376
|
security: {
|
|
1376
1377
|
title: "Sicurezza",
|
|
1377
|
-
help: "Come accede
|
|
1378
|
+
help: "Come accede, e su quali dispositivi la sessione è aperta.",
|
|
1378
1379
|
},
|
|
1379
1380
|
notifications: {
|
|
1380
1381
|
title: "Notifiche",
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -949,6 +949,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
949
949
|
resendIn: (seconds) => `${n(seconds)} 秒后可重新发送`,
|
|
950
950
|
resent: "确认邮件已发送",
|
|
951
951
|
resendError: "无法发送确认邮件",
|
|
952
|
+
rateLimited,
|
|
952
953
|
banner: "请确认您的电子邮箱地址。",
|
|
953
954
|
dismiss: "暂不",
|
|
954
955
|
},
|
|
@@ -1308,7 +1309,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1308
1309
|
},
|
|
1309
1310
|
security: {
|
|
1310
1311
|
title: "安全",
|
|
1311
|
-
help: "
|
|
1312
|
+
help: "您的登录方式,以及已在哪些设备上登录。",
|
|
1312
1313
|
},
|
|
1313
1314
|
notifications: {
|
|
1314
1315
|
title: "通知",
|
package/src/i18n/review.ts
CHANGED
|
@@ -309,6 +309,7 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
|
|
|
309
309
|
"register.rateLimited": [[], [30], [300]],
|
|
310
310
|
"forgotPassword.rateLimited": [[], [30], [300]],
|
|
311
311
|
"resetPassword.rateLimited": [[], [30], [300]],
|
|
312
|
+
"verifyEmail.rateLimited": [[], [30], [300]],
|
|
312
313
|
"emailChange.emailTagUse": [["{{address}}"]],
|
|
313
314
|
"emailChange.pending": [["{{newEmail}}"]],
|
|
314
315
|
"emailChange.pendingHint": [["{{currentEmail}}"]],
|
package/src/index.ts
CHANGED
|
@@ -667,7 +667,7 @@ export {
|
|
|
667
667
|
settingsHref,
|
|
668
668
|
} from "./settings/settings-catalogue";
|
|
669
669
|
export type { SettingsGroup, SettingsEntry, SettingsSearchEntriesOptions } from "./settings/settings-catalogue";
|
|
670
|
-
export { DEFAULT_SETTINGS_LABELS } from "./settings/settings-labels";
|
|
670
|
+
export { DEFAULT_SETTINGS_LABELS, useSettingsLabels } from "./settings/settings-labels";
|
|
671
671
|
export type { SettingsLabels, SettingsGroupLabels, SettingsCoreGroup } from "./settings/settings-labels";
|
|
672
672
|
// Only the language follows the account (§6.2).
|
|
673
673
|
export { useAccountLanguage, resolveAccountLanguage, matchOfferedLanguage } from "./settings/use-account-language";
|
|
@@ -709,7 +709,13 @@ export {
|
|
|
709
709
|
NEXT_PARAM,
|
|
710
710
|
DEFAULT_LAST_VISITED_EXCLUDES,
|
|
711
711
|
} from "./landing/routing";
|
|
712
|
-
export type {
|
|
712
|
+
export type {
|
|
713
|
+
RootEntryProps,
|
|
714
|
+
RedirectIfAuthedProps,
|
|
715
|
+
RoutingSession,
|
|
716
|
+
LastVisitedPageOptions,
|
|
717
|
+
PathPattern,
|
|
718
|
+
} from "./landing/routing";
|
|
713
719
|
export { PublicHeader } from "./landing/public-header";
|
|
714
720
|
export type { PublicHeaderProps, PublicHeaderBrand } from "./landing/public-header";
|
|
715
721
|
export { DEFAULT_DEMO_LABELS, useDemoLabels } from "./demo/demo-labels";
|
|
@@ -23,8 +23,10 @@ export interface LandingActionsProps extends Omit<ComponentPropsWithoutRef<"div"
|
|
|
23
23
|
session: LandingSession;
|
|
24
24
|
access: AccessChoice;
|
|
25
25
|
/** "Try the demo" without a session, "Continue the demo" with a demo one. Default
|
|
26
|
-
* `/demo`, which starts a demo — or, for a live one, continues it (§3.1).
|
|
27
|
-
|
|
26
|
+
* `/demo`, which starts a demo — or, for a live one, continues it (§3.1). `null`
|
|
27
|
+
* (0.31.1): the app has no demo yet, and the action is left out — "Request access"
|
|
28
|
+
* stands alone (Kurvenschmiede's 0.31 adoption). */
|
|
29
|
+
demoHref?: string | null;
|
|
28
30
|
/** "Open app" for a real session: the resume target. Default `/`, which resumes
|
|
29
31
|
* through `RootEntry`. */
|
|
30
32
|
openAppHref?: string;
|
|
@@ -82,9 +84,11 @@ export function LandingActions({
|
|
|
82
84
|
<Button href={accessLink.href} variant="brand" size={buttonSize} className={extra}>
|
|
83
85
|
{accessLink.label}
|
|
84
86
|
</Button>
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
|
|
87
|
+
{demoHref !== null && (
|
|
88
|
+
<Button href={demoHref} variant="secondary" size={buttonSize} className={extra}>
|
|
89
|
+
{session === "demo" ? labels.continueDemo : labels.tryDemo}
|
|
90
|
+
</Button>
|
|
91
|
+
)}
|
|
88
92
|
</>
|
|
89
93
|
)}
|
|
90
94
|
</div>
|
package/src/landing/routing.tsx
CHANGED
|
@@ -4,6 +4,15 @@ import { Navigate, Outlet, useLocation } from "react-router";
|
|
|
4
4
|
|
|
5
5
|
import { readStored, writeStored } from "../lib/safe-storage";
|
|
6
6
|
import type { LandingSession } from "./landing-actions";
|
|
7
|
+
import { LoadingState } from "../components/loading-state";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 0.31.1: a session that is still being restored — an app whose access token lives in
|
|
11
|
+
* memory reads it back from its refresh token first (kastlan's 0.31 adoption). Until
|
|
12
|
+
* then `/` neither shows the landing nor resumes, and a public page neither shows nor
|
|
13
|
+
* redirects: both render `loading` instead.
|
|
14
|
+
*/
|
|
15
|
+
export type RoutingSession = LandingSession | "loading";
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* The public routes' logic (docs/landing-demo-harmonization.md §3): what `/` shows,
|
|
@@ -144,8 +153,11 @@ export function clearLastVisitedPage(key: string): void {
|
|
|
144
153
|
/* ── RootEntry ───────────────────────────────────────────────────────────── */
|
|
145
154
|
|
|
146
155
|
export interface RootEntryProps {
|
|
147
|
-
/** Without a session `/` is the landing; with one (real or demo) it resumes
|
|
148
|
-
|
|
156
|
+
/** Without a session `/` is the landing; with one (real or demo) it resumes;
|
|
157
|
+
* `"loading"` while the app restores one. */
|
|
158
|
+
session: RoutingSession;
|
|
159
|
+
/** What shows while `session` is `"loading"`. Default: the kit's `LoadingState`. */
|
|
160
|
+
loading?: ReactNode;
|
|
149
161
|
/** The remembered page — `readLastVisitedPage(key)`. */
|
|
150
162
|
resumePath?: string | null;
|
|
151
163
|
/** The landing page element. */
|
|
@@ -164,7 +176,8 @@ export interface RootEntryProps {
|
|
|
164
176
|
* home instead, because a session at `/` came to use the app. The landing stays one tap
|
|
165
177
|
* away at `/welcome`, where the signed-in brand link points.
|
|
166
178
|
*/
|
|
167
|
-
export function RootEntry({ session, resumePath, landing, home }: RootEntryProps) {
|
|
179
|
+
export function RootEntry({ session, resumePath, landing, home, loading }: RootEntryProps) {
|
|
180
|
+
if (session === "loading") return <>{loading ?? <LoadingState />}</>;
|
|
168
181
|
if (session === "none") return <>{landing}</>;
|
|
169
182
|
const resume = safeNextPath(resumePath);
|
|
170
183
|
return <Navigate to={resume && resume !== "/" ? resume : home} replace />;
|
|
@@ -173,7 +186,10 @@ export function RootEntry({ session, resumePath, landing, home }: RootEntryProps
|
|
|
173
186
|
/* ── RedirectIfAuthed ────────────────────────────────────────────────────── */
|
|
174
187
|
|
|
175
188
|
export interface RedirectIfAuthedProps {
|
|
176
|
-
session:
|
|
189
|
+
/** `"loading"` while the app restores a session: neither the page nor a redirect. */
|
|
190
|
+
session: RoutingSession;
|
|
191
|
+
/** What shows while `session` is `"loading"`. Default: the kit's `LoadingState`. */
|
|
192
|
+
loading?: ReactNode;
|
|
177
193
|
/**
|
|
178
194
|
* Let a DEMO session through (§5.6): on `/login`, `/register` and the app's invitation
|
|
179
195
|
* pages, so a demo visitor can sign in or accept an invitation — a successful one
|
|
@@ -199,8 +215,9 @@ export interface RedirectIfAuthedProps {
|
|
|
199
215
|
* `allowDemo` around `/login`, `/register` and the invitation pages, and one without
|
|
200
216
|
* around `/demo` (and `/forgot-password`).
|
|
201
217
|
*/
|
|
202
|
-
export function RedirectIfAuthed({ session, allowDemo = false, fallback, children }: RedirectIfAuthedProps) {
|
|
218
|
+
export function RedirectIfAuthed({ session, allowDemo = false, fallback, children, loading }: RedirectIfAuthedProps) {
|
|
203
219
|
const { search } = useLocation();
|
|
220
|
+
if (session === "loading") return <>{loading ?? <LoadingState />}</>;
|
|
204
221
|
if (session === "none" || (session === "demo" && allowDemo)) {
|
|
205
222
|
return children === undefined ? <Outlet /> : <>{children}</>;
|
|
206
223
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The `settings` namespace (docs/settings-harmonization.md §4.3): the settings page's
|
|
3
5
|
* own words, and the names and help lines of the five core groups every app shares —
|
|
@@ -61,7 +63,7 @@ export const DEFAULT_SETTINGS_LABELS: SettingsLabels = {
|
|
|
61
63
|
},
|
|
62
64
|
security: {
|
|
63
65
|
title: "Security",
|
|
64
|
-
help: "How you sign in
|
|
66
|
+
help: "How you sign in, and where you are signed in.",
|
|
65
67
|
},
|
|
66
68
|
notifications: {
|
|
67
69
|
title: "Notifications",
|
|
@@ -73,3 +75,13 @@ export const DEFAULT_SETTINGS_LABELS: SettingsLabels = {
|
|
|
73
75
|
},
|
|
74
76
|
},
|
|
75
77
|
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 0.31.1: the `settings` namespace, resolved — English, then the provider, then `labels`.
|
|
81
|
+
* What an app reads for its catalogue's core group titles and help lines, and for the
|
|
82
|
+
* page title "<group> · Settings · <app>" (settings contract §3.1), without restating
|
|
83
|
+
* the namespace and its defaults (Kurvenschmiede's 0.31 adoption).
|
|
84
|
+
*/
|
|
85
|
+
export function useSettingsLabels(labels?: Partial<SettingsLabels>): SettingsLabels {
|
|
86
|
+
return useKitLabels("settings", DEFAULT_SETTINGS_LABELS, labels);
|
|
87
|
+
}
|
package/src/shell.ts
CHANGED
|
@@ -52,6 +52,12 @@ export {
|
|
|
52
52
|
NEXT_PARAM,
|
|
53
53
|
DEFAULT_LAST_VISITED_EXCLUDES,
|
|
54
54
|
} from "./landing/routing";
|
|
55
|
-
export type {
|
|
55
|
+
export type {
|
|
56
|
+
RootEntryProps,
|
|
57
|
+
RedirectIfAuthedProps,
|
|
58
|
+
RoutingSession,
|
|
59
|
+
LastVisitedPageOptions,
|
|
60
|
+
PathPattern,
|
|
61
|
+
} from "./landing/routing";
|
|
56
62
|
export { PublicHeader } from "./landing/public-header";
|
|
57
63
|
export type { PublicHeaderProps, PublicHeaderBrand } from "./landing/public-header";
|