@eifi1/ui-kit 0.43.0 → 0.45.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 +11 -7
- package/dist/admin/admin-action-log-labels.js +9 -0
- package/dist/admin/admin-action-log-labels.js.map +1 -1
- package/dist/admin/admin-action-log.d.ts +18 -1
- package/dist/admin/admin-action-log.js +25 -16
- package/dist/admin/admin-action-log.js.map +1 -1
- package/dist/admin/admin-area-labels.d.ts +6 -0
- package/dist/admin/admin-area-labels.js +1 -0
- package/dist/admin/admin-area-labels.js.map +1 -1
- package/dist/admin/mail-test-card-labels.d.ts +3 -0
- package/dist/admin/mail-test-card-labels.js +17 -0
- package/dist/admin/mail-test-card-labels.js.map +1 -0
- package/dist/admin/mail-test-card.d.ts +74 -0
- package/dist/admin/mail-test-card.js +80 -0
- package/dist/admin/mail-test-card.js.map +1 -0
- package/dist/billing/provider-page.d.ts +9 -0
- package/dist/billing/provider-page.js.map +1 -1
- package/dist/components/data-table-mobile.js +17 -2
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-shared.d.ts +2 -0
- package/dist/components/data-table-shared.js +8 -0
- package/dist/components/data-table-shared.js.map +1 -1
- package/dist/components/data-table.js +1 -6
- package/dist/components/data-table.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/i18n/defaults.js +9 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +42 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +13 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +60 -4
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +40 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +42 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +40 -0
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +41 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +40 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +13 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/format.d.ts +2 -1
- package/dist/lib/format.js +6 -1
- package/dist/lib/format.js.map +1 -1
- package/dist/offline/pwa-update-prompt-labels.d.ts +2 -0
- package/dist/offline/pwa-update-prompt-labels.js +9 -0
- package/dist/offline/pwa-update-prompt-labels.js.map +1 -0
- package/dist/offline/pwa-update-prompt.d.ts +32 -0
- package/dist/offline/pwa-update-prompt.js +36 -0
- package/dist/offline/pwa-update-prompt.js.map +1 -0
- package/dist/offline/query-persistence.d.ts +100 -0
- package/dist/offline/query-persistence.js +179 -0
- package/dist/offline/query-persistence.js.map +1 -0
- package/dist/offline/shared-computer-labels.d.ts +2 -0
- package/dist/offline/shared-computer-labels.js +9 -0
- package/dist/offline/shared-computer-labels.js.map +1 -0
- package/dist/offline/shared-computer.d.ts +44 -0
- package/dist/offline/shared-computer.js +51 -0
- package/dist/offline/shared-computer.js.map +1 -0
- package/dist/offline/sign-out-pending-dialog-labels.d.ts +2 -0
- package/dist/offline/sign-out-pending-dialog-labels.js +14 -0
- package/dist/offline/sign-out-pending-dialog-labels.js.map +1 -0
- package/dist/offline/sign-out-pending-dialog.d.ts +58 -0
- package/dist/offline/sign-out-pending-dialog.js +66 -0
- package/dist/offline/sign-out-pending-dialog.js.map +1 -0
- package/dist/offline.d.ts +8 -0
- package/dist/offline.js +24 -0
- package/dist/offline.js.map +1 -0
- package/dist/shell/admin-menu.d.ts +24 -0
- package/dist/shell/admin-menu.js +12 -3
- package/dist/shell/admin-menu.js.map +1 -1
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +2 -0
- package/dist/shell.js.map +1 -1
- package/dist/tour/tour-catalogue-labels.js +2 -2
- package/dist/tour/tour-catalogue-labels.js.map +1 -1
- package/dist/tour/tour-catalogue.d.ts +7 -1
- package/dist/tour/tour-catalogue.js.map +1 -1
- package/dist/tour/tour-info.d.ts +2 -2
- package/dist/tour/tour-info.js.map +1 -1
- package/dist/tour/tour.d.ts +38 -2
- package/dist/tour/tour.js +42 -3
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-progress.d.ts +165 -12
- package/dist/tour/use-tour-progress.js +271 -25
- package/dist/tour/use-tour-progress.js.map +1 -1
- package/dist/tour.d.ts +3 -1
- package/dist/tour.js +4 -1
- package/dist/tour.js.map +1 -1
- package/package.json +25 -1
- package/src/admin/admin-action-log-labels.ts +9 -0
- package/src/admin/admin-action-log.tsx +41 -5
- package/src/admin/admin-area-labels.ts +7 -0
- package/src/admin/mail-test-card-labels.ts +19 -0
- package/src/admin/mail-test-card.tsx +186 -0
- package/src/billing/provider-page.ts +9 -0
- package/src/components/data-table-mobile.tsx +26 -3
- package/src/components/data-table-shared.tsx +18 -1
- package/src/components/data-table.tsx +1 -15
- package/src/i18n/defaults.ts +8 -0
- package/src/i18n/german.ts +48 -2
- package/src/i18n/kit-labels.tsx +13 -0
- package/src/i18n/locales/en.ts +13 -1
- package/src/i18n/locales/es.ts +46 -0
- package/src/i18n/locales/fr.ts +48 -2
- package/src/i18n/locales/hu.ts +42 -0
- package/src/i18n/locales/it.ts +47 -1
- package/src/i18n/locales/zh.ts +40 -0
- package/src/i18n/review.ts +12 -0
- package/src/index.ts +3 -0
- package/src/lib/format.ts +13 -2
- package/src/offline/pwa-update-prompt-labels.ts +9 -0
- package/src/offline/pwa-update-prompt.tsx +72 -0
- package/src/offline/query-persistence.ts +323 -0
- package/src/offline/shared-computer-labels.ts +8 -0
- package/src/offline/shared-computer.tsx +87 -0
- package/src/offline/sign-out-pending-dialog-labels.ts +21 -0
- package/src/offline/sign-out-pending-dialog.tsx +135 -0
- package/src/offline.ts +26 -0
- package/src/shell/admin-menu.tsx +40 -3
- package/src/shell.ts +4 -0
- package/src/tour/tour-catalogue-labels.ts +2 -2
- package/src/tour/tour-catalogue.tsx +7 -1
- package/src/tour/tour-info.ts +2 -2
- package/src/tour/tour.tsx +87 -3
- package/src/tour/use-tour-progress.ts +501 -34
- package/src/tour.ts +5 -1
package/src/shell.ts
CHANGED
|
@@ -20,6 +20,10 @@ export type { AdminMenuEntryOptions, AdminMenuRowProps } from "./shell/admin-men
|
|
|
20
20
|
// 0.43.0 (kastlan): the entry with the provider's words.
|
|
21
21
|
export { useAdminMenuEntry } from "./shell/admin-menu";
|
|
22
22
|
export type { UseAdminMenuEntryOptions } from "./shell/admin-menu";
|
|
23
|
+
// 0.45.0 (docs/admin-harmonization.md §9.3.4): the row's waiting pill on its own, for
|
|
24
|
+
// IconButton's `badge` (kastlan's platform button); its name is `adminArea.waitingLabel`.
|
|
25
|
+
export { AdminWaitingBadge } from "./shell/admin-menu";
|
|
26
|
+
export type { AdminWaitingBadgeProps } from "./shell/admin-menu";
|
|
23
27
|
// 0.31.0: the settings and admin pages' shell (docs/settings-harmonization.md §3, §5).
|
|
24
28
|
export { SettingsLayout, SETTINGS_SEARCH_THRESHOLD } from "./settings/settings-layout";
|
|
25
29
|
export type { SettingsLayoutProps, SettingsLayoutWidth, SettingsDocumentTitleParts } from "./settings/settings-layout";
|
|
@@ -54,11 +54,11 @@ export const DEFAULT_TOUR_CATALOGUE_LABELS: TourCatalogueLabels = {
|
|
|
54
54
|
groupOption: (group, count) => `${group} (${count})`,
|
|
55
55
|
stateLabel: "Filter tours",
|
|
56
56
|
stateAll: (count) => `All (${count})`,
|
|
57
|
-
stateTodo: (count) => `
|
|
57
|
+
stateTodo: (count) => `Not taken (${count})`,
|
|
58
58
|
stateDone: (count) => `Completed (${count})`,
|
|
59
59
|
showing: (shown, total) => `Showing ${shown} of ${total} tours`,
|
|
60
60
|
noMatches: "No tour matches",
|
|
61
|
-
noMatchesHint: "Try another word, or clear the
|
|
61
|
+
noMatchesHint: "Try another word, or clear the filter.",
|
|
62
62
|
clearFilters: "Clear filters",
|
|
63
63
|
badgeNew: "New",
|
|
64
64
|
badgeUpdated: "Updated",
|
|
@@ -30,7 +30,12 @@ export interface TourCatalogueCardProps {
|
|
|
30
30
|
onLaunch: (id: string) => void;
|
|
31
31
|
/** "Mark as not done". Without it, the link is not drawn. */
|
|
32
32
|
onReset?: (id: string) => void;
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Formats the completion and update days. Default: the kit's medium date in the provider's locale.
|
|
35
|
+
* It gets the completion's ISO instant and the update's bare `"YYYY-MM-DD"` (`updated.date`): read
|
|
36
|
+
* the bare day on its calendar (the kit's `formatDate`, `parseIsoDate`), never `new Date(day)`,
|
|
37
|
+
* which is UTC midnight and the day before west of UTC (round 0.45, N9).
|
|
38
|
+
*/
|
|
34
39
|
formatDate?: (iso: string) => string;
|
|
35
40
|
labels?: LabelOverride<TourCatalogueLabels>;
|
|
36
41
|
className?: string;
|
|
@@ -161,6 +166,7 @@ export interface TourCatalogueProps extends TourCatalogueFilter {
|
|
|
161
166
|
onReset?: (id: string) => void;
|
|
162
167
|
/** Above the filter bar: the app's page header with its points tile (§6.1). */
|
|
163
168
|
header?: ReactNode;
|
|
169
|
+
/** Each card's `formatDate`: see {@link TourCatalogueCardProps.formatDate} (a bare day for the update). */
|
|
164
170
|
formatDate?: (iso: string) => string;
|
|
165
171
|
/** The search's locale (case folding). Default: the provider's. */
|
|
166
172
|
locale?: string;
|
package/src/tour/tour-info.ts
CHANGED
|
@@ -59,8 +59,8 @@ export interface TourGroup {
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Which tours are completed and when: tour id → the ISO timestamp of the completion, or
|
|
62
|
-
* `null` for one recorded without a date (kastlan's old id list, §6.2
|
|
63
|
-
* not a key is untaken.
|
|
62
|
+
* `null` for one recorded without a date (kastlan's old id list, §6.2; stored as `""`,
|
|
63
|
+
* §9.6.6). A tour that is not a key is untaken.
|
|
64
64
|
*/
|
|
65
65
|
export type TourCompletions = Readonly<Record<string, string | null>>;
|
|
66
66
|
|
package/src/tour/tour.tsx
CHANGED
|
@@ -17,6 +17,8 @@ import { sameRect } from "../hooks/use-anchored-rect";
|
|
|
17
17
|
import { dirOf, horizontalStep } from "../lib/direction";
|
|
18
18
|
import type { Direction } from "../lib/direction";
|
|
19
19
|
import { DEFAULT_TOUR_LABELS } from "./tour-labels";
|
|
20
|
+
import type { TourInfo } from "./tour-info";
|
|
21
|
+
import { completeStored, rememberAccount, rememberMigrate, type UseTourProgressOptions } from "./use-tour-progress";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* `start`/`end` follow the reading direction (`end` is the right side in LTR and the
|
|
@@ -67,16 +69,34 @@ export { DEFAULT_TOUR_LABELS };
|
|
|
67
69
|
|
|
68
70
|
interface StartOptions {
|
|
69
71
|
/** Stable id of the tour, surfaced as `activeId` so the app can mirror it to
|
|
70
|
-
* the URL (deep-link / evaluation tracking) and record completion.
|
|
72
|
+
* the URL (deep-link / evaluation tracking) and record completion. Defaults to
|
|
73
|
+
* `tour.id`. */
|
|
71
74
|
id?: string;
|
|
75
|
+
/** The tour being started, as the app lists it (docs/tours-harmonization.md §9.6.1).
|
|
76
|
+
* When its last step is finished, the provider records the completion in its
|
|
77
|
+
* `progress` and hands this to `onComplete`. */
|
|
78
|
+
tour?: TourInfo;
|
|
72
79
|
/** Start at this step index instead of 0 (clamped) — for URL/deep-link resume. */
|
|
73
80
|
startIndex?: number;
|
|
74
81
|
/** Fires synchronously when the tour starts (e.g. enable a mock interceptor). */
|
|
75
82
|
onStart?: () => void;
|
|
83
|
+
/** After the last step: runs after the provider's progress write and `onComplete`. */
|
|
76
84
|
onFinish?: () => void;
|
|
77
85
|
onSkip?: () => void;
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
/** What `TourProvider`'s `onComplete` learns besides the tour (§9.1, §9.6). */
|
|
89
|
+
export interface TourCompletion {
|
|
90
|
+
/**
|
|
91
|
+
* `true` when the completion counts anew (the first time, or the first time after the
|
|
92
|
+
* tour's update, §8): the moment for the app's points toast. `false` for a replay, and
|
|
93
|
+
* when the stored text is in an old shape no `migrate` reads: nothing is overwritten
|
|
94
|
+
* then, and the error goes to `console.error` (keksdose B-1). `undefined` when the provider has no `progress` or the start carried no `tour`, so
|
|
95
|
+
* nothing was recorded.
|
|
96
|
+
*/
|
|
97
|
+
awarded: boolean | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
80
100
|
interface TourContextValue {
|
|
81
101
|
active: boolean;
|
|
82
102
|
/** Id of the running tour (from {@link StartOptions.id}), else null. */
|
|
@@ -129,6 +149,11 @@ export function useTourOptional(): TourContextValue | null {
|
|
|
129
149
|
return useContext(TourContext);
|
|
130
150
|
}
|
|
131
151
|
|
|
152
|
+
/** The kit's dev switch (as number-input's): a development build, never under vitest. */
|
|
153
|
+
function devWarnings(): boolean {
|
|
154
|
+
return Boolean(import.meta.env?.DEV) && !import.meta.env?.VITEST;
|
|
155
|
+
}
|
|
156
|
+
|
|
132
157
|
/**
|
|
133
158
|
* Provides the guided-tour engine: a dimmed spotlight overlay + step card driven
|
|
134
159
|
* by a list of {@link TourStep}s. Domain-free — the app supplies the steps (copy,
|
|
@@ -136,13 +161,32 @@ export function useTourOptional(): TourContextValue | null {
|
|
|
136
161
|
* Mount once inside the router; start a tour from anywhere via {@link useTour},
|
|
137
162
|
* which also exposes step control (`next`/`prev`/`goToStep`) and the current
|
|
138
163
|
* `index`/`total` for URL mirroring, `awaitClick` and deep-link resume.
|
|
164
|
+
*
|
|
165
|
+
* **Completion in one place** (docs/tours-harmonization.md §9.6): give the provider the
|
|
166
|
+
* app's `progress` (the same `{storageKey, migrate}` as `useTourProgress`) and start each
|
|
167
|
+
* tour with `start(steps, {tour})`. When the last step is finished the provider writes the
|
|
168
|
+
* completion ONCE, through the store every `useTourProgress` reader of the key shares
|
|
169
|
+
* (never a second copy), then calls `onComplete(tour, {awarded})`, then the start's own
|
|
170
|
+
* `onFinish`. A skip or `stop()` records nothing and calls neither. The app removes its
|
|
171
|
+
* own `complete()` from `onFinish`, or that second write answers `false`.
|
|
172
|
+
*
|
|
173
|
+
* Both props are optional: without `progress` or a `tour` on the start, `onComplete`
|
|
174
|
+
* still fires, with `awarded: undefined` (and `tour` as given, possibly `undefined`). A
|
|
175
|
+
* new `storageKey` (an account switch on a shared PC) is followed on the next
|
|
176
|
+
* completion, without a remount.
|
|
139
177
|
*/
|
|
140
178
|
export function TourProvider({
|
|
141
179
|
children,
|
|
142
180
|
labels,
|
|
181
|
+
progress,
|
|
182
|
+
onComplete,
|
|
143
183
|
}: {
|
|
144
184
|
children: ReactNode;
|
|
145
185
|
labels?: LabelOverride<TourLabels>;
|
|
186
|
+
/** The app's tour progress: the same options its `useTourProgress` readers use. */
|
|
187
|
+
progress?: UseTourProgressOptions;
|
|
188
|
+
/** A tour's last step was finished; see {@link TourCompletion}. */
|
|
189
|
+
onComplete?: (tour: TourInfo | undefined, completion: TourCompletion) => void;
|
|
146
190
|
}) {
|
|
147
191
|
// `labels` over the provider's `tour` over English. Resolved once here and handed
|
|
148
192
|
// down: the step card is portalled, but context crosses portals anyway — this is
|
|
@@ -152,6 +196,23 @@ export function TourProvider({
|
|
|
152
196
|
const [index, setIndex] = useState(0);
|
|
153
197
|
const [activeId, setActiveId] = useState<string | null>(null);
|
|
154
198
|
const optsRef = useRef<StartOptions>({});
|
|
199
|
+
// The latest props, read at the finish: a new `storageKey` or callback needs no remount.
|
|
200
|
+
const progressRef = useRef(progress);
|
|
201
|
+
const onCompleteRef = useRef(onComplete);
|
|
202
|
+
useLayoutEffect(() => {
|
|
203
|
+
progressRef.current = progress;
|
|
204
|
+
onCompleteRef.current = onComplete;
|
|
205
|
+
});
|
|
206
|
+
// The provider's migrate becomes the key's, for readers that pass none (keksdose B-2).
|
|
207
|
+
const storageKey = progress?.storageKey;
|
|
208
|
+
const migrate = progress?.migrate;
|
|
209
|
+
// 0.45: and its `account` (§10.3), so a finish on an account key is sent as readers' are.
|
|
210
|
+
const account = progress?.account;
|
|
211
|
+
useLayoutEffect(() => {
|
|
212
|
+
if (storageKey !== undefined) rememberMigrate(storageKey, migrate);
|
|
213
|
+
if (storageKey !== undefined) rememberAccount(storageKey, account);
|
|
214
|
+
}, [storageKey, migrate, account]);
|
|
215
|
+
const warnedNoTourRef = useRef(false);
|
|
155
216
|
// Live mirrors so the step-control callbacks can stay stable (identity-safe for
|
|
156
217
|
// consumers) while still reading the current steps/index. Synced in a layout
|
|
157
218
|
// effect — after the commit, before paint and before any passive effect or event
|
|
@@ -169,7 +230,7 @@ export function TourProvider({
|
|
|
169
230
|
optsRef.current = opts ?? {};
|
|
170
231
|
optsRef.current.onStart?.();
|
|
171
232
|
setIndex(Math.min(Math.max(0, opts?.startIndex ?? 0), Math.max(0, s.length - 1)));
|
|
172
|
-
setActiveId(opts?.id ?? null);
|
|
233
|
+
setActiveId(opts?.id ?? opts?.tour?.id ?? null);
|
|
173
234
|
setSteps(s);
|
|
174
235
|
}, []);
|
|
175
236
|
const stop = useCallback(() => {
|
|
@@ -177,7 +238,30 @@ export function TourProvider({
|
|
|
177
238
|
setActiveId(null);
|
|
178
239
|
}, []);
|
|
179
240
|
const finish = useCallback(() => {
|
|
180
|
-
|
|
241
|
+
// Once per tour: a second "Done" before the overlay unmounts records nothing more.
|
|
242
|
+
if (!stepsRef.current) return;
|
|
243
|
+
stepsRef.current = null;
|
|
244
|
+
const { tour, onFinish } = optsRef.current;
|
|
245
|
+
const store = progressRef.current;
|
|
246
|
+
// Exactly one write per completion (§9.6.2), so `awarded` is what `complete(tour)` answered.
|
|
247
|
+
let awarded: boolean | undefined;
|
|
248
|
+
if (store && tour) {
|
|
249
|
+
try {
|
|
250
|
+
awarded = completeStored(store.storageKey, store.migrate, tour);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
// A stored text no migrate reads is left as it is (keksdose B-1); the tour still ends.
|
|
253
|
+
console.error(error);
|
|
254
|
+
awarded = false;
|
|
255
|
+
}
|
|
256
|
+
} else if (store && !tour && devWarnings() && !warnedNoTourRef.current) {
|
|
257
|
+
warnedNoTourRef.current = true;
|
|
258
|
+
console.warn(
|
|
259
|
+
"[ui-kit] TourProvider has `progress`, but a finished tour's start carried no `tour`: " +
|
|
260
|
+
"nothing was recorded and `awarded` is undefined. Start it with `start(steps, { tour })`.",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
onCompleteRef.current?.(tour, { awarded });
|
|
264
|
+
onFinish?.();
|
|
181
265
|
setSteps(null);
|
|
182
266
|
setActiveId(null);
|
|
183
267
|
}, []);
|