@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.
Files changed (210) hide show
  1. package/README.md +83 -4
  2. package/dist/chart.d.ts +13 -1
  3. package/dist/components/account-chips.d.ts +153 -0
  4. package/dist/components/account-chips.js +135 -0
  5. package/dist/components/account-chips.js.map +1 -0
  6. package/dist/components/account-settings-labels.d.ts +10 -2
  7. package/dist/components/account-settings-labels.js +3 -0
  8. package/dist/components/account-settings-labels.js.map +1 -1
  9. package/dist/components/amount-input.d.ts +13 -1
  10. package/dist/components/button-group.d.ts +13 -1
  11. package/dist/components/calculator.d.ts +13 -1
  12. package/dist/components/chip.d.ts +25 -1
  13. package/dist/components/chip.js +143 -6
  14. package/dist/components/chip.js.map +1 -1
  15. package/dist/components/confirm-dialog.d.ts +42 -1
  16. package/dist/components/confirm-dialog.js +36 -5
  17. package/dist/components/confirm-dialog.js.map +1 -1
  18. package/dist/components/copy-button.d.ts +13 -1
  19. package/dist/components/danger-confirm.d.ts +55 -4
  20. package/dist/components/danger-confirm.js +67 -30
  21. package/dist/components/danger-confirm.js.map +1 -1
  22. package/dist/components/data-table-cells.d.ts +1 -1
  23. package/dist/components/data-table-filter-popover.d.ts +1 -1
  24. package/dist/components/data-table-filters.d.ts +1 -1
  25. package/dist/components/data-table.d.ts +1 -1
  26. package/dist/components/data-table.js +2 -0
  27. package/dist/components/data-table.js.map +1 -1
  28. package/dist/components/facing-pair.d.ts +13 -1
  29. package/dist/components/file-button.d.ts +13 -1
  30. package/dist/components/file-dropzone.d.ts +13 -1
  31. package/dist/components/floating-panel.d.ts +4 -1
  32. package/dist/components/floating-panel.js +15 -1
  33. package/dist/components/floating-panel.js.map +1 -1
  34. package/dist/components/form-actions.d.ts +13 -1
  35. package/dist/components/form-actions.js +3 -0
  36. package/dist/components/form-actions.js.map +1 -1
  37. package/dist/components/legal.d.ts +84 -0
  38. package/dist/components/legal.js +55 -0
  39. package/dist/components/legal.js.map +1 -0
  40. package/dist/components/money-field.d.ts +13 -1
  41. package/dist/components/number-field.d.ts +14 -2
  42. package/dist/components/number-field.js +23 -22
  43. package/dist/components/number-field.js.map +1 -1
  44. package/dist/components/number-input.d.ts +13 -1
  45. package/dist/components/numpad-sheet.d.ts +13 -1
  46. package/dist/components/passkeys-setting.d.ts +27 -1
  47. package/dist/components/passkeys-setting.js +31 -5
  48. package/dist/components/passkeys-setting.js.map +1 -1
  49. package/dist/components/progress-bar.d.ts +15 -1
  50. package/dist/components/progress-bar.js +8 -1
  51. package/dist/components/progress-bar.js.map +1 -1
  52. package/dist/components/reauth-dialog.d.ts +90 -0
  53. package/dist/components/reauth-dialog.js +158 -0
  54. package/dist/components/reauth-dialog.js.map +1 -0
  55. package/dist/components/series-chart.d.ts +13 -1
  56. package/dist/components/server-wake.d.ts +60 -0
  57. package/dist/components/server-wake.js +79 -0
  58. package/dist/components/server-wake.js.map +1 -0
  59. package/dist/components/settings-fields.d.ts +13 -1
  60. package/dist/components/share-card.d.ts +64 -0
  61. package/dist/components/share-card.js +350 -0
  62. package/dist/components/share-card.js.map +1 -0
  63. package/dist/components/status-dot.d.ts +2 -0
  64. package/dist/components/text-link.d.ts +13 -1
  65. package/dist/components/time-input.d.ts +13 -1
  66. package/dist/components/toast.d.ts +10 -1
  67. package/dist/components/toast.js +42 -2
  68. package/dist/components/toast.js.map +1 -1
  69. package/dist/components/toggle-group.js +5 -2
  70. package/dist/components/toggle-group.js.map +1 -1
  71. package/dist/components/translation-review-editor.d.ts +56 -0
  72. package/dist/components/translation-review-editor.js +159 -0
  73. package/dist/components/translation-review-editor.js.map +1 -0
  74. package/dist/components/translation-review-labels.d.ts +92 -0
  75. package/dist/components/translation-review-labels.js +67 -0
  76. package/dist/components/translation-review-labels.js.map +1 -0
  77. package/dist/components/translation-review-parts.d.ts +97 -0
  78. package/dist/components/translation-review-parts.js +163 -0
  79. package/dist/components/translation-review-parts.js.map +1 -0
  80. package/dist/components/translation-review.d.ts +94 -0
  81. package/dist/components/translation-review.js +344 -0
  82. package/dist/components/translation-review.js.map +1 -0
  83. package/dist/components/ui.d.ts +13 -1
  84. package/dist/components/ui.js +43 -28
  85. package/dist/components/ui.js.map +1 -1
  86. package/dist/components/use-table-state.d.ts +1 -1
  87. package/dist/components/user-avatar.d.ts +2 -0
  88. package/dist/components/write-lock.d.ts +68 -0
  89. package/dist/components/write-lock.js +33 -0
  90. package/dist/components/write-lock.js.map +1 -0
  91. package/dist/{data-table-CpkWUJBY.d.ts → data-table-3mi2R3so.d.ts} +19 -1
  92. package/dist/data-table.d.ts +1 -1
  93. package/dist/hooks/use-file-drop.d.ts +13 -1
  94. package/dist/hooks/use-overlay-history.js +16 -0
  95. package/dist/hooks/use-overlay-history.js.map +1 -1
  96. package/dist/i18n/defaults.d.ts +13 -1
  97. package/dist/i18n/defaults.js +15 -1
  98. package/dist/i18n/defaults.js.map +1 -1
  99. package/dist/i18n/german.d.ts +81 -0
  100. package/dist/i18n/{locales/de.js → german.js} +110 -9
  101. package/dist/i18n/german.js.map +1 -0
  102. package/dist/i18n/kit-labels.d.ts +13 -1
  103. package/dist/i18n/kit-labels.js +5 -0
  104. package/dist/i18n/kit-labels.js.map +1 -1
  105. package/dist/i18n/languages.d.ts +243 -0
  106. package/dist/i18n/languages.js +70 -0
  107. package/dist/i18n/languages.js.map +1 -0
  108. package/dist/i18n/locales/de-CH.d.ts +26 -8
  109. package/dist/i18n/locales/de-CH.js +7 -3
  110. package/dist/i18n/locales/de-CH.js.map +1 -1
  111. package/dist/i18n/locales/en.d.ts +13 -1
  112. package/dist/i18n/locales/en.js +8 -0
  113. package/dist/i18n/locales/en.js.map +1 -1
  114. package/dist/i18n/locales/es.d.ts +13 -1
  115. package/dist/i18n/locales/es.js +127 -27
  116. package/dist/i18n/locales/es.js.map +1 -1
  117. package/dist/i18n/locales/fr.d.ts +13 -1
  118. package/dist/i18n/locales/fr.js +101 -1
  119. package/dist/i18n/locales/fr.js.map +1 -1
  120. package/dist/i18n/locales/hu.d.ts +13 -1
  121. package/dist/i18n/locales/hu.js +105 -1
  122. package/dist/i18n/locales/hu.js.map +1 -1
  123. package/dist/i18n/locales/it.d.ts +13 -1
  124. package/dist/i18n/locales/it.js +134 -34
  125. package/dist/i18n/locales/it.js.map +1 -1
  126. package/dist/i18n/locales/zh.d.ts +13 -1
  127. package/dist/i18n/locales/zh.js +101 -1
  128. package/dist/i18n/locales/zh.js.map +1 -1
  129. package/dist/i18n/review.d.ts +102 -0
  130. package/dist/i18n/review.js +158 -0
  131. package/dist/i18n/review.js.map +1 -0
  132. package/dist/i18n/swiss.d.ts +3 -3
  133. package/dist/i18n/swiss.js.map +1 -1
  134. package/dist/index.d.ts +17 -4
  135. package/dist/index.js +97 -1
  136. package/dist/index.js.map +1 -1
  137. package/dist/{kit-labels-C4uB_zmg.d.ts → kit-labels-D1J2_jyD.d.ts} +236 -9
  138. package/dist/lib/floating-stack.d.ts +31 -0
  139. package/dist/lib/floating-stack.js +42 -0
  140. package/dist/lib/floating-stack.js.map +1 -0
  141. package/dist/lib/server-wake.d.ts +146 -0
  142. package/dist/lib/server-wake.js +122 -0
  143. package/dist/lib/server-wake.js.map +1 -0
  144. package/dist/lib/translation-review.d.ts +232 -0
  145. package/dist/lib/translation-review.js +194 -0
  146. package/dist/lib/translation-review.js.map +1 -0
  147. package/dist/rhf/fields.d.ts +13 -1
  148. package/dist/rhf/form.d.ts +13 -1
  149. package/dist/rhf.d.ts +13 -1
  150. package/dist/search/global-search.d.ts +3 -1
  151. package/dist/search/global-search.js.map +1 -1
  152. package/dist/shell/app-shell.d.ts +13 -1
  153. package/dist/shell/top-bar-brand.d.ts +13 -1
  154. package/dist/shell/topbar-action-menu.d.ts +2 -0
  155. package/dist/shell.d.ts +13 -1
  156. package/dist/wizard/stepper-nav.d.ts +13 -1
  157. package/dist/wizard.d.ts +13 -1
  158. package/package.json +1 -1
  159. package/src/components/account-chips.tsx +302 -0
  160. package/src/components/account-settings-labels.ts +16 -2
  161. package/src/components/chip.tsx +239 -6
  162. package/src/components/confirm-dialog.tsx +82 -7
  163. package/src/components/danger-confirm.tsx +118 -33
  164. package/src/components/data-table.tsx +20 -0
  165. package/src/components/floating-panel.tsx +19 -1
  166. package/src/components/form-actions.tsx +22 -3
  167. package/src/components/legal.tsx +159 -0
  168. package/src/components/number-field.tsx +32 -26
  169. package/src/components/passkeys-setting.tsx +68 -7
  170. package/src/components/progress-bar.tsx +21 -1
  171. package/src/components/reauth-dialog.tsx +273 -0
  172. package/src/components/server-wake.tsx +156 -0
  173. package/src/components/share-card.tsx +615 -0
  174. package/src/components/toast.tsx +72 -2
  175. package/src/components/toggle-group.tsx +5 -2
  176. package/src/components/translation-review-editor.tsx +261 -0
  177. package/src/components/translation-review-labels.ts +152 -0
  178. package/src/components/translation-review-parts.tsx +269 -0
  179. package/src/components/translation-review.tsx +502 -0
  180. package/src/components/ui.tsx +89 -32
  181. package/src/components/write-lock.tsx +96 -0
  182. package/src/hooks/use-overlay-history.ts +51 -0
  183. package/src/i18n/defaults.ts +14 -0
  184. package/src/i18n/{locales/de.ts → german.ts} +120 -13
  185. package/src/i18n/kit-labels.tsx +29 -2
  186. package/src/i18n/languages.ts +285 -0
  187. package/src/i18n/locales/de-CH.ts +17 -8
  188. package/src/i18n/locales/en.ts +8 -0
  189. package/src/i18n/locales/es.ts +128 -26
  190. package/src/i18n/locales/fr.ts +103 -0
  191. package/src/i18n/locales/hu.ts +107 -0
  192. package/src/i18n/locales/it.ts +135 -33
  193. package/src/i18n/locales/zh.ts +102 -0
  194. package/src/i18n/review.ts +193 -0
  195. package/src/i18n/swiss.ts +3 -3
  196. package/src/index.ts +86 -1
  197. package/src/lib/floating-stack.ts +66 -0
  198. package/src/lib/server-wake.ts +291 -0
  199. package/src/lib/translation-review.ts +465 -0
  200. package/src/search/global-search.tsx +3 -1
  201. package/dist/i18n/locales/de-CH-informal.d.ts +0 -61
  202. package/dist/i18n/locales/de-CH-informal.js +0 -8
  203. package/dist/i18n/locales/de-CH-informal.js.map +0 -1
  204. package/dist/i18n/locales/de-informal.d.ts +0 -82
  205. package/dist/i18n/locales/de-informal.js +0 -37
  206. package/dist/i18n/locales/de-informal.js.map +0 -1
  207. package/dist/i18n/locales/de.d.ts +0 -66
  208. package/dist/i18n/locales/de.js.map +0 -1
  209. package/src/i18n/locales/de-CH-informal.ts +0 -10
  210. 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
+ }