@eifi1/ui-kit 0.22.0 → 0.24.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 (208) hide show
  1. package/README.md +42 -25
  2. package/dist/chart.d.ts +8 -7
  3. package/dist/components/amount-input.d.ts +34 -7
  4. package/dist/components/amount-input.js +14 -10
  5. package/dist/components/amount-input.js.map +1 -1
  6. package/dist/components/autocomplete.d.ts +26 -0
  7. package/dist/components/autocomplete.js +164 -137
  8. package/dist/components/autocomplete.js.map +1 -1
  9. package/dist/components/button-group.d.ts +8 -7
  10. package/dist/components/calculator.d.ts +8 -7
  11. package/dist/components/column-mapper.d.ts +235 -0
  12. package/dist/components/column-mapper.js +411 -0
  13. package/dist/components/column-mapper.js.map +1 -0
  14. package/dist/components/combobox-core.d.ts +39 -2
  15. package/dist/components/combobox-core.js +29 -6
  16. package/dist/components/combobox-core.js.map +1 -1
  17. package/dist/components/combobox.d.ts +113 -3
  18. package/dist/components/combobox.js +456 -313
  19. package/dist/components/combobox.js.map +1 -1
  20. package/dist/components/confirm-dialog.d.ts +8 -7
  21. package/dist/components/confirm-dialog.js +7 -8
  22. package/dist/components/confirm-dialog.js.map +1 -1
  23. package/dist/components/copy-button.d.ts +5 -4
  24. package/dist/components/country-select.d.ts +50 -7
  25. package/dist/components/country-select.js +65 -9
  26. package/dist/components/country-select.js.map +1 -1
  27. package/dist/components/danger-confirm.d.ts +8 -7
  28. package/dist/components/danger-confirm.js +64 -7
  29. package/dist/components/danger-confirm.js.map +1 -1
  30. package/dist/components/date-picker.d.ts +41 -0
  31. package/dist/components/date-picker.js +269 -8
  32. package/dist/components/date-picker.js.map +1 -1
  33. package/dist/components/entity-combobox.d.ts +48 -2
  34. package/dist/components/entity-combobox.js +159 -114
  35. package/dist/components/entity-combobox.js.map +1 -1
  36. package/dist/components/facing-pair.d.ts +8 -7
  37. package/dist/components/field-parts.d.ts +116 -6
  38. package/dist/components/field-parts.js +84 -0
  39. package/dist/components/field-parts.js.map +1 -1
  40. package/dist/components/field-strip.d.ts +105 -0
  41. package/dist/components/field-strip.js +46 -0
  42. package/dist/components/field-strip.js.map +1 -0
  43. package/dist/components/file-button.d.ts +8 -7
  44. package/dist/components/file-dropzone.d.ts +8 -7
  45. package/dist/components/form-actions.d.ts +8 -7
  46. package/dist/components/form-actions.js +23 -3
  47. package/dist/components/form-actions.js.map +1 -1
  48. package/dist/components/iban-input.d.ts +8 -7
  49. package/dist/components/icon-picker.d.ts +10 -1
  50. package/dist/components/icon-picker.js +12 -4
  51. package/dist/components/icon-picker.js.map +1 -1
  52. package/dist/components/language-select.d.ts +8 -7
  53. package/dist/components/money-field.d.ts +11 -7
  54. package/dist/components/money-field.js.map +1 -1
  55. package/dist/components/month-picker.js +2 -1
  56. package/dist/components/month-picker.js.map +1 -1
  57. package/dist/components/multi-entity-combobox.d.ts +31 -2
  58. package/dist/components/multi-entity-combobox.js +152 -103
  59. package/dist/components/multi-entity-combobox.js.map +1 -1
  60. package/dist/components/number-field.d.ts +8 -7
  61. package/dist/components/number-input.d.ts +8 -7
  62. package/dist/components/numpad-sheet.d.ts +8 -7
  63. package/dist/components/phone-input.d.ts +8 -7
  64. package/dist/components/reauth-dialog.d.ts +2 -2
  65. package/dist/components/reauth-dialog.js +6 -8
  66. package/dist/components/reauth-dialog.js.map +1 -1
  67. package/dist/components/series-chart.d.ts +8 -7
  68. package/dist/components/settings-fields.d.ts +8 -7
  69. package/dist/components/share-card.d.ts +8 -7
  70. package/dist/components/swatch-picker.d.ts +17 -1
  71. package/dist/components/swatch-picker.js +10 -4
  72. package/dist/components/swatch-picker.js.map +1 -1
  73. package/dist/components/text-link.d.ts +8 -7
  74. package/dist/components/tile-radio.d.ts +20 -0
  75. package/dist/components/tile-radio.js +25 -8
  76. package/dist/components/tile-radio.js.map +1 -1
  77. package/dist/components/time-input.d.ts +8 -7
  78. package/dist/components/toggle-group.js +2 -2
  79. package/dist/components/toggle-group.js.map +1 -1
  80. package/dist/components/trigger-aria.d.ts +3 -0
  81. package/dist/components/trigger-aria.js +3 -1
  82. package/dist/components/trigger-aria.js.map +1 -1
  83. package/dist/components/ui.d.ts +5 -4
  84. package/dist/components/ui.js +60 -28
  85. package/dist/components/ui.js.map +1 -1
  86. package/dist/feedback/feedback-attachment.d.ts +66 -1
  87. package/dist/feedback/feedback-attachment.js +185 -65
  88. package/dist/feedback/feedback-attachment.js.map +1 -1
  89. package/dist/feedback/feedback-dialog.d.ts +66 -1
  90. package/dist/feedback/feedback-dialog.js.map +1 -1
  91. package/dist/feedback/feedback-inbox.d.ts +67 -252
  92. package/dist/feedback/feedback-inbox.js.map +1 -1
  93. package/dist/feedback/feedback-thread.d.ts +66 -287
  94. package/dist/feedback/feedback-thread.js +16 -3
  95. package/dist/feedback/feedback-thread.js.map +1 -1
  96. package/dist/{kit-labels-R8mIAc9N.d.ts → feedback-DOwPu-Il.d.ts} +1273 -30
  97. package/dist/feedback.d.ts +65 -3
  98. package/dist/hooks/use-file-drop.d.ts +8 -7
  99. package/dist/i18n/defaults.d.ts +8 -7
  100. package/dist/i18n/defaults.js +3 -1
  101. package/dist/i18n/defaults.js.map +1 -1
  102. package/dist/i18n/german.d.ts +8 -7
  103. package/dist/i18n/german.js +36 -2
  104. package/dist/i18n/german.js.map +1 -1
  105. package/dist/i18n/kit-labels.d.ts +5 -4
  106. package/dist/i18n/kit-labels.js.map +1 -1
  107. package/dist/i18n/languages.d.ts +8 -7
  108. package/dist/i18n/locales/de-CH.d.ts +8 -7
  109. package/dist/i18n/locales/en.d.ts +8 -7
  110. package/dist/i18n/locales/en.js +7 -0
  111. package/dist/i18n/locales/en.js.map +1 -1
  112. package/dist/i18n/locales/es.d.ts +8 -7
  113. package/dist/i18n/locales/es.js +37 -2
  114. package/dist/i18n/locales/es.js.map +1 -1
  115. package/dist/i18n/locales/fr.d.ts +8 -7
  116. package/dist/i18n/locales/fr.js +36 -2
  117. package/dist/i18n/locales/fr.js.map +1 -1
  118. package/dist/i18n/locales/hu.d.ts +8 -7
  119. package/dist/i18n/locales/hu.js +38 -2
  120. package/dist/i18n/locales/hu.js.map +1 -1
  121. package/dist/i18n/locales/it.d.ts +8 -7
  122. package/dist/i18n/locales/it.js +37 -2
  123. package/dist/i18n/locales/it.js.map +1 -1
  124. package/dist/i18n/locales/zh.d.ts +8 -7
  125. package/dist/i18n/locales/zh.js +34 -2
  126. package/dist/i18n/locales/zh.js.map +1 -1
  127. package/dist/i18n/review.d.ts +8 -7
  128. package/dist/i18n/review.js +20 -1
  129. package/dist/i18n/review.js.map +1 -1
  130. package/dist/index.d.ts +6 -4
  131. package/dist/index.js +16 -0
  132. package/dist/index.js.map +1 -1
  133. package/dist/lib/column-mapping.d.ts +81 -0
  134. package/dist/lib/column-mapping.js +108 -0
  135. package/dist/lib/column-mapping.js.map +1 -0
  136. package/dist/lib/strip-fade.d.ts +2 -2
  137. package/dist/lib/strip-fade.js.map +1 -1
  138. package/dist/lib/table-text.d.ts +109 -1
  139. package/dist/lib/table-text.js +122 -1
  140. package/dist/lib/table-text.js.map +1 -1
  141. package/dist/rhf/fields.d.ts +142 -13
  142. package/dist/rhf/fields.js +185 -3
  143. package/dist/rhf/fields.js.map +1 -1
  144. package/dist/rhf/form.d.ts +8 -7
  145. package/dist/rhf.d.ts +9 -8
  146. package/dist/rhf.js.map +1 -1
  147. package/dist/shell/app-shell.d.ts +8 -7
  148. package/dist/shell/top-bar-brand.d.ts +8 -7
  149. package/dist/shell/topbar-action-menu.d.ts +36 -3
  150. package/dist/shell/topbar-action-menu.js +74 -33
  151. package/dist/shell/topbar-action-menu.js.map +1 -1
  152. package/dist/shell.d.ts +8 -7
  153. package/dist/table-text.d.ts +1 -1
  154. package/dist/wizard/stepper-nav.d.ts +38 -9
  155. package/dist/wizard/stepper-nav.js +4 -0
  156. package/dist/wizard/stepper-nav.js.map +1 -1
  157. package/dist/wizard.d.ts +8 -7
  158. package/package.json +1 -1
  159. package/src/components/amount-input.tsx +50 -14
  160. package/src/components/autocomplete.tsx +188 -116
  161. package/src/components/column-mapper.tsx +768 -0
  162. package/src/components/combobox-core.tsx +82 -9
  163. package/src/components/combobox.tsx +655 -335
  164. package/src/components/confirm-dialog.tsx +12 -8
  165. package/src/components/country-select.tsx +135 -22
  166. package/src/components/danger-confirm.tsx +274 -24
  167. package/src/components/date-picker.tsx +423 -10
  168. package/src/components/entity-combobox.tsx +243 -129
  169. package/src/components/field-parts.tsx +224 -5
  170. package/src/components/field-strip.tsx +149 -0
  171. package/src/components/form-actions.tsx +52 -5
  172. package/src/components/icon-picker.tsx +23 -4
  173. package/src/components/money-field.tsx +3 -0
  174. package/src/components/month-picker.tsx +2 -1
  175. package/src/components/multi-entity-combobox.tsx +221 -121
  176. package/src/components/reauth-dialog.tsx +17 -18
  177. package/src/components/swatch-picker.tsx +29 -5
  178. package/src/components/tile-radio.tsx +74 -13
  179. package/src/components/toggle-group.tsx +2 -2
  180. package/src/components/trigger-aria.ts +5 -0
  181. package/src/components/ui.tsx +114 -33
  182. package/src/feedback/feedback-attachment.tsx +372 -72
  183. package/src/feedback/feedback-dialog.tsx +7 -3
  184. package/src/feedback/feedback-inbox.tsx +3 -2
  185. package/src/feedback/feedback-thread.tsx +112 -9
  186. package/src/i18n/defaults.ts +2 -0
  187. package/src/i18n/german.ts +42 -0
  188. package/src/i18n/kit-labels.tsx +4 -0
  189. package/src/i18n/locales/en.ts +27 -5
  190. package/src/i18n/locales/es.ts +41 -0
  191. package/src/i18n/locales/fr.ts +42 -0
  192. package/src/i18n/locales/hu.ts +37 -0
  193. package/src/i18n/locales/it.ts +41 -0
  194. package/src/i18n/locales/zh.ts +32 -0
  195. package/src/i18n/review.ts +19 -0
  196. package/src/index.ts +18 -0
  197. package/src/lib/column-mapping.ts +234 -0
  198. package/src/lib/strip-fade.ts +2 -2
  199. package/src/lib/table-text.ts +271 -0
  200. package/src/rhf/fields.tsx +421 -6
  201. package/src/rhf.ts +2 -1
  202. package/src/shell/topbar-action-menu.tsx +134 -38
  203. package/src/wizard/stepper-nav.tsx +35 -1
  204. package/dist/components/field-anatomy.d.ts +0 -95
  205. package/dist/components/field-anatomy.js +0 -84
  206. package/dist/components/field-anatomy.js.map +0 -1
  207. package/dist/feedback-attachment-NhUm7Zga.d.ts +0 -367
  208. package/src/components/field-anatomy.tsx +0 -190
@@ -1,367 +0,0 @@
1
- import * as react from 'react';
2
- import { ReactNode, RefObject } from 'react';
3
-
4
- interface FeedbackCategoryOption {
5
- value: string;
6
- label: string;
7
- }
8
- interface FeedbackAttachmentLabels {
9
- /** The heading over the field. Optional: a note editor puts the buttons
10
- * straight under its textarea, where a second heading is noise. */
11
- attachment?: string;
12
- attachmentAdd: string;
13
- /** Label for the "capture screenshot" button. Optional — falls back to the provider's
14
- * `feedbackAttachment.attachmentCapture`, then English. */
15
- attachmentCapture?: string;
16
- /** The line under the attachment buttons saying a screenshot can be pasted
17
- * straight in. Optional — falls back to the provider's `feedbackAttachment`, then English. */
18
- attachmentPaste?: string;
19
- attachmentRemove: string;
20
- /** `multiple` mode (0.15.5): the chip list's accessible name — and, in
21
- * `<FeedbackDialog attachments="multiple">`, the heading over it (0.16.0). Optional,
22
- * as are the keys below — they fall back to the provider's `feedbackAttachment`,
23
- * then English. */
24
- attachmentList?: string;
25
- /** `multiple` mode (0.16.0): the screenshot slot's chip title, in place of its file name. */
26
- attachmentScreenshot?: string;
27
- /** `multiple` mode: one chip's remove button, given the file's name. */
28
- attachmentRemoveFile?: (name: string) => string;
29
- /** `multiple` mode: the line shown once `max` files are attached. */
30
- attachmentLimit?: (max: number) => string;
31
- /** `refs` mode (0.22.0): the second line of a chip whose upload is still running. */
32
- attachmentUploading?: string;
33
- }
34
- interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
35
- title: string;
36
- category: string;
37
- subject: string;
38
- body: string;
39
- attachment: string;
40
- submitHint: string;
41
- cancel: string;
42
- save: string;
43
- /** @deprecated since 0.16.0 — use `attachmentList`, the one key for the heading and
44
- * the chip list it names. Still honoured: set, it wins over the provider's
45
- * `attachmentList` (but not over an `attachmentList` passed beside it). */
46
- attachments?: string;
47
- }
48
- /**
49
- * The dialog's own strings — the `feedbackDialog` namespace of `<UiKitProvider
50
- * labels>` (0.12.0). The attachment keys are not repeated here: they are the
51
- * `feedbackAttachment` namespace's, which the field inside already reads.
52
- *
53
- * Before, every key was required at the call site, so each app restated ten strings
54
- * wherever it mounted the dialog (kastlan feedback-button.tsx built the object inline).
55
- */
56
- interface FeedbackDialogTextLabels {
57
- title: string;
58
- category: string;
59
- subject: string;
60
- body: string;
61
- /** The body's label when `requireBody={false}`: "What happened? (optional)".
62
- * Optional, so a complete `UiKitLabels` typed before 0.14.2 still compiles. */
63
- bodyOptional?: string;
64
- /** The heading over the attachment buttons. */
65
- attachment: string;
66
- /** The line beside the buttons naming the shortcut. */
67
- submitHint: string;
68
- cancel: string;
69
- save: string;
70
- /** @deprecated since 0.16.0 — use `feedbackAttachment.attachmentList`. It and this
71
- * key were two names for one heading ("Attachments" over the chips that list names
72
- * "Attachments"), and a catalogue that translated one left the other English. Still
73
- * honoured when set: it wins over `feedbackAttachment.attachmentList`. */
74
- attachments?: string;
75
- }
76
- declare const DEFAULT_FEEDBACK_DIALOG_LABELS: FeedbackDialogTextLabels;
77
- interface FeedbackSubmission {
78
- title: string;
79
- body: string;
80
- category: string;
81
- attachment: File | null;
82
- }
83
- /**
84
- * What `onSubmit` gets under `attachments="multiple"` (0.15.5): the screenshot and
85
- * the picked/pasted files apart, because an app files them differently — keksdose
86
- * attaches the screenshot to the report and the photos as its evidence list. No
87
- * `attachment`: there is no one file it could name.
88
- */
89
- interface FeedbackMultipleSubmission {
90
- title: string;
91
- body: string;
92
- category: string;
93
- /** What `onCaptureScreenshot` returned, or `null` — at most one. */
94
- screenshot: File | null;
95
- /** Picked and pasted files, in the order they were added; at most `maxAttachments`. */
96
- attachments: File[];
97
- }
98
- interface FeedbackDialogBaseProps {
99
- open: boolean;
100
- onClose: () => void;
101
- categories: FeedbackCategoryOption[];
102
- category: string;
103
- onCategoryChange: (value: string) => void;
104
- /** Prop > `<UiKitProvider labels={{ feedbackDialog, feedbackAttachment }}>` >
105
- * English. Optional since 0.12.0; a whole `FeedbackDialogLabels` still fits. */
106
- labels?: Partial<FeedbackDialogLabels>;
107
- submitting?: boolean;
108
- contextSlot?: ReactNode;
109
- attachmentAccept?: string[];
110
- maxAttachmentBytes?: number;
111
- /**
112
- * Optional: capture a screenshot of the underlying app view and return it as a
113
- * File. When provided, a "Capture screenshot" button is shown next to "Add
114
- * attachment"; the returned file is fed through the same validation + preview.
115
- */
116
- onCaptureScreenshot?: () => Promise<File | null>;
117
- /**
118
- * `false`: a report with only a subject can be sent, and the body's label says it is
119
- * optional (`bodyOptional`, unless `labels.body` is passed). Default `true`, the
120
- * dialog's behaviour until 0.14.2 (keksdose K1: its backend takes title-only reports).
121
- */
122
- requireBody?: boolean;
123
- }
124
- /** One attachment — a screenshot, a picked or a pasted image; a second replaces
125
- * nothing, it has to wait for the first to be removed. The default. */
126
- interface FeedbackDialogSingleProps extends FeedbackDialogBaseProps {
127
- attachments?: "single";
128
- onSubmit: (data: FeedbackSubmission) => void | Promise<void>;
129
- onAttachmentError?: (kind: "type" | "size") => void;
130
- /** Multiple mode only — refused here rather than silently ignored. */
131
- maxAttachments?: never;
132
- }
133
- /**
134
- * One screenshot plus up to `maxAttachments` picked or pasted files (0.15.5, keksdose
135
- * dev#578: "pasted two photos, the second overwrote the first"). The capture button is
136
- * offered while there is no screenshot yet; the add button while there is room.
137
- */
138
- interface FeedbackDialogMultipleProps extends FeedbackDialogBaseProps {
139
- attachments: "multiple";
140
- /** Default `DEFAULT_MAX_ATTACHMENTS` (5). The screenshot does not count. */
141
- maxAttachments?: number;
142
- onSubmit: (data: FeedbackMultipleSubmission) => void | Promise<void>;
143
- /** `"count"`: more files arrived at once than there was room for; the surplus was dropped. */
144
- onAttachmentError?: (kind: FeedbackAttachmentError) => void;
145
- }
146
- type FeedbackDialogProps = FeedbackDialogSingleProps | FeedbackDialogMultipleProps;
147
- /**
148
- * The generic feedback form dialog: category + subject + body + an optional image
149
- * attachment, with Ctrl/Cmd+Enter to submit. Domain-free — the app supplies the
150
- * category options, labels and an `onSubmit` that talks to its own backend, plus
151
- * an optional `contextSlot` for app-specific context (user, current URL, …).
152
- *
153
- * **A screenshot can be pasted straight in.** Ctrl/Cmd+V anywhere in the dialog
154
- * takes an image off the clipboard and makes it the attachment, through the same
155
- * validation and the same preview as the file picker. It is the gesture the two
156
- * ways in did not cover: `onCaptureScreenshot` snapshots the *whole* app view,
157
- * and the file picker needs a file — so somebody who wanted to show one panel,
158
- * or one region of one, had to save a crop to disk first and then find it again
159
- * (Steering Design feedback #39). The clipboard is where a region snip already
160
- * is on every platform.
161
- *
162
- * **`attachments="multiple"`** (0.15.5) takes several: every paste and pick adds a
163
- * chip, and `onSubmit` gets `{ screenshot, attachments }` ({@link
164
- * FeedbackMultipleSubmission}) instead of `attachment`. Opt-in, so a caller typed
165
- * against the one-file submission keeps compiling and behaving as before.
166
- */
167
- declare function FeedbackDialog(props: FeedbackDialogProps): react.JSX.Element | null;
168
-
169
- declare const DEFAULT_ATTACHMENT_ACCEPT: string[];
170
- declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
171
- /** How many files `<FeedbackAttachmentField multiple>` takes when `max` is not given
172
- * (0.15.5). Five photos of one problem is already a lot to look through; a report
173
- * that needs more is a conversation, which the thread is for. */
174
- declare const DEFAULT_MAX_ATTACHMENTS = 5;
175
- /**
176
- * The field's own strings — the `feedbackAttachment` namespace of `<UiKitProvider
177
- * labels>`. The keys are {@link FeedbackAttachmentLabels}' own, so the `labels` prop
178
- * (and a whole `FeedbackDialogLabels` handed down by the dialog) merges over them
179
- * one to one. The optional heading, `attachment`, stays a prop: a note editor puts
180
- * the buttons straight under its textarea and wants none.
181
- *
182
- * Before 0.7.0 the two optional keys fell back to hard-coded English, so a German app
183
- * that relied on its provider got "Capture screenshot" under a German form.
184
- *
185
- * The three keys added in 0.15.5 for `multiple` mode are optional, so a complete
186
- * `UiKitLabels` typed before it still compiles; they fall back to English. So is
187
- * `attachmentScreenshot` (0.16.0).
188
- */
189
- interface FeedbackAttachmentFieldLabels {
190
- attachmentAdd: string;
191
- attachmentCapture: string;
192
- attachmentPaste: string;
193
- attachmentRemove: string;
194
- /** `multiple` mode: the accessible name of the list of chosen files — and, since
195
- * 0.16.0, the heading `<FeedbackDialog attachments="multiple">` puts over it. */
196
- attachmentList?: string;
197
- /** `multiple` mode (0.16.0): what the screenshot slot's chip is called — "Screenshot"
198
- * rather than the capture's file name ("screenshot.webp"), which says nothing a
199
- * reporter chose. The file name stays as the chip's second line. */
200
- attachmentScreenshot?: string;
201
- /** `multiple` mode: one chip's remove button — "Remove photo.jpg". Named per file,
202
- * because five buttons all called "Remove attachment" say nothing about which. */
203
- attachmentRemoveFile?: (name: string) => string;
204
- /** `multiple` mode: the line shown in place of the add buttons once `max` is reached. */
205
- attachmentLimit?: (max: number) => string;
206
- /** `refs` mode (0.22.0, keksdose K16): the second line of a chip whose upload is still
207
- * running, where an uploaded one shows its size — "Uploading…". Optional, like the
208
- * keys above, so a `UiKitLabels` typed before it still compiles. */
209
- attachmentUploading?: string;
210
- }
211
- declare const DEFAULT_FEEDBACK_ATTACHMENT_LABELS: FeedbackAttachmentFieldLabels;
212
- /** Why a file was turned away. `"count"` only ever comes from `multiple` (and `refs`)
213
- * mode: more files arrived at once than `max` left room for, and the surplus was
214
- * dropped. */
215
- type FeedbackAttachmentError = "type" | "size" | "count";
216
- /** `refs` mode's reasons: {@link FeedbackAttachmentError}, or `"upload"` — `onUpload`
217
- * rejected, and what it rejected with is `onError`'s second argument. A type of its
218
- * own so a host's exhaustive switch over the File modes' reasons stays exhaustive. */
219
- type FeedbackAttachmentRefsError = FeedbackAttachmentError | "upload";
220
- /**
221
- * One uploaded attachment in `refs` mode: what the host's upload answered with. `key`
222
- * identifies it (a storage key, an upload id) and is what a remove goes by; `name` is
223
- * what the chip says. `size` (bytes) and `type` (mime) are optional — given, the chip
224
- * shows the size and an image icon or a file icon accordingly.
225
- */
226
- interface FeedbackAttachmentRef {
227
- key: string;
228
- name: string;
229
- size?: number;
230
- type?: string;
231
- }
232
- interface FeedbackAttachmentFieldBaseProps {
233
- /** Prop > `<UiKitProvider labels={{ feedbackAttachment }}>` > English. Optional
234
- * since 0.7.0; `attachment` (the heading) is only ever read from here. */
235
- labels?: Partial<FeedbackAttachmentLabels> & Pick<Partial<FeedbackAttachmentFieldLabels>, "attachmentUploading">;
236
- accept?: string[];
237
- maxBytes?: number;
238
- /** Snapshot the app view behind this and return it as a File. A "Capture
239
- * screenshot" button appears only when it is given. */
240
- onCaptureScreenshot?: () => Promise<File | null>;
241
- /** Listen for the paste on `document` rather than on this field's own
242
- * subtree. For a modal, which owns the whole page while it is up. */
243
- documentPaste?: boolean;
244
- /** Listen for the paste within this element's subtree rather than this
245
- * field's own — for a field standing *beside* the text box a paste is made
246
- * in, whose common parent is where the event bubbles to. Ignored when
247
- * `documentPaste` is set. */
248
- pasteFrom?: RefObject<HTMLElement | null>;
249
- className?: string;
250
- }
251
- /** One file: choosing a second means removing the first. The field's only mode until 0.15.5. */
252
- interface FeedbackAttachmentFieldSingleProps extends FeedbackAttachmentFieldBaseProps {
253
- multiple?: false;
254
- refs?: false;
255
- value: File | null;
256
- onChange: (file: File | null) => void;
257
- onError?: (kind: "type" | "size") => void;
258
- max?: never;
259
- screenshot?: never;
260
- onScreenshotChange?: never;
261
- }
262
- /**
263
- * Several files (0.15.5, keksdose dev#578: "pasted two photos, the second overwrote
264
- * the first"). Every way in ADDS — a pick of several files, a paste, a capture — up to
265
- * `max`, and each file is a removable chip.
266
- */
267
- interface FeedbackAttachmentFieldMultipleProps extends FeedbackAttachmentFieldBaseProps {
268
- multiple: true;
269
- refs?: false;
270
- value: File[];
271
- onChange: (files: File[]) => void;
272
- /** How many files `value` may hold. Default {@link DEFAULT_MAX_ATTACHMENTS} (5). The
273
- * add buttons and the paste hint are offered only while there is room. */
274
- max?: number;
275
- /** Per file: a rejected one is reported and the others are still added. */
276
- onError?: (kind: FeedbackAttachmentError) => void;
277
- /**
278
- * A separate slot for THE screenshot, outside `value` and `max`. Given
279
- * `onScreenshotChange`, what `onCaptureScreenshot` returns lands here instead of in
280
- * `value`, shown as the first chip, and the capture button is offered only while the
281
- * slot is empty — a second snapshot of the same view is not more evidence. This is
282
- * how `<FeedbackDialog attachments="multiple">` keeps the screenshot apart from the
283
- * photos; a form built from the parts can do the same.
284
- */
285
- screenshot?: File | null;
286
- onScreenshotChange?: (file: File | null) => void;
287
- }
288
- /**
289
- * Uploaded on pick (0.22.0, keksdose K16). keksdose's support chat uploads each file
290
- * the moment it is chosen and sends the message with the REFS the uploads answered —
291
- * a 10 MB screenshot is refused while the reporter is still typing, not when they
292
- * press Send — so its `AttachmentPicker` could not use this field, whose value is
293
- * `File`s held until the form is submitted. Here the value is
294
- * {@link FeedbackAttachmentRef}s: every way in (pick, paste, capture) validates the
295
- * file as the other modes do, then hands it to `onUpload` and shows a chip with a
296
- * spinner until it answers; the ref it resolves to joins `value` through `onChange`.
297
- * A rejection drops the chip and reports `onError("upload", error)`. A chip is removed
298
- * BY KEY: `onChange` gets the list without it, and `onRemove` the key, for a host that
299
- * deletes the upload on the server too.
300
- *
301
- * Uploads still running count against `max`, so five picks in quick succession cannot
302
- * overshoot it; they are not in `value` until they land, so a send that reads `value`
303
- * sends only what has arrived — hold Send back while {@link onUploadingChange} says
304
- * an upload is running. Several files upload side by side and join in the order they
305
- * finish. The File modes are unchanged.
306
- */
307
- interface FeedbackAttachmentFieldRefsProps extends FeedbackAttachmentFieldBaseProps {
308
- refs: true;
309
- multiple?: never;
310
- value: FeedbackAttachmentRef[];
311
- onChange: (refs: FeedbackAttachmentRef[]) => void;
312
- /** Upload one file; resolve to its ref, or reject to refuse it. Called once per
313
- * accepted file, the moment it is chosen. */
314
- onUpload: (file: File) => Promise<FeedbackAttachmentRef>;
315
- /** A chip was removed — after `onChange` — with its ref's key. */
316
- onRemove?: (key: string) => void;
317
- /** Whether any upload is running: `true` when the first starts, `false` when the
318
- * last settles — for the host's Send, which should wait. */
319
- onUploadingChange?: (uploading: boolean) => void;
320
- /** How many refs `value` may hold, uploads in flight included. Default
321
- * {@link DEFAULT_MAX_ATTACHMENTS} (5). */
322
- max?: number;
323
- /** Per file. `"upload"` carries what `onUpload` rejected with. */
324
- onError?: (kind: FeedbackAttachmentRefsError, error?: unknown) => void;
325
- screenshot?: never;
326
- onScreenshotChange?: never;
327
- }
328
- type FeedbackAttachmentFieldProps = FeedbackAttachmentFieldSingleProps | FeedbackAttachmentFieldMultipleProps | FeedbackAttachmentFieldRefsProps;
329
- /**
330
- * Picking one picture: the file dialog, a capture of the app view, or a paste.
331
- * With `multiple`, picking several (see {@link FeedbackAttachmentFieldMultipleProps}).
332
- *
333
- * Lifted out of {@link FeedbackDialog} so the *reply* half of the feature can
334
- * have it too (Steering Design feedback #128). A report is a conversation — it
335
- * is filed, it is answered, and the reporter sends it back saying that is not
336
- * what they meant — and the screenshot showing what they mean is taken at
337
- * whichever of those points they looked. Only the first of them had a way to
338
- * attach one, so everything after it had to be described in words.
339
- *
340
- * **Three ways in, and they are three because no one of them covers the others.**
341
- * The file dialog needs a file on disk; `onCaptureScreenshot` snapshots the
342
- * *whole* app view; and the clipboard is where a region snip already is on every
343
- * platform (Steering Design feedback #39). Somebody who wanted to show one panel
344
- * had to save a crop and then find it again.
345
- *
346
- * `documentPaste` and `pasteFrom` decide where the paste is listened for, and
347
- * it is a real choice rather than a flag with a default. A modal traps focus,
348
- * so while it is up every paste in the page is meant for it — including one
349
- * made with nothing in particular focused, which never reaches a React
350
- * `onPaste` on a child: that is `documentPaste`. An editor **inline on a
351
- * page** is not that: the page around it has its own fields, so it listens
352
- * within its own subtree and a paste elsewhere stays where it was aimed. But
353
- * "its own subtree" has to include the text box the paste is actually made in,
354
- * and that box is this field's *sibling*, not its child — a paste in it bubbles
355
- * to their common parent and never through here. `pasteFrom` is that parent
356
- * (Steering Design feedback #140): the element whose subtree is listened to,
357
- * handed in by whoever renders both the box and this field side by side.
358
- */
359
- declare function FeedbackAttachmentField(props: FeedbackAttachmentFieldProps): react.JSX.Element;
360
- /** What a pasted image is called once it is an attachment.
361
- *
362
- * The extension is read off the mime type rather than assumed to be `.png`:
363
- * Safari puts TIFF on the clipboard and a file called `pasted.png` that is not
364
- * a PNG is one the receiving end opens wrong. */
365
- declare function pastedName(type: string): string;
366
-
367
- export { DEFAULT_ATTACHMENT_ACCEPT as D, type FeedbackAttachmentError as F, DEFAULT_FEEDBACK_ATTACHMENT_LABELS as a, DEFAULT_FEEDBACK_DIALOG_LABELS as b, DEFAULT_MAX_ATTACHMENTS as c, DEFAULT_MAX_ATTACHMENT_BYTES as d, FeedbackAttachmentField as e, type FeedbackAttachmentFieldLabels as f, type FeedbackAttachmentFieldMultipleProps as g, type FeedbackAttachmentFieldProps as h, type FeedbackAttachmentFieldRefsProps as i, type FeedbackAttachmentFieldSingleProps as j, type FeedbackAttachmentLabels as k, type FeedbackAttachmentRef as l, type FeedbackAttachmentRefsError as m, type FeedbackCategoryOption as n, FeedbackDialog as o, type FeedbackDialogLabels as p, type FeedbackDialogMultipleProps as q, type FeedbackDialogProps as r, type FeedbackDialogSingleProps as s, type FeedbackDialogTextLabels as t, type FeedbackMultipleSubmission as u, type FeedbackSubmission as v, pastedName as w };
@@ -1,190 +0,0 @@
1
- // Internal — not re-exported from the barrel. The pieces of a field's anatomy (its
2
- // label line, the caption and the error under it, the label STRIP over a bare group)
3
- // for the controls that are not built on `FloatingField`: the date and month pickers,
4
- // whose trigger is a button in a wrapper of their own, and the groups (ToggleGroup,
5
- // SwatchPicker, IconPicker), which have no field box at all.
6
- //
7
- // `Input` and `Select` keep their own copies in ui.tsx; these follow them class for
8
- // class, so a DatePicker's caption and a Select's caption are the same 11px line in
9
- // the same place (keksdose K4: "uniform field anatomy … today it is a patchwork").
10
-
11
- import { useId } from "react";
12
- import type { ReactNode } from "react";
13
- import { cn } from "../lib/cn";
14
- import { FieldLabel } from "./ui";
15
-
16
- /** `undefined`, `null`, `false` and `""` are what a caller's `cond && "…"` evaluates to
17
- * when there is nothing to say; none of them is content. A numeric `0` is. */
18
- export function hasContent(node: ReactNode): boolean {
19
- return node !== undefined && node !== null && node !== false && node !== "";
20
- }
21
-
22
- /**
23
- * A `hint` that is plain TEXT is a caption under the field; anything else (a
24
- * `FieldHint` "?") rides the label line — the rule `Select` set in 0.15: the label
25
- * line is 11px of strip shared with the label, and a sentence placed there was set on
26
- * top of both the label and the value.
27
- */
28
- export function isTextHint(hint: ReactNode): boolean {
29
- return (typeof hint === "string" && hint !== "") || typeof hint === "number";
30
- }
31
-
32
- const FIELD_CAPTION_CLASS = "mt-1 text-[11px] leading-tight text-[var(--text-muted)]";
33
- const FIELD_ERROR_CLASS = "mt-1 text-[11px] leading-tight text-[var(--danger)]";
34
-
35
- export interface FieldMessages {
36
- /** `invalid`, a message, or an `aria-invalid` from outside (a form library). */
37
- isInvalid: boolean;
38
- /** The caller's description, then the caption, then the error — the standing
39
- * advice before the news, as `useFieldError` in ui.tsx orders them. */
40
- describedBy: string | undefined;
41
- /** The caption and the error, for under the field; `null` when there is neither. */
42
- below: ReactNode;
43
- /** The hint for the LABEL line (a `FieldHint`), or `undefined` when the hint is a
44
- * caption or there is none. */
45
- labelHint: ReactNode;
46
- }
47
-
48
- /** `hint` / `error` / `invalid` resolved the way `Select` resolves them. */
49
- export function useFieldMessages({
50
- hint,
51
- error,
52
- invalid,
53
- describedBy,
54
- ariaInvalid,
55
- }: {
56
- hint?: ReactNode;
57
- error?: ReactNode;
58
- invalid?: boolean;
59
- describedBy?: string;
60
- ariaInvalid?: unknown;
61
- }): FieldMessages {
62
- const hintId = useId();
63
- const errorId = useId();
64
- const textHint = isTextHint(hint);
65
- const hasError = hasContent(error);
66
- return {
67
- isInvalid: Boolean(invalid) || hasError || ariaInvalid === true || ariaInvalid === "true",
68
- describedBy:
69
- [describedBy, textHint && hintId, hasError && errorId].filter(Boolean).join(" ") || undefined,
70
- below:
71
- textHint || hasError ? (
72
- <>
73
- {textHint && (
74
- <p id={hintId} className={FIELD_CAPTION_CLASS}>
75
- {hint}
76
- </p>
77
- )}
78
- {hasError && (
79
- <p id={errorId} className={FIELD_ERROR_CLASS}>
80
- {error}
81
- </p>
82
- )}
83
- </>
84
- ) : null,
85
- labelHint: !textHint && hasContent(hint) ? hint : undefined,
86
- };
87
- }
88
-
89
- /**
90
- * The field and what is under it, as one box — `FieldGroup` in ui.tsx, for the same
91
- * reasons written there. Nothing of its own while there is nothing under the field and
92
- * the caller passed no `error` key (the DOM is exactly what it was before these props);
93
- * once `error` is passed the box is kept even with no message, so the trigger is never
94
- * reparented — and remounted, losing focus — as the message comes and goes.
95
- */
96
- export function FieldBox({
97
- below,
98
- reserve,
99
- children,
100
- }: {
101
- below: ReactNode;
102
- reserve: boolean;
103
- children: ReactNode;
104
- }) {
105
- if (below === null && !reserve) return <>{children}</>;
106
- return (
107
- <div>
108
- {children}
109
- {below}
110
- </div>
111
- );
112
- }
113
-
114
- /**
115
- * The static label in a trigger field's top strip — a {@link FieldLabel}, or, with a
116
- * `FieldHint`, the label and its "?" laid out on ONE flex row (dev#468: two absolute
117
- * offsets guessing at the same baseline put the "?" three pixels above its word).
118
- * `FloatingField`'s static-label row, except that it stops at `end-9`: the date and
119
- * month triggers keep that column (`pe-9`) for the calendar glyph or the clear ×, and a
120
- * long label truncates before it rather than putting the "?" on top of it.
121
- */
122
- export function FieldLabelLine({ label, hint }: { label: ReactNode; hint?: ReactNode }) {
123
- if (!hasContent(hint)) return <FieldLabel>{label}</FieldLabel>;
124
- return (
125
- // `z-10` as FieldLabel has it: the trigger after it is `relative` and would paint
126
- // over the row (and take the "?"'s hover) otherwise.
127
- <div className="pointer-events-none absolute start-3 end-9 top-1 z-10 flex items-center gap-1">
128
- <FieldLabel className="static min-w-0 max-w-full">{label}</FieldLabel>
129
- <span className="pointer-events-auto flex shrink-0 items-center">{hint}</span>
130
- </div>
131
- );
132
- }
133
-
134
- /**
135
- * How tall the strip over a bare group is.
136
- *
137
- * - `"toggle"` — 16px. keksdose's `ClearedStatusPicker` (live #288 rework, #431,
138
- * measured): *"16px of label strip plus a 26px group is exactly a labelled Input"*,
139
- * so a status group in a row of 42px fields ends where they end. The label's line
140
- * box runs into the group's top hairline, but the ACTIVE fill — the thing feedback
141
- * #117 was about — starts at 19px, below the label's 17.75px.
142
- * - `"tiles"` — 20px, keksdose's `FlagPicker` and location field (`pt-5`). A tile has
143
- * no inset fill: its selection IS its frame, a 2px dark border-and-ring from the
144
- * tile's top edge, so the tile has to start below the whole label line.
145
- */
146
- export const LABEL_STRIP_PAD = { toggle: "pt-4", tiles: "pt-5" } as const;
147
-
148
- /**
149
- * The 11px label (and its `FieldHint`) over a group that is NOT a field — keksdose K2:
150
- * a segmented control or a row of swatches standing in a form row beside labelled
151
- * inputs, whose label must sit on the same line as theirs (`top-1`, `inset-x-3`, the
152
- * type of a field's static label) without the group growing a field's border round
153
- * it. `id` goes on the label so the group can be `aria-labelledby` it.
154
- *
155
- * `relative` is load-bearing twice over: the row is positioned against it, and a
156
- * caller's `sr-only` label cannot escape it (sr-only-containment).
157
- */
158
- export function LabelStrip({
159
- labelId,
160
- label,
161
- hint,
162
- disabled,
163
- pad,
164
- className,
165
- children,
166
- }: {
167
- labelId: string;
168
- label: ReactNode;
169
- hint?: ReactNode;
170
- /** Dim the label with its group, as `peer-disabled:` dims a field's own. */
171
- disabled?: boolean;
172
- pad: keyof typeof LABEL_STRIP_PAD;
173
- className?: string;
174
- children: ReactNode;
175
- }) {
176
- return (
177
- <div className={cn("relative min-w-0", LABEL_STRIP_PAD[pad], className)}>
178
- <div className="pointer-events-none absolute inset-x-3 top-1 flex items-center gap-1">
179
- {/* `static`: FieldLabel positions itself absolutely; here the ROW is
180
- positioned, so the label and its "?" are centred on one line by the flex
181
- layout (dev#468). */}
182
- <FieldLabel id={labelId} className={cn("static min-w-0 max-w-full", disabled && "opacity-50")}>
183
- {label}
184
- </FieldLabel>
185
- {hasContent(hint) && <span className="pointer-events-auto flex shrink-0 items-center">{hint}</span>}
186
- </div>
187
- {children}
188
- </div>
189
- );
190
- }