@eifi1/ui-kit 0.28.0 → 0.29.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.
- package/README.md +22 -4
- package/dist/auth/accept-invitation.d.ts +90 -0
- package/dist/auth/accept-invitation.js +117 -0
- package/dist/auth/accept-invitation.js.map +1 -0
- package/dist/auth/auth-errors.d.ts +60 -0
- package/dist/auth/auth-errors.js +32 -0
- package/dist/auth/auth-errors.js.map +1 -0
- package/dist/auth/complete-name-dialog.d.ts +71 -0
- package/dist/auth/complete-name-dialog.js +168 -0
- package/dist/auth/complete-name-dialog.js.map +1 -0
- package/dist/auth/email-tag.d.ts +60 -0
- package/dist/auth/email-tag.js +23 -0
- package/dist/auth/email-tag.js.map +1 -0
- package/dist/auth/forgot-password-form.d.ts +79 -0
- package/dist/auth/forgot-password-form.js +120 -0
- package/dist/auth/forgot-password-form.js.map +1 -0
- package/dist/auth/form-rules.d.ts +27 -0
- package/dist/auth/form-rules.js +23 -0
- package/dist/auth/form-rules.js.map +1 -0
- package/dist/auth/new-password-fields.d.ts +38 -0
- package/dist/auth/new-password-fields.js +54 -0
- package/dist/auth/new-password-fields.js.map +1 -0
- package/dist/auth/not-found-page.d.ts +57 -0
- package/dist/auth/not-found-page.js +40 -0
- package/dist/auth/not-found-page.js.map +1 -0
- package/dist/auth/register-form.d.ts +86 -0
- package/dist/auth/register-form.js +268 -0
- package/dist/auth/register-form.js.map +1 -0
- package/dist/auth/reset-password-form.d.ts +124 -0
- package/dist/auth/reset-password-form.js +164 -0
- package/dist/auth/reset-password-form.js.map +1 -0
- package/dist/auth/sign-in-form.d.ts +243 -0
- package/dist/auth/sign-in-form.js +345 -0
- package/dist/auth/sign-in-form.js.map +1 -0
- package/dist/auth/status-parts.d.ts +78 -0
- package/dist/auth/status-parts.js +83 -0
- package/dist/auth/status-parts.js.map +1 -0
- package/dist/auth/verify-email.d.ts +121 -0
- package/dist/auth/verify-email.js +207 -0
- package/dist/auth/verify-email.js.map +1 -0
- package/dist/chart.d.ts +17 -2
- package/dist/components/amount-input.d.ts +18 -2
- package/dist/components/button-group.d.ts +18 -2
- package/dist/components/calculator.d.ts +18 -2
- package/dist/components/confirm-dialog.d.ts +18 -2
- package/dist/components/copy-button.d.ts +18 -2
- package/dist/components/danger-confirm.d.ts +18 -2
- package/dist/components/facing-pair.d.ts +18 -2
- package/dist/components/file-button.d.ts +18 -2
- package/dist/components/file-dropzone.d.ts +18 -2
- package/dist/components/form-actions.d.ts +18 -2
- package/dist/components/iban-input.d.ts +18 -2
- package/dist/components/language-select.d.ts +18 -9
- package/dist/components/legal-page.d.ts +26 -4
- package/dist/components/legal-page.js +24 -5
- package/dist/components/legal-page.js.map +1 -1
- package/dist/components/legal.d.ts +18 -2
- package/dist/components/legal.js +49 -5
- package/dist/components/legal.js.map +1 -1
- package/dist/components/money-field.d.ts +18 -2
- package/dist/components/number-field.d.ts +18 -2
- package/dist/components/number-input.d.ts +18 -2
- package/dist/components/numpad-sheet.d.ts +18 -2
- package/dist/components/phone-input.d.ts +18 -2
- package/dist/components/series-chart.d.ts +18 -2
- package/dist/components/settings-fields.d.ts +15 -2
- package/dist/components/share-card.d.ts +18 -2
- package/dist/components/text-link.d.ts +18 -2
- package/dist/components/time-input.d.ts +18 -2
- package/dist/components/ui.d.ts +17 -1
- package/dist/components/user-avatar.d.ts +16 -2
- package/dist/components/user-avatar.js +7 -3
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +18 -2
- package/dist/feedback/feedback-context.d.ts +18 -2
- package/dist/feedback/feedback-crash.d.ts +18 -2
- package/dist/feedback/feedback-dialog.d.ts +18 -2
- package/dist/feedback/feedback-inbox.d.ts +18 -2
- package/dist/feedback/feedback-labels.d.ts +18 -2
- package/dist/feedback/feedback-menu.d.ts +18 -2
- package/dist/feedback/feedback-record.d.ts +18 -2
- package/dist/feedback/feedback-row-detail.d.ts +18 -2
- package/dist/feedback/feedback-status-undo.d.ts +18 -2
- package/dist/feedback/feedback-submit.d.ts +18 -2
- package/dist/feedback/feedback-swipe.d.ts +18 -2
- package/dist/feedback/feedback-table.d.ts +18 -2
- package/dist/feedback/feedback-thread.d.ts +18 -2
- package/dist/feedback.d.ts +18 -2
- package/dist/hooks/use-file-drop.d.ts +18 -2
- package/dist/i18n/defaults.d.ts +18 -2
- package/dist/i18n/defaults.js +18 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +18 -2
- package/dist/i18n/german.js +145 -0
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +18 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +13 -201
- package/dist/i18n/locales/de-CH.d.ts +18 -2
- package/dist/i18n/locales/en.d.ts +18 -2
- package/dist/i18n/locales/es.d.ts +18 -2
- package/dist/i18n/locales/es.js +147 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +18 -2
- package/dist/i18n/locales/fr.js +147 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +18 -2
- package/dist/i18n/locales/hu.js +149 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +18 -2
- package/dist/i18n/locales/it.js +146 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +18 -2
- package/dist/i18n/locales/zh.js +146 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +18 -2
- package/dist/i18n/review.js +17 -7
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +49 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/person-name.d.ts +69 -0
- package/dist/lib/person-name.js +65 -0
- package/dist/lib/person-name.js.map +1 -0
- package/dist/{kit-labels-SBN0biPl.d.ts → register-form-V1OeNlMr.d.ts} +423 -6
- package/dist/rhf/fields.d.ts +18 -2
- package/dist/rhf/form.d.ts +18 -2
- package/dist/rhf.d.ts +18 -2
- package/dist/shell/app-shell.d.ts +18 -2
- package/dist/shell/company-switcher.d.ts +59 -0
- package/dist/shell/company-switcher.js +85 -0
- package/dist/shell/company-switcher.js.map +1 -0
- package/dist/shell/top-bar-brand.d.ts +18 -2
- package/dist/shell/topbar-action-menu.d.ts +1 -0
- package/dist/shell.d.ts +12 -2
- package/dist/wizard/stepper-nav.d.ts +18 -2
- package/dist/wizard.d.ts +18 -2
- package/package.json +1 -1
- package/src/auth/accept-invitation.tsx +227 -0
- package/src/auth/auth-errors.ts +105 -0
- package/src/auth/complete-name-dialog.tsx +253 -0
- package/src/auth/email-tag.ts +75 -0
- package/src/auth/forgot-password-form.tsx +213 -0
- package/src/auth/form-rules.ts +40 -0
- package/src/auth/new-password-fields.tsx +85 -0
- package/src/auth/not-found-page.tsx +98 -0
- package/src/auth/register-form.tsx +459 -0
- package/src/auth/reset-password-form.tsx +337 -0
- package/src/auth/sign-in-form.tsx +687 -0
- package/src/auth/status-parts.tsx +138 -0
- package/src/auth/verify-email.tsx +358 -0
- package/src/components/legal-page.tsx +35 -0
- package/src/components/legal.tsx +93 -7
- package/src/components/user-avatar.tsx +22 -4
- package/src/i18n/defaults.ts +18 -0
- package/src/i18n/german.ts +153 -0
- package/src/i18n/kit-labels.tsx +27 -0
- package/src/i18n/locales/es.ts +156 -3
- package/src/i18n/locales/fr.ts +157 -0
- package/src/i18n/locales/hu.ts +159 -3
- package/src/i18n/locales/it.ts +154 -1
- package/src/i18n/locales/zh.ts +149 -1
- package/src/i18n/review.ts +15 -6
- package/src/index.ts +63 -1
- package/src/lib/person-name.ts +157 -0
- package/src/shell/company-switcher.tsx +140 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { Building2 } from "lucide-react";
|
|
4
|
+
import { cn } from "../lib/cn";
|
|
5
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
6
|
+
import { useAnnounce } from "../hooks/use-announce";
|
|
7
|
+
import { Spinner } from "../components/ui";
|
|
8
|
+
import { OptionSwitcherMenu } from "./option-switcher-menu";
|
|
9
|
+
import { TOPBAR_TRIGGER_CLASS } from "./topbar-controls";
|
|
10
|
+
|
|
11
|
+
/** Every string the switcher renders or speaks — the `companySwitcher` namespace. A
|
|
12
|
+
* different context (keksdose's budgets) renames them all. */
|
|
13
|
+
export interface CompanySwitcherLabels {
|
|
14
|
+
/** The trigger's and the menu's accessible name. */
|
|
15
|
+
switchCompany: string;
|
|
16
|
+
/** The small heading over the list. */
|
|
17
|
+
heading: string;
|
|
18
|
+
/** The trigger's tooltip (and description), given the current company's name. */
|
|
19
|
+
current: (name: string) => string;
|
|
20
|
+
/** Read after the current company's name in the list, where the eye sees a check. */
|
|
21
|
+
currentMark: string;
|
|
22
|
+
/** Said while a switch is running. */
|
|
23
|
+
switching: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_COMPANY_SWITCHER_LABELS: CompanySwitcherLabels = {
|
|
27
|
+
switchCompany: "Switch company",
|
|
28
|
+
heading: "Companies",
|
|
29
|
+
current: (name) => `Company: ${name}`,
|
|
30
|
+
currentMark: "(current)",
|
|
31
|
+
switching: "Switching company…",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** One company the user belongs to — kastlan's `/auth/me` `companies[]` (§5.3). */
|
|
35
|
+
export interface CompanySwitcherCompany<Id extends string | number = string | number> {
|
|
36
|
+
/** kastlan's ids are numbers; a string works the same. */
|
|
37
|
+
id: Id;
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CompanySwitcherProps<Id extends string | number> {
|
|
42
|
+
/** Every company the user belongs to. With fewer than two there is nothing to
|
|
43
|
+
* switch, and nothing renders. */
|
|
44
|
+
companies: readonly CompanySwitcherCompany<Id>[];
|
|
45
|
+
/** The company the session is in — `/auth/me`'s `current_company_id`. */
|
|
46
|
+
currentId: Id;
|
|
47
|
+
/**
|
|
48
|
+
* Another company was picked: `POST /auth/switch-company {company_id}`, store the
|
|
49
|
+
* fresh tokens, reload what is company-scoped. While the promise runs the switcher is
|
|
50
|
+
* disabled, so a second pick cannot race the first; a rejection frees it (the app
|
|
51
|
+
* says why). Picking the current company does nothing.
|
|
52
|
+
*/
|
|
53
|
+
onSwitch: (id: Id) => Promise<unknown> | void;
|
|
54
|
+
/** The trigger's glyph. Default a building; keksdose's budgets would pass a wallet. */
|
|
55
|
+
icon?: ReactNode;
|
|
56
|
+
labels?: Partial<CompanySwitcherLabels>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The company switcher in the top bar (docs/auth-harmonization.md §5.3, §8): a kastlan
|
|
61
|
+
* user may belong to several companies, and the access token carries the current one,
|
|
62
|
+
* so switching is a request for new tokens — `onSwitch`, the app's. Built on
|
|
63
|
+
* {@link OptionSwitcherMenu}, like `RoleSwitcher`: the icon trigger, the current company
|
|
64
|
+
* in its tooltip, the list with a check on the current one.
|
|
65
|
+
*
|
|
66
|
+
* **Only for more than one company** — a user in one company has nothing to switch, and
|
|
67
|
+
* the top bar stays as it was. **Disabled while a switch runs**: the trigger is swapped
|
|
68
|
+
* for a disabled one with a spinner (`aria-busy`), and "Switching company…" is said, so
|
|
69
|
+
* a second pick cannot race the first. The kit never sends the request.
|
|
70
|
+
*
|
|
71
|
+
* A generic context switcher: the labels rename it ("Switch budget", "Budgets"), `icon`
|
|
72
|
+
* replaces the building, and `id` may be a number (kastlan's) or a string.
|
|
73
|
+
*/
|
|
74
|
+
export function CompanySwitcher<Id extends string | number>({
|
|
75
|
+
companies,
|
|
76
|
+
currentId,
|
|
77
|
+
onSwitch,
|
|
78
|
+
icon,
|
|
79
|
+
labels: labelsProp,
|
|
80
|
+
}: CompanySwitcherProps<Id>) {
|
|
81
|
+
const labels = useKitLabels("companySwitcher", DEFAULT_COMPANY_SWITCHER_LABELS, labelsProp);
|
|
82
|
+
const [pending, setPending] = useState(false);
|
|
83
|
+
const { announce, regionProps } = useAnnounce();
|
|
84
|
+
if (companies.length < 2) return null;
|
|
85
|
+
|
|
86
|
+
const current = companies.find((c) => c.id === currentId);
|
|
87
|
+
const select = (value: string) => {
|
|
88
|
+
if (pending) return;
|
|
89
|
+
const picked = companies.find((c) => String(c.id) === value);
|
|
90
|
+
if (!picked || picked.id === currentId) return;
|
|
91
|
+
const result = onSwitch(picked.id);
|
|
92
|
+
if (typeof (result as PromiseLike<unknown> | undefined)?.then !== "function") return;
|
|
93
|
+
setPending(true);
|
|
94
|
+
announce(labels.switching);
|
|
95
|
+
(result as PromiseLike<unknown>).then(
|
|
96
|
+
() => setPending(false),
|
|
97
|
+
() => setPending(false),
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
{pending ? (
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
disabled
|
|
107
|
+
aria-busy="true"
|
|
108
|
+
aria-label={labels.switchCompany}
|
|
109
|
+
className={cn(TOPBAR_TRIGGER_CLASS, "cursor-wait opacity-60 hover:bg-transparent")}
|
|
110
|
+
>
|
|
111
|
+
<Spinner label={null} />
|
|
112
|
+
</button>
|
|
113
|
+
) : (
|
|
114
|
+
<OptionSwitcherMenu<string>
|
|
115
|
+
ariaLabel={labels.switchCompany}
|
|
116
|
+
title={current ? labels.current(current.name) : undefined}
|
|
117
|
+
heading={labels.heading}
|
|
118
|
+
icon={icon ?? <Building2 className="size-5" />}
|
|
119
|
+
options={companies.map((c) => ({
|
|
120
|
+
value: String(c.id),
|
|
121
|
+
label:
|
|
122
|
+
c.id === currentId ? (
|
|
123
|
+
// `relative`: the sr-only mark needs a local containing block.
|
|
124
|
+
<span className="relative">
|
|
125
|
+
{c.name}
|
|
126
|
+
<span className="sr-only"> {labels.currentMark}</span>
|
|
127
|
+
</span>
|
|
128
|
+
) : (
|
|
129
|
+
c.name
|
|
130
|
+
),
|
|
131
|
+
}))}
|
|
132
|
+
value={String(currentId)}
|
|
133
|
+
onSelect={select}
|
|
134
|
+
/>
|
|
135
|
+
)}
|
|
136
|
+
{/* Outside both triggers, so it survives the swap and is read when it changes. */}
|
|
137
|
+
<span {...regionProps} />
|
|
138
|
+
</>
|
|
139
|
+
);
|
|
140
|
+
}
|