@eifi1/ui-kit 0.24.1 → 0.25.1

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 (148) hide show
  1. package/README.md +6 -6
  2. package/dist/chart.d.ts +2 -2
  3. package/dist/components/account-chips.d.ts +1 -1
  4. package/dist/components/amount-input.d.ts +2 -2
  5. package/dist/components/button-group.d.ts +2 -2
  6. package/dist/components/calculator.d.ts +2 -2
  7. package/dist/components/column-mapper.d.ts +28 -9
  8. package/dist/components/column-mapper.js +63 -77
  9. package/dist/components/column-mapper.js.map +1 -1
  10. package/dist/components/confirm-dialog.d.ts +2 -2
  11. package/dist/components/copy-button.d.ts +2 -2
  12. package/dist/components/danger-confirm.d.ts +2 -2
  13. package/dist/components/data-table-cells.d.ts +1 -1
  14. package/dist/components/data-table-filter-popover.d.ts +1 -1
  15. package/dist/components/data-table-filters.d.ts +1 -1
  16. package/dist/components/data-table-labels.d.ts +1 -1
  17. package/dist/components/data-table-pagination.d.ts +1 -1
  18. package/dist/components/data-table-pagination.js +6 -1
  19. package/dist/components/data-table-pagination.js.map +1 -1
  20. package/dist/components/data-table.d.ts +1 -1
  21. package/dist/components/data-table.js +7 -1
  22. package/dist/components/data-table.js.map +1 -1
  23. package/dist/components/facing-pair.d.ts +2 -2
  24. package/dist/components/file-button.d.ts +2 -2
  25. package/dist/components/file-dropzone.d.ts +2 -2
  26. package/dist/components/form-actions.d.ts +2 -2
  27. package/dist/components/iban-input.d.ts +2 -2
  28. package/dist/components/language-select.d.ts +2 -2
  29. package/dist/components/money-field.d.ts +2 -2
  30. package/dist/components/number-field.d.ts +2 -2
  31. package/dist/components/number-input.d.ts +2 -2
  32. package/dist/components/numpad-sheet.d.ts +2 -2
  33. package/dist/components/phone-input.d.ts +2 -2
  34. package/dist/components/series-chart.d.ts +2 -2
  35. package/dist/components/settings-fields.d.ts +2 -2
  36. package/dist/components/share-card.d.ts +2 -2
  37. package/dist/components/swipeable-row.js +1 -1
  38. package/dist/components/swipeable-row.js.map +1 -1
  39. package/dist/components/table.d.ts +35 -1
  40. package/dist/components/table.js +4 -0
  41. package/dist/components/table.js.map +1 -1
  42. package/dist/components/text-link.d.ts +2 -2
  43. package/dist/components/time-input.d.ts +2 -2
  44. package/dist/components/tooltip.d.ts +103 -14
  45. package/dist/components/tooltip.js +194 -57
  46. package/dist/components/tooltip.js.map +1 -1
  47. package/dist/components/translation-review-editor.d.ts +9 -1
  48. package/dist/components/translation-review-editor.js +11 -1
  49. package/dist/components/translation-review-editor.js.map +1 -1
  50. package/dist/components/translation-review-labels.d.ts +10 -0
  51. package/dist/components/translation-review-labels.js +5 -1
  52. package/dist/components/translation-review-labels.js.map +1 -1
  53. package/dist/components/translation-review.d.ts +145 -8
  54. package/dist/components/translation-review.js +372 -91
  55. package/dist/components/translation-review.js.map +1 -1
  56. package/dist/components/ui.d.ts +2 -2
  57. package/dist/components/ui.js +55 -18
  58. package/dist/components/ui.js.map +1 -1
  59. package/dist/components/use-table-state.d.ts +1 -1
  60. package/dist/{data-table-labels-B7OdnM0S.d.ts → data-table-labels-Cdn1QS2k.d.ts} +21 -1
  61. package/dist/data-table.d.ts +1 -1
  62. package/dist/feedback/feedback-attachment.d.ts +2 -2
  63. package/dist/feedback/feedback-dialog.d.ts +2 -2
  64. package/dist/feedback/feedback-dialog.js +6 -1
  65. package/dist/feedback/feedback-dialog.js.map +1 -1
  66. package/dist/feedback/feedback-inbox.d.ts +2 -2
  67. package/dist/feedback/feedback-inbox.js +2 -0
  68. package/dist/feedback/feedback-inbox.js.map +1 -1
  69. package/dist/feedback/feedback-thread.d.ts +2 -2
  70. package/dist/{feedback-DOwPu-Il.d.ts → feedback-BXKH-ToU.d.ts} +32 -3
  71. package/dist/feedback.d.ts +2 -2
  72. package/dist/hooks/use-body-scroll-lock.js +16 -0
  73. package/dist/hooks/use-body-scroll-lock.js.map +1 -1
  74. package/dist/hooks/use-file-drop.d.ts +2 -2
  75. package/dist/hooks/use-search-param-state.d.ts +58 -3
  76. package/dist/hooks/use-search-param-state.js +107 -35
  77. package/dist/hooks/use-search-param-state.js.map +1 -1
  78. package/dist/i18n/defaults.d.ts +2 -2
  79. package/dist/i18n/german.d.ts +2 -2
  80. package/dist/i18n/german.js +8 -1
  81. package/dist/i18n/german.js.map +1 -1
  82. package/dist/i18n/kit-labels.d.ts +2 -2
  83. package/dist/i18n/languages.d.ts +2 -2
  84. package/dist/i18n/locales/de-CH.d.ts +2 -2
  85. package/dist/i18n/locales/en.d.ts +2 -2
  86. package/dist/i18n/locales/en.js +5 -1
  87. package/dist/i18n/locales/en.js.map +1 -1
  88. package/dist/i18n/locales/es.d.ts +2 -2
  89. package/dist/i18n/locales/es.js +7 -1
  90. package/dist/i18n/locales/es.js.map +1 -1
  91. package/dist/i18n/locales/fr.d.ts +2 -2
  92. package/dist/i18n/locales/fr.js +6 -1
  93. package/dist/i18n/locales/fr.js.map +1 -1
  94. package/dist/i18n/locales/hu.d.ts +2 -2
  95. package/dist/i18n/locales/hu.js +9 -1
  96. package/dist/i18n/locales/hu.js.map +1 -1
  97. package/dist/i18n/locales/it.d.ts +2 -2
  98. package/dist/i18n/locales/it.js +6 -1
  99. package/dist/i18n/locales/it.js.map +1 -1
  100. package/dist/i18n/locales/zh.d.ts +2 -2
  101. package/dist/i18n/locales/zh.js +6 -1
  102. package/dist/i18n/locales/zh.js.map +1 -1
  103. package/dist/i18n/review.d.ts +2 -2
  104. package/dist/i18n/review.js +11 -0
  105. package/dist/i18n/review.js.map +1 -1
  106. package/dist/index.d.ts +6 -6
  107. package/dist/index.js +9 -1
  108. package/dist/index.js.map +1 -1
  109. package/dist/lib/strip-fade.d.ts +84 -5
  110. package/dist/lib/strip-fade.js +109 -11
  111. package/dist/lib/strip-fade.js.map +1 -1
  112. package/dist/lib/translation-review.d.ts +45 -1
  113. package/dist/lib/translation-review.js +38 -1
  114. package/dist/lib/translation-review.js.map +1 -1
  115. package/dist/rhf/fields.d.ts +2 -2
  116. package/dist/rhf/form.d.ts +2 -2
  117. package/dist/rhf.d.ts +2 -2
  118. package/dist/shell/app-shell.d.ts +2 -2
  119. package/dist/shell/top-bar-brand.d.ts +2 -2
  120. package/dist/shell.d.ts +2 -2
  121. package/dist/wizard/stepper-nav.d.ts +2 -2
  122. package/dist/wizard.d.ts +2 -2
  123. package/package.json +1 -1
  124. package/src/components/column-mapper.tsx +120 -113
  125. package/src/components/data-table-pagination.tsx +10 -1
  126. package/src/components/data-table.tsx +34 -1
  127. package/src/components/swipeable-row.tsx +1 -1
  128. package/src/components/table.tsx +39 -0
  129. package/src/components/tooltip.tsx +487 -104
  130. package/src/components/translation-review-editor.tsx +21 -1
  131. package/src/components/translation-review-labels.ts +17 -0
  132. package/src/components/translation-review.tsx +657 -109
  133. package/src/components/ui.tsx +104 -4
  134. package/src/feedback/feedback-dialog.tsx +21 -0
  135. package/src/feedback/feedback-inbox.tsx +19 -2
  136. package/src/hooks/use-body-scroll-lock.ts +25 -0
  137. package/src/hooks/use-search-param-state.ts +283 -41
  138. package/src/i18n/german.ts +10 -0
  139. package/src/i18n/locales/en.ts +7 -0
  140. package/src/i18n/locales/es.ts +9 -0
  141. package/src/i18n/locales/fr.ts +9 -0
  142. package/src/i18n/locales/hu.ts +11 -0
  143. package/src/i18n/locales/it.ts +9 -0
  144. package/src/i18n/locales/zh.ts +8 -0
  145. package/src/i18n/review.ts +11 -0
  146. package/src/index.ts +12 -0
  147. package/src/lib/strip-fade.ts +220 -15
  148. package/src/lib/translation-review.ts +85 -0
@@ -27,6 +27,30 @@ import 'lucide-react';
27
27
  * write to the server's batch limit is the app's; the panel sends one call per action.
28
28
  */
29
29
  type MaybePromise = void | Promise<unknown>;
30
+ /** What asked for a write — see {@link TranslationReviewSaveInfo}. */
31
+ type TranslationReviewOrigin = "editor" | "row" | "swipe" | "group" | "selection" | "undo";
32
+ /**
33
+ * The second argument of `onSave` and `onClear` (0.25): where the write came from, and
34
+ * whether the panel answers it with a toast of its own.
35
+ *
36
+ * WHY THE APP IS TOLD. keksdose and kastlan toast "Saved" from their mutation on every
37
+ * save. With {@link TranslationReviewPanelProps.undo} on, an approval is answered by the
38
+ * kit's Undo toast instead — and a reviewer swiping through a list then read two toasts
39
+ * per card. The app skips its own where `toasted` is true:
40
+ *
41
+ * onSave={(writes, { toasted }) => save.mutateAsync({ items: writes.map(toApiWrite), quiet: toasted })}
42
+ *
43
+ * An app that ignores the argument keeps working; it only says "Saved" twice.
44
+ */
45
+ interface TranslationReviewSaveInfo {
46
+ /** The open editor, a row's approve button, a swipe on a phone card, a group's
47
+ * "Approve unreviewed", the selection's bulk bar, or an Undo pressed on the toast. */
48
+ origin: TranslationReviewOrigin;
49
+ /** The panel shows its own toast for this write (an approval's Undo toast). */
50
+ toasted: boolean;
51
+ }
52
+ /** How {@link TranslationReviewPanelProps.groupBy} names a row's group. */
53
+ type TranslationReviewGroupBy = "namespace" | "source" | ((row: TranslationRow) => string);
30
54
  interface TranslationReviewPanelProps {
31
55
  /** One locale's rows, from `translationRows`. */
32
56
  rows: readonly TranslationRow[];
@@ -35,10 +59,12 @@ interface TranslationReviewPanelProps {
35
59
  /** The reference language's own name — the reference column's header. */
36
60
  referenceLabel: string;
37
61
  /** Store verdicts: one for an editor or a row's approve button, many for a bulk
38
- * approve. Left out (or `readOnly`): nothing can be changed. */
39
- onSave?: (writes: TranslationReviewWrite[]) => MaybePromise;
40
- /** Clear verdicts — back to unreviewed. Left out: no reset. */
41
- onClear?: (keys: TranslationReviewKey[]) => MaybePromise;
62
+ * approve. Left out (or `readOnly`): nothing can be changed. The second argument
63
+ * says where the write came from ({@link TranslationReviewSaveInfo}). */
64
+ onSave?: (writes: TranslationReviewWrite[], info: TranslationReviewSaveInfo) => MaybePromise;
65
+ /** Clear verdicts — back to unreviewed. Left out: no reset, and an approval of a row
66
+ * that had no verdict cannot be undone. */
67
+ onClear?: (keys: TranslationReviewKey[], info: TranslationReviewSaveInfo) => MaybePromise;
42
68
  /** A locale the viewer may read but not review: the rows and their verdicts, no
43
69
  * actions, and a line saying so. */
44
70
  readOnly?: boolean;
@@ -58,9 +84,18 @@ interface TranslationReviewPanelProps {
58
84
  sourceLabels?: Readonly<Record<string, string>>;
59
85
  /**
60
86
  * Controlled filters — for an app that keeps them in the URL (keksdose's `?status=`,
61
- * `?area=`, `?q=`). Whatever is left out is the panel's own. Pair with `onFilterChange`.
87
+ * `?ns=`, `?q=`). Whatever is left out is the panel's own. Pair with `onFilterChange`.
88
+ *
89
+ * A field passed here is the app's, and the control shows what the app says: one the app
90
+ * passes but does not write back from `onFilterChange` is a control that does nothing.
91
+ * A development build warns about that once per field (0.25.1, Kurvenschmiede); a field
92
+ * left out entirely is fine — the panel keeps it.
62
93
  */
63
94
  filter?: Partial<TranslationReviewFilter>;
95
+ /** Every filter, on every change — write them in ONE update: `useSearchParamsState`'s
96
+ * setter takes this object as it is (`filter={filter} onFilterChange={setFilter}`).
97
+ * Single `useSearchParamState` setters compose since 0.25; before, four in a row
98
+ * clobbered each other (keksdose live #378). */
64
99
  onFilterChange?: (filter: TranslationReviewFilter) => void;
65
100
  /** The progress bar over the filters. Default true. */
66
101
  progress?: boolean;
@@ -72,6 +107,101 @@ interface TranslationReviewPanelProps {
72
107
  formatDate?: (iso: string) => string;
73
108
  /** Turns a callback's rejection into words. Default {@link TranslationReviewLabels.failed}. */
74
109
  formatError?: (error: unknown) => ReactNode;
110
+ /**
111
+ * Swipe the phone cards (0.25, keksdose live #377 — Marcel reviewing on a phone): toward
112
+ * the reading END to approve, toward the START for "Needs a change", which OPENS the
113
+ * editor with the cursor in the wording rather than sending the string back blind — a
114
+ * verdict without a note or a better wording tells whoever fixes it nothing. In a
115
+ * right-to-left page the two sides mirror with the reading direction.
116
+ *
117
+ * DataTable's swipe sides (`mobileSwipeActions`) on {@link SwipeableRow}: the same
118
+ * gesture, the same feedback, and the same keyboard path — every swipe is also a real
119
+ * button, read out and reachable by Tab, so the gesture is a shortcut and never the only
120
+ * way. A row has no swipe while the editor under it is open, while its own write is out,
121
+ * under a {@link WriteLockProvider} lock, in a `readOnly` locale, and toward "Approve"
122
+ * where there is nothing to approve (a missing string, an approved one). A reviewer
123
+ * limited to `areas` never sees a row outside them, so never swipes one.
124
+ *
125
+ * Off by default, so no existing page gains a gesture on the bump. Phones only; the
126
+ * desktop table keeps its approve button.
127
+ */
128
+ swipe?: boolean;
129
+ /**
130
+ * Answer every approval with the kit's Undo toast (`toast.undo`): a row's button, a
131
+ * swipe, the editor's Approve, a group's and the selection's bulk approve. Undo stores
132
+ * again what the rows said before ({@link reviewUndo}) — or clears them, for rows that
133
+ * had no verdict, which needs `onClear` (without it the toast reports and offers
134
+ * nothing). A batch has ONE Undo.
135
+ *
136
+ * Default: on with `swipe` or `groupBy` — the two ways to approve in one movement or many
137
+ * rows at once bring their way back — and off otherwise, so an existing page does not
138
+ * gain a toast on the bump. Needs the app's `<Toaster>`. Tell the app's own "Saved"
139
+ * toast apart with the `toasted` flag ({@link TranslationReviewSaveInfo}).
140
+ */
141
+ undo?: boolean;
142
+ /**
143
+ * Group the strings under a header per area (0.25, keksdose live #377: *"grouping to the
144
+ * translation review entries for faster reviewing"*): `"namespace"` — the row's
145
+ * `namespace`, which `translationRows` derives from the key (`budget`, `kit.dataTable`
146
+ * with keksdose's `namespaceOf`) — `"source"`, or a function of the row.
147
+ *
148
+ * Each group is a section with a heading ({@link groupHeadingAs}): its name, "n
149
+ * unreviewed / total", and "Approve unreviewed (n)", which approves the group's rows
150
+ * nobody has given a verdict ({@link unreviewedRows}) under the same rules as one
151
+ * approval — write lock, `readOnly`, one write at a time — with one Undo for the batch.
152
+ * A batch larger than `pageSize` asks first, inside the opened group: more than a page
153
+ * is more than the reviewer can have had on screen.
154
+ *
155
+ * WHAT IT MEANS FOR THE FILTERS, THE SORT AND THE PAGES. The filters narrow the rows
156
+ * first, then the rows are grouped: a header counts what the filters show in it (so
157
+ * "Approve unreviewed" never takes a row the reviewer filtered away), and an area with
158
+ * nothing left is not shown. Groups stand in the order their first string has in the
159
+ * reference; a column sort orders the rows INSIDE every group alike. Each group pages
160
+ * on its own, `pageSize` rows at a time — a header then always stands over its own
161
+ * rows, and paging one area does not move another — rather than one pager cutting
162
+ * through the headers. `storageKey` keeps persisting the ungrouped table only.
163
+ *
164
+ * Groups fold (each on its own, remembered while the panel lives), and a folded group's
165
+ * rows are not rendered at all — only its header. Which groups start open is
166
+ * {@link defaultGroupsOpen}: by default, on a phone they start folded when there are
167
+ * several — the headers are then the area list, and the reviewer opens the one to work
168
+ * through — and on a wide screen they start open while that is cheap (see there).
169
+ */
170
+ groupBy?: TranslationReviewGroupBy;
171
+ /**
172
+ * Which groups start open under {@link groupBy} (0.25.1). A group the reviewer opens or
173
+ * folds keeps that; this is what every other group follows.
174
+ *
175
+ * - `"auto"` (default): on a phone, open when there is one group and folded when there
176
+ * are several, as in 0.25.0. On a wide screen, open while opening every group renders
177
+ * at most 100 rows (each group shows up to `pageSize` of them; the budget is one page
178
+ * where `pageSize` is larger) — and folded above that, the headers then being the area
179
+ * list, as on a phone.
180
+ * - `true`: every group starts open, on a phone too.
181
+ * - `false`: every group starts folded, on a wide screen too.
182
+ *
183
+ * WHY A BUDGET OF ROWS (keksdose, measured on 0.25.0): a wide screen opened EVERY group.
184
+ * keksdose's /translations has 122 areas and ~4700 strings — 122 tables and 2053 rows at
185
+ * once, ~46 000 elements, 5.2 s before the panel appeared and 2.5 s for a filter click
186
+ * to paint, against 50 rows and 0.34 s ungrouped; its page test went from 4 s to 146 s.
187
+ * kastlan (~31 areas, ~1300 rows open) and Kurvenschmiede (28, ~700) had the same page.
188
+ * What costs is the rows on screen, so the rule counts them rather than the groups:
189
+ * 100 rows is two of the panel's default pages — any one or two areas still open as
190
+ * before, however long, and so do a dozen small ones, or a search whose hits fall in
191
+ * many areas — while a whole catalogue starts as its list of areas: keksdose's shape
192
+ * in jsdom, 122 headers and ~2100 elements instead of ~44 000 (the ungrouped panel has
193
+ * ~1200). The rule follows the filters: narrow them and the groups open.
194
+ *
195
+ * A boolean rather than a controlled open state: the panel already keeps each group's
196
+ * own state, and an app that only wants "all open" or "all folded" should not have to
197
+ * keep a record of 122 flags to say it.
198
+ */
199
+ defaultGroupsOpen?: boolean | "auto";
200
+ /** A group's name. Default: `sourceLabels` for `groupBy="source"`, else the key. */
201
+ groupLabel?: (group: string) => string;
202
+ /** The level of the group headings. Default `h2`: the panel is a page's body, under
203
+ * the page's `h1`. */
204
+ groupHeadingAs?: "h2" | "h3" | "h4";
75
205
  className?: string;
76
206
  labels?: Partial<TranslationReviewLabels>;
77
207
  }
@@ -87,8 +217,15 @@ interface TranslationReviewPanelProps {
87
217
  * Bulk approve takes the selected rows that are not approved yet and have a text:
88
218
  * approving an absent string says nothing, and re-approving one says it again. On a
89
219
  * phone the table has no checkboxes, so "Select all shown" selects what the filters
90
- * show — filter to `Unreviewed` in one area, read, select, approve.
220
+ * show — filter to `Unreviewed` in one area, read, select, approve. With `groupBy`, a
221
+ * group's own "Approve unreviewed" is the shorter way, and with `swipe` a card is approved
222
+ * by a swipe.
223
+ *
224
+ * The panel's writes go out ONE AT A TIME, in the order they were asked for: two writes on
225
+ * one locale's verdicts can answer out of order, and an Undo must not overtake the
226
+ * approval it takes back. A swipe on the next card while one is out is queued rather than
227
+ * refused — a reviewer swiping down a list is faster than the server.
91
228
  */
92
- declare function TranslationReviewPanel({ rows: rowsProp, localeLabel, referenceLabel, onSave, onClear, readOnly, areas, areaLabels, inArea, sourceLabels, filter: filterProp, onFilterChange, progress, pageSize, storageKey, formatDate, formatError, className, labels: labelsProp, }: TranslationReviewPanelProps): react.JSX.Element;
229
+ declare function TranslationReviewPanel({ rows: rowsProp, localeLabel, referenceLabel, onSave, onClear, readOnly, areas, areaLabels, inArea, sourceLabels, filter: filterProp, onFilterChange, progress, pageSize, storageKey, formatDate, formatError, swipe, undo: undoProp, groupBy, groupLabel, groupHeadingAs, defaultGroupsOpen, className, labels: labelsProp, }: TranslationReviewPanelProps): react.JSX.Element;
93
230
 
94
- export { TranslationReviewLabels, TranslationReviewPanel, type TranslationReviewPanelProps };
231
+ export { type TranslationReviewGroupBy, TranslationReviewLabels, type TranslationReviewOrigin, TranslationReviewPanel, type TranslationReviewPanelProps, type TranslationReviewSaveInfo };