@eifi1/ui-kit 0.18.0 → 0.19.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 +60 -6
- package/dist/chart.d.ts +4 -1
- package/dist/components/amount-input.d.ts +4 -1
- package/dist/components/button-group.d.ts +4 -1
- package/dist/components/calculator.d.ts +4 -1
- package/dist/components/copy-button.d.ts +4 -1
- package/dist/components/facing-pair.d.ts +4 -1
- package/dist/components/file-button.d.ts +4 -1
- package/dist/components/file-dropzone.d.ts +4 -1
- package/dist/components/floating-panel.d.ts +4 -1
- package/dist/components/floating-panel.js +15 -1
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +4 -1
- package/dist/components/legal.d.ts +84 -0
- package/dist/components/legal.js +55 -0
- package/dist/components/legal.js.map +1 -0
- package/dist/components/money-field.d.ts +4 -1
- package/dist/components/number-field.d.ts +4 -1
- package/dist/components/number-input.d.ts +4 -1
- package/dist/components/numpad-sheet.d.ts +4 -1
- package/dist/components/series-chart.d.ts +4 -1
- package/dist/components/server-wake.d.ts +4 -2
- package/dist/components/server-wake.js +23 -2
- package/dist/components/server-wake.js.map +1 -1
- package/dist/components/settings-fields.d.ts +4 -1
- package/dist/components/share-card.d.ts +4 -1
- package/dist/components/text-link.d.ts +4 -1
- package/dist/components/time-input.d.ts +4 -1
- package/dist/components/translation-review-editor.d.ts +56 -0
- package/dist/components/translation-review-editor.js +159 -0
- package/dist/components/translation-review-editor.js.map +1 -0
- package/dist/components/translation-review-labels.d.ts +92 -0
- package/dist/components/translation-review-labels.js +67 -0
- package/dist/components/translation-review-labels.js.map +1 -0
- package/dist/components/translation-review-parts.d.ts +97 -0
- package/dist/components/translation-review-parts.js +163 -0
- package/dist/components/translation-review-parts.js.map +1 -0
- package/dist/components/translation-review.d.ts +94 -0
- package/dist/components/translation-review.js +344 -0
- package/dist/components/translation-review.js.map +1 -0
- package/dist/components/ui.d.ts +4 -1
- package/dist/hooks/use-file-drop.d.ts +4 -1
- package/dist/i18n/defaults.d.ts +4 -1
- package/dist/i18n/defaults.js +5 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +4 -1
- package/dist/i18n/german.js +51 -0
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +4 -1
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +243 -0
- package/dist/i18n/languages.js +70 -0
- package/dist/i18n/languages.js.map +1 -0
- package/dist/i18n/locales/de-CH.d.ts +4 -1
- package/dist/i18n/locales/en.d.ts +4 -1
- package/dist/i18n/locales/en.js +8 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +4 -1
- package/dist/i18n/locales/es.js +83 -33
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +4 -1
- package/dist/i18n/locales/fr.js +50 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +4 -1
- package/dist/i18n/locales/hu.js +51 -0
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +4 -1
- package/dist/i18n/locales/it.js +90 -40
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +4 -1
- package/dist/i18n/locales/zh.js +51 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +102 -0
- package/dist/i18n/review.js +158 -0
- package/dist/i18n/review.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -1
- package/dist/{kit-labels-CPbsjlXr.d.ts → kit-labels-D1J2_jyD.d.ts} +7 -0
- package/dist/lib/floating-stack.d.ts +31 -0
- package/dist/lib/floating-stack.js +42 -0
- package/dist/lib/floating-stack.js.map +1 -0
- package/dist/lib/server-wake.d.ts +24 -8
- package/dist/lib/server-wake.js +6 -5
- package/dist/lib/server-wake.js.map +1 -1
- package/dist/lib/translation-review.d.ts +232 -0
- package/dist/lib/translation-review.js +194 -0
- package/dist/lib/translation-review.js.map +1 -0
- package/dist/rhf/fields.d.ts +4 -1
- package/dist/rhf/form.d.ts +4 -1
- package/dist/rhf.d.ts +4 -1
- package/dist/shell/app-shell.d.ts +4 -1
- package/dist/shell/top-bar-brand.d.ts +4 -1
- package/dist/shell.d.ts +4 -1
- package/dist/wizard/stepper-nav.d.ts +4 -1
- package/dist/wizard.d.ts +4 -1
- package/package.json +1 -1
- package/src/components/floating-panel.tsx +19 -1
- package/src/components/legal.tsx +159 -0
- package/src/components/server-wake.tsx +37 -5
- package/src/components/translation-review-editor.tsx +261 -0
- package/src/components/translation-review-labels.ts +152 -0
- package/src/components/translation-review-parts.tsx +269 -0
- package/src/components/translation-review.tsx +502 -0
- package/src/i18n/defaults.ts +4 -0
- package/src/i18n/german.ts +52 -0
- package/src/i18n/kit-labels.tsx +7 -0
- package/src/i18n/languages.ts +285 -0
- package/src/i18n/locales/en.ts +8 -0
- package/src/i18n/locales/es.ts +84 -33
- package/src/i18n/locales/fr.ts +51 -0
- package/src/i18n/locales/hu.ts +52 -0
- package/src/i18n/locales/it.ts +91 -40
- package/src/i18n/locales/zh.ts +52 -0
- package/src/i18n/review.ts +193 -0
- package/src/index.ts +63 -0
- package/src/lib/floating-stack.ts +66 -0
- package/src/lib/server-wake.ts +34 -13
- package/src/lib/translation-review.ts +465 -0
package/src/index.ts
CHANGED
|
@@ -341,3 +341,66 @@ export * from "./search/global-search";
|
|
|
341
341
|
// prop > the provider > the English default. See src/i18n/kit-labels.tsx.
|
|
342
342
|
export * from "./i18n/kit-labels";
|
|
343
343
|
export * from "./i18n/defaults";
|
|
344
|
+
// The kit's words as `key → text` rows for an app's translation review (keksdose).
|
|
345
|
+
export { kitLabelStrings } from "./i18n/review";
|
|
346
|
+
// The seven languages, how an app resolves one, and the kit catalogue loader for it.
|
|
347
|
+
export * from "./i18n/languages";
|
|
348
|
+
// Translation review (0.19): one contract for kastlan's and keksdose's review pages —
|
|
349
|
+
// the pure rows/status/placeholder logic and the presentational page parts.
|
|
350
|
+
export {
|
|
351
|
+
REVIEW_STATUSES,
|
|
352
|
+
fromApiReview,
|
|
353
|
+
toApiWrite,
|
|
354
|
+
flattenStrings,
|
|
355
|
+
keyNamespace,
|
|
356
|
+
placeholderTokens,
|
|
357
|
+
placeholderMismatch,
|
|
358
|
+
reviewStatus,
|
|
359
|
+
keyInArea,
|
|
360
|
+
keyInAreas,
|
|
361
|
+
translationRows,
|
|
362
|
+
summariseRows,
|
|
363
|
+
DEFAULT_TRANSLATION_REVIEW_FILTER,
|
|
364
|
+
filterTranslationRows,
|
|
365
|
+
reviewWrite,
|
|
366
|
+
mergeReviews,
|
|
367
|
+
dropReviews,
|
|
368
|
+
translationCorrections,
|
|
369
|
+
} from "./lib/translation-review";
|
|
370
|
+
export type {
|
|
371
|
+
TranslationVerdict,
|
|
372
|
+
TranslationReview,
|
|
373
|
+
TranslationReviewWrite,
|
|
374
|
+
TranslationReviewKey,
|
|
375
|
+
ReviewStatus,
|
|
376
|
+
TranslationRow,
|
|
377
|
+
ApiTranslationReview,
|
|
378
|
+
ApiTranslationReviewWrite,
|
|
379
|
+
TranslationRowsInput,
|
|
380
|
+
TranslationSummary,
|
|
381
|
+
TranslationReviewFilter,
|
|
382
|
+
TranslationCorrection,
|
|
383
|
+
} from "./lib/translation-review";
|
|
384
|
+
export {
|
|
385
|
+
TranslationReviewPanel,
|
|
386
|
+
TranslationReviewEditor,
|
|
387
|
+
ReviewStatusChip,
|
|
388
|
+
TranslationProgress,
|
|
389
|
+
TranslationLocaleTabs,
|
|
390
|
+
TranslationExportButton,
|
|
391
|
+
REVIEW_STATUS_TONES,
|
|
392
|
+
DEFAULT_TRANSLATION_REVIEW_LABELS,
|
|
393
|
+
} from "./components/translation-review";
|
|
394
|
+
export type {
|
|
395
|
+
TranslationReviewPanelProps,
|
|
396
|
+
TranslationReviewEditorProps,
|
|
397
|
+
ReviewStatusChipProps,
|
|
398
|
+
TranslationProgressProps,
|
|
399
|
+
TranslationLocaleTab,
|
|
400
|
+
TranslationLocaleTabsProps,
|
|
401
|
+
TranslationExportButtonProps,
|
|
402
|
+
TranslationReviewLabels,
|
|
403
|
+
} from "./components/translation-review";
|
|
404
|
+
// The legal pages' shell (0.19, H10): Imprint, Privacy Policy, Terms in every app.
|
|
405
|
+
export { LegalLayout, LegalSection, LegalLinks, DEFAULT_LEGAL_LABELS } from "./components/legal";
|
|
406
|
+
export type { LegalLabels, LegalLink, LegalLinksProps, LegalSectionProps, LegalLayoutProps } from "./components/legal";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What floats over the bottom of the screen right now, so a notice that comes and goes
|
|
3
|
+
* can sit above it instead of on it.
|
|
4
|
+
*
|
|
5
|
+
* kastlan 0.18: its offline pill is a `bottom-start` FloatingActionButton, and
|
|
6
|
+
* `ServerWakeNotice` sits in the same corner — right after a break both show at once
|
|
7
|
+
* (the photo outbox syncs while the first reads wake the server), one over the other.
|
|
8
|
+
* The FAB's `offset` cannot help: the notice comes and goes, and on a phone it is
|
|
9
|
+
* nearly as wide as the screen, so a FAB in EITHER corner can be under it.
|
|
10
|
+
*
|
|
11
|
+
* Measured, not declared: a FloatingActionButton registers its fixed element while it
|
|
12
|
+
* is mounted, and a notice asks {@link clearanceAbove} for the height it has to clear —
|
|
13
|
+
* whatever offset, `extended` label or `hidden` state the controls are in. A control
|
|
14
|
+
* that is `hidden` measures 0 × 0 and is skipped, so kastlan's pill, which stays
|
|
15
|
+
* mounted and hidden while there is nothing to report, costs the notice nothing.
|
|
16
|
+
*
|
|
17
|
+
* Internal: not exported from the barrel.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const occupants = new Set<HTMLElement>();
|
|
21
|
+
const listeners = new Set<() => void>();
|
|
22
|
+
let observer: ResizeObserver | null = null;
|
|
23
|
+
|
|
24
|
+
function notify() {
|
|
25
|
+
for (const listener of listeners) listener();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Track `element` until the returned function is called. Size changes — a label that
|
|
29
|
+
* grows, `hidden` toggling — notify the subscribers too. */
|
|
30
|
+
export function registerFloating(element: HTMLElement): () => void {
|
|
31
|
+
occupants.add(element);
|
|
32
|
+
if (observer === null && typeof ResizeObserver !== "undefined") observer = new ResizeObserver(notify);
|
|
33
|
+
observer?.observe(element);
|
|
34
|
+
notify();
|
|
35
|
+
return () => {
|
|
36
|
+
occupants.delete(element);
|
|
37
|
+
observer?.unobserve(element);
|
|
38
|
+
notify();
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Hear every registration, removal and size change. */
|
|
43
|
+
export function subscribeFloating(listener: () => void): () => void {
|
|
44
|
+
listeners.add(listener);
|
|
45
|
+
return () => {
|
|
46
|
+
listeners.delete(listener);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The distance from the viewport's bottom edge to the top of the highest visible
|
|
52
|
+
* floating control that shares a column with `element` — what `element` has to clear —
|
|
53
|
+
* or `null` when nothing is under it.
|
|
54
|
+
*/
|
|
55
|
+
export function clearanceAbove(element: HTMLElement | null): number | null {
|
|
56
|
+
if (!element) return null;
|
|
57
|
+
const own = element.getBoundingClientRect();
|
|
58
|
+
let top = Infinity;
|
|
59
|
+
for (const other of occupants) {
|
|
60
|
+
const box = other.getBoundingClientRect();
|
|
61
|
+
if (box.width === 0 || box.height === 0) continue;
|
|
62
|
+
if (box.right <= own.left || box.left >= own.right) continue;
|
|
63
|
+
top = Math.min(top, box.top);
|
|
64
|
+
}
|
|
65
|
+
return top === Infinity ? null : Math.max(0, window.innerHeight - top);
|
|
66
|
+
}
|
package/src/lib/server-wake.ts
CHANGED
|
@@ -25,6 +25,13 @@
|
|
|
25
25
|
* simply be false. With the default that holds by itself (an upload is a POST);
|
|
26
26
|
* an app widening `shouldWatch` keeps its upload routes out of it.
|
|
27
27
|
*
|
|
28
|
+
* Nor a DOWNLOAD, for the same reason (kastlan 0.18): axios answers only once the
|
|
29
|
+
* whole body is in, so a generated PDF or a large photo — a GET — ran past both
|
|
30
|
+
* thresholds on a warm server, and held the count for every read beside it. The
|
|
31
|
+
* default filter skips a request whose `responseType` is `"blob"`, `"arraybuffer"` or
|
|
32
|
+
* `"stream"`; a custom filter gets the request as its third argument to do the same.
|
|
33
|
+
* (`wrapFetch` ends the wait at the headers, so a download through it is harmless.)
|
|
34
|
+
*
|
|
28
35
|
* It never arms while the browser is offline, whatever `shouldWatch` says: that state
|
|
29
36
|
* has its own, truthful indicator, and a request that never left the device is not
|
|
30
37
|
* evidence of anything about the server.
|
|
@@ -35,8 +42,10 @@
|
|
|
35
42
|
export type ServerWakeStage = "idle" | "slow" | "waking";
|
|
36
43
|
|
|
37
44
|
/** Decides which requests count. `method` arrives lower-cased (`"get"` when the
|
|
38
|
-
* request named none — axios's and fetch's default); `url` as the client had it
|
|
39
|
-
|
|
45
|
+
* request named none — axios's and fetch's default); `url` as the client had it;
|
|
46
|
+
* `request` is what the adapter saw — for axios the request config itself, so
|
|
47
|
+
* `responseType` or the app's own fields can decide. */
|
|
48
|
+
export type ServerWakeFilter = (method: string, url: string, request: ServerWakeRequest) => boolean;
|
|
40
49
|
|
|
41
50
|
export interface ServerWakeOptions {
|
|
42
51
|
/** Past this, a warm round trip is out of the question (a cold database query on a
|
|
@@ -44,15 +53,18 @@ export interface ServerWakeOptions {
|
|
|
44
53
|
slowMs?: number;
|
|
45
54
|
/** Past this, the container was almost certainly scaled to zero. Default 7000 ms. */
|
|
46
55
|
wakingMs?: number;
|
|
47
|
-
/** Which requests count. Default {@link watchReadsAnd}`()` — GETs only
|
|
48
|
-
* guard runs before it and cannot be overridden. */
|
|
56
|
+
/** Which requests count. Default {@link watchReadsAnd}`()` — GETs only, downloads
|
|
57
|
+
* excluded. The offline guard runs before it and cannot be overridden. */
|
|
49
58
|
shouldWatch?: ServerWakeFilter;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
|
-
/** What a request is, for {@link ServerWakeWatcher.track}. */
|
|
61
|
+
/** What a request is, for {@link ServerWakeWatcher.track} and a {@link ServerWakeFilter}. */
|
|
53
62
|
export interface ServerWakeRequest {
|
|
54
63
|
method?: string;
|
|
55
64
|
url?: string;
|
|
65
|
+
/** axios's `responseType`. `"blob"`, `"arraybuffer"` and `"stream"` mark a download,
|
|
66
|
+
* which the default filter does not count. */
|
|
67
|
+
responseType?: string;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
70
|
export interface ServerWakeWatcher {
|
|
@@ -61,8 +73,9 @@ export interface ServerWakeWatcher {
|
|
|
61
73
|
* Returns whether it was counted — the caller MUST call {@link end} exactly once
|
|
62
74
|
* for every `true`, or the counter never returns to zero and the notice sticks.
|
|
63
75
|
* (The axios adapter stashes it on the request config for that reason.)
|
|
76
|
+
* `request` reaches the filter as its third argument; default `{ method, url }`.
|
|
64
77
|
*/
|
|
65
|
-
start(method?: string, url?: string): boolean;
|
|
78
|
+
start(method?: string, url?: string, request?: ServerWakeRequest): boolean;
|
|
66
79
|
/** A counted request settled — either way: an error still proves the server
|
|
67
80
|
* answered or gave up. When the last one does, the notice goes away. Never takes
|
|
68
81
|
* the counter below zero. */
|
|
@@ -79,13 +92,19 @@ export interface ServerWakeWatcher {
|
|
|
79
92
|
getInFlight(): number;
|
|
80
93
|
}
|
|
81
94
|
|
|
95
|
+
/** Response types that make a request a download — see the module comment. */
|
|
96
|
+
const DOWNLOAD_RESPONSE_TYPES = new Set(["blob", "arraybuffer", "stream"]);
|
|
97
|
+
|
|
82
98
|
/**
|
|
83
99
|
* The usual filter: every GET, plus any other request whose URL matches `writes` —
|
|
84
|
-
* keksdose's `/\/auth\/(login|register|demo-session)\b
|
|
100
|
+
* keksdose's `/\/auth\/(login|register|demo-session)\b/` — but never a download
|
|
101
|
+
* (`responseType` `"blob"`, `"arraybuffer"` or `"stream"`). Keep upload routes out of
|
|
85
102
|
* the pattern (see the module comment).
|
|
86
103
|
*/
|
|
87
104
|
export function watchReadsAnd(writes?: RegExp): ServerWakeFilter {
|
|
88
|
-
return (method, url) =>
|
|
105
|
+
return (method, url, request) =>
|
|
106
|
+
!DOWNLOAD_RESPONSE_TYPES.has(request?.responseType ?? "") &&
|
|
107
|
+
(method === "get" || (writes !== undefined && writes.test(url)));
|
|
89
108
|
}
|
|
90
109
|
|
|
91
110
|
function isOffline(): boolean {
|
|
@@ -116,9 +135,9 @@ export function createServerWake({
|
|
|
116
135
|
wakingTimer = null;
|
|
117
136
|
};
|
|
118
137
|
|
|
119
|
-
const start = (method?: string, url?: string): boolean => {
|
|
138
|
+
const start = (method?: string, url?: string, request: ServerWakeRequest = { method, url }): boolean => {
|
|
120
139
|
if (isOffline()) return false;
|
|
121
|
-
if (!shouldWatch((method ?? "get").toLowerCase(), url ?? "")) return false;
|
|
140
|
+
if (!shouldWatch((method ?? "get").toLowerCase(), url ?? "", request)) return false;
|
|
122
141
|
inFlight += 1;
|
|
123
142
|
// The clock runs from the FIRST outstanding request: a page firing a second read
|
|
124
143
|
// at 1.9 s is still waiting on the same sleeping server, not starting afresh.
|
|
@@ -141,7 +160,7 @@ export function createServerWake({
|
|
|
141
160
|
start,
|
|
142
161
|
end,
|
|
143
162
|
track<T>(work: PromiseLike<T> | (() => PromiseLike<T>), request: ServerWakeRequest = {}) {
|
|
144
|
-
const counted = start(request.method, request.url);
|
|
163
|
+
const counted = start(request.method, request.url, request);
|
|
145
164
|
let promise: Promise<T>;
|
|
146
165
|
try {
|
|
147
166
|
promise = Promise.resolve(typeof work === "function" ? work() : work);
|
|
@@ -177,10 +196,12 @@ export const serverWake: ServerWakeWatcher = createServerWake();
|
|
|
177
196
|
|
|
178
197
|
// ── axios ─────────────────────────────────────────────────────────────────────
|
|
179
198
|
|
|
180
|
-
/** The
|
|
199
|
+
/** The config fields the watchdog reads — what axios's request config has. The whole
|
|
200
|
+
* config reaches the filter, so an app's filter can read the rest of it. */
|
|
181
201
|
export interface ServerWakeAxiosConfig {
|
|
182
202
|
method?: string;
|
|
183
203
|
url?: string;
|
|
204
|
+
responseType?: string;
|
|
184
205
|
}
|
|
185
206
|
|
|
186
207
|
/** The slice of an interceptor manager the adapter uses. A method (not a function
|
|
@@ -229,7 +250,7 @@ export function attachServerWake<C extends ServerWakeAxiosConfig, R extends { co
|
|
|
229
250
|
};
|
|
230
251
|
const request = instance.interceptors.request.use(
|
|
231
252
|
(config) => {
|
|
232
|
-
(config as C & Marked)[COUNTED] = watcher.start(config.method, config.url);
|
|
253
|
+
(config as C & Marked)[COUNTED] = watcher.start(config.method, config.url, config);
|
|
233
254
|
return config;
|
|
234
255
|
},
|
|
235
256
|
(error) => Promise.reject(error),
|