@eifi1/ui-kit 0.17.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 +83 -4
- package/dist/chart.d.ts +13 -1
- package/dist/components/account-chips.d.ts +153 -0
- package/dist/components/account-chips.js +135 -0
- package/dist/components/account-chips.js.map +1 -0
- package/dist/components/account-settings-labels.d.ts +10 -2
- package/dist/components/account-settings-labels.js +3 -0
- package/dist/components/account-settings-labels.js.map +1 -1
- package/dist/components/amount-input.d.ts +13 -1
- package/dist/components/button-group.d.ts +13 -1
- package/dist/components/calculator.d.ts +13 -1
- package/dist/components/chip.d.ts +25 -1
- package/dist/components/chip.js +143 -6
- package/dist/components/chip.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +42 -1
- package/dist/components/confirm-dialog.js +36 -5
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +13 -1
- package/dist/components/danger-confirm.d.ts +55 -4
- package/dist/components/danger-confirm.js +67 -30
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +1 -1
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +2 -0
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/facing-pair.d.ts +13 -1
- package/dist/components/file-button.d.ts +13 -1
- package/dist/components/file-dropzone.d.ts +13 -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 +13 -1
- package/dist/components/form-actions.js +3 -0
- package/dist/components/form-actions.js.map +1 -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 +13 -1
- package/dist/components/number-field.d.ts +14 -2
- package/dist/components/number-field.js +23 -22
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +13 -1
- package/dist/components/numpad-sheet.d.ts +13 -1
- package/dist/components/passkeys-setting.d.ts +27 -1
- package/dist/components/passkeys-setting.js +31 -5
- package/dist/components/passkeys-setting.js.map +1 -1
- package/dist/components/progress-bar.d.ts +15 -1
- package/dist/components/progress-bar.js +8 -1
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/reauth-dialog.d.ts +90 -0
- package/dist/components/reauth-dialog.js +158 -0
- package/dist/components/reauth-dialog.js.map +1 -0
- package/dist/components/series-chart.d.ts +13 -1
- package/dist/components/server-wake.d.ts +60 -0
- package/dist/components/server-wake.js +79 -0
- package/dist/components/server-wake.js.map +1 -0
- package/dist/components/settings-fields.d.ts +13 -1
- package/dist/components/share-card.d.ts +64 -0
- package/dist/components/share-card.js +350 -0
- package/dist/components/share-card.js.map +1 -0
- package/dist/components/status-dot.d.ts +2 -0
- package/dist/components/text-link.d.ts +13 -1
- package/dist/components/time-input.d.ts +13 -1
- package/dist/components/toast.d.ts +10 -1
- package/dist/components/toast.js +42 -2
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.js +5 -2
- package/dist/components/toggle-group.js.map +1 -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 +13 -1
- package/dist/components/ui.js +43 -28
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/user-avatar.d.ts +2 -0
- package/dist/components/write-lock.d.ts +68 -0
- package/dist/components/write-lock.js +33 -0
- package/dist/components/write-lock.js.map +1 -0
- package/dist/{data-table-CpkWUJBY.d.ts → data-table-3mi2R3so.d.ts} +19 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/hooks/use-file-drop.d.ts +13 -1
- package/dist/hooks/use-overlay-history.js +16 -0
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.d.ts +13 -1
- package/dist/i18n/defaults.js +15 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +81 -0
- package/dist/i18n/{locales/de.js → german.js} +110 -9
- package/dist/i18n/german.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +13 -1
- package/dist/i18n/kit-labels.js +5 -0
- 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 +26 -8
- package/dist/i18n/locales/de-CH.js +7 -3
- package/dist/i18n/locales/de-CH.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +13 -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 +13 -1
- package/dist/i18n/locales/es.js +127 -27
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +13 -1
- package/dist/i18n/locales/fr.js +101 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +13 -1
- package/dist/i18n/locales/hu.js +105 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +13 -1
- package/dist/i18n/locales/it.js +134 -34
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +13 -1
- package/dist/i18n/locales/zh.js +101 -1
- 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/i18n/swiss.d.ts +3 -3
- package/dist/i18n/swiss.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.js +97 -1
- package/dist/index.js.map +1 -1
- package/dist/{kit-labels-C4uB_zmg.d.ts → kit-labels-D1J2_jyD.d.ts} +236 -9
- 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 +146 -0
- package/dist/lib/server-wake.js +122 -0
- package/dist/lib/server-wake.js.map +1 -0
- 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 +13 -1
- package/dist/rhf/form.d.ts +13 -1
- package/dist/rhf.d.ts +13 -1
- package/dist/search/global-search.d.ts +3 -1
- package/dist/search/global-search.js.map +1 -1
- package/dist/shell/app-shell.d.ts +13 -1
- package/dist/shell/top-bar-brand.d.ts +13 -1
- package/dist/shell/topbar-action-menu.d.ts +2 -0
- package/dist/shell.d.ts +13 -1
- package/dist/wizard/stepper-nav.d.ts +13 -1
- package/dist/wizard.d.ts +13 -1
- package/package.json +1 -1
- package/src/components/account-chips.tsx +302 -0
- package/src/components/account-settings-labels.ts +16 -2
- package/src/components/chip.tsx +239 -6
- package/src/components/confirm-dialog.tsx +82 -7
- package/src/components/danger-confirm.tsx +118 -33
- package/src/components/data-table.tsx +20 -0
- package/src/components/floating-panel.tsx +19 -1
- package/src/components/form-actions.tsx +22 -3
- package/src/components/legal.tsx +159 -0
- package/src/components/number-field.tsx +32 -26
- package/src/components/passkeys-setting.tsx +68 -7
- package/src/components/progress-bar.tsx +21 -1
- package/src/components/reauth-dialog.tsx +273 -0
- package/src/components/server-wake.tsx +156 -0
- package/src/components/share-card.tsx +615 -0
- package/src/components/toast.tsx +72 -2
- package/src/components/toggle-group.tsx +5 -2
- 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/components/ui.tsx +89 -32
- package/src/components/write-lock.tsx +96 -0
- package/src/hooks/use-overlay-history.ts +51 -0
- package/src/i18n/defaults.ts +14 -0
- package/src/i18n/{locales/de.ts → german.ts} +120 -13
- package/src/i18n/kit-labels.tsx +29 -2
- package/src/i18n/languages.ts +285 -0
- package/src/i18n/locales/de-CH.ts +17 -8
- package/src/i18n/locales/en.ts +8 -0
- package/src/i18n/locales/es.ts +128 -26
- package/src/i18n/locales/fr.ts +103 -0
- package/src/i18n/locales/hu.ts +107 -0
- package/src/i18n/locales/it.ts +135 -33
- package/src/i18n/locales/zh.ts +102 -0
- package/src/i18n/review.ts +193 -0
- package/src/i18n/swiss.ts +3 -3
- package/src/index.ts +86 -1
- package/src/lib/floating-stack.ts +66 -0
- package/src/lib/server-wake.ts +291 -0
- package/src/lib/translation-review.ts +465 -0
- package/src/search/global-search.tsx +3 -1
- package/dist/i18n/locales/de-CH-informal.d.ts +0 -61
- package/dist/i18n/locales/de-CH-informal.js +0 -8
- package/dist/i18n/locales/de-CH-informal.js.map +0 -1
- package/dist/i18n/locales/de-informal.d.ts +0 -82
- package/dist/i18n/locales/de-informal.js +0 -37
- package/dist/i18n/locales/de-informal.js.map +0 -1
- package/dist/i18n/locales/de.d.ts +0 -66
- package/dist/i18n/locales/de.js.map +0 -1
- package/src/i18n/locales/de-CH-informal.ts +0 -10
- package/src/i18n/locales/de-informal.ts +0 -61
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { Download } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
6
|
+
import { useKitFormat } from "../lib/format";
|
|
7
|
+
import { summariseRows, translationCorrections } from "../lib/translation-review";
|
|
8
|
+
import type {
|
|
9
|
+
ReviewStatus,
|
|
10
|
+
TranslationCorrection,
|
|
11
|
+
TranslationRow,
|
|
12
|
+
TranslationSummary,
|
|
13
|
+
} from "../lib/translation-review";
|
|
14
|
+
import { Chip } from "./chip";
|
|
15
|
+
import type { ChipShape, ChipSize, ChipTone, ChipVariant } from "./chip";
|
|
16
|
+
import { ProgressBar } from "./progress-bar";
|
|
17
|
+
import type { ProgressBarSegment } from "./progress-bar";
|
|
18
|
+
import { DEFAULT_TRANSLATION_REVIEW_LABELS, reviewStatusLabel } from "./translation-review-labels";
|
|
19
|
+
import type { TranslationReviewLabels } from "./translation-review-labels";
|
|
20
|
+
import { Button, Tabs } from "./ui";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The small parts of the translation review, each usable on its own: the status
|
|
24
|
+
* vocabulary as a chip, one locale's progress, the locale tabs and the export. The page
|
|
25
|
+
* body that puts them together is `TranslationReviewPanel` (translation-review.tsx).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── ReviewStatusChip ──────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The tone of each status: nothing yet is neutral, a gap is orange (kastlan's choice — a
|
|
32
|
+
* hue, not a verdict), a change since review is amber, sent back is red, done is green.
|
|
33
|
+
*/
|
|
34
|
+
export const REVIEW_STATUS_TONES: Readonly<Record<ReviewStatus, ChipTone>> = {
|
|
35
|
+
missing: "orange",
|
|
36
|
+
unreviewed: "neutral",
|
|
37
|
+
changed: "warning",
|
|
38
|
+
needs_change: "danger",
|
|
39
|
+
approved: "success",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export interface ReviewStatusChipProps {
|
|
43
|
+
status: ReviewStatus;
|
|
44
|
+
/** Over {@link REVIEW_STATUS_TONES}. */
|
|
45
|
+
tone?: ChipTone;
|
|
46
|
+
/** Over the label. */
|
|
47
|
+
children?: ReactNode;
|
|
48
|
+
/** Default `sm` — a chip in a table cell. */
|
|
49
|
+
size?: ChipSize;
|
|
50
|
+
variant?: ChipVariant;
|
|
51
|
+
shape?: ChipShape;
|
|
52
|
+
caps?: boolean;
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Prop > `<UiKitProvider labels={{ translationReview }}>` > English. */
|
|
55
|
+
labels?: Partial<TranslationReviewLabels>;
|
|
56
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** One row's status as a chip, in the kit's words and tones. Carries `data-status`. */
|
|
60
|
+
export function ReviewStatusChip({
|
|
61
|
+
status,
|
|
62
|
+
tone,
|
|
63
|
+
children,
|
|
64
|
+
size = "sm",
|
|
65
|
+
variant,
|
|
66
|
+
shape,
|
|
67
|
+
caps,
|
|
68
|
+
className,
|
|
69
|
+
labels: labelsProp,
|
|
70
|
+
...rest
|
|
71
|
+
}: ReviewStatusChipProps) {
|
|
72
|
+
const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
|
|
73
|
+
return (
|
|
74
|
+
<Chip
|
|
75
|
+
{...rest}
|
|
76
|
+
data-status={status}
|
|
77
|
+
tone={tone ?? REVIEW_STATUS_TONES[status]}
|
|
78
|
+
size={size}
|
|
79
|
+
variant={variant}
|
|
80
|
+
shape={shape}
|
|
81
|
+
caps={caps}
|
|
82
|
+
className={className}
|
|
83
|
+
>
|
|
84
|
+
{children ?? reviewStatusLabel(labels, status)}
|
|
85
|
+
</Chip>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── TranslationProgress ───────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export interface TranslationProgressProps {
|
|
92
|
+
/** One locale's rows — or their {@link summariseRows} when the app has only counts. */
|
|
93
|
+
rows?: readonly TranslationRow[];
|
|
94
|
+
summary?: TranslationSummary;
|
|
95
|
+
/** The list under the bar naming each part with its count. Default true. */
|
|
96
|
+
legend?: boolean;
|
|
97
|
+
className?: string;
|
|
98
|
+
labels?: Partial<TranslationReviewLabels>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* "120 of 3859 approved", and the bar split by status: approved, sent back, changed since
|
|
103
|
+
* review, missing — in the chips' colours. What is left of the track is unreviewed. The
|
|
104
|
+
* legend counts rows rather than percentages: "12 need a change" is the next hour's work,
|
|
105
|
+
* "0.3 %" is not.
|
|
106
|
+
*/
|
|
107
|
+
export function TranslationProgress({
|
|
108
|
+
rows,
|
|
109
|
+
summary: summaryProp,
|
|
110
|
+
legend = true,
|
|
111
|
+
className,
|
|
112
|
+
labels: labelsProp,
|
|
113
|
+
}: TranslationProgressProps) {
|
|
114
|
+
const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
|
|
115
|
+
const { formatNumber } = useKitFormat();
|
|
116
|
+
const summary = useMemo(() => summaryProp ?? summariseRows(rows ?? []), [summaryProp, rows]);
|
|
117
|
+
const part = (status: ReviewStatus, tone: ProgressBarSegment["tone"], color?: string): ProgressBarSegment => ({
|
|
118
|
+
key: status,
|
|
119
|
+
value: summary[status],
|
|
120
|
+
tone,
|
|
121
|
+
color,
|
|
122
|
+
label: reviewStatusLabel(labels, status),
|
|
123
|
+
// A status with no rows stays in the legend ("Needs a change 0" is news too) but out
|
|
124
|
+
// of the bar, where it would only be a gap.
|
|
125
|
+
legendOnly: summary[status] === 0 ? true : undefined,
|
|
126
|
+
});
|
|
127
|
+
const segments = [
|
|
128
|
+
part("approved", "success"),
|
|
129
|
+
part("needs_change", "danger"),
|
|
130
|
+
part("changed", "warning"),
|
|
131
|
+
// The chip's orange, which is a hue and not a ProgressBar tone. Only where there is
|
|
132
|
+
// a gap: an app whose locales are complete never hears of "missing".
|
|
133
|
+
...(summary.missing > 0 ? [part("missing", undefined, "var(--hue-orange)")] : []),
|
|
134
|
+
];
|
|
135
|
+
return (
|
|
136
|
+
<ProgressBar
|
|
137
|
+
className={className}
|
|
138
|
+
label={labels.progress(summary.approved, summary.total)}
|
|
139
|
+
max={Math.max(summary.total, 1)}
|
|
140
|
+
segments={segments}
|
|
141
|
+
legend={legend}
|
|
142
|
+
formatValue={(value) => formatNumber(value)}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── TranslationLocaleTabs ─────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
export interface TranslationLocaleTab {
|
|
150
|
+
/** The locale code. */
|
|
151
|
+
value: string;
|
|
152
|
+
/** Its name, in its own language ("Français"). */
|
|
153
|
+
label: ReactNode;
|
|
154
|
+
/** The locale's rows, for the badge — built with the same `areas` as the panel's, so
|
|
155
|
+
* a scoped reviewer's tab counts what they can see. */
|
|
156
|
+
rows: readonly TranslationRow[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface TranslationLocaleTabsProps {
|
|
160
|
+
/** The locales the viewer may see, in the app's order — the server's answer, never a
|
|
161
|
+
* guess from a role name. */
|
|
162
|
+
locales: readonly TranslationLocaleTab[];
|
|
163
|
+
active: string;
|
|
164
|
+
onChange: (locale: string) => void;
|
|
165
|
+
className?: string;
|
|
166
|
+
labels?: Partial<TranslationReviewLabels>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* One tab per locale with its approved/total, wrapping on a phone. Nothing for a single
|
|
171
|
+
* locale: a strip with one tab is a heading that looks clickable.
|
|
172
|
+
*/
|
|
173
|
+
export function TranslationLocaleTabs({ locales, active, onChange, className, labels: labelsProp }: TranslationLocaleTabsProps) {
|
|
174
|
+
const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
|
|
175
|
+
if (locales.length < 2) return null;
|
|
176
|
+
return (
|
|
177
|
+
<Tabs
|
|
178
|
+
aria-label={labels.locales}
|
|
179
|
+
wrap
|
|
180
|
+
className={className}
|
|
181
|
+
tabs={locales.map((locale) => {
|
|
182
|
+
const summary = summariseRows(locale.rows);
|
|
183
|
+
return {
|
|
184
|
+
id: locale.value,
|
|
185
|
+
label: locale.label,
|
|
186
|
+
name: typeof locale.label === "string" ? locale.label : locale.value,
|
|
187
|
+
badge: (
|
|
188
|
+
// The tab's own colour, dimmed — a fixed muted grey vanished on the selected
|
|
189
|
+
// tab's brand fill (390px browser check).
|
|
190
|
+
<span className="text-xs tabular-nums opacity-75">
|
|
191
|
+
{labels.localeProgress(summary.approved, summary.total)}
|
|
192
|
+
</span>
|
|
193
|
+
),
|
|
194
|
+
};
|
|
195
|
+
})}
|
|
196
|
+
active={active}
|
|
197
|
+
onChange={onChange}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ── TranslationExportButton ───────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
export interface TranslationExportButtonProps {
|
|
205
|
+
/** Every row the export covers — usually every locale's, not only the open one. */
|
|
206
|
+
rows: readonly TranslationRow[];
|
|
207
|
+
/** Listed in the file. Default: the locales the rows come from. */
|
|
208
|
+
locales?: readonly string[];
|
|
209
|
+
/** Default `translation-corrections-<date>.json`. */
|
|
210
|
+
fileName?: string;
|
|
211
|
+
/**
|
|
212
|
+
* Take the export instead of downloading it — the entries, and the file as a ready
|
|
213
|
+
* JSON Blob. Left out, the kit downloads the Blob as `fileName`.
|
|
214
|
+
*/
|
|
215
|
+
onExport?: (corrections: TranslationCorrection[], file: Blob) => void;
|
|
216
|
+
className?: string;
|
|
217
|
+
labels?: Partial<TranslationReviewLabels>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* "Export corrections (n)": every string sent back, as the JSON file keksdose and kastlan
|
|
222
|
+
* hand a developer — `{ exported_at, locales, corrections }`. Corrections are not applied
|
|
223
|
+
* on the page: the strings are source code. Disabled while there is nothing to export.
|
|
224
|
+
*/
|
|
225
|
+
export function TranslationExportButton({
|
|
226
|
+
rows,
|
|
227
|
+
locales,
|
|
228
|
+
fileName,
|
|
229
|
+
onExport,
|
|
230
|
+
className,
|
|
231
|
+
labels: labelsProp,
|
|
232
|
+
}: TranslationExportButtonProps) {
|
|
233
|
+
const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
|
|
234
|
+
const corrections = useMemo(() => translationCorrections(rows), [rows]);
|
|
235
|
+
const run = () => {
|
|
236
|
+
const now = new Date();
|
|
237
|
+
const file = new Blob(
|
|
238
|
+
[
|
|
239
|
+
JSON.stringify(
|
|
240
|
+
{
|
|
241
|
+
exported_at: now.toISOString(),
|
|
242
|
+
locales: locales ?? [...new Set(rows.map((r) => r.locale))],
|
|
243
|
+
corrections,
|
|
244
|
+
},
|
|
245
|
+
null,
|
|
246
|
+
2,
|
|
247
|
+
),
|
|
248
|
+
],
|
|
249
|
+
{ type: "application/json" },
|
|
250
|
+
);
|
|
251
|
+
if (onExport) {
|
|
252
|
+
onExport(corrections, file);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const url = URL.createObjectURL(file);
|
|
256
|
+
const a = document.createElement("a");
|
|
257
|
+
a.href = url;
|
|
258
|
+
a.download = fileName ?? `translation-corrections-${now.toISOString().slice(0, 10)}.json`;
|
|
259
|
+
a.click();
|
|
260
|
+
// After the click has handed the URL to the download, not before.
|
|
261
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
262
|
+
};
|
|
263
|
+
return (
|
|
264
|
+
<Button variant="secondary" disabled={corrections.length === 0} onClick={run} className={className}>
|
|
265
|
+
<Download className="size-4" aria-hidden />
|
|
266
|
+
{labels.exportCorrections(corrections.length)}
|
|
267
|
+
</Button>
|
|
268
|
+
);
|
|
269
|
+
}
|