@eifi1/ui-kit 0.22.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 +11 -8
- package/dist/components/amount-input.d.ts +28 -8
- package/dist/components/amount-input.js +3 -1
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.d.ts +21 -0
- package/dist/components/autocomplete.js +128 -103
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +11 -8
- package/dist/components/calculator.d.ts +11 -8
- 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 +80 -2
- package/dist/components/combobox.js +448 -311
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +11 -8
- package/dist/components/confirm-dialog.js +7 -8
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +8 -5
- package/dist/components/country-select.d.ts +50 -7
- package/dist/components/country-select.js +65 -9
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +11 -8
- package/dist/components/danger-confirm.js +13 -3
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/date-picker.d.ts +41 -0
- package/dist/components/date-picker.js +269 -8
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +35 -1
- package/dist/components/entity-combobox.js +148 -112
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +11 -8
- package/dist/components/field-parts.d.ts +116 -6
- package/dist/components/field-parts.js +84 -0
- package/dist/components/field-parts.js.map +1 -1
- 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 +11 -8
- package/dist/components/file-dropzone.d.ts +11 -8
- package/dist/components/form-actions.d.ts +10 -7
- package/dist/components/form-actions.js +8 -2
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +11 -8
- package/dist/components/icon-picker.d.ts +10 -1
- package/dist/components/icon-picker.js +12 -4
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/language-select.d.ts +11 -8
- package/dist/components/money-field.d.ts +14 -8
- package/dist/components/money-field.js.map +1 -1
- package/dist/components/month-picker.js +2 -1
- 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/number-field.d.ts +11 -8
- package/dist/components/number-input.d.ts +11 -8
- package/dist/components/numpad-sheet.d.ts +11 -8
- package/dist/components/phone-input.d.ts +11 -8
- 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 +11 -8
- package/dist/components/settings-fields.d.ts +11 -8
- package/dist/components/share-card.d.ts +10 -7
- package/dist/components/swatch-picker.d.ts +17 -1
- package/dist/components/swatch-picker.js +10 -4
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/text-link.d.ts +10 -7
- package/dist/components/tile-radio.d.ts +20 -0
- package/dist/components/tile-radio.js +25 -8
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +11 -8
- package/dist/components/toggle-group.js +2 -2
- 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 +8 -5
- package/dist/components/ui.js +5 -11
- package/dist/components/ui.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +1 -1
- package/dist/feedback/feedback-attachment.js +156 -56
- 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 -287
- package/dist/feedback/feedback-thread.js +8 -1
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/{kit-labels-R8mIAc9N.d.ts → feedback-BxeQVzwq.d.ts} +405 -22
- package/dist/{feedback-attachment-NhUm7Zga.d.ts → feedback-attachment-fGAzZPf0.d.ts} +112 -10
- package/dist/feedback.d.ts +66 -2
- package/dist/hooks/use-file-drop.d.ts +11 -8
- package/dist/i18n/defaults.d.ts +11 -8
- package/dist/i18n/defaults.js +3 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +11 -8
- package/dist/i18n/german.js +36 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +7 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +11 -8
- package/dist/i18n/locales/de-CH.d.ts +11 -8
- package/dist/i18n/locales/en.d.ts +11 -8
- package/dist/i18n/locales/en.js +7 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +11 -8
- package/dist/i18n/locales/es.js +37 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +11 -8
- package/dist/i18n/locales/fr.js +36 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +11 -8
- package/dist/i18n/locales/hu.js +38 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +11 -8
- package/dist/i18n/locales/it.js +37 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +11 -8
- package/dist/i18n/locales/zh.js +34 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +11 -8
- package/dist/i18n/review.js +20 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +16 -0
- 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/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 +89 -11
- package/dist/rhf/fields.js +124 -0
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +11 -8
- package/dist/rhf.d.ts +12 -9
- package/dist/rhf.js.map +1 -1
- package/dist/shell/app-shell.d.ts +10 -7
- package/dist/shell/top-bar-brand.d.ts +11 -8
- 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 +10 -7
- package/dist/table-text.d.ts +1 -1
- package/dist/wizard/stepper-nav.d.ts +41 -10
- package/dist/wizard/stepper-nav.js +4 -0
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +11 -8
- package/package.json +1 -1
- package/src/components/amount-input.tsx +20 -1
- package/src/components/autocomplete.tsx +172 -111
- package/src/components/column-mapper.tsx +666 -0
- package/src/components/combobox-core.tsx +82 -9
- package/src/components/combobox.tsx +614 -332
- package/src/components/confirm-dialog.tsx +12 -8
- package/src/components/country-select.tsx +135 -22
- package/src/components/danger-confirm.tsx +91 -12
- package/src/components/date-picker.tsx +423 -10
- package/src/components/entity-combobox.tsx +217 -126
- package/src/components/field-parts.tsx +224 -5
- package/src/components/field-strip.tsx +149 -0
- package/src/components/form-actions.tsx +32 -4
- package/src/components/icon-picker.tsx +23 -4
- package/src/components/money-field.tsx +3 -0
- package/src/components/month-picker.tsx +2 -1
- package/src/components/multi-entity-combobox.tsx +207 -120
- package/src/components/reauth-dialog.tsx +17 -18
- package/src/components/swatch-picker.tsx +29 -5
- package/src/components/tile-radio.tsx +74 -13
- package/src/components/toggle-group.tsx +2 -2
- package/src/components/trigger-aria.ts +5 -0
- package/src/components/ui.tsx +7 -33
- package/src/feedback/feedback-attachment.tsx +308 -61
- package/src/feedback/feedback-dialog.tsx +7 -3
- package/src/feedback/feedback-inbox.tsx +3 -2
- package/src/feedback/feedback-thread.tsx +47 -4
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +42 -0
- package/src/i18n/kit-labels.tsx +4 -0
- package/src/i18n/locales/en.ts +27 -5
- package/src/i18n/locales/es.ts +41 -0
- package/src/i18n/locales/fr.ts +42 -0
- package/src/i18n/locales/hu.ts +37 -0
- package/src/i18n/locales/it.ts +41 -0
- package/src/i18n/locales/zh.ts +32 -0
- package/src/i18n/review.ts +19 -0
- package/src/index.ts +18 -0
- package/src/lib/column-mapping.ts +234 -0
- package/src/lib/table-text.ts +271 -0
- package/src/rhf/fields.tsx +254 -0
- package/src/rhf.ts +2 -1
- package/src/shell/topbar-action-menu.tsx +134 -38
- package/src/wizard/stepper-nav.tsx +35 -1
- package/dist/components/field-anatomy.d.ts +0 -95
- package/dist/components/field-anatomy.js +0 -84
- package/dist/components/field-anatomy.js.map +0 -1
- package/src/components/field-anatomy.tsx +0 -190
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import 'react';
|
|
2
|
-
export { D as DEFAULT_ATTACHMENT_ACCEPT, a as DEFAULT_FEEDBACK_ATTACHMENT_LABELS, c as DEFAULT_MAX_ATTACHMENTS, d as DEFAULT_MAX_ATTACHMENT_BYTES, F as FeedbackAttachmentError, e as
|
|
2
|
+
export { D as DEFAULT_ATTACHMENT_ACCEPT, a as DEFAULT_FEEDBACK_ATTACHMENT_LABELS, c as DEFAULT_MAX_ATTACHMENTS, d as DEFAULT_MAX_ATTACHMENT_BYTES, F as FeedbackAttachmentError, e as FeedbackAttachmentErrorInfo, f as FeedbackAttachmentField, g as FeedbackAttachmentFieldLabels, h as FeedbackAttachmentFieldMultipleProps, i as FeedbackAttachmentFieldProps, j as FeedbackAttachmentFieldRefsProps, k as FeedbackAttachmentFieldSingleProps, m as FeedbackAttachmentRef, n as FeedbackAttachmentRefsError, x as pastedName } from '../feedback-attachment-fGAzZPf0.js';
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../lib/cn.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useEffect,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
5
10
|
import { Camera, FileText, Image as ImageIcon, Paperclip, X } from "lucide-react";
|
|
6
|
-
import { Button, Spinner } from "../components/ui.js";
|
|
11
|
+
import { Button, IconButton, Spinner } from "../components/ui.js";
|
|
12
|
+
import { DEFAULT_FILE_PICKER_LABELS, formatAccept } from "../components/file-button.js";
|
|
13
|
+
import { useCommitReason } from "../components/write-lock.js";
|
|
7
14
|
import { useKitFileLabels, useKitLabels } from "../i18n/kit-labels.js";
|
|
8
15
|
const DEFAULT_ATTACHMENT_ACCEPT = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
|
9
16
|
const DEFAULT_MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
@@ -17,7 +24,8 @@ const DEFAULT_FEEDBACK_ATTACHMENT_LABELS = {
|
|
|
17
24
|
attachmentScreenshot: "Screenshot",
|
|
18
25
|
attachmentRemoveFile: (name) => `Remove ${name}`,
|
|
19
26
|
attachmentLimit: (max) => `Up to ${max} ${max === 1 ? "attachment" : "attachments"} \u2014 remove one to add another.`,
|
|
20
|
-
attachmentUploading: "Uploading\u2026"
|
|
27
|
+
attachmentUploading: "Uploading\u2026",
|
|
28
|
+
attachmentUploadFailed: (name) => `\u201C${name}\u201D could not be uploaded`
|
|
21
29
|
};
|
|
22
30
|
function FeedbackAttachmentField(props) {
|
|
23
31
|
if (props.refs) return /* @__PURE__ */ jsx(RefsField, { ...props });
|
|
@@ -33,6 +41,7 @@ function SingleField({
|
|
|
33
41
|
onCaptureScreenshot,
|
|
34
42
|
documentPaste = false,
|
|
35
43
|
pasteFrom,
|
|
44
|
+
disabled = false,
|
|
36
45
|
className
|
|
37
46
|
}) {
|
|
38
47
|
const [previewFor, setPreviewFor] = useState(null);
|
|
@@ -40,6 +49,7 @@ function SingleField({
|
|
|
40
49
|
const fileInputRef = useRef(null);
|
|
41
50
|
const fileText = useKitFileLabels();
|
|
42
51
|
const text = useKitLabels("feedbackAttachment", DEFAULT_FEEDBACK_ATTACHMENT_LABELS, labelsProp);
|
|
52
|
+
const errorInfo = useErrorInfo(accept, maxBytes, void 0, text);
|
|
43
53
|
useEffect(() => {
|
|
44
54
|
if (!value) return;
|
|
45
55
|
const url = URL.createObjectURL(value);
|
|
@@ -50,7 +60,7 @@ function SingleField({
|
|
|
50
60
|
if (!file) return;
|
|
51
61
|
const problem = rejection(file, accept, maxBytes);
|
|
52
62
|
if (problem) {
|
|
53
|
-
onError?.(problem);
|
|
63
|
+
onError?.(problem, errorInfo(problem, [file]));
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
56
66
|
onChange(file);
|
|
@@ -61,7 +71,8 @@ function SingleField({
|
|
|
61
71
|
pick(new File([file], pastedName(file.type), { type: file.type }));
|
|
62
72
|
},
|
|
63
73
|
documentPaste,
|
|
64
|
-
pasteFrom
|
|
74
|
+
pasteFrom,
|
|
75
|
+
disabled
|
|
65
76
|
);
|
|
66
77
|
return /* @__PURE__ */ jsxs(
|
|
67
78
|
"div",
|
|
@@ -87,24 +98,32 @@ function SingleField({
|
|
|
87
98
|
/* @__PURE__ */ jsx("div", { className: "truncate text-sm text-[var(--text-secondary)]", children: value.name }),
|
|
88
99
|
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--text-muted)]", children: fileText.size(value.size) })
|
|
89
100
|
] }),
|
|
90
|
-
/* @__PURE__ */ jsx(
|
|
91
|
-
"button",
|
|
92
|
-
{
|
|
93
|
-
type: "button",
|
|
94
|
-
onClick: () => onChange(null),
|
|
95
|
-
"aria-label": text.attachmentRemove,
|
|
96
|
-
className: "rounded p-1.5 text-[var(--text-placeholder)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]",
|
|
97
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
98
|
-
}
|
|
99
|
-
)
|
|
101
|
+
/* @__PURE__ */ jsx(RemoveButton, { label: text.attachmentRemove, onClick: () => onChange(null), disabled })
|
|
100
102
|
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
101
103
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
102
|
-
/* @__PURE__ */ jsxs(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
/* @__PURE__ */ jsxs(
|
|
105
|
+
Button,
|
|
106
|
+
{
|
|
107
|
+
type: "button",
|
|
108
|
+
variant: "secondary",
|
|
109
|
+
disabled,
|
|
110
|
+
onClick: () => fileInputRef.current?.click(),
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Paperclip, { className: "size-4" }),
|
|
113
|
+
" ",
|
|
114
|
+
text.attachmentAdd
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
onCaptureScreenshot && /* @__PURE__ */ jsx(
|
|
119
|
+
CaptureButton,
|
|
120
|
+
{
|
|
121
|
+
capture: onCaptureScreenshot,
|
|
122
|
+
onFile: pick,
|
|
123
|
+
label: text.attachmentCapture,
|
|
124
|
+
disabled
|
|
125
|
+
}
|
|
126
|
+
)
|
|
108
127
|
] }),
|
|
109
128
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: text.attachmentPaste })
|
|
110
129
|
] }),
|
|
@@ -115,6 +134,7 @@ function SingleField({
|
|
|
115
134
|
type: "file",
|
|
116
135
|
accept: accept.join(","),
|
|
117
136
|
className: "sr-only",
|
|
137
|
+
disabled,
|
|
118
138
|
onChange: (e) => {
|
|
119
139
|
pick(e.target.files?.[0]);
|
|
120
140
|
e.target.value = "";
|
|
@@ -138,6 +158,7 @@ function MultipleField({
|
|
|
138
158
|
onCaptureScreenshot,
|
|
139
159
|
documentPaste = false,
|
|
140
160
|
pasteFrom,
|
|
161
|
+
disabled = false,
|
|
141
162
|
className
|
|
142
163
|
}) {
|
|
143
164
|
const rootRef = useRef(null);
|
|
@@ -147,6 +168,7 @@ function MultipleField({
|
|
|
147
168
|
const removeLabel = text.attachmentRemoveFile ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentRemoveFile;
|
|
148
169
|
const limitLabel = text.attachmentLimit ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentLimit;
|
|
149
170
|
const screenshotLabel = text.attachmentScreenshot ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentScreenshot;
|
|
171
|
+
const errorInfo = useErrorInfo(accept, maxBytes, max, text);
|
|
150
172
|
const latest = useRef(value);
|
|
151
173
|
useLayoutEffect(() => {
|
|
152
174
|
latest.current = value;
|
|
@@ -163,29 +185,29 @@ function MultipleField({
|
|
|
163
185
|
const accepted = [];
|
|
164
186
|
const taken = new Set([...current, ...screenshot ? [screenshot] : []].map((f) => f.name));
|
|
165
187
|
for (const raw of incoming) {
|
|
166
|
-
const
|
|
188
|
+
const file = pasted ? renamed(raw, uniqueName(pastedName(raw.type), taken)) : raw;
|
|
189
|
+
const problem = rejection(file, accept, maxBytes);
|
|
167
190
|
if (problem) {
|
|
168
|
-
onError?.(problem);
|
|
191
|
+
onError?.(problem, errorInfo(problem, [file]));
|
|
169
192
|
continue;
|
|
170
193
|
}
|
|
171
|
-
const file = pasted ? renamed(raw, uniqueName(pastedName(raw.type), taken)) : raw;
|
|
172
194
|
taken.add(file.name);
|
|
173
195
|
if (!current.includes(file)) accepted.push(file);
|
|
174
196
|
}
|
|
175
197
|
if (accepted.length === 0) return;
|
|
176
198
|
const space = Math.max(0, max - current.length);
|
|
177
|
-
if (accepted.length > space) onError?.("count");
|
|
199
|
+
if (accepted.length > space) onError?.("count", errorInfo("count", accepted.slice(space)));
|
|
178
200
|
const kept = accepted.slice(0, space);
|
|
179
201
|
if (kept.length > 0) commit([...current, ...kept]);
|
|
180
202
|
};
|
|
181
|
-
const onPaste = usePaste((images) => add(images, true), documentPaste, pasteFrom);
|
|
203
|
+
const onPaste = usePaste((images) => add(images, true), documentPaste, pasteFrom, disabled);
|
|
182
204
|
const capture = (file) => {
|
|
183
205
|
if (!hasSlot) {
|
|
184
206
|
add([file]);
|
|
185
207
|
return;
|
|
186
208
|
}
|
|
187
209
|
const problem = rejection(file, accept, maxBytes);
|
|
188
|
-
if (problem) onError?.(problem);
|
|
210
|
+
if (problem) onError?.(problem, errorInfo(problem, [file]));
|
|
189
211
|
else onScreenshotChange(file);
|
|
190
212
|
};
|
|
191
213
|
const pendingFocus = useRef(null);
|
|
@@ -224,17 +246,14 @@ function MultipleField({
|
|
|
224
246
|
/* @__PURE__ */ jsx("div", { className: "truncate text-xs text-[var(--text-muted)]", children: chip.screenshot ? `${chip.file.name} \xB7 ${fileText.size(chip.file.size)}` : fileText.size(chip.file.size) })
|
|
225
247
|
] }),
|
|
226
248
|
/* @__PURE__ */ jsx(
|
|
227
|
-
|
|
249
|
+
RemoveButton,
|
|
228
250
|
{
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
label: removeLabel(chip.name),
|
|
252
|
+
disabled,
|
|
231
253
|
onClick: () => {
|
|
232
254
|
pendingFocus.current = index;
|
|
233
255
|
chip.remove();
|
|
234
|
-
}
|
|
235
|
-
"aria-label": removeLabel(chip.name),
|
|
236
|
-
className: "shrink-0 rounded p-1.5 text-[var(--text-placeholder)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]",
|
|
237
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
256
|
+
}
|
|
238
257
|
}
|
|
239
258
|
)
|
|
240
259
|
]
|
|
@@ -248,6 +267,7 @@ function MultipleField({
|
|
|
248
267
|
type: "button",
|
|
249
268
|
variant: "secondary",
|
|
250
269
|
"data-attachment-action": "",
|
|
270
|
+
disabled,
|
|
251
271
|
onClick: () => fileInputRef.current?.click(),
|
|
252
272
|
children: [
|
|
253
273
|
/* @__PURE__ */ jsx(Paperclip, { className: "size-4" }),
|
|
@@ -262,6 +282,7 @@ function MultipleField({
|
|
|
262
282
|
capture: onCaptureScreenshot,
|
|
263
283
|
onFile: capture,
|
|
264
284
|
label: text.attachmentCapture,
|
|
285
|
+
disabled,
|
|
265
286
|
"data-attachment-action": ""
|
|
266
287
|
}
|
|
267
288
|
)
|
|
@@ -277,6 +298,7 @@ function MultipleField({
|
|
|
277
298
|
className: "sr-only",
|
|
278
299
|
tabIndex: -1,
|
|
279
300
|
"aria-hidden": true,
|
|
301
|
+
disabled,
|
|
280
302
|
onChange: (e) => {
|
|
281
303
|
add(Array.from(e.target.files ?? []));
|
|
282
304
|
e.target.value = "";
|
|
@@ -300,6 +322,9 @@ function RefsField({
|
|
|
300
322
|
onCaptureScreenshot,
|
|
301
323
|
documentPaste = false,
|
|
302
324
|
pasteFrom,
|
|
325
|
+
disabled = false,
|
|
326
|
+
commit: commits,
|
|
327
|
+
disabledReason: ownDisabledReason,
|
|
303
328
|
className
|
|
304
329
|
}) {
|
|
305
330
|
const rootRef = useRef(null);
|
|
@@ -309,6 +334,11 @@ function RefsField({
|
|
|
309
334
|
const removeLabel = text.attachmentRemoveFile ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentRemoveFile;
|
|
310
335
|
const limitLabel = text.attachmentLimit ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentLimit;
|
|
311
336
|
const uploadingLabel = text.attachmentUploading ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentUploading;
|
|
337
|
+
const errorInfo = useErrorInfo(accept, maxBytes, max, text);
|
|
338
|
+
const disabledReason = useCommitReason(commits, ownDisabledReason);
|
|
339
|
+
const locked = hasReason(disabledReason);
|
|
340
|
+
const reason = locked ? disabledReason : void 0;
|
|
341
|
+
const inert = disabled || locked;
|
|
312
342
|
const latest = useRef(value);
|
|
313
343
|
useLayoutEffect(() => {
|
|
314
344
|
latest.current = value;
|
|
@@ -355,32 +385,32 @@ function RefsField({
|
|
|
355
385
|
},
|
|
356
386
|
(error) => {
|
|
357
387
|
settle();
|
|
358
|
-
if (mounted.current) onError?.("upload", error);
|
|
388
|
+
if (mounted.current) onError?.("upload", error, errorInfo("upload", [file], error));
|
|
359
389
|
}
|
|
360
390
|
);
|
|
361
391
|
};
|
|
362
392
|
const add = (incoming, pasted = false) => {
|
|
363
393
|
let space = Math.max(0, max - latest.current.length - inFlight.current.length);
|
|
364
394
|
const taken = /* @__PURE__ */ new Set([...latest.current.map((r) => r.name), ...inFlight.current.map((u) => u.file.name)]);
|
|
365
|
-
|
|
395
|
+
const dropped = [];
|
|
366
396
|
for (const raw of incoming) {
|
|
367
|
-
const
|
|
397
|
+
const file = pasted ? renamed(raw, uniqueName(pastedName(raw.type), taken)) : raw;
|
|
398
|
+
const problem = rejection(file, accept, maxBytes);
|
|
368
399
|
if (problem) {
|
|
369
|
-
onError?.(problem);
|
|
400
|
+
onError?.(problem, void 0, errorInfo(problem, [file]));
|
|
370
401
|
continue;
|
|
371
402
|
}
|
|
403
|
+
taken.add(file.name);
|
|
372
404
|
if (space === 0) {
|
|
373
|
-
dropped
|
|
405
|
+
dropped.push(file);
|
|
374
406
|
continue;
|
|
375
407
|
}
|
|
376
|
-
const file = pasted ? renamed(raw, uniqueName(pastedName(raw.type), taken)) : raw;
|
|
377
|
-
taken.add(file.name);
|
|
378
408
|
space -= 1;
|
|
379
409
|
start(file);
|
|
380
410
|
}
|
|
381
|
-
if (dropped) onError?.("count");
|
|
411
|
+
if (dropped.length > 0) onError?.("count", void 0, errorInfo("count", dropped));
|
|
382
412
|
};
|
|
383
|
-
const onPaste = usePaste((images) => add(images, true), documentPaste, pasteFrom);
|
|
413
|
+
const onPaste = usePaste((images) => add(images, true), documentPaste, pasteFrom, inert);
|
|
384
414
|
const pendingFocus = useRef(null);
|
|
385
415
|
useEffect(() => {
|
|
386
416
|
const index = pendingFocus.current;
|
|
@@ -393,6 +423,7 @@ function RefsField({
|
|
|
393
423
|
target?.focus();
|
|
394
424
|
}, [value]);
|
|
395
425
|
const remove = (key, index) => {
|
|
426
|
+
if (inert) return;
|
|
396
427
|
pendingFocus.current = index;
|
|
397
428
|
commit(latest.current.filter((r) => r.key !== key));
|
|
398
429
|
onRemove?.(key);
|
|
@@ -412,14 +443,12 @@ function RefsField({
|
|
|
412
443
|
ref.size !== void 0 && /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-[var(--text-muted)]", children: fileText.size(ref.size) })
|
|
413
444
|
] }),
|
|
414
445
|
/* @__PURE__ */ jsx(
|
|
415
|
-
|
|
446
|
+
RemoveButton,
|
|
416
447
|
{
|
|
417
|
-
|
|
418
|
-
"data-attachment-remove": "",
|
|
448
|
+
label: removeLabel(ref.name),
|
|
419
449
|
onClick: () => remove(ref.key, index),
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
450
|
+
disabled,
|
|
451
|
+
reason
|
|
423
452
|
}
|
|
424
453
|
)
|
|
425
454
|
]
|
|
@@ -450,6 +479,8 @@ function RefsField({
|
|
|
450
479
|
type: "button",
|
|
451
480
|
variant: "secondary",
|
|
452
481
|
"data-attachment-action": "",
|
|
482
|
+
disabled,
|
|
483
|
+
disabledReason: reason,
|
|
453
484
|
onClick: () => fileInputRef.current?.click(),
|
|
454
485
|
children: [
|
|
455
486
|
/* @__PURE__ */ jsx(Paperclip, { className: "size-4" }),
|
|
@@ -464,11 +495,17 @@ function RefsField({
|
|
|
464
495
|
capture: onCaptureScreenshot,
|
|
465
496
|
onFile: (file) => add([file]),
|
|
466
497
|
label: text.attachmentCapture,
|
|
498
|
+
disabled,
|
|
499
|
+
disabledReason: reason,
|
|
467
500
|
"data-attachment-action": ""
|
|
468
501
|
}
|
|
469
502
|
)
|
|
470
503
|
] }),
|
|
471
|
-
|
|
504
|
+
locked ? (
|
|
505
|
+
// Where the paste hint was: a phone never hovers, so the Tooltip alone would
|
|
506
|
+
// leave a touch user with buttons that do nothing and no word why.
|
|
507
|
+
/* @__PURE__ */ jsx("div", { "data-attachment-reason": "", className: "text-xs text-[var(--text-muted)]", children: disabledReason })
|
|
508
|
+
) : /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: room > 0 ? text.attachmentPaste : limitLabel(max) }),
|
|
472
509
|
/* @__PURE__ */ jsx(
|
|
473
510
|
"input",
|
|
474
511
|
{
|
|
@@ -479,6 +516,7 @@ function RefsField({
|
|
|
479
516
|
className: "sr-only",
|
|
480
517
|
tabIndex: -1,
|
|
481
518
|
"aria-hidden": true,
|
|
519
|
+
disabled: inert,
|
|
482
520
|
onChange: (e) => {
|
|
483
521
|
add(Array.from(e.target.files ?? []));
|
|
484
522
|
e.target.value = "";
|
|
@@ -498,6 +536,8 @@ function CaptureButton({
|
|
|
498
536
|
capture,
|
|
499
537
|
onFile,
|
|
500
538
|
label,
|
|
539
|
+
disabled = false,
|
|
540
|
+
disabledReason,
|
|
501
541
|
...rest
|
|
502
542
|
}) {
|
|
503
543
|
const [capturing, setCapturing] = useState(false);
|
|
@@ -506,10 +546,11 @@ function CaptureButton({
|
|
|
506
546
|
{
|
|
507
547
|
type: "button",
|
|
508
548
|
variant: "secondary",
|
|
509
|
-
disabled: capturing,
|
|
549
|
+
disabled: capturing || disabled,
|
|
550
|
+
disabledReason,
|
|
510
551
|
...rest,
|
|
511
552
|
onClick: () => {
|
|
512
|
-
if (capturing) return;
|
|
553
|
+
if (capturing || disabled) return;
|
|
513
554
|
setCapturing(true);
|
|
514
555
|
void capture().then((file) => {
|
|
515
556
|
if (file) onFile(file);
|
|
@@ -523,6 +564,60 @@ function CaptureButton({
|
|
|
523
564
|
}
|
|
524
565
|
);
|
|
525
566
|
}
|
|
567
|
+
function RemoveButton({
|
|
568
|
+
label,
|
|
569
|
+
onClick,
|
|
570
|
+
disabled,
|
|
571
|
+
reason
|
|
572
|
+
}) {
|
|
573
|
+
return /* @__PURE__ */ jsx(
|
|
574
|
+
IconButton,
|
|
575
|
+
{
|
|
576
|
+
type: "button",
|
|
577
|
+
size: "xs",
|
|
578
|
+
tone: "muted",
|
|
579
|
+
"data-attachment-remove": "",
|
|
580
|
+
"aria-label": label,
|
|
581
|
+
disabled,
|
|
582
|
+
disabledReason: reason,
|
|
583
|
+
onClick,
|
|
584
|
+
className: "shrink-0",
|
|
585
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
function hasReason(reason) {
|
|
590
|
+
return reason !== void 0 && reason !== null && reason !== false && reason !== "";
|
|
591
|
+
}
|
|
592
|
+
function useErrorInfo(accept, maxBytes, max, text) {
|
|
593
|
+
const picker = useKitLabels("filePicker", DEFAULT_FILE_PICKER_LABELS);
|
|
594
|
+
const fileText = useKitFileLabels();
|
|
595
|
+
const uploadFailed = text.attachmentUploadFailed ?? DEFAULT_FEEDBACK_ATTACHMENT_LABELS.attachmentUploadFailed;
|
|
596
|
+
return (kind, files, error) => {
|
|
597
|
+
const file = files[0];
|
|
598
|
+
let message;
|
|
599
|
+
if (kind === "type") {
|
|
600
|
+
const acceptText = formatAccept(accept.join(","));
|
|
601
|
+
const useOnly = acceptText !== "" && (picker.rejectedTypeOnly !== DEFAULT_FILE_PICKER_LABELS.rejectedTypeOnly || picker.rejectedType === DEFAULT_FILE_PICKER_LABELS.rejectedType);
|
|
602
|
+
message = useOnly ? picker.rejectedTypeOnly(acceptText, file.name) : picker.rejectedType(file.name);
|
|
603
|
+
} else if (kind === "size") {
|
|
604
|
+
message = picker.rejectedSize(file.name, fileText.size(maxBytes));
|
|
605
|
+
} else if (kind === "count") {
|
|
606
|
+
message = files.length === 1 ? picker.rejectedCount(file.name, max ?? 1) : picker.rejectedMany(files.length);
|
|
607
|
+
} else {
|
|
608
|
+
message = uploadFailed(file.name);
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
file,
|
|
612
|
+
files,
|
|
613
|
+
message,
|
|
614
|
+
maxBytes,
|
|
615
|
+
accept,
|
|
616
|
+
...max !== void 0 ? { max } : null,
|
|
617
|
+
...kind === "upload" ? { error } : null
|
|
618
|
+
};
|
|
619
|
+
};
|
|
620
|
+
}
|
|
526
621
|
function ChipPreview({ file, screenshot }) {
|
|
527
622
|
const isImage = file.type.startsWith("image/");
|
|
528
623
|
const [previewFor, setPreviewFor] = useState(null);
|
|
@@ -565,15 +660,17 @@ function uniqueName(name, taken) {
|
|
|
565
660
|
while (taken.has(`${stem}-${n}${ext}`)) n++;
|
|
566
661
|
return `${stem}-${n}${ext}`;
|
|
567
662
|
}
|
|
568
|
-
function usePaste(onImages, documentPaste, pasteFrom) {
|
|
569
|
-
const
|
|
663
|
+
function usePaste(onImages, documentPaste, pasteFrom, off) {
|
|
664
|
+
const handler = off ? null : onImages;
|
|
665
|
+
const latest = useRef(handler);
|
|
570
666
|
useEffect(() => {
|
|
571
|
-
latest.current =
|
|
667
|
+
latest.current = handler;
|
|
572
668
|
});
|
|
573
669
|
useEffect(() => {
|
|
574
670
|
const target = documentPaste ? document : pasteFrom?.current ?? null;
|
|
575
671
|
if (!target) return;
|
|
576
672
|
const onPaste = (event) => {
|
|
673
|
+
if (!latest.current) return;
|
|
577
674
|
const clipboard = event.clipboardData;
|
|
578
675
|
takeImages(clipboard, () => event.preventDefault(), latest.current);
|
|
579
676
|
};
|
|
@@ -581,7 +678,10 @@ function usePaste(onImages, documentPaste, pasteFrom) {
|
|
|
581
678
|
return () => target.removeEventListener("paste", onPaste);
|
|
582
679
|
}, [documentPaste, pasteFrom]);
|
|
583
680
|
const listensElsewhere = documentPaste || pasteFrom !== void 0;
|
|
584
|
-
return listensElsewhere ? void 0 : (event) =>
|
|
681
|
+
return listensElsewhere ? void 0 : (event) => {
|
|
682
|
+
if (!latest.current) return;
|
|
683
|
+
takeImages(event.clipboardData, () => event.preventDefault(), latest.current);
|
|
684
|
+
};
|
|
585
685
|
}
|
|
586
686
|
function takeImages(clipboard, stop, onImages) {
|
|
587
687
|
const images = Array.from(clipboard?.items ?? []).filter((item) => item.kind === "file" && item.type.startsWith("image/")).map((item) => item.getAsFile()).filter((file) => file !== null);
|