@eifi1/ui-kit 0.45.0 → 0.46.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 (228) hide show
  1. package/README.md +23 -12
  2. package/dist/admin/admin-area-labels.d.ts +9 -0
  3. package/dist/admin/admin-area-labels.js +5 -1
  4. package/dist/admin/admin-area-labels.js.map +1 -1
  5. package/dist/admin/admin-overview.d.ts +16 -1
  6. package/dist/admin/admin-overview.js +12 -1
  7. package/dist/admin/admin-overview.js.map +1 -1
  8. package/dist/admin/mail-test-card.d.ts +3 -2
  9. package/dist/admin/mail-test-card.js +3 -1
  10. package/dist/admin/mail-test-card.js.map +1 -1
  11. package/dist/components/appearance-labels.d.ts +10 -0
  12. package/dist/components/appearance-labels.js +6 -1
  13. package/dist/components/appearance-labels.js.map +1 -1
  14. package/dist/components/data-table-column-settings.js +2 -2
  15. package/dist/components/data-table-column-settings.js.map +1 -1
  16. package/dist/components/data-table-head.js +2 -2
  17. package/dist/components/data-table-head.js.map +1 -1
  18. package/dist/components/data-table-row-actions.d.ts +18 -2
  19. package/dist/components/data-table-row-actions.js +86 -2
  20. package/dist/components/data-table-row-actions.js.map +1 -1
  21. package/dist/components/data-table-shared.js +9 -2
  22. package/dist/components/data-table-shared.js.map +1 -1
  23. package/dist/components/data-table.d.ts +43 -2
  24. package/dist/components/data-table.js +9 -4
  25. package/dist/components/data-table.js.map +1 -1
  26. package/dist/components/date-format-setting.d.ts +29 -0
  27. package/dist/components/date-format-setting.js +36 -0
  28. package/dist/components/date-format-setting.js.map +1 -0
  29. package/dist/components/swipeable-row.d.ts +8 -0
  30. package/dist/components/swipeable-row.js.map +1 -1
  31. package/dist/components/tabs.d.ts +3 -2
  32. package/dist/components/tabs.js +1 -1
  33. package/dist/components/tabs.js.map +1 -1
  34. package/dist/components/translation-review-editor.js +1 -0
  35. package/dist/components/translation-review-editor.js.map +1 -1
  36. package/dist/components/translation-review-labels.d.ts +5 -0
  37. package/dist/components/translation-review-labels.js +2 -0
  38. package/dist/components/translation-review-labels.js.map +1 -1
  39. package/dist/components/translation-review.d.ts +18 -8
  40. package/dist/components/translation-review.js +64 -7
  41. package/dist/components/translation-review.js.map +1 -1
  42. package/dist/components/write-lock-labels.js +2 -1
  43. package/dist/components/write-lock-labels.js.map +1 -1
  44. package/dist/components/write-lock.d.ts +33 -3
  45. package/dist/components/write-lock.js +9 -0
  46. package/dist/components/write-lock.js.map +1 -1
  47. package/dist/data-table.d.ts +1 -1
  48. package/dist/data-table.js.map +1 -1
  49. package/dist/eslint/index.js +1 -1
  50. package/dist/feedback/feedback-row-detail.d.ts +4 -2
  51. package/dist/feedback/feedback-row-detail.js +17 -26
  52. package/dist/feedback/feedback-row-detail.js.map +1 -1
  53. package/dist/feedback/feedback-swipe.d.ts +18 -0
  54. package/dist/feedback/feedback-swipe.js +10 -1
  55. package/dist/feedback/feedback-swipe.js.map +1 -1
  56. package/dist/i18n/german.js +41 -5
  57. package/dist/i18n/german.js.map +1 -1
  58. package/dist/i18n/kit-labels.d.ts +12 -1
  59. package/dist/i18n/kit-labels.js +8 -2
  60. package/dist/i18n/kit-labels.js.map +1 -1
  61. package/dist/i18n/locales/en.js +40 -6
  62. package/dist/i18n/locales/en.js.map +2 -2
  63. package/dist/i18n/locales/es.js +41 -5
  64. package/dist/i18n/locales/es.js.map +1 -1
  65. package/dist/i18n/locales/fr.js +41 -5
  66. package/dist/i18n/locales/fr.js.map +1 -1
  67. package/dist/i18n/locales/hu.js +41 -5
  68. package/dist/i18n/locales/hu.js.map +1 -1
  69. package/dist/i18n/locales/it.js +41 -5
  70. package/dist/i18n/locales/it.js.map +1 -1
  71. package/dist/i18n/locales/zh.js +41 -5
  72. package/dist/i18n/locales/zh.js.map +1 -1
  73. package/dist/i18n/review.js +3 -1
  74. package/dist/i18n/review.js.map +1 -1
  75. package/dist/index.d.ts +15 -2
  76. package/dist/index.js +35 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/lib/date-format.d.ts +81 -0
  79. package/dist/lib/date-format.js +56 -0
  80. package/dist/lib/date-format.js.map +1 -0
  81. package/dist/lib/format.d.ts +3 -0
  82. package/dist/lib/format.js +5 -4
  83. package/dist/lib/format.js.map +1 -1
  84. package/dist/notifications/device-zone-report.d.ts +27 -0
  85. package/dist/notifications/device-zone-report.js +24 -0
  86. package/dist/notifications/device-zone-report.js.map +1 -0
  87. package/dist/notifications/feedback-notification-types-labels.d.ts +7 -1
  88. package/dist/notifications/feedback-notification-types-labels.js +4 -1
  89. package/dist/notifications/feedback-notification-types-labels.js.map +1 -1
  90. package/dist/notifications/feedback-notification-types.d.ts +11 -5
  91. package/dist/notifications/feedback-notification-types.js +2 -1
  92. package/dist/notifications/feedback-notification-types.js.map +1 -1
  93. package/dist/notifications/notification-bell.d.ts +33 -0
  94. package/dist/notifications/notification-bell.js +74 -0
  95. package/dist/notifications/notification-bell.js.map +1 -0
  96. package/dist/notifications/notification-inbox.d.ts +77 -0
  97. package/dist/notifications/notification-inbox.js +206 -0
  98. package/dist/notifications/notification-inbox.js.map +1 -0
  99. package/dist/notifications/notification-types-setting.d.ts +6 -4
  100. package/dist/notifications/notification-types-setting.js +103 -45
  101. package/dist/notifications/notification-types-setting.js.map +1 -1
  102. package/dist/notifications/notification-types.d.ts +19 -7
  103. package/dist/notifications/notifications-labels.d.ts +41 -7
  104. package/dist/notifications/notifications-labels.js +18 -1
  105. package/dist/notifications/notifications-labels.js.map +1 -1
  106. package/dist/notifications/push-devices-setting.d.ts +20 -3
  107. package/dist/notifications/push-devices-setting.js +93 -33
  108. package/dist/notifications/push-devices-setting.js.map +1 -1
  109. package/dist/notifications/push-worker.d.ts +108 -0
  110. package/dist/notifications/push-worker.js +62 -0
  111. package/dist/notifications/push-worker.js.map +1 -0
  112. package/dist/notifications/quiet-hours-setting.d.ts +29 -17
  113. package/dist/notifications/quiet-hours-setting.js +9 -41
  114. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  115. package/dist/notifications/time-zone-select.d.ts +5 -2
  116. package/dist/notifications/time-zone-select.js.map +1 -1
  117. package/dist/notifications/use-push-device.d.ts +4 -17
  118. package/dist/notifications/use-push-device.js +7 -15
  119. package/dist/notifications/use-push-device.js.map +1 -1
  120. package/dist/offline/local-first.d.ts +33 -0
  121. package/dist/offline/local-first.js +39 -0
  122. package/dist/offline/local-first.js.map +1 -0
  123. package/dist/offline/query-persistence.d.ts +26 -1
  124. package/dist/offline/query-persistence.js +27 -23
  125. package/dist/offline/query-persistence.js.map +1 -1
  126. package/dist/offline/service-worker-updates.d.ts +46 -0
  127. package/dist/offline/service-worker-updates.js +39 -0
  128. package/dist/offline/service-worker-updates.js.map +1 -0
  129. package/dist/offline.d.ts +5 -2
  130. package/dist/offline.js +11 -2
  131. package/dist/offline.js.map +1 -1
  132. package/dist/push-worker.d.ts +2 -0
  133. package/dist/push-worker.js +14 -0
  134. package/dist/push-worker.js.map +1 -0
  135. package/dist/shell/account-menu-text-size.d.ts +33 -0
  136. package/dist/shell/account-menu-text-size.js +51 -0
  137. package/dist/shell/account-menu-text-size.js.map +1 -0
  138. package/dist/shell/top-bar-actions.d.ts +6 -2
  139. package/dist/shell/top-bar-actions.js +1 -0
  140. package/dist/shell/top-bar-actions.js.map +1 -1
  141. package/dist/shell.d.ts +2 -0
  142. package/dist/shell.js +2 -0
  143. package/dist/shell.js.map +1 -1
  144. package/dist/sync/db.d.ts +76 -0
  145. package/dist/sync/db.js +24 -0
  146. package/dist/sync/db.js.map +1 -0
  147. package/dist/sync/engine.d.ts +124 -0
  148. package/dist/sync/engine.js +474 -0
  149. package/dist/sync/engine.js.map +1 -0
  150. package/dist/sync/hooks.d.ts +30 -0
  151. package/dist/sync/hooks.js +67 -0
  152. package/dist/sync/hooks.js.map +1 -0
  153. package/dist/sync/testing.d.ts +28 -0
  154. package/dist/sync/testing.js +41 -0
  155. package/dist/sync/testing.js.map +1 -0
  156. package/dist/sync/wire.d.ts +50 -0
  157. package/dist/sync/wire.js +2 -0
  158. package/dist/sync/wire.js.map +1 -0
  159. package/dist/sync.d.ts +7 -0
  160. package/dist/sync.js +22 -0
  161. package/dist/sync.js.map +1 -0
  162. package/dist/tour/tour-labels.js +1 -0
  163. package/dist/tour/tour-labels.js.map +1 -1
  164. package/dist/tour/tour.d.ts +30 -0
  165. package/dist/tour/tour.js +50 -6
  166. package/dist/tour/tour.js.map +1 -1
  167. package/package.json +19 -1
  168. package/src/admin/admin-area-labels.ts +13 -0
  169. package/src/admin/admin-overview.tsx +37 -2
  170. package/src/admin/mail-test-card.tsx +7 -3
  171. package/src/components/appearance-labels.ts +16 -0
  172. package/src/components/data-table-column-settings.tsx +2 -2
  173. package/src/components/data-table-head.tsx +2 -2
  174. package/src/components/data-table-row-actions.tsx +118 -2
  175. package/src/components/data-table-shared.tsx +21 -2
  176. package/src/components/data-table.tsx +51 -6
  177. package/src/components/date-format-setting.tsx +64 -0
  178. package/src/components/swipeable-row.tsx +8 -0
  179. package/src/components/tabs.tsx +4 -3
  180. package/src/components/translation-review-editor.tsx +2 -0
  181. package/src/components/translation-review-labels.ts +7 -0
  182. package/src/components/translation-review.tsx +102 -14
  183. package/src/components/write-lock-labels.ts +1 -0
  184. package/src/components/write-lock.tsx +36 -3
  185. package/src/data-table.ts +1 -0
  186. package/src/feedback/feedback-row-detail.tsx +18 -23
  187. package/src/feedback/feedback-swipe.tsx +28 -1
  188. package/src/i18n/german.ts +44 -4
  189. package/src/i18n/kit-labels.tsx +18 -1
  190. package/src/i18n/locales/en.ts +1 -0
  191. package/src/i18n/locales/es.ts +44 -4
  192. package/src/i18n/locales/fr.ts +44 -4
  193. package/src/i18n/locales/hu.ts +44 -4
  194. package/src/i18n/locales/it.ts +44 -4
  195. package/src/i18n/locales/zh.ts +44 -2
  196. package/src/i18n/review.ts +3 -1
  197. package/src/index.ts +42 -0
  198. package/src/lib/date-format.ts +151 -0
  199. package/src/lib/format.ts +10 -4
  200. package/src/notifications/device-zone-report.ts +54 -0
  201. package/src/notifications/feedback-notification-types-labels.ts +10 -1
  202. package/src/notifications/feedback-notification-types.ts +12 -5
  203. package/src/notifications/notification-bell.tsx +116 -0
  204. package/src/notifications/notification-inbox.tsx +304 -0
  205. package/src/notifications/notification-types-setting.tsx +151 -55
  206. package/src/notifications/notification-types.ts +20 -8
  207. package/src/notifications/notifications-labels.ts +69 -11
  208. package/src/notifications/push-devices-setting.tsx +123 -34
  209. package/src/notifications/push-worker.ts +160 -0
  210. package/src/notifications/quiet-hours-setting.tsx +41 -58
  211. package/src/notifications/time-zone-select.tsx +5 -2
  212. package/src/notifications/use-push-device.ts +20 -33
  213. package/src/offline/local-first.ts +76 -0
  214. package/src/offline/query-persistence.ts +57 -24
  215. package/src/offline/service-worker-updates.ts +81 -0
  216. package/src/offline.ts +7 -1
  217. package/src/push-worker.ts +25 -0
  218. package/src/shell/account-menu-text-size.tsx +77 -0
  219. package/src/shell/top-bar-actions.tsx +6 -1
  220. package/src/shell.ts +3 -0
  221. package/src/sync/db.ts +103 -0
  222. package/src/sync/engine.ts +699 -0
  223. package/src/sync/hooks.ts +93 -0
  224. package/src/sync/testing.ts +73 -0
  225. package/src/sync/wire.ts +61 -0
  226. package/src/sync.ts +29 -0
  227. package/src/tour/tour-labels.ts +1 -0
  228. package/src/tour/tour.tsx +100 -7
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/translation-review-labels.ts"],"sourcesContent":["import type { ReviewStatus } from \"../lib/translation-review\";\n\n/**\n * The `translationReview` namespace: every word of the translation-review parts\n * (`ReviewStatusChip`, `TranslationProgress`, `TranslationLocaleTabs`,\n * `TranslationExportButton`, `TranslationReviewEditor`, `TranslationReviewPanel`).\n *\n * Its own module, like `data-table-labels.ts`: `kit-labels.tsx` and `defaults.ts` import\n * it, and neither has to load the components to get the words. The English is keksdose's\n * and kastlan's own (`translation_review.*`, `translations:*`), British.\n */\nexport interface TranslationReviewLabels {\n /** The five statuses — the chip, the filter, the progress legend. */\n statusMissing: string;\n statusUnreviewed: string;\n statusChanged: string;\n statusNeedsChange: string;\n statusApproved: string;\n /** Accessible name of the status filter. */\n statusFilter: string;\n /** The status filter's first option. */\n all: string;\n /** A status filter option with its count: \"Unreviewed · 12\". */\n filterCount: (label: string, count: number) => string;\n /** The source filter's label — shown only when the rows come from several sources\n * (kastlan's screen texts and documents). */\n source: string;\n allSources: string;\n /** The namespace filter's label and its \"every one\" option. */\n namespace: string;\n allNamespaces: string;\n search: string;\n /** The placeholder filter, with how many rows it would show. */\n placeholdersOnly: (count: number) => string;\n /** The chip on a row whose placeholders differ from the reference's. */\n placeholderChip: string;\n /** The key column, and the key's caption in the editor. */\n key: string;\n statusColumn: string;\n /** No row matches the filters. */\n empty: string;\n /** In place of the text of a string the locale lacks. */\n missingText: string;\n /** The progress bar's label. */\n progress: (approved: number, total: number) => string;\n /** A locale tab's badge. */\n localeProgress: (approved: number, total: number) => string;\n /** Accessible name of the locale tabs. */\n locales: string;\n approve: string;\n /** Send a string back: \"Needs a change\". */\n flag: string;\n /** \"Needs a change\" for a MISSING string: the suggestion is the translation to add. */\n suggest: string;\n /** Clear a verdict: back to unreviewed. */\n reset: string;\n /** The editor's way out without saving. */\n cancel: string;\n approveSelected: string;\n resetSelected: string;\n /** Selects every row the filters show — the phone's way into the bulk actions, where\n * the table has no checkboxes. */\n selectShown: string;\n /** Over the old wording of a row that changed since its verdict. */\n changedSince: string;\n reviewedWording: string;\n reviewedReference: string;\n /** The placeholders differ: the reference's and the text's, each a list. */\n placeholderMismatch: (reference: string, text: string) => string;\n lastApproved: (name: string, date: string) => string;\n lastFlagged: (name: string, date: string) => string;\n /** The reviewer's name once the account is gone — what `name` above becomes. */\n erasedReviewer: string;\n /** The editor's wording field, opened on the current text. */\n suggestion: string;\n /** The same field for a missing string. */\n translation: string;\n note: string;\n notePlaceholder: string;\n /** The viewer may read this locale but not review it. */\n readOnly: string;\n /** The reviewer is limited to some areas — `areas` is their names, joined. */\n scope: (areas: string) => string;\n exportCorrections: (count: number) => string;\n /** A callback rejected and no `formatError` says better. */\n failed: string;\n /** 0.25: the Undo toast after an approval (`undo`) — one string, or a batch of them. */\n approvedToast: (count: number) => string;\n /** 0.25: a group's header under `groupBy` — how many of its rows nobody has read yet,\n * of how many the filters show in it. */\n groupCount: (unreviewed: number, total: number) => string;\n /** 0.25: a group's bulk action — approve its unreviewed rows. */\n approveGroup: (count: number) => string;\n /** 0.25: asked first when a group's bulk approve is larger than a page — more than the\n * reviewer can have had on screen. `group` is the group's name. */\n confirmGroup: (count: number, group: string) => string;\n /** The Undo toast after a swipe bound to `clear` took a verdict back (keksdose, live\n * #377 rework: the swipes bound in its settings) — one string, or a batch of them.\n * The swipe itself is named by {@link reset}. */\n clearedToast: (count: number) => string;\n}\n\nexport const DEFAULT_TRANSLATION_REVIEW_LABELS: TranslationReviewLabels = {\n statusMissing: \"Missing\",\n statusUnreviewed: \"Unreviewed\",\n statusChanged: \"Changed since review\",\n statusNeedsChange: \"Needs a change\",\n statusApproved: \"Approved\",\n statusFilter: \"Status\",\n all: \"All\",\n filterCount: (label, count) => `${label} · ${count}`,\n source: \"Texts\",\n allSources: \"All texts\",\n namespace: \"Area\",\n allNamespaces: \"All areas\",\n search: \"Search keys and text\",\n placeholdersOnly: (count) => `Only placeholder problems (${count})`,\n placeholderChip: \"Placeholders\",\n key: \"Key\",\n statusColumn: \"Status\",\n empty: \"No strings match these filters.\",\n missingText: \"Missing in this language\",\n progress: (approved, total) => `${approved} of ${total} approved`,\n localeProgress: (approved, total) => `${approved}/${total}`,\n locales: \"Languages\",\n approve: \"Approve\",\n flag: \"Needs a change\",\n suggest: \"Suggest translation\",\n reset: \"Mark unreviewed\",\n cancel: \"Cancel\",\n approveSelected: \"Approve selected\",\n resetSelected: \"Mark selected unreviewed\",\n selectShown: \"Select all shown\",\n changedSince: \"This string changed after it was reviewed. Please read it again.\",\n reviewedWording: \"Wording when reviewed\",\n reviewedReference: \"Reference when reviewed\",\n placeholderMismatch: (reference, text) =>\n `The placeholders differ from the reference — reference: ${reference}; this text: ${text}. The app fills these in, so they must stay exactly as they are.`,\n lastApproved: (name, date) => `Approved by ${name} on ${date}`,\n lastFlagged: (name, date) => `Marked as needing a change by ${name} on ${date}`,\n erasedReviewer: \"a deleted account\",\n suggestion: \"Better wording\",\n translation: \"Translation\",\n note: \"Note (optional)\",\n notePlaceholder: \"What is wrong, or what to keep in mind\",\n readOnly: \"You can read this language but not review it.\",\n scope: (areas) => `Your review is limited to: ${areas}.`,\n exportCorrections: (count) => `Export corrections (${count})`,\n failed: \"That did not work. Please try again.\",\n approvedToast: (count) => (count === 1 ? \"String approved\" : `${count} strings approved`),\n groupCount: (unreviewed, total) => `${unreviewed} unreviewed / ${total}`,\n approveGroup: (count) => `Approve unreviewed (${count})`,\n confirmGroup: (count, group) =>\n count === 1\n ? `Approve the unreviewed string in ${group}?`\n : `Approve all ${count} unreviewed strings in ${group}, including those not on screen?`,\n clearedToast: (count) => (count === 1 ? \"String marked unreviewed\" : `${count} strings marked unreviewed`),\n};\n\n/** A status's word, from the namespace's five. */\nexport function reviewStatusLabel(labels: TranslationReviewLabels, status: ReviewStatus): string {\n switch (status) {\n case \"missing\":\n return labels.statusMissing;\n case \"unreviewed\":\n return labels.statusUnreviewed;\n case \"changed\":\n return labels.statusChanged;\n case \"needs_change\":\n return labels.statusNeedsChange;\n case \"approved\":\n return labels.statusApproved;\n }\n}\n"],"mappings":";AAsGO,MAAM,oCAA6D;AAAA,EACxE,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,KAAK;AAAA,EACL,aAAa,CAAC,OAAO,UAAU,GAAG,KAAK,SAAM,KAAK;AAAA,EAClD,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,kBAAkB,CAAC,UAAU,8BAA8B,KAAK;AAAA,EAChE,iBAAiB;AAAA,EACjB,KAAK;AAAA,EACL,cAAc;AAAA,EACd,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU,CAAC,UAAU,UAAU,GAAG,QAAQ,OAAO,KAAK;AAAA,EACtD,gBAAgB,CAAC,UAAU,UAAU,GAAG,QAAQ,IAAI,KAAK;AAAA,EACzD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,qBAAqB,CAAC,WAAW,SAC/B,gEAA2D,SAAS,gBAAgB,IAAI;AAAA,EAC1F,cAAc,CAAC,MAAM,SAAS,eAAe,IAAI,OAAO,IAAI;AAAA,EAC5D,aAAa,CAAC,MAAM,SAAS,iCAAiC,IAAI,OAAO,IAAI;AAAA,EAC7E,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO,CAAC,UAAU,8BAA8B,KAAK;AAAA,EACrD,mBAAmB,CAAC,UAAU,uBAAuB,KAAK;AAAA,EAC1D,QAAQ;AAAA,EACR,eAAe,CAAC,UAAW,UAAU,IAAI,oBAAoB,GAAG,KAAK;AAAA,EACrE,YAAY,CAAC,YAAY,UAAU,GAAG,UAAU,iBAAiB,KAAK;AAAA,EACtE,cAAc,CAAC,UAAU,uBAAuB,KAAK;AAAA,EACrD,cAAc,CAAC,OAAO,UACpB,UAAU,IACN,oCAAoC,KAAK,MACzC,eAAe,KAAK,0BAA0B,KAAK;AAAA,EACzD,cAAc,CAAC,UAAW,UAAU,IAAI,6BAA6B,GAAG,KAAK;AAC/E;AAGO,SAAS,kBAAkB,QAAiC,QAA8B;AAC/F,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,EAClB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/components/translation-review-labels.ts"],"sourcesContent":["import type { ReviewStatus } from \"../lib/translation-review\";\n\n/**\n * The `translationReview` namespace: every word of the translation-review parts\n * (`ReviewStatusChip`, `TranslationProgress`, `TranslationLocaleTabs`,\n * `TranslationExportButton`, `TranslationReviewEditor`, `TranslationReviewPanel`).\n *\n * Its own module, like `data-table-labels.ts`: `kit-labels.tsx` and `defaults.ts` import\n * it, and neither has to load the components to get the words. The English is keksdose's\n * and kastlan's own (`translation_review.*`, `translations:*`), British.\n */\nexport interface TranslationReviewLabels {\n /** The five statuses — the chip, the filter, the progress legend. */\n statusMissing: string;\n statusUnreviewed: string;\n statusChanged: string;\n statusNeedsChange: string;\n statusApproved: string;\n /** Accessible name of the status filter. */\n statusFilter: string;\n /** The status filter's first option. */\n all: string;\n /** A status filter option with its count: \"Unreviewed · 12\". */\n filterCount: (label: string, count: number) => string;\n /** The source filter's label — shown only when the rows come from several sources\n * (kastlan's screen texts and documents). */\n source: string;\n allSources: string;\n /** The namespace filter's label and its \"every one\" option. */\n namespace: string;\n allNamespaces: string;\n search: string;\n /** The placeholder filter, with how many rows it would show. */\n placeholdersOnly: (count: number) => string;\n /** The chip on a row whose placeholders differ from the reference's. */\n placeholderChip: string;\n /** The key column, and the key's caption in the editor. */\n key: string;\n statusColumn: string;\n /** No row matches the filters. */\n empty: string;\n /** In place of the text of a string the locale lacks. */\n missingText: string;\n /** The progress bar's label. */\n progress: (approved: number, total: number) => string;\n /** A locale tab's badge. */\n localeProgress: (approved: number, total: number) => string;\n /** Accessible name of the locale tabs. */\n locales: string;\n approve: string;\n /** Send a string back: \"Needs a change\". */\n flag: string;\n /** \"Needs a change\" for a MISSING string: the suggestion is the translation to add. */\n suggest: string;\n /** 0.46 (keksdose B2/S3): the desktop row's button that OPENS the editor to send a\n * string back — its own name, apart from the editor's `flag`, which saves. */\n flagRow: string;\n /** The same for a MISSING string: opens the editor to add the translation. */\n suggestRow: string;\n /** Clear a verdict: back to unreviewed. */\n reset: string;\n /** The editor's way out without saving. */\n cancel: string;\n approveSelected: string;\n resetSelected: string;\n /** Selects every row the filters show — the phone's way into the bulk actions, where\n * the table has no checkboxes. */\n selectShown: string;\n /** Over the old wording of a row that changed since its verdict. */\n changedSince: string;\n reviewedWording: string;\n reviewedReference: string;\n /** The placeholders differ: the reference's and the text's, each a list. */\n placeholderMismatch: (reference: string, text: string) => string;\n lastApproved: (name: string, date: string) => string;\n lastFlagged: (name: string, date: string) => string;\n /** The reviewer's name once the account is gone — what `name` above becomes. */\n erasedReviewer: string;\n /** The editor's wording field, opened on the current text. */\n suggestion: string;\n /** The same field for a missing string. */\n translation: string;\n note: string;\n notePlaceholder: string;\n /** The viewer may read this locale but not review it. */\n readOnly: string;\n /** The reviewer is limited to some areas — `areas` is their names, joined. */\n scope: (areas: string) => string;\n exportCorrections: (count: number) => string;\n /** A callback rejected and no `formatError` says better. */\n failed: string;\n /** 0.25: the Undo toast after an approval (`undo`) — one string, or a batch of them. */\n approvedToast: (count: number) => string;\n /** 0.25: a group's header under `groupBy` — how many of its rows nobody has read yet,\n * of how many the filters show in it. */\n groupCount: (unreviewed: number, total: number) => string;\n /** 0.25: a group's bulk action — approve its unreviewed rows. */\n approveGroup: (count: number) => string;\n /** 0.25: asked first when a group's bulk approve is larger than a page — more than the\n * reviewer can have had on screen. `group` is the group's name. */\n confirmGroup: (count: number, group: string) => string;\n /** The Undo toast after a swipe bound to `clear` took a verdict back (keksdose, live\n * #377 rework: the swipes bound in its settings) — one string, or a batch of them.\n * The swipe itself is named by {@link reset}. */\n clearedToast: (count: number) => string;\n}\n\nexport const DEFAULT_TRANSLATION_REVIEW_LABELS: TranslationReviewLabels = {\n statusMissing: \"Missing\",\n statusUnreviewed: \"Unreviewed\",\n statusChanged: \"Changed since review\",\n statusNeedsChange: \"Needs a change\",\n statusApproved: \"Approved\",\n statusFilter: \"Status\",\n all: \"All\",\n filterCount: (label, count) => `${label} · ${count}`,\n source: \"Texts\",\n allSources: \"All texts\",\n namespace: \"Area\",\n allNamespaces: \"All areas\",\n search: \"Search keys and text\",\n placeholdersOnly: (count) => `Only placeholder problems (${count})`,\n placeholderChip: \"Placeholders\",\n key: \"Key\",\n statusColumn: \"Status\",\n empty: \"No strings match these filters.\",\n missingText: \"Missing in this language\",\n progress: (approved, total) => `${approved} of ${total} approved`,\n localeProgress: (approved, total) => `${approved}/${total}`,\n locales: \"Languages\",\n approve: \"Approve\",\n flag: \"Needs a change\",\n suggest: \"Suggest translation\",\n flagRow: \"Flag for a change…\",\n suggestRow: \"Add a translation…\",\n reset: \"Mark unreviewed\",\n cancel: \"Cancel\",\n approveSelected: \"Approve selected\",\n resetSelected: \"Mark selected unreviewed\",\n selectShown: \"Select all shown\",\n changedSince: \"This string changed after it was reviewed. Please read it again.\",\n reviewedWording: \"Wording when reviewed\",\n reviewedReference: \"Reference when reviewed\",\n placeholderMismatch: (reference, text) =>\n `The placeholders differ from the reference — reference: ${reference}; this text: ${text}. The app fills these in, so they must stay exactly as they are.`,\n lastApproved: (name, date) => `Approved by ${name} on ${date}`,\n lastFlagged: (name, date) => `Marked as needing a change by ${name} on ${date}`,\n erasedReviewer: \"a deleted account\",\n suggestion: \"Better wording\",\n translation: \"Translation\",\n note: \"Note (optional)\",\n notePlaceholder: \"What is wrong, or what to keep in mind\",\n readOnly: \"You can read this language but not review it.\",\n scope: (areas) => `Your review is limited to: ${areas}.`,\n exportCorrections: (count) => `Export corrections (${count})`,\n failed: \"That did not work. Please try again.\",\n approvedToast: (count) => (count === 1 ? \"String approved\" : `${count} strings approved`),\n groupCount: (unreviewed, total) => `${unreviewed} unreviewed / ${total}`,\n approveGroup: (count) => `Approve unreviewed (${count})`,\n confirmGroup: (count, group) =>\n count === 1\n ? `Approve the unreviewed string in ${group}?`\n : `Approve all ${count} unreviewed strings in ${group}, including those not on screen?`,\n clearedToast: (count) => (count === 1 ? \"String marked unreviewed\" : `${count} strings marked unreviewed`),\n};\n\n/** A status's word, from the namespace's five. */\nexport function reviewStatusLabel(labels: TranslationReviewLabels, status: ReviewStatus): string {\n switch (status) {\n case \"missing\":\n return labels.statusMissing;\n case \"unreviewed\":\n return labels.statusUnreviewed;\n case \"changed\":\n return labels.statusChanged;\n case \"needs_change\":\n return labels.statusNeedsChange;\n case \"approved\":\n return labels.statusApproved;\n }\n}\n"],"mappings":";AA2GO,MAAM,oCAA6D;AAAA,EACxE,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,KAAK;AAAA,EACL,aAAa,CAAC,OAAO,UAAU,GAAG,KAAK,SAAM,KAAK;AAAA,EAClD,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,kBAAkB,CAAC,UAAU,8BAA8B,KAAK;AAAA,EAChE,iBAAiB;AAAA,EACjB,KAAK;AAAA,EACL,cAAc;AAAA,EACd,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU,CAAC,UAAU,UAAU,GAAG,QAAQ,OAAO,KAAK;AAAA,EACtD,gBAAgB,CAAC,UAAU,UAAU,GAAG,QAAQ,IAAI,KAAK;AAAA,EACzD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,qBAAqB,CAAC,WAAW,SAC/B,gEAA2D,SAAS,gBAAgB,IAAI;AAAA,EAC1F,cAAc,CAAC,MAAM,SAAS,eAAe,IAAI,OAAO,IAAI;AAAA,EAC5D,aAAa,CAAC,MAAM,SAAS,iCAAiC,IAAI,OAAO,IAAI;AAAA,EAC7E,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO,CAAC,UAAU,8BAA8B,KAAK;AAAA,EACrD,mBAAmB,CAAC,UAAU,uBAAuB,KAAK;AAAA,EAC1D,QAAQ;AAAA,EACR,eAAe,CAAC,UAAW,UAAU,IAAI,oBAAoB,GAAG,KAAK;AAAA,EACrE,YAAY,CAAC,YAAY,UAAU,GAAG,UAAU,iBAAiB,KAAK;AAAA,EACtE,cAAc,CAAC,UAAU,uBAAuB,KAAK;AAAA,EACrD,cAAc,CAAC,OAAO,UACpB,UAAU,IACN,oCAAoC,KAAK,MACzC,eAAe,KAAK,0BAA0B,KAAK;AAAA,EACzD,cAAc,CAAC,UAAW,UAAU,IAAI,6BAA6B,GAAG,KAAK;AAC/E;AAGO,SAAS,kBAAkB,QAAiC,QAA8B;AAC/F,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,EAClB;AACF;","names":[]}
@@ -129,13 +129,23 @@ export interface TranslationReviewPanelProps {
129
129
  * DataTable's swipe sides (`mobileSwipeActions`) on {@link SwipeableRow}: the same
130
130
  * gesture, the same feedback, and the same keyboard path — every swipe is also a real
131
131
  * button, read out and reachable by Tab, so the gesture is a shortcut and never the only
132
- * way. A row has no swipe while the editor under it is open, while its own write is out,
133
- * under a {@link WriteLockProvider} lock, in a `readOnly` locale, and toward "Approve"
134
- * where there is nothing to approve (a missing string, an approved one). A reviewer
135
- * limited to `areas` never sees a row outside them, so never swipes one.
132
+ * way. A row has no swipe while its own write is out (a swipe's, a button's, or the open
133
+ * editor's own), under a {@link WriteLockProvider} lock, in a `readOnly` locale, and
134
+ * toward "Approve" where there is nothing to approve (a missing string, an approved one).
135
+ * A reviewer limited to `areas` never sees a row outside them, so never swipes one.
136
136
  *
137
- * Off by default, so no existing page gains a gesture on the bump. Phones only; the
138
- * desktop table keeps its approve button.
137
+ * An OPEN row swipes too, on its top line (keksdose live #416, Marcel: *"The swipe shall
138
+ * still be possible since it is faster than scrolling down to the buttons"*), as
139
+ * DataTable's open card does since 0.40: the editor below is outside the gesture, and a
140
+ * swipe neither toggles it shut nor resets it. A swipe acts on the STORED string, as the
141
+ * row's buttons do — Approve approves what the locale says, not the editor's draft — and
142
+ * the editor stays open with whatever was typed in it; "Needs a change" puts the cursor
143
+ * back at the end of that draft.
144
+ *
145
+ * Off by default, so no existing page gains a gesture on the bump. The gesture is the
146
+ * phone's; on a wide screen the binding is the row's actions column instead (0.46,
147
+ * keksdose dev#598): Approve where the row can take it, bound or not, then the bound
148
+ * `edit` and `clear`, each once — under a write lock they stay and say why.
139
149
  *
140
150
  * A BINDING instead of `true` (keksdose — Marcel's live #377 rework, 2026-10-03: *"Add
141
151
  * the swipe options to be shown also to the settings /settings#interaction area where
@@ -153,8 +163,8 @@ export interface TranslationReviewPanelProps {
153
163
  * `onClear`). An action a row cannot offer drops out and the ladder closes up behind it
154
164
  * ({@link translationReviewSwipePlan}): `approve` on an approved or missing string,
155
165
  * `clear` on a row with no verdict. Every rule above still holds for every binding —
156
- * no side swipes in a `readOnly` locale, under a write lock, with the editor open or
157
- * the row's write out.
166
+ * no side swipes in a `readOnly` locale, under a write lock, or while the row's write
167
+ * (or its open editor's) is out.
158
168
  *
159
169
  * `true` is {@link DEFAULT_TRANSLATION_REVIEW_SWIPE} — `{ end: ["approve"], start:
160
170
  * ["edit"] }`, 0.25's mapping exactly — and `false` or nothing is no swipe. A binding
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useId, useMemo, useRef, useState } from "react";
4
4
  import { Check, ListChecks, PencilLine, RotateCcw } from "lucide-react";
5
5
  import { usePhoneLayout } from "../hooks/use-breakpoint.js";
@@ -33,6 +33,7 @@ import { DEFAULT_TRANSLATION_REVIEW_LABELS, reviewStatusLabel } from "./translat
33
33
  import { ReviewStatusChip, TranslationProgress } from "./translation-review-parts.js";
34
34
  import { Button } from "./button.js";
35
35
  import { IconButton } from "./icon-button.js";
36
+ import { DEFAULT_ROW_ACTIONS_LABELS, rowActionsColumn } from "./row-actions.js";
36
37
  import { Select } from "./select.js";
37
38
  import { COMMIT_EXCEPT_BILLING, useWriteLock } from "./write-lock.js";
38
39
  import {
@@ -125,6 +126,7 @@ function TranslationReviewPanel({
125
126
  const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
126
127
  const locale = useKitLocale();
127
128
  const lock = useWriteLock(COMMIT_EXCEPT_BILLING);
129
+ const rowActionLabels = useKitLabels("rowActions", DEFAULT_ROW_ACTIONS_LABELS);
128
130
  const phone = usePhoneLayout();
129
131
  const baseId = useId();
130
132
  const rootRef = useRef(null);
@@ -147,6 +149,19 @@ function TranslationReviewPanel({
147
149
  const [failure, setFailure] = useState(null);
148
150
  const busy = queued.length > 0;
149
151
  const isQueued = (key) => queued.includes(key);
152
+ const [editorWrites, setEditorWrites] = useState(() => /* @__PURE__ */ new Set());
153
+ const whileEditorWrites = async (rowId, write) => {
154
+ setEditorWrites((prev) => new Set(prev).add(rowId));
155
+ try {
156
+ await write();
157
+ } finally {
158
+ setEditorWrites((prev) => {
159
+ const next = new Set(prev);
160
+ next.delete(rowId);
161
+ return next;
162
+ });
163
+ }
164
+ };
150
165
  const rows = useMemo(
151
166
  () => areas ? rowsProp.filter((row) => keyInAreas(row.key, areas, inArea)) : [...rowsProp],
152
167
  [rowsProp, areas, inArea]
@@ -238,16 +253,16 @@ function TranslationReviewPanel({
238
253
  if (ok && undoOn) sayDone(targets, description);
239
254
  return ok;
240
255
  };
241
- const clearRow = async (row) => {
256
+ const clearRow = async (row, origin = "swipe") => {
242
257
  if (!onClear || row.review === null) return;
243
258
  const clear = onClear;
244
259
  const keys = [{ locale: row.locale, key: row.key }];
245
- const ok = await run(`row:${row.id}`, () => clear(keys, { origin: "swipe", toasted: undoOn }));
260
+ const ok = await run(`row:${row.id}`, () => clear(keys, { origin, toasted: undoOn }));
246
261
  if (ok && undoOn) sayDone([row], row.key, true);
247
262
  };
248
263
  const saveFromEditor = async (row, write) => {
249
264
  const toasted = undoOn && write.verdict === "APPROVED";
250
- await onSave?.([write], { origin: "editor", toasted });
265
+ await whileEditorWrites(row.id, () => onSave?.([write], { origin: "editor", toasted }));
251
266
  if (toasted) sayDone([row], row.key);
252
267
  };
253
268
  const keepFocusAt = (groupKey) => {
@@ -324,7 +339,40 @@ function TranslationReviewPanel({
324
339
  sortBy: (r) => REVIEW_STATUSES.indexOf(r.status)
325
340
  }
326
341
  ];
327
- if (editable) {
342
+ if (editable && binding) {
343
+ columns.push(
344
+ rowActionsColumn({
345
+ key: "actions",
346
+ headerText: rowActionLabels.actions,
347
+ name: (r) => r.key,
348
+ tooltipSide: "start",
349
+ actions: (r) => translationReviewSwipePlan(
350
+ { end: ["approve", ...binding.end ?? [], ...binding.start ?? []] },
351
+ r,
352
+ { canClear: onClear !== void 0 }
353
+ ).end.map((id) => {
354
+ const swipe2 = swipeAction(r, id);
355
+ const writes = id !== "edit";
356
+ return {
357
+ key: id,
358
+ // `edit` opens the editor: its own name, apart from the editor's submit
359
+ // ("Needs a change"), which saves (keksdose B2/S3).
360
+ label: id === "edit" ? r.text === "" ? labels.suggestRow : labels.flagRow : swipe2.label,
361
+ icon: /* @__PURE__ */ jsx(Fragment, { children: swipe2.icon }),
362
+ tone: "muted",
363
+ commit: writes ? COMMIT_EXCEPT_BILLING : void 0,
364
+ pending: writes && isQueued(`row:${r.id}`),
365
+ disabled: busy,
366
+ onSelect: id === "approve" ? () => void approve(`row:${r.id}`, [r], "row", r.key) : id === "clear" ? () => void clearRow(r, "row") : (
367
+ // The row's group stays open while its editor is (`keepGroupOpen`).
368
+ () => swipeAction(r, id, groups?.find((g) => g.rows.includes(r))?.key).onCommit()
369
+ )
370
+ };
371
+ }),
372
+ column: { mobileHidden: true }
373
+ })
374
+ );
375
+ } else if (editable) {
328
376
  columns.push({
329
377
  key: "approve",
330
378
  header: /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.approve }),
@@ -353,6 +401,7 @@ function TranslationReviewPanel({
353
401
  switch (id) {
354
402
  case "approve":
355
403
  return {
404
+ id,
356
405
  label: labels.approve,
357
406
  icon: /* @__PURE__ */ jsx(Check, { className: "size-4", "aria-hidden": true }),
358
407
  onCommit: () => void approve(`row:${r.id}`, [r], "swipe", r.key),
@@ -362,6 +411,7 @@ function TranslationReviewPanel({
362
411
  };
363
412
  case "edit":
364
413
  return {
414
+ id,
365
415
  label: missing ? labels.suggest : labels.flag,
366
416
  icon: /* @__PURE__ */ jsx(PencilLine, { className: "size-4", "aria-hidden": true }),
367
417
  // Opens the editor rather than sending the string back: "needs a change"
@@ -370,11 +420,17 @@ function TranslationReviewPanel({
370
420
  keepGroupOpen(groupKey);
371
421
  setOpenId(r.id);
372
422
  setFocusId(r.id);
423
+ if (openId === r.id) {
424
+ const field = rootRef.current?.querySelector("[data-review-wording]");
425
+ field?.focus();
426
+ field?.setSelectionRange(field.value.length, field.value.length);
427
+ }
373
428
  },
374
429
  tone: missing ? "brand" : "danger"
375
430
  };
376
431
  case "clear":
377
432
  return {
433
+ id,
378
434
  label: labels.reset,
379
435
  icon: /* @__PURE__ */ jsx(RotateCcw, { className: "size-4", "aria-hidden": true }),
380
436
  onCommit: () => void clearRow(r),
@@ -386,7 +442,7 @@ function TranslationReviewPanel({
386
442
  }
387
443
  };
388
444
  const swipeActions = (r, groupKey) => {
389
- if (!binding || !editable || lock.locked || isQueued(`row:${r.id}`)) return null;
445
+ if (!binding || !editable || lock.locked || isQueued(`row:${r.id}`) || editorWrites.has(r.id)) return null;
390
446
  const plan = translationReviewSwipePlan(binding, r, { canClear: onClear !== void 0 });
391
447
  if (plan.start.length === 0 && plan.end.length === 0) return null;
392
448
  return {
@@ -449,7 +505,7 @@ function TranslationReviewPanel({
449
505
  readOnly: !editable,
450
506
  focusWording: focusId === r.id,
451
507
  onSave: onSave ? (write) => saveFromEditor(r, write) : void 0,
452
- onClear: onClear ? (key) => onClear([key], { origin: "editor", toasted: false }) : void 0,
508
+ onClear: onClear ? (key) => whileEditorWrites(r.id, () => onClear([key], { origin: "editor", toasted: false })) : void 0,
453
509
  onClose: () => {
454
510
  setOpenId(null);
455
511
  setFocusId(null);
@@ -469,6 +525,7 @@ function TranslationReviewPanel({
469
525
  ] })
470
526
  ] }),
471
527
  mobileSwipeActions: binding ? (r) => swipeActions(r, group?.key) : void 0,
528
+ desktopSwipeActions: false,
472
529
  selection: selectionFor(list)
473
530
  }
474
531
  );