@eifi1/ui-kit 0.21.0 → 0.23.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 +42 -25
- package/dist/chart.d.ts +19 -14
- package/dist/components/account-chips.d.ts +10 -3
- package/dist/components/account-chips.js +5 -2
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/account-settings.d.ts +33 -2
- package/dist/components/account-settings.js +43 -21
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +53 -14
- package/dist/components/amount-input.js +17 -28
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.d.ts +29 -0
- package/dist/components/autocomplete.js +134 -104
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +19 -14
- package/dist/components/calculator.d.ts +19 -14
- package/dist/components/checkbox-group.d.ts +104 -0
- package/dist/components/checkbox-group.js +111 -0
- package/dist/components/checkbox-group.js.map +1 -0
- package/dist/components/checkbox.d.ts +19 -0
- package/dist/components/checkbox.js +30 -7
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +18 -0
- package/dist/components/chip.js +47 -10
- package/dist/components/chip.js.map +1 -1
- package/dist/components/column-mapper.d.ts +182 -0
- package/dist/components/column-mapper.js +375 -0
- package/dist/components/column-mapper.js.map +1 -0
- package/dist/components/combobox-core.d.ts +39 -2
- package/dist/components/combobox-core.js +29 -6
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +91 -2
- package/dist/components/combobox.js +454 -308
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +69 -161
- package/dist/components/confirm-dialog.js +7 -8
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +16 -11
- package/dist/components/country-select.d.ts +167 -0
- package/dist/components/country-select.js +293 -0
- package/dist/components/country-select.js.map +1 -0
- package/dist/components/currency-select.d.ts +17 -1
- package/dist/components/currency-select.js +56 -23
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +69 -166
- package/dist/components/danger-confirm.js +144 -50
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +1 -2
- package/dist/components/data-table-filter-popover.d.ts +1 -2
- package/dist/components/data-table-filters.d.ts +1 -2
- package/dist/components/data-table-labels.d.ts +4 -97
- package/dist/components/data-table-labels.js +9 -1
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -1
- package/dist/components/data-table.d.ts +1 -2
- package/dist/components/data-table.js +98 -3
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +66 -5
- package/dist/components/date-picker.js +378 -66
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +42 -1
- package/dist/components/entity-combobox.js +154 -113
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +19 -14
- package/dist/components/field-parts.d.ts +224 -0
- package/dist/components/field-parts.js +156 -0
- package/dist/components/field-parts.js.map +1 -0
- package/dist/components/field-strip.d.ts +105 -0
- package/dist/components/field-strip.js +46 -0
- package/dist/components/field-strip.js.map +1 -0
- package/dist/components/file-button.d.ts +19 -14
- package/dist/components/file-button.js +13 -3
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +41 -15
- package/dist/components/file-dropzone.js +57 -34
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/form-actions.d.ts +18 -13
- package/dist/components/form-actions.js +69 -5
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +69 -0
- package/dist/components/iban-input.js +125 -0
- package/dist/components/iban-input.js.map +1 -0
- package/dist/components/icon-picker.d.ts +21 -2
- package/dist/components/icon-picker.js +19 -4
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/inline-edit-field.d.ts +167 -0
- package/dist/components/inline-edit-field.js +293 -0
- package/dist/components/inline-edit-field.js.map +1 -0
- package/dist/components/language-select.d.ts +130 -0
- package/dist/components/language-select.js +18 -0
- package/dist/components/language-select.js.map +1 -0
- package/dist/components/line-items.d.ts +25 -3
- package/dist/components/line-items.js +55 -46
- package/dist/components/line-items.js.map +1 -1
- package/dist/components/list.d.ts +25 -0
- package/dist/components/list.js +16 -5
- package/dist/components/list.js.map +1 -1
- package/dist/components/money-field.d.ts +32 -14
- package/dist/components/money-field.js.map +1 -1
- package/dist/components/month-picker.d.ts +52 -4
- package/dist/components/month-picker.js +291 -86
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +26 -1
- package/dist/components/multi-entity-combobox.js +143 -102
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +8 -1
- package/dist/components/multi-select.js +9 -4
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +34 -19
- package/dist/components/number-field.js +6 -20
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +79 -19
- package/dist/components/number-input.js +90 -38
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +19 -14
- package/dist/components/one-time-code-input.d.ts +100 -0
- package/dist/components/one-time-code-input.js +137 -0
- package/dist/components/one-time-code-input.js.map +1 -0
- package/dist/components/phone-input.d.ts +69 -0
- package/dist/components/phone-input.js +171 -0
- package/dist/components/phone-input.js.map +1 -0
- package/dist/components/reauth-dialog.d.ts +2 -2
- package/dist/components/reauth-dialog.js +6 -8
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/series-chart.d.ts +19 -14
- package/dist/components/settings-fields.d.ts +19 -14
- package/dist/components/share-card.d.ts +16 -11
- package/dist/components/sign-chip.d.ts +66 -0
- package/dist/components/sign-chip.js +39 -0
- package/dist/components/sign-chip.js.map +1 -0
- package/dist/components/swatch-picker.d.ts +43 -2
- package/dist/components/swatch-picker.js +80 -32
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/switch.d.ts +19 -0
- package/dist/components/switch.js +24 -6
- package/dist/components/switch.js.map +1 -1
- package/dist/components/text-link.d.ts +18 -13
- package/dist/components/tile-radio.d.ts +88 -13
- package/dist/components/tile-radio.js +28 -11
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +24 -15
- package/dist/components/time-input.js.map +1 -1
- package/dist/components/toggle-group.d.ts +47 -1
- package/dist/components/toggle-group.js +110 -69
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/trigger-aria.d.ts +3 -0
- package/dist/components/trigger-aria.js +3 -1
- package/dist/components/trigger-aria.js.map +1 -1
- package/dist/components/ui.d.ts +16 -11
- package/dist/components/ui.js +284 -88
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -2
- package/dist/{data-table-3mi2R3so.d.ts → data-table-labels-B7OdnM0S.d.ts} +140 -3
- package/dist/data-table.d.ts +1 -2
- package/dist/data-table.js +2 -1
- package/dist/data-table.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +1 -1
- package/dist/feedback/feedback-attachment.js +348 -41
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +1 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +3 -2
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +68 -253
- package/dist/feedback/feedback-thread.js +46 -10
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/{kit-labels-D1J2_jyD.d.ts → feedback-BxeQVzwq.d.ts} +1206 -20
- package/dist/{feedback-attachment-WFiXJ8We.d.ts → feedback-attachment-fGAzZPf0.d.ts} +178 -11
- package/dist/feedback.d.ts +66 -2
- package/dist/hooks/use-file-drop.d.ts +19 -14
- package/dist/i18n/defaults.d.ts +19 -14
- package/dist/i18n/defaults.js +19 -2
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +19 -14
- package/dist/i18n/german.js +84 -5
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +15 -10
- package/dist/i18n/kit-labels.js +8 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +19 -14
- package/dist/i18n/locales/de-CH.d.ts +19 -14
- package/dist/i18n/locales/en.d.ts +19 -14
- package/dist/i18n/locales/en.js +16 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +19 -14
- package/dist/i18n/locales/es.js +83 -5
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +19 -14
- package/dist/i18n/locales/fr.js +82 -5
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +19 -14
- package/dist/i18n/locales/hu.js +85 -5
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +19 -14
- package/dist/i18n/locales/it.js +84 -5
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +19 -14
- package/dist/i18n/locales/zh.js +80 -5
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +19 -14
- package/dist/i18n/review.js +27 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +21 -10
- package/dist/index.js +73 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/column-mapping.d.ts +81 -0
- package/dist/lib/column-mapping.js +108 -0
- package/dist/lib/column-mapping.js.map +1 -0
- package/dist/lib/countries.d.ts +43 -0
- package/dist/lib/countries.js +39 -0
- package/dist/lib/countries.js.map +1 -0
- package/dist/lib/decimal-marks.d.ts +46 -0
- package/dist/lib/decimal-marks.js +40 -0
- package/dist/lib/decimal-marks.js.map +1 -0
- package/dist/lib/iban.d.ts +98 -0
- package/dist/lib/iban.js +154 -0
- package/dist/lib/iban.js.map +1 -0
- package/dist/lib/isin.d.ts +30 -0
- package/dist/lib/isin.js +36 -0
- package/dist/lib/isin.js.map +1 -0
- package/dist/lib/phone.d.ts +69 -0
- package/dist/lib/phone.js +121 -0
- package/dist/lib/phone.js.map +1 -0
- package/dist/lib/table-text.d.ts +109 -1
- package/dist/lib/table-text.js +122 -1
- package/dist/lib/table-text.js.map +1 -1
- package/dist/rhf/fields.d.ts +233 -18
- package/dist/rhf/fields.js +456 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +19 -14
- package/dist/rhf.d.ts +22 -15
- package/dist/rhf.js.map +1 -1
- package/dist/shell/app-shell.d.ts +18 -13
- package/dist/shell/top-bar-brand.d.ts +19 -14
- package/dist/shell/topbar-action-menu.d.ts +36 -3
- package/dist/shell/topbar-action-menu.js +74 -33
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell.d.ts +18 -13
- package/dist/table-text.d.ts +1 -1
- package/dist/tour/tour.js +20 -24
- package/dist/tour/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +49 -16
- package/dist/wizard/stepper-nav.js +4 -0
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +19 -14
- package/package.json +1 -1
- package/src/components/account-chips.tsx +17 -3
- package/src/components/account-settings.tsx +92 -26
- package/src/components/amount-input.tsx +68 -56
- package/src/components/autocomplete.tsx +198 -114
- package/src/components/checkbox-group.tsx +229 -0
- package/src/components/checkbox.tsx +72 -8
- package/src/components/chip.tsx +80 -10
- package/src/components/column-mapper.tsx +666 -0
- package/src/components/combobox-core.tsx +82 -9
- package/src/components/combobox.tsx +661 -340
- package/src/components/confirm-dialog.tsx +12 -8
- package/src/components/country-select.tsx +556 -0
- package/src/components/currency-select.tsx +109 -33
- package/src/components/danger-confirm.tsx +429 -82
- package/src/components/data-table-labels.ts +16 -1
- package/src/components/data-table.tsx +175 -6
- package/src/components/date-picker.tsx +595 -71
- package/src/components/entity-combobox.tsx +235 -124
- package/src/components/field-parts.tsx +403 -0
- package/src/components/field-strip.tsx +149 -0
- package/src/components/file-button.tsx +36 -3
- package/src/components/file-dropzone.tsx +106 -38
- package/src/components/form-actions.tsx +147 -6
- package/src/components/iban-input.tsx +231 -0
- package/src/components/icon-picker.tsx +47 -6
- package/src/components/inline-edit-field.tsx +533 -0
- package/src/components/language-select.tsx +78 -0
- package/src/components/line-items.tsx +35 -2
- package/src/components/list.tsx +55 -6
- package/src/components/money-field.tsx +13 -0
- package/src/components/month-picker.tsx +400 -63
- package/src/components/multi-entity-combobox.tsx +207 -120
- package/src/components/multi-select.tsx +80 -58
- package/src/components/number-field.tsx +28 -46
- package/src/components/number-input.tsx +210 -43
- package/src/components/one-time-code-input.tsx +288 -0
- package/src/components/phone-input.tsx +299 -0
- package/src/components/reauth-dialog.tsx +17 -18
- package/src/components/sign-chip.tsx +103 -0
- package/src/components/swatch-picker.tsx +143 -31
- package/src/components/switch.tsx +58 -7
- package/src/components/tile-radio.tsx +145 -29
- package/src/components/time-input.tsx +8 -1
- package/src/components/toggle-group.tsx +206 -73
- package/src/components/trigger-aria.ts +5 -0
- package/src/components/ui.tsx +479 -138
- package/src/data-table.ts +2 -1
- package/src/feedback/feedback-attachment.tsx +597 -46
- package/src/feedback/feedback-dialog.tsx +9 -3
- package/src/feedback/feedback-inbox.tsx +3 -2
- package/src/feedback/feedback-thread.tsx +113 -9
- package/src/i18n/defaults.ts +18 -1
- package/src/i18n/german.ts +89 -0
- package/src/i18n/kit-labels.tsx +82 -2
- package/src/i18n/locales/en.ts +36 -4
- package/src/i18n/locales/es.ts +87 -0
- package/src/i18n/locales/fr.ts +87 -0
- package/src/i18n/locales/hu.ts +83 -0
- package/src/i18n/locales/it.ts +88 -0
- package/src/i18n/locales/zh.ts +76 -0
- package/src/i18n/review.ts +26 -0
- package/src/index.ts +71 -3
- package/src/lib/column-mapping.ts +234 -0
- package/src/lib/countries.ts +91 -0
- package/src/lib/decimal-marks.ts +99 -0
- package/src/lib/iban.ts +171 -0
- package/src/lib/isin.ts +62 -0
- package/src/lib/phone.ts +211 -0
- package/src/lib/table-text.ts +271 -0
- package/src/rhf/fields.tsx +860 -9
- package/src/rhf.ts +3 -1
- package/src/shell/topbar-action-menu.tsx +134 -38
- package/src/tour/tour.tsx +66 -28
- package/src/wizard/stepper-nav.tsx +35 -1
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DEFAULT_MAX_ATTACHMENT_BYTES,
|
|
11
11
|
FeedbackAttachmentField,
|
|
12
12
|
type FeedbackAttachmentError,
|
|
13
|
+
type FeedbackAttachmentErrorInfo,
|
|
13
14
|
} from "./feedback-attachment";
|
|
14
15
|
|
|
15
16
|
export interface FeedbackCategoryOption {
|
|
@@ -40,6 +41,8 @@ export interface FeedbackAttachmentLabels {
|
|
|
40
41
|
attachmentRemoveFile?: (name: string) => string;
|
|
41
42
|
/** `multiple` mode: the line shown once `max` files are attached. */
|
|
42
43
|
attachmentLimit?: (max: number) => string;
|
|
44
|
+
/** `refs` mode (0.22.0): the second line of a chip whose upload is still running. */
|
|
45
|
+
attachmentUploading?: string;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
|
|
@@ -155,7 +158,9 @@ interface FeedbackDialogBaseProps {
|
|
|
155
158
|
export interface FeedbackDialogSingleProps extends FeedbackDialogBaseProps {
|
|
156
159
|
attachments?: "single";
|
|
157
160
|
onSubmit: (data: FeedbackSubmission) => void | Promise<void>;
|
|
158
|
-
|
|
161
|
+
/** `info` (0.23.0) names the refused file and the limit — see
|
|
162
|
+
* {@link FeedbackAttachmentErrorInfo}. */
|
|
163
|
+
onAttachmentError?: (kind: "type" | "size", info: FeedbackAttachmentErrorInfo) => void;
|
|
159
164
|
/** Multiple mode only — refused here rather than silently ignored. */
|
|
160
165
|
maxAttachments?: never;
|
|
161
166
|
}
|
|
@@ -170,8 +175,9 @@ export interface FeedbackDialogMultipleProps extends FeedbackDialogBaseProps {
|
|
|
170
175
|
/** Default `DEFAULT_MAX_ATTACHMENTS` (5). The screenshot does not count. */
|
|
171
176
|
maxAttachments?: number;
|
|
172
177
|
onSubmit: (data: FeedbackMultipleSubmission) => void | Promise<void>;
|
|
173
|
-
/** `"count"`: more files arrived at once than there was room for; the surplus was
|
|
174
|
-
|
|
178
|
+
/** `"count"`: more files arrived at once than there was room for; the surplus was
|
|
179
|
+
* dropped. `info` (0.23.0) names the refused file(s) and the limit. */
|
|
180
|
+
onAttachmentError?: (kind: FeedbackAttachmentError, info: FeedbackAttachmentErrorInfo) => void;
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
export type FeedbackDialogProps = FeedbackDialogSingleProps | FeedbackDialogMultipleProps;
|
|
@@ -18,7 +18,7 @@ import type { LucideIcon } from "lucide-react";
|
|
|
18
18
|
import { cn } from "../lib/cn";
|
|
19
19
|
import { Button, Textarea } from "../components/ui";
|
|
20
20
|
import { Tooltip } from "../components/tooltip";
|
|
21
|
-
import { FeedbackAttachmentField } from "./feedback-attachment";
|
|
21
|
+
import { FeedbackAttachmentField, type FeedbackAttachmentErrorInfo } from "./feedback-attachment";
|
|
22
22
|
import type { FeedbackAttachmentLabels } from "./feedback-dialog";
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -566,7 +566,8 @@ export interface FeedbackNoteAttachment {
|
|
|
566
566
|
labels: FeedbackAttachmentLabels;
|
|
567
567
|
accept?: string[];
|
|
568
568
|
maxBytes?: number;
|
|
569
|
-
|
|
569
|
+
/** `info` (0.23.0) names the refused file and the limit. */
|
|
570
|
+
onError?: (kind: "type" | "size", info: FeedbackAttachmentErrorInfo) => void;
|
|
570
571
|
onCaptureScreenshot?: () => Promise<File | null>;
|
|
571
572
|
}
|
|
572
573
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
1
|
+
import { useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
2
2
|
import type { ReactNode, Ref } from "react";
|
|
3
3
|
import { ImageIcon, ImageOff, Paperclip, Send } from "lucide-react";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
5
|
import { formatDate, formatRelativeTime } from "../lib/format";
|
|
6
6
|
import { Button, EmptyState, Spinner, Textarea } from "../components/ui";
|
|
7
|
+
import type { CharacterCountLabels } from "../components/ui";
|
|
7
8
|
import { Skeleton } from "../components/skeleton";
|
|
8
9
|
import { Tooltip } from "../components/tooltip";
|
|
9
10
|
import { useKitFileLabels, useKitLabels, useKitLocale } from "../i18n/kit-labels";
|
|
10
11
|
import { FeedbackAttachmentField } from "./feedback-attachment";
|
|
12
|
+
import type { FeedbackAttachmentFieldSingleProps } from "./feedback-attachment";
|
|
11
13
|
import type { FeedbackNoteAttachment } from "./feedback-inbox";
|
|
12
14
|
import type { FeedbackAttachmentLabels } from "./feedback-dialog";
|
|
13
15
|
|
|
@@ -463,9 +465,12 @@ function toDate(value: Date | string | number): Date | null {
|
|
|
463
465
|
/* ── Composer ─────────────────────────────────────────────────────────────── */
|
|
464
466
|
|
|
465
467
|
/** What the composer's attach control takes — {@link FeedbackNoteAttachment} with the
|
|
466
|
-
* labels optional, since the field reads `feedbackAttachment` from the provider
|
|
467
|
-
|
|
468
|
+
* labels optional, since the field reads `feedbackAttachment` from the provider, and
|
|
469
|
+
* `onError` the field's own (0.23.0): it is handed the refused file and the limit too
|
|
470
|
+
* (`FeedbackAttachmentErrorInfo`), and a `(kind) => …` handler still fits. */
|
|
471
|
+
export type FeedbackComposerAttachment = Omit<FeedbackNoteAttachment, "labels" | "onError"> & {
|
|
468
472
|
labels?: Partial<FeedbackAttachmentLabels>;
|
|
473
|
+
onError?: FeedbackAttachmentFieldSingleProps["onError"];
|
|
469
474
|
};
|
|
470
475
|
|
|
471
476
|
export interface FeedbackComposerProps {
|
|
@@ -498,7 +503,7 @@ export interface FeedbackComposerProps {
|
|
|
498
503
|
* for state the composer cannot see, i.e. `attachmentSlot`'s (keksdose G5): `true`
|
|
499
504
|
* with files ready makes an empty draft sendable (`onSend("", null)`), `false` while
|
|
500
505
|
* an upload is still running holds even a written reply back. Left out, the text
|
|
501
|
-
* decides. `pending` blocks a send either way
|
|
506
|
+
* decides. `pending` blocks a send either way, as does a draft past `maxLength`.
|
|
502
507
|
*/
|
|
503
508
|
canSend?: boolean;
|
|
504
509
|
/**
|
|
@@ -513,6 +518,31 @@ export interface FeedbackComposerProps {
|
|
|
513
518
|
* reader. */
|
|
514
519
|
disabledReason?: ReactNode;
|
|
515
520
|
rows?: number;
|
|
521
|
+
/**
|
|
522
|
+
* The longest draft the box takes, in UTF-16 code units as the browser counts — the
|
|
523
|
+
* server's limit, so a reply too long for it is cut off while it is typed rather than
|
|
524
|
+
* refused by a 422 after the press (keksdose G6: its support replies are capped at
|
|
525
|
+
* 4,000 on the server, and its other text fields already count against that with
|
|
526
|
+
* Textarea's `maxLength` + `showCount` since 0.22; the reply box, a composer, could
|
|
527
|
+
* not). The box's own `maxLength`, so typing and pasting stop at it.
|
|
528
|
+
*
|
|
529
|
+
* {@link FeedbackComposerHandle.insertText} is not stopped by it — the browser limits
|
|
530
|
+
* typing, not a script — and a canned reply is not cut mid-sentence to fit. A draft
|
|
531
|
+
* past the limit therefore holds Send back (whatever `canSend` says, as `pending`
|
|
532
|
+
* does) until it is trimmed; with `showCount` the counter, red at the limit, says by
|
|
533
|
+
* how much.
|
|
534
|
+
*/
|
|
535
|
+
maxLength?: number;
|
|
536
|
+
/**
|
|
537
|
+
* A live "12/4000" under the box, with `maxLength` — Textarea's `showCount`, the same
|
|
538
|
+
* counter (keksdose G6): the count is part of the box's description, read with it on
|
|
539
|
+
* focus, and a polite live region speaks only on entering the last stretch and at the
|
|
540
|
+
* limit, never per keystroke. Under `size="sm"` the hidden send hint stays the box's
|
|
541
|
+
* description too, before the count. Ignored without `maxLength`.
|
|
542
|
+
*/
|
|
543
|
+
showCount?: boolean;
|
|
544
|
+
/** The counter's words — the `characterCount` namespace, as on Textarea. */
|
|
545
|
+
countLabels?: Partial<CharacterCountLabels>;
|
|
516
546
|
/**
|
|
517
547
|
* The box's hint, over the `placeholder` label — keksdose F9: an admin answering a
|
|
518
548
|
* support ticket is asked something else ("Reply to Anna…") than a member adding to
|
|
@@ -535,6 +565,23 @@ export interface FeedbackComposerProps {
|
|
|
535
565
|
ref?: Ref<FeedbackComposerHandle>;
|
|
536
566
|
labels?: Partial<FeedbackComposerLabels>;
|
|
537
567
|
className?: string;
|
|
568
|
+
/**
|
|
569
|
+
* `"sm"` for a narrow host — keksdose's assistant launcher, a small floating panel
|
|
570
|
+
* (0.22): a small Send button, and the send hint ("Enter to send, Shift + Enter for a
|
|
571
|
+
* new line") hidden from view but kept as the box's description, so a keyboard or
|
|
572
|
+
* screen-reader user still learns the shortcut. Default `"md"`.
|
|
573
|
+
*/
|
|
574
|
+
size?: "md" | "sm";
|
|
575
|
+
/**
|
|
576
|
+
* On the composer's root — the box, or the `disabledReason` line in its place —
|
|
577
|
+
* with any `data-*` attribute beside it (keksdose K17). keksdose's assistant tour
|
|
578
|
+
* points at the chat box with `[data-tour="assistant-ask"]`, and a composer that
|
|
579
|
+
* dropped every attribute it was not told about left the anchor nowhere: the
|
|
580
|
+
* assistant kept its hand-built box rather than lose the tour step.
|
|
581
|
+
*/
|
|
582
|
+
id?: string;
|
|
583
|
+
/** `data-*` attributes for the root, as `id` — a tour anchor, a test id. */
|
|
584
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
538
585
|
}
|
|
539
586
|
|
|
540
587
|
/**
|
|
@@ -577,14 +624,22 @@ export function FeedbackComposer({
|
|
|
577
624
|
sendOn = "mod-enter",
|
|
578
625
|
disabledReason,
|
|
579
626
|
rows = 3,
|
|
627
|
+
maxLength,
|
|
628
|
+
showCount,
|
|
629
|
+
countLabels,
|
|
580
630
|
placeholder,
|
|
581
631
|
value,
|
|
582
632
|
onValueChange,
|
|
583
633
|
ref,
|
|
584
634
|
labels: labelsProp,
|
|
585
635
|
className,
|
|
636
|
+
id,
|
|
637
|
+
size = "md",
|
|
638
|
+
...rest
|
|
586
639
|
}: FeedbackComposerProps) {
|
|
587
640
|
const labels = useKitLabels("feedbackComposer", DEFAULT_FEEDBACK_COMPOSER_LABELS, labelsProp);
|
|
641
|
+
const rootAttributes = { id, ...dataAttributes(rest) };
|
|
642
|
+
const hintId = useId();
|
|
588
643
|
const [ownDraft, setOwnDraft] = useState("");
|
|
589
644
|
const controlled = value !== undefined;
|
|
590
645
|
const draft = controlled ? value : ownDraft;
|
|
@@ -639,10 +694,17 @@ export function FeedbackComposer({
|
|
|
639
694
|
);
|
|
640
695
|
|
|
641
696
|
if (disabledReason != null && disabledReason !== false) {
|
|
642
|
-
return
|
|
697
|
+
return (
|
|
698
|
+
<p {...rootAttributes} className={cn("text-xs text-[var(--text-muted)]", className)}>
|
|
699
|
+
{disabledReason}
|
|
700
|
+
</p>
|
|
701
|
+
);
|
|
643
702
|
}
|
|
644
703
|
|
|
645
|
-
|
|
704
|
+
// Longer than `maxLength` only through `insertText` or a controlled value (typing
|
|
705
|
+
// stops at it): the server would refuse it, so it does not go.
|
|
706
|
+
const tooLong = maxLength !== undefined && draft.length > maxLength;
|
|
707
|
+
const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong;
|
|
646
708
|
const send = () => {
|
|
647
709
|
if (!canSend) return;
|
|
648
710
|
const sentDraft = draft;
|
|
@@ -658,11 +720,19 @@ export function FeedbackComposer({
|
|
|
658
720
|
const config = attachment === true ? {} : attachment || null;
|
|
659
721
|
|
|
660
722
|
return (
|
|
661
|
-
<div ref={root} className={cn("space-y-2", className)}>
|
|
723
|
+
<div {...rootAttributes} ref={root} className={cn("space-y-2", className)}>
|
|
662
724
|
<Textarea
|
|
663
725
|
rows={rows}
|
|
664
726
|
dir="auto"
|
|
665
727
|
aria-label={labels.field}
|
|
728
|
+
// Textarea puts the counter's description after this one, so under "sm" the box
|
|
729
|
+
// reads "Enter to send… 12 of 4000 characters".
|
|
730
|
+
aria-describedby={size === "sm" ? hintId : undefined}
|
|
731
|
+
maxLength={maxLength}
|
|
732
|
+
// Passed only when given: Textarea keeps a wrapper for a `showCount` it was
|
|
733
|
+
// handed at all, and a composer without one keeps the DOM it had.
|
|
734
|
+
{...(showCount !== undefined ? { showCount } : null)}
|
|
735
|
+
countLabels={countLabels}
|
|
666
736
|
ref={box}
|
|
667
737
|
placeholder={placeholder ?? labels.placeholder}
|
|
668
738
|
value={draft}
|
|
@@ -707,10 +777,18 @@ export function FeedbackComposer({
|
|
|
707
777
|
<span />
|
|
708
778
|
)}
|
|
709
779
|
<div className="ms-auto flex items-center gap-2">
|
|
710
|
-
<span
|
|
780
|
+
<span
|
|
781
|
+
id={hintId}
|
|
782
|
+
className={size === "sm" ? "sr-only" : "text-xs text-[var(--text-placeholder)]"}
|
|
783
|
+
>
|
|
711
784
|
{sendOn === "enter" ? labels.sendHintEnter : labels.sendHint(modifier)}
|
|
712
785
|
</span>
|
|
713
|
-
<Button
|
|
786
|
+
<Button
|
|
787
|
+
onClick={send}
|
|
788
|
+
disabled={!canSend}
|
|
789
|
+
aria-busy={pending || undefined}
|
|
790
|
+
size={size === "sm" ? "sm" : undefined}
|
|
791
|
+
>
|
|
714
792
|
{pending ? (
|
|
715
793
|
<Spinner label={null} className="size-4" />
|
|
716
794
|
) : (
|
|
@@ -723,3 +801,29 @@ export function FeedbackComposer({
|
|
|
723
801
|
</div>
|
|
724
802
|
);
|
|
725
803
|
}
|
|
804
|
+
|
|
805
|
+
/** The `data-*` entries of a rest-props object, and nothing else: whatever a caller
|
|
806
|
+
* spread in that the composer does not know is not the root's to receive. */
|
|
807
|
+
function dataAttributes(props: object): Record<string, unknown> {
|
|
808
|
+
const out: Record<string, unknown> = {};
|
|
809
|
+
for (const [key, value] of Object.entries(props)) if (key.startsWith("data-")) out[key] = value;
|
|
810
|
+
return out;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* {@link FeedbackComposer} under a name that is not about feedback (keksdose K17).
|
|
815
|
+
*
|
|
816
|
+
* The component was never feedback-specific: a textarea, an optional picture or a
|
|
817
|
+
* host's upload slot, Send, `sendOn="enter"` for the chat way. keksdose's assistant
|
|
818
|
+
* and support chat are chats, not reports, and an import of `FeedbackComposer` there
|
|
819
|
+
* reads as a mistake to the next person. The same function, not a wrapper, so the
|
|
820
|
+
* two names cannot drift.
|
|
821
|
+
*/
|
|
822
|
+
export const ChatComposer = FeedbackComposer;
|
|
823
|
+
/** {@link FeedbackComposerProps}, for {@link ChatComposer}. */
|
|
824
|
+
export type ChatComposerProps = FeedbackComposerProps;
|
|
825
|
+
/** {@link FeedbackComposerHandle}, for {@link ChatComposer}. */
|
|
826
|
+
export type ChatComposerHandle = FeedbackComposerHandle;
|
|
827
|
+
/** {@link FeedbackComposerLabels}, for {@link ChatComposer} — still the `feedbackComposer`
|
|
828
|
+
* namespace of the provider. */
|
|
829
|
+
export type ChatComposerLabels = FeedbackComposerLabels;
|
package/src/i18n/defaults.ts
CHANGED
|
@@ -6,7 +6,11 @@ import { DEFAULT_PAGE_CONTENTS_LABELS } from "../components/page-contents";
|
|
|
6
6
|
import { DEFAULT_POPOVER_LABELS } from "../components/popover";
|
|
7
7
|
import { DEFAULT_CHIP_INPUT_LABELS } from "../components/chip";
|
|
8
8
|
import { DEFAULT_FIELD_SYNC_LABELS } from "../components/field-sync";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_CHARACTER_COUNT_LABELS,
|
|
11
|
+
DEFAULT_PASSWORD_REVEAL_LABELS,
|
|
12
|
+
DEFAULT_TABS_LABELS,
|
|
13
|
+
} from "../components/ui";
|
|
10
14
|
import { DEFAULT_WIZARD_LABELS } from "../wizard/types";
|
|
11
15
|
import { DEFAULT_TOUR_LABELS } from "../tour/tour";
|
|
12
16
|
import { DEFAULT_COMMAND_PALETTE_LABELS } from "../search/command-palette";
|
|
@@ -53,6 +57,12 @@ import { DEFAULT_SHARE_CARD_LABELS } from "../components/share-card";
|
|
|
53
57
|
import { DEFAULT_REAUTH_DIALOG_LABELS } from "../components/reauth-dialog";
|
|
54
58
|
import { DEFAULT_SERVER_WAKE_LABELS } from "../components/server-wake";
|
|
55
59
|
import { DEFAULT_TRANSLATION_REVIEW_LABELS } from "../components/translation-review-labels";
|
|
60
|
+
import { DEFAULT_COUNTRY_SELECT_LABELS } from "../components/country-select";
|
|
61
|
+
import { DEFAULT_INLINE_EDIT_LABELS } from "../components/inline-edit-field";
|
|
62
|
+
import { DEFAULT_IBAN_INPUT_LABELS } from "../components/iban-input";
|
|
63
|
+
import { DEFAULT_PHONE_INPUT_LABELS } from "../components/phone-input";
|
|
64
|
+
import { DEFAULT_SIGN_CHIP_LABELS } from "../components/sign-chip";
|
|
65
|
+
import { DEFAULT_COLUMN_MAPPER_LABELS } from "../components/column-mapper";
|
|
56
66
|
import {
|
|
57
67
|
DEFAULT_APP_SHELL_LABELS,
|
|
58
68
|
DEFAULT_CALCULATOR_LABELS,
|
|
@@ -142,4 +152,11 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
142
152
|
serverWake: DEFAULT_SERVER_WAKE_LABELS,
|
|
143
153
|
translationReview: DEFAULT_TRANSLATION_REVIEW_LABELS,
|
|
144
154
|
legal: DEFAULT_LEGAL_LABELS,
|
|
155
|
+
characterCount: DEFAULT_CHARACTER_COUNT_LABELS,
|
|
156
|
+
countrySelect: DEFAULT_COUNTRY_SELECT_LABELS,
|
|
157
|
+
inlineEdit: DEFAULT_INLINE_EDIT_LABELS,
|
|
158
|
+
ibanInput: DEFAULT_IBAN_INPUT_LABELS,
|
|
159
|
+
phoneInput: DEFAULT_PHONE_INPUT_LABELS,
|
|
160
|
+
signChip: DEFAULT_SIGN_CHIP_LABELS,
|
|
161
|
+
columnMapper: DEFAULT_COLUMN_MAPPER_LABELS,
|
|
145
162
|
};
|
package/src/i18n/german.ts
CHANGED
|
@@ -28,6 +28,8 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
28
28
|
attachmentScreenshot: "Screenshot",
|
|
29
29
|
attachmentRemoveFile: (name) => `${name} entfernen`,
|
|
30
30
|
attachmentLimit: (max) => `Maximal ${n(max)} ${max === 1 ? "Anhang" : "Anhänge"} – zum Hinzufügen erst einen entfernen.`,
|
|
31
|
+
attachmentUploading: "Wird hochgeladen…",
|
|
32
|
+
attachmentUploadFailed: (name) => `„${name}“ konnte nicht hochgeladen werden`,
|
|
31
33
|
},
|
|
32
34
|
feedbackDialog: {
|
|
33
35
|
title: "Feedback senden",
|
|
@@ -210,6 +212,10 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
210
212
|
booleanTrue: "Ja",
|
|
211
213
|
booleanFalse: "Nein",
|
|
212
214
|
booleanUnset: "Nicht festgelegt",
|
|
215
|
+
sortBy: "Sortieren nach",
|
|
216
|
+
sortDefault: "Standardreihenfolge",
|
|
217
|
+
sortAscending: "Aufsteigend",
|
|
218
|
+
sortDescending: "Absteigend",
|
|
213
219
|
},
|
|
214
220
|
miniCalendar: {
|
|
215
221
|
previousMonth: "Vorheriger Monat",
|
|
@@ -249,6 +255,10 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
249
255
|
previousMonth: "Vorheriger Monat",
|
|
250
256
|
nextMonth: "Nächster Monat",
|
|
251
257
|
today: "Heute",
|
|
258
|
+
yearPanel: "Jahr wählen",
|
|
259
|
+
earlierYears: "Frühere Jahre",
|
|
260
|
+
laterYears: "Spätere Jahre",
|
|
261
|
+
thisYear: "Dieses Jahr",
|
|
252
262
|
},
|
|
253
263
|
popover: {
|
|
254
264
|
panel: "Aufklappfenster",
|
|
@@ -330,6 +340,10 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
330
340
|
prompt: "Dies kann nicht rückgängig gemacht werden.",
|
|
331
341
|
password: "Passwort",
|
|
332
342
|
phrase: (phrase) => `Geben Sie zur Bestätigung „${phrase}“ ein`,
|
|
343
|
+
acknowledge: "Ich habe gelesen, was dies bewirkt, und möchte fortfahren.",
|
|
344
|
+
needsPhrase: (phrase) => `Geben Sie zur Bestätigung „${phrase}“ ein`,
|
|
345
|
+
needsAcknowledge: "Setzen Sie zur Bestätigung das Häkchen",
|
|
346
|
+
needsPassword: "Geben Sie zur Bestätigung Ihr Passwort ein",
|
|
333
347
|
},
|
|
334
348
|
tabs: {
|
|
335
349
|
// "Tab" is what German UIs say; "Registerkarte" reads like a 1990s manual.
|
|
@@ -541,6 +555,8 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
541
555
|
form: {
|
|
542
556
|
save: "Speichern",
|
|
543
557
|
cancel: "Abbrechen",
|
|
558
|
+
// German keyboards label the key "Strg"; Apple's ⌘ is the same everywhere.
|
|
559
|
+
submitShortcut: (apple) => (apple ? "⌘ Enter" : "Strg+Enter"),
|
|
544
560
|
},
|
|
545
561
|
descriptionList: {
|
|
546
562
|
empty: "—",
|
|
@@ -651,6 +667,79 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
651
667
|
legal: {
|
|
652
668
|
navLabel: "Rechtliches",
|
|
653
669
|
},
|
|
670
|
+
characterCount: {
|
|
671
|
+
// "Zeichen" is the same in singular and plural.
|
|
672
|
+
count: (used, max) => `${n(used)} von ${n(max)} Zeichen`,
|
|
673
|
+
remaining: (left) => `Noch ${n(left)} Zeichen`,
|
|
674
|
+
limitReached: "Zeichenlimit erreicht",
|
|
675
|
+
},
|
|
676
|
+
countrySelect: {
|
|
677
|
+
country: "Land",
|
|
678
|
+
search: "Land suchen",
|
|
679
|
+
others: "Weitere Länder",
|
|
680
|
+
},
|
|
681
|
+
inlineEdit: {
|
|
682
|
+
edit: (label) => `${label} bearbeiten`,
|
|
683
|
+
failed: "Die Änderung konnte nicht gespeichert werden.",
|
|
684
|
+
empty: "Leer",
|
|
685
|
+
},
|
|
686
|
+
ibanInput: {
|
|
687
|
+
format: "Eine IBAN beginnt mit einem Ländercode aus zwei Buchstaben und zwei Prüfziffern.",
|
|
688
|
+
country: (code) => `„${code}“ ist kein Ländercode einer IBAN.`,
|
|
689
|
+
length: (actual, expected) =>
|
|
690
|
+
`Eine IBAN aus diesem Land hat ${n(expected)} Zeichen – diese hat ${n(actual)}.`,
|
|
691
|
+
checksum: "Die Prüfziffern stimmen nicht – vermutlich ist ein Zeichen falsch eingegeben.",
|
|
692
|
+
qrRequired: "Dies ist eine reguläre IBAN. Eine QR-Rechnung braucht die QR-IBAN des Kontos.",
|
|
693
|
+
qrNotAllowed:
|
|
694
|
+
"Dies ist eine QR-IBAN, die nur Zahlungen mit QR-Rechnung empfängt. Geben Sie die reguläre IBAN des Kontos ein.",
|
|
695
|
+
},
|
|
696
|
+
phoneInput: {
|
|
697
|
+
countryCode: "Ländervorwahl",
|
|
698
|
+
other: "Andere",
|
|
699
|
+
},
|
|
700
|
+
signChip: {
|
|
701
|
+
outflow: "Ausgabe",
|
|
702
|
+
inflow: "Einnahme",
|
|
703
|
+
direction: (current, next) => `Richtung: ${current} – zu ${next} wechseln`,
|
|
704
|
+
},
|
|
705
|
+
columnMapper: {
|
|
706
|
+
paste: "Tabelle einfügen",
|
|
707
|
+
pasteHint:
|
|
708
|
+
"Die Zeilen aus einer Tabellenkalkulation kopieren und hier einfügen – oder eine CSV- oder Textdatei hier ablegen.",
|
|
709
|
+
chooseFile: "Datei auswählen",
|
|
710
|
+
readError: (name) => `„${name}“ konnte nicht gelesen werden`,
|
|
711
|
+
headerRow: "Die erste Zeile enthält die Spaltennamen",
|
|
712
|
+
summary: (columns, rows) =>
|
|
713
|
+
`${n(columns)} ${columns === 1 ? "Spalte" : "Spalten"}, ${n(rows)} ${rows === 1 ? "Zeile" : "Zeilen"}`,
|
|
714
|
+
// Fragments of the summary line ("3 Spalten, 120 Zeilen · durch Semikolons getrennt").
|
|
715
|
+
separatorSemicolon: "durch Semikolons getrennt",
|
|
716
|
+
separatorComma: "durch Kommas getrennt",
|
|
717
|
+
separatorTab: "durch Tabulatoren getrennt",
|
|
718
|
+
separatorSpace: "durch Leerzeichen getrennt",
|
|
719
|
+
// The examples stay as written: they show the convention, not the reader's locale.
|
|
720
|
+
decimalComma: "Dezimalkomma (1,5)",
|
|
721
|
+
decimalPoint: "Dezimalpunkt (1.5)",
|
|
722
|
+
unreadCount: (count) =>
|
|
723
|
+
count === 1
|
|
724
|
+
? "1 Zeile konnte nicht gelesen werden"
|
|
725
|
+
: `${n(count)} Zeilen konnten nicht gelesen werden`,
|
|
726
|
+
// A line number is a position, not a count: unformatted, as `measuredGrid.lineError`.
|
|
727
|
+
unreadLine: (line) => `Zeile ${line} konnte nicht gelesen werden`,
|
|
728
|
+
unreadMore: (count) => `…und ${n(count)} weitere`,
|
|
729
|
+
noRows: "Keine Zeile dieses Textes lässt sich als Tabellenzeile lesen.",
|
|
730
|
+
table: "Spaltenzuordnung",
|
|
731
|
+
columnN: (column) => `Spalte ${column}`,
|
|
732
|
+
roleOf: (column) => `Was enthält die Spalte „${column}“?`,
|
|
733
|
+
ignore: "Ignorieren",
|
|
734
|
+
requiredRole: (role) => `${role} (erforderlich)`,
|
|
735
|
+
previewOf: (shown, total) =>
|
|
736
|
+
shown === 1
|
|
737
|
+
? `Die erste von ${n(total)} Zeilen`
|
|
738
|
+
: `Die ersten ${n(shown)} von ${n(total)} Zeilen`,
|
|
739
|
+
// `roles` arrives joined with "oder" ("Soll oder Haben").
|
|
740
|
+
oneOf: (roles) => `entweder ${roles}`,
|
|
741
|
+
missing: (roles) => `Noch erforderlich: ${roles}.`,
|
|
742
|
+
},
|
|
654
743
|
translationReview: {
|
|
655
744
|
statusMissing: "Fehlt",
|
|
656
745
|
statusUnreviewed: "Ungeprüft",
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import type { CalendarHeatmapLabels } from "../components/calendar-heatmap";
|
|
|
7
7
|
import type { PopoverLabels } from "../components/popover";
|
|
8
8
|
import type { ChipInputLabels } from "../components/chip";
|
|
9
9
|
import type { FieldSyncLabels } from "../components/field-sync";
|
|
10
|
-
import type { PasswordRevealLabels, TabsLabels } from "../components/ui";
|
|
10
|
+
import type { CharacterCountLabels, PasswordRevealLabels, TabsLabels } from "../components/ui";
|
|
11
11
|
import type { WizardLabels } from "../wizard/types";
|
|
12
12
|
import type { TourLabels } from "../tour/tour";
|
|
13
13
|
import type { CommandPaletteLabels } from "../search/command-palette";
|
|
@@ -53,6 +53,12 @@ import type { ShareCardLabels } from "../components/share-card";
|
|
|
53
53
|
import type { ReauthDialogLabels } from "../components/reauth-dialog";
|
|
54
54
|
import type { ServerWakeLabels } from "../components/server-wake";
|
|
55
55
|
import type { TranslationReviewLabels } from "../components/translation-review-labels";
|
|
56
|
+
import type { CountrySelectLabels } from "../components/country-select";
|
|
57
|
+
import type { InlineEditLabels } from "../components/inline-edit-field";
|
|
58
|
+
import type { IbanInputLabels } from "../components/iban-input";
|
|
59
|
+
import type { PhoneInputLabels } from "../components/phone-input";
|
|
60
|
+
import type { SignChipLabels } from "../components/sign-chip";
|
|
61
|
+
import type { ColumnMapperLabels } from "../components/column-mapper";
|
|
56
62
|
|
|
57
63
|
/**
|
|
58
64
|
* EVERY string the kit renders, as one typed tree — and an optional provider that
|
|
@@ -294,6 +300,21 @@ export interface UiKitLabels {
|
|
|
294
300
|
translationReview: TranslationReviewLabels;
|
|
295
301
|
/** 0.19.0: `LegalLinks`' navigation name — the legal pages' shell, for every app. */
|
|
296
302
|
legal: LegalLabels;
|
|
303
|
+
/** 0.22.0: the screen-reader words of `Input` / `Textarea`'s `showCount` counter. */
|
|
304
|
+
characterCount: CharacterCountLabels;
|
|
305
|
+
/** 0.22.0: `CountrySelect`. Its list's "no results" and counts are `combobox`'s. */
|
|
306
|
+
countrySelect: CountrySelectLabels;
|
|
307
|
+
/** 0.22.0: `InlineEditField` — keksdose K9. */
|
|
308
|
+
inlineEdit: InlineEditLabels;
|
|
309
|
+
/** 0.22.0: `IbanInput`'s messages, one per problem. */
|
|
310
|
+
ibanInput: IbanInputLabels;
|
|
311
|
+
/** 0.22.0: `PhoneInput`'s country-code select. */
|
|
312
|
+
phoneInput: PhoneInputLabels;
|
|
313
|
+
/** 0.22.0: `SignChip` — the outflow / inflow toggle beside an amount. */
|
|
314
|
+
signChip: SignChipLabels;
|
|
315
|
+
/** 0.23.0: `ColumnMapper` / `ColumnRoleTable` — a pasted or dropped table, a role per
|
|
316
|
+
* column. */
|
|
317
|
+
columnMapper: ColumnMapperLabels;
|
|
297
318
|
}
|
|
298
319
|
|
|
299
320
|
/**
|
|
@@ -438,8 +459,40 @@ interface KitI18n {
|
|
|
438
459
|
weekStartsOn?: WeekDay;
|
|
439
460
|
linkComponent?: KitLinkComponent;
|
|
440
461
|
chartTooltipPlacement?: ChartTooltipPlacement;
|
|
462
|
+
formatDate?: KitDateFormatter;
|
|
441
463
|
}
|
|
442
464
|
|
|
465
|
+
/** What a {@link KitDateFormatter} is told about the date it is formatting. */
|
|
466
|
+
export interface KitDateFormatContext {
|
|
467
|
+
/**
|
|
468
|
+
* How much of a date the string is: `"day"` — the `iso` is `"YYYY-MM-DD"`; `"month"` —
|
|
469
|
+
* `"YYYY-MM"` (a `MonthPicker`); `"year"` — `"YYYY"` (`MonthPicker mode="year"`).
|
|
470
|
+
*/
|
|
471
|
+
unit: "day" | "month" | "year";
|
|
472
|
+
/** Which kit surface asks: the trigger of a `DatePicker`, of a `DateRangePicker`
|
|
473
|
+
* (called once per end), of a `MonthPicker`, or a `DateMark`. */
|
|
474
|
+
source: "datePicker" | "dateRangePicker" | "monthPicker" | "dateMark";
|
|
475
|
+
/** The locale the kit would have formatted in — the component's `locale` prop, else
|
|
476
|
+
* the provider's — or `undefined` for the runtime's. */
|
|
477
|
+
locale: string | undefined;
|
|
478
|
+
/**
|
|
479
|
+
* The kit's suggestion: whether this date stands alone where the day of the week
|
|
480
|
+
* helps — `true` for a `DatePicker`'s trigger and a `DateMark`, `false` for the two
|
|
481
|
+
* ends of a range (twice the width, in a sentence-like "from – to") and for a month
|
|
482
|
+
* or a year. keksdose's rule, which this follows: *"A date in a COLUMN carries the
|
|
483
|
+
* weekday. A date in a SENTENCE does not."* (date-cell.tsx, dev#546). A formatter
|
|
484
|
+
* is free to ignore it.
|
|
485
|
+
*/
|
|
486
|
+
weekday: boolean;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* The app's ONE answer to "what does a date look like here" — keksdose K12. Returns the
|
|
491
|
+
* text for an ISO date (see {@link KitDateFormatContext.unit} for the three shapes).
|
|
492
|
+
* Return `""` to fall back to the kit's own formatting for that date.
|
|
493
|
+
*/
|
|
494
|
+
export type KitDateFormatter = (iso: string, context: KitDateFormatContext) => string;
|
|
495
|
+
|
|
443
496
|
/** Where a chart shows the values under the pointer — see `SeriesChartTooltip.placement`. */
|
|
444
497
|
export type ChartTooltipPlacement = "cursor" | "above" | "below" | "auto";
|
|
445
498
|
|
|
@@ -506,6 +559,25 @@ export interface UiKitProviderProps {
|
|
|
506
559
|
* pointer, as before.
|
|
507
560
|
*/
|
|
508
561
|
chartTooltipPlacement?: ChartTooltipPlacement;
|
|
562
|
+
/**
|
|
563
|
+
* How every kit date below is written, when the component is not told otherwise —
|
|
564
|
+
* keksdose K12. keksdose has a date-format preference (Settings ▸ date format, feedback
|
|
565
|
+
* #180) that is not the UI language, and a weekday in the UI language beside digits
|
|
566
|
+
* in the preference's order (live #246, dev#546): two locales in one string, which
|
|
567
|
+
* no `locale` + `Intl` options can say. Its `DateField` wraps every `DatePicker` to
|
|
568
|
+
* pass `formatValue`, and its report range field formats both ends by hand; with this
|
|
569
|
+
* set once on the provider, those wrappers thin to nothing.
|
|
570
|
+
*
|
|
571
|
+
* Used for the trigger text of `DatePicker`, `DateRangePicker` and `MonthPicker`
|
|
572
|
+
* (month and year mode), and for `DateMark display="date"`. A component's own
|
|
573
|
+
* `formatValue` or `formatOptions` (`dateStyle` on a DateMark) wins: prop >
|
|
574
|
+
* provider > the kit's `Intl` default, the order every kit setting resolves in. Left
|
|
575
|
+
* out (or returning `""`), everything formats exactly as before.
|
|
576
|
+
*
|
|
577
|
+
* Keep it stable (`useCallback`, or a module function): it is in the provider's
|
|
578
|
+
* context value, so a new function every render re-renders every consumer.
|
|
579
|
+
*/
|
|
580
|
+
formatDate?: KitDateFormatter;
|
|
509
581
|
children: ReactNode;
|
|
510
582
|
}
|
|
511
583
|
|
|
@@ -523,6 +595,7 @@ export function UiKitProvider({
|
|
|
523
595
|
weekStartsOn,
|
|
524
596
|
linkComponent,
|
|
525
597
|
chartTooltipPlacement,
|
|
598
|
+
formatDate,
|
|
526
599
|
children,
|
|
527
600
|
}: UiKitProviderProps) {
|
|
528
601
|
const outer = useContext(KitI18nContext);
|
|
@@ -533,8 +606,9 @@ export function UiKitProvider({
|
|
|
533
606
|
labels: mergeOverrides(outer.labels, labels),
|
|
534
607
|
linkComponent: linkComponent ?? outer.linkComponent,
|
|
535
608
|
chartTooltipPlacement: chartTooltipPlacement ?? outer.chartTooltipPlacement,
|
|
609
|
+
formatDate: formatDate ?? outer.formatDate,
|
|
536
610
|
}),
|
|
537
|
-
[outer, labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement],
|
|
611
|
+
[outer, labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement, formatDate],
|
|
538
612
|
);
|
|
539
613
|
return <KitI18nContext.Provider value={value}>{children}</KitI18nContext.Provider>;
|
|
540
614
|
}
|
|
@@ -627,6 +701,12 @@ export function useKitChartTooltipPlacement(): ChartTooltipPlacement | undefined
|
|
|
627
701
|
return useContext(KitI18nContext).chartTooltipPlacement;
|
|
628
702
|
}
|
|
629
703
|
|
|
704
|
+
/** The nearest provider's `formatDate`, or `undefined` — a component's own
|
|
705
|
+
* `formatValue` / `formatOptions` wins over it. See {@link KitDateFormatter}. */
|
|
706
|
+
export function useKitDateFormatter(): KitDateFormatter | undefined {
|
|
707
|
+
return useContext(KitI18nContext).formatDate;
|
|
708
|
+
}
|
|
709
|
+
|
|
630
710
|
/** {@link DEFAULT_FILE_LABELS}, but formatting in the provider's locale. */
|
|
631
711
|
export function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels {
|
|
632
712
|
const locale = useKitLocale();
|
package/src/i18n/locales/en.ts
CHANGED
|
@@ -33,7 +33,9 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
|
|
|
33
33
|
...d.measuredGrid,
|
|
34
34
|
points: (count) => (count === 1 ? "1 point" : `${n(count)} points`),
|
|
35
35
|
problems: (count) =>
|
|
36
|
-
count === 1
|
|
36
|
+
count === 1
|
|
37
|
+
? "1 cell is not a number"
|
|
38
|
+
: `${n(count)} cells are not numbers`,
|
|
37
39
|
},
|
|
38
40
|
dataTable: {
|
|
39
41
|
...d.dataTable,
|
|
@@ -51,9 +53,12 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
|
|
|
51
53
|
combobox: {
|
|
52
54
|
...d.combobox,
|
|
53
55
|
selectedCount: (count) => `${n(count)} selected`,
|
|
54
|
-
resultCount: (count) =>
|
|
56
|
+
resultCount: (count) =>
|
|
57
|
+
count === 1 ? "1 result" : `${n(count)} results`,
|
|
55
58
|
minChars: (count) =>
|
|
56
|
-
count === 1
|
|
59
|
+
count === 1
|
|
60
|
+
? "Type at least 1 character"
|
|
61
|
+
: `Type at least ${n(count)} characters`,
|
|
57
62
|
},
|
|
58
63
|
multiSelect: {
|
|
59
64
|
...d.multiSelect,
|
|
@@ -81,7 +86,9 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
|
|
|
81
86
|
`“${name}” was not added: at most ${n(maxFiles)} ${maxFiles === 1 ? "file" : "files"}`,
|
|
82
87
|
rejectedMany: (count) => `${n(count)} files were not added`,
|
|
83
88
|
rejectedPick: (count) =>
|
|
84
|
-
count === 1
|
|
89
|
+
count === 1
|
|
90
|
+
? "The file was not added"
|
|
91
|
+
: `None of the ${n(count)} files were added`,
|
|
85
92
|
selected: (count, firstName) =>
|
|
86
93
|
count === 1 ? `“${firstName}” selected` : `${n(count)} files selected`,
|
|
87
94
|
},
|
|
@@ -120,5 +127,30 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
|
|
|
120
127
|
localeProgress: (approved, total) => `${n(approved)}/${n(total)}`,
|
|
121
128
|
exportCorrections: (count) => `Export corrections (${n(count)})`,
|
|
122
129
|
},
|
|
130
|
+
characterCount: {
|
|
131
|
+
...d.characterCount,
|
|
132
|
+
count: (used, max) => `${n(used)} of ${n(max)} characters`,
|
|
133
|
+
remaining: (left) =>
|
|
134
|
+
left === 1 ? "1 character left" : `${n(left)} characters left`,
|
|
135
|
+
},
|
|
136
|
+
ibanInput: {
|
|
137
|
+
...d.ibanInput,
|
|
138
|
+
length: (actual, expected) =>
|
|
139
|
+
`An IBAN from this country has ${n(expected)} characters — this one has ${n(actual)}.`,
|
|
140
|
+
},
|
|
141
|
+
columnMapper: {
|
|
142
|
+
...d.columnMapper,
|
|
143
|
+
summary: (columns, rows) =>
|
|
144
|
+
`${n(columns)} ${columns === 1 ? "column" : "columns"}, ${n(rows)} ${rows === 1 ? "row" : "rows"}`,
|
|
145
|
+
unreadCount: (count) =>
|
|
146
|
+
count === 1
|
|
147
|
+
? "1 line could not be read"
|
|
148
|
+
: `${n(count)} lines could not be read`,
|
|
149
|
+
unreadMore: (count) => `…and ${n(count)} more`,
|
|
150
|
+
previewOf: (shown, total) =>
|
|
151
|
+
shown === 1
|
|
152
|
+
? `The first of ${n(total)} rows`
|
|
153
|
+
: `The first ${n(shown)} of ${n(total)} rows`,
|
|
154
|
+
},
|
|
123
155
|
};
|
|
124
156
|
}
|