@ibanzajoe/uploader 0.1.0 → 0.3.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 +83 -0
- package/dist/index.cjs +752 -289
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +193 -9
- package/dist/index.d.ts +193 -9
- package/dist/index.js +750 -291
- package/dist/index.js.map +1 -1
- package/dist/styles.css +561 -129
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
CameraCapture: () => CameraCapture,
|
|
23
24
|
DropPane: () => DropPane,
|
|
24
25
|
ImageEditor: () => ImageEditor,
|
|
25
26
|
PickerOverlay: () => PickerOverlay,
|
|
@@ -28,10 +29,13 @@ __export(src_exports, {
|
|
|
28
29
|
editImage: () => editImage,
|
|
29
30
|
flip: () => flip,
|
|
30
31
|
flop: () => flop,
|
|
32
|
+
isCameraSupported: () => isCameraSupported,
|
|
31
33
|
output: () => output,
|
|
32
34
|
quality: () => quality,
|
|
33
35
|
resize: () => resize,
|
|
34
36
|
rotate: () => rotate,
|
|
37
|
+
shouldOfferCamera: () => shouldOfferCamera,
|
|
38
|
+
themeToVars: () => themeToVars,
|
|
35
39
|
transformUrl: () => transformUrl,
|
|
36
40
|
usePicker: () => usePicker
|
|
37
41
|
});
|
|
@@ -337,7 +341,7 @@ function transformUrl({ handle, ops, apiUrl = "" }) {
|
|
|
337
341
|
}
|
|
338
342
|
|
|
339
343
|
// src/react/PickerOverlay.tsx
|
|
340
|
-
var
|
|
344
|
+
var import_react6 = require("react");
|
|
341
345
|
|
|
342
346
|
// src/react/usePicker.ts
|
|
343
347
|
var import_react = require("react");
|
|
@@ -960,8 +964,495 @@ function ImageEditor({
|
|
|
960
964
|
);
|
|
961
965
|
}
|
|
962
966
|
|
|
963
|
-
// src/react/
|
|
967
|
+
// src/react/Dropzone.tsx
|
|
968
|
+
var import_react3 = require("react");
|
|
964
969
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
970
|
+
function Dropzone({
|
|
971
|
+
onFiles,
|
|
972
|
+
isDragOver,
|
|
973
|
+
onDragEnter,
|
|
974
|
+
onDragLeave,
|
|
975
|
+
onDragOver,
|
|
976
|
+
onDrop,
|
|
977
|
+
accept,
|
|
978
|
+
multiple = true,
|
|
979
|
+
onCamera,
|
|
980
|
+
hint,
|
|
981
|
+
className,
|
|
982
|
+
style
|
|
983
|
+
}) {
|
|
984
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
985
|
+
const openFileDialog = (0, import_react3.useCallback)(() => inputRef.current?.click(), []);
|
|
986
|
+
const handleInputChange = (0, import_react3.useCallback)(
|
|
987
|
+
(e) => {
|
|
988
|
+
if (e.target.files && e.target.files.length > 0) {
|
|
989
|
+
onFiles(e.target.files);
|
|
990
|
+
e.target.value = "";
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
[onFiles]
|
|
994
|
+
);
|
|
995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
996
|
+
"div",
|
|
997
|
+
{
|
|
998
|
+
className: ["uploader-dropzone", className ?? ""].filter(Boolean).join(" "),
|
|
999
|
+
"data-drag-over": isDragOver ? "true" : void 0,
|
|
1000
|
+
role: "button",
|
|
1001
|
+
tabIndex: 0,
|
|
1002
|
+
"aria-label": "Drop files here or click to browse",
|
|
1003
|
+
style,
|
|
1004
|
+
onDragEnter,
|
|
1005
|
+
onDragLeave,
|
|
1006
|
+
onDragOver,
|
|
1007
|
+
onDrop,
|
|
1008
|
+
onClick: openFileDialog,
|
|
1009
|
+
onKeyDown: (e) => {
|
|
1010
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1011
|
+
e.preventDefault();
|
|
1012
|
+
openFileDialog();
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
children: [
|
|
1016
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "uploader-dropzone-icon-wrap", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "uploader-dropzone-icon" }) }),
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "uploader-dropzone-label", children: "Drag & drop files here" }),
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "uploader-dropzone-sublabel", children: "or" }),
|
|
1019
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "uploader-dropzone-actions", children: [
|
|
1020
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1021
|
+
"button",
|
|
1022
|
+
{
|
|
1023
|
+
type: "button",
|
|
1024
|
+
className: "uploader-btn-browse",
|
|
1025
|
+
onClick: (e) => {
|
|
1026
|
+
e.stopPropagation();
|
|
1027
|
+
openFileDialog();
|
|
1028
|
+
},
|
|
1029
|
+
children: "Browse files"
|
|
1030
|
+
}
|
|
1031
|
+
),
|
|
1032
|
+
onCamera && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1033
|
+
"button",
|
|
1034
|
+
{
|
|
1035
|
+
type: "button",
|
|
1036
|
+
className: "uploader-btn-camera",
|
|
1037
|
+
onClick: (e) => {
|
|
1038
|
+
e.stopPropagation();
|
|
1039
|
+
onCamera();
|
|
1040
|
+
},
|
|
1041
|
+
children: [
|
|
1042
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1043
|
+
"svg",
|
|
1044
|
+
{
|
|
1045
|
+
className: "uploader-btn-camera-icon",
|
|
1046
|
+
width: "16",
|
|
1047
|
+
height: "16",
|
|
1048
|
+
viewBox: "0 0 24 24",
|
|
1049
|
+
fill: "none",
|
|
1050
|
+
"aria-hidden": "true",
|
|
1051
|
+
children: [
|
|
1052
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1053
|
+
"path",
|
|
1054
|
+
{
|
|
1055
|
+
d: "M4 8.5A2.5 2.5 0 0 1 6.5 6h1.2a1 1 0 0 0 .83-.45l.74-1.1A1 1 0 0 1 10.1 4h3.8a1 1 0 0 1 .83.45l.74 1.1a1 1 0 0 0 .83.45h1.2A2.5 2.5 0 0 1 20 8.5v8A2.5 2.5 0 0 1 17.5 19h-11A2.5 2.5 0 0 1 4 16.5v-8Z",
|
|
1056
|
+
stroke: "currentColor",
|
|
1057
|
+
strokeWidth: "1.6",
|
|
1058
|
+
strokeLinejoin: "round"
|
|
1059
|
+
}
|
|
1060
|
+
),
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12.5", r: "3.2", stroke: "currentColor", strokeWidth: "1.6" })
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
),
|
|
1065
|
+
"Take photo"
|
|
1066
|
+
]
|
|
1067
|
+
}
|
|
1068
|
+
)
|
|
1069
|
+
] }),
|
|
1070
|
+
hint && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "uploader-dropzone-hint", children: hint }),
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1072
|
+
"input",
|
|
1073
|
+
{
|
|
1074
|
+
ref: inputRef,
|
|
1075
|
+
type: "file",
|
|
1076
|
+
multiple,
|
|
1077
|
+
accept,
|
|
1078
|
+
className: "uploader-file-input",
|
|
1079
|
+
"aria-hidden": "true",
|
|
1080
|
+
tabIndex: -1,
|
|
1081
|
+
onChange: handleInputChange
|
|
1082
|
+
}
|
|
1083
|
+
)
|
|
1084
|
+
]
|
|
1085
|
+
}
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/react/CameraCapture.tsx
|
|
1090
|
+
var import_react4 = require("react");
|
|
1091
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1092
|
+
function isCameraSupported() {
|
|
1093
|
+
return typeof navigator !== "undefined" && !!navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === "function" && typeof document !== "undefined" && typeof HTMLCanvasElement !== "undefined";
|
|
1094
|
+
}
|
|
1095
|
+
var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp|svg|avif|heic|heif)$/i;
|
|
1096
|
+
function acceptsImages(accept) {
|
|
1097
|
+
if (!accept || accept.length === 0) return true;
|
|
1098
|
+
return accept.some((a) => a.startsWith("image/") || a === "image/*" || IMAGE_EXT.test(a.trim()));
|
|
1099
|
+
}
|
|
1100
|
+
function shouldOfferCamera(options) {
|
|
1101
|
+
if (!isCameraSupported()) return false;
|
|
1102
|
+
if (!acceptsImages(options?.accept)) return false;
|
|
1103
|
+
const sources = options?.fromSources;
|
|
1104
|
+
if (sources && !sources.includes("camera")) return false;
|
|
1105
|
+
return true;
|
|
1106
|
+
}
|
|
1107
|
+
function extForType(mime) {
|
|
1108
|
+
switch (mime) {
|
|
1109
|
+
case "image/png":
|
|
1110
|
+
return "png";
|
|
1111
|
+
case "image/webp":
|
|
1112
|
+
return "webp";
|
|
1113
|
+
default:
|
|
1114
|
+
return "jpg";
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function describeCameraError(err) {
|
|
1118
|
+
const name = err instanceof DOMException ? err.name : "";
|
|
1119
|
+
switch (name) {
|
|
1120
|
+
case "NotAllowedError":
|
|
1121
|
+
case "SecurityError":
|
|
1122
|
+
return "Camera access was blocked. Allow camera permission and try again.";
|
|
1123
|
+
case "NotFoundError":
|
|
1124
|
+
case "OverconstrainedError":
|
|
1125
|
+
return "No camera was found on this device.";
|
|
1126
|
+
case "NotReadableError":
|
|
1127
|
+
return "The camera is already in use by another application.";
|
|
1128
|
+
default:
|
|
1129
|
+
return "Could not start the camera.";
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
function CameraCapture({
|
|
1133
|
+
onCapture,
|
|
1134
|
+
onCancel,
|
|
1135
|
+
facingMode = "user",
|
|
1136
|
+
outputType = "image/jpeg",
|
|
1137
|
+
quality: quality2 = 0.92,
|
|
1138
|
+
fileNamePrefix = "camera-photo",
|
|
1139
|
+
className,
|
|
1140
|
+
style
|
|
1141
|
+
}) {
|
|
1142
|
+
const videoRef = (0, import_react4.useRef)(null);
|
|
1143
|
+
const streamRef = (0, import_react4.useRef)(null);
|
|
1144
|
+
const blobRef = (0, import_react4.useRef)(null);
|
|
1145
|
+
const [status, setStatus] = (0, import_react4.useState)("initializing");
|
|
1146
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
1147
|
+
const [stillUrl, setStillUrl] = (0, import_react4.useState)("");
|
|
1148
|
+
const mirror = facingMode === "user";
|
|
1149
|
+
const stopStream = (0, import_react4.useCallback)(() => {
|
|
1150
|
+
streamRef.current?.getTracks().forEach((t) => t.stop());
|
|
1151
|
+
streamRef.current = null;
|
|
1152
|
+
if (videoRef.current) videoRef.current.srcObject = null;
|
|
1153
|
+
}, []);
|
|
1154
|
+
const startStream = (0, import_react4.useCallback)(async () => {
|
|
1155
|
+
setError(null);
|
|
1156
|
+
setStatus("initializing");
|
|
1157
|
+
if (!isCameraSupported()) {
|
|
1158
|
+
setError("Camera capture is not supported in this browser.");
|
|
1159
|
+
setStatus("error");
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
try {
|
|
1163
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
1164
|
+
video: { facingMode },
|
|
1165
|
+
audio: false
|
|
1166
|
+
});
|
|
1167
|
+
if (videoRef.current == null) {
|
|
1168
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
streamRef.current = stream;
|
|
1172
|
+
videoRef.current.srcObject = stream;
|
|
1173
|
+
setStatus("live");
|
|
1174
|
+
} catch (err) {
|
|
1175
|
+
setError(describeCameraError(err));
|
|
1176
|
+
setStatus("error");
|
|
1177
|
+
}
|
|
1178
|
+
}, [facingMode]);
|
|
1179
|
+
(0, import_react4.useEffect)(() => {
|
|
1180
|
+
void startStream();
|
|
1181
|
+
return () => {
|
|
1182
|
+
stopStream();
|
|
1183
|
+
};
|
|
1184
|
+
}, [startStream, stopStream]);
|
|
1185
|
+
(0, import_react4.useEffect)(() => {
|
|
1186
|
+
if (!stillUrl) return;
|
|
1187
|
+
return () => URL.revokeObjectURL(stillUrl);
|
|
1188
|
+
}, [stillUrl]);
|
|
1189
|
+
const capture = (0, import_react4.useCallback)(() => {
|
|
1190
|
+
const video = videoRef.current;
|
|
1191
|
+
if (!video) return;
|
|
1192
|
+
const w = video.videoWidth;
|
|
1193
|
+
const h = video.videoHeight;
|
|
1194
|
+
if (!w || !h) {
|
|
1195
|
+
setError("The camera is still warming up \u2014 try again in a moment.");
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const canvas = document.createElement("canvas");
|
|
1199
|
+
canvas.width = w;
|
|
1200
|
+
canvas.height = h;
|
|
1201
|
+
const ctx = canvas.getContext("2d");
|
|
1202
|
+
if (!ctx) {
|
|
1203
|
+
setError("Canvas is unavailable for capturing.");
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (mirror) {
|
|
1207
|
+
ctx.translate(w, 0);
|
|
1208
|
+
ctx.scale(-1, 1);
|
|
1209
|
+
}
|
|
1210
|
+
ctx.drawImage(video, 0, 0, w, h);
|
|
1211
|
+
canvas.toBlob(
|
|
1212
|
+
(blob) => {
|
|
1213
|
+
if (!blob) {
|
|
1214
|
+
setError("Failed to capture the photo.");
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
blobRef.current = blob;
|
|
1218
|
+
setStillUrl(URL.createObjectURL(blob));
|
|
1219
|
+
setStatus("captured");
|
|
1220
|
+
stopStream();
|
|
1221
|
+
},
|
|
1222
|
+
outputType,
|
|
1223
|
+
quality2
|
|
1224
|
+
);
|
|
1225
|
+
}, [mirror, outputType, quality2, stopStream]);
|
|
1226
|
+
const retake = (0, import_react4.useCallback)(() => {
|
|
1227
|
+
blobRef.current = null;
|
|
1228
|
+
setStillUrl("");
|
|
1229
|
+
void startStream();
|
|
1230
|
+
}, [startStream]);
|
|
1231
|
+
const confirm = (0, import_react4.useCallback)(() => {
|
|
1232
|
+
const blob = blobRef.current;
|
|
1233
|
+
if (!blob) return;
|
|
1234
|
+
const name = `${fileNamePrefix}-${Date.now()}.${extForType(outputType)}`;
|
|
1235
|
+
const file = new File([blob], name, { type: outputType, lastModified: Date.now() });
|
|
1236
|
+
onCapture(file);
|
|
1237
|
+
}, [fileNamePrefix, outputType, onCapture]);
|
|
1238
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1239
|
+
"div",
|
|
1240
|
+
{
|
|
1241
|
+
className: ["uploader-camera", className ?? ""].filter(Boolean).join(" "),
|
|
1242
|
+
style,
|
|
1243
|
+
"data-testid": "camera-capture",
|
|
1244
|
+
children: [
|
|
1245
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "uploader-camera-stage", children: [
|
|
1246
|
+
status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "uploader-camera-error", role: "alert", children: error }) : status === "captured" ? (
|
|
1247
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("img", { src: stillUrl, alt: "Captured preview", className: "uploader-camera-still" })
|
|
1249
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1250
|
+
"video",
|
|
1251
|
+
{
|
|
1252
|
+
ref: videoRef,
|
|
1253
|
+
className: "uploader-camera-video",
|
|
1254
|
+
autoPlay: true,
|
|
1255
|
+
playsInline: true,
|
|
1256
|
+
muted: true,
|
|
1257
|
+
"data-mirror": mirror ? "true" : void 0
|
|
1258
|
+
}
|
|
1259
|
+
),
|
|
1260
|
+
status === "initializing" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "uploader-camera-status", "aria-live": "polite", children: "Starting camera\u2026" })
|
|
1261
|
+
] }),
|
|
1262
|
+
status === "live" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "uploader-camera-hint", children: "Frame your shot, then tap the shutter to capture." }),
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "uploader-camera-actions", children: status === "captured" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "uploader-btn-secondary", onClick: retake, children: "Retake" }),
|
|
1265
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "uploader-btn-primary", onClick: confirm, children: "Use photo" })
|
|
1266
|
+
] }) : status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "uploader-btn-secondary", onClick: onCancel, children: "Back" }),
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "uploader-btn-primary", onClick: () => void startStream(), children: "Try again" })
|
|
1269
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "uploader-btn-secondary", onClick: onCancel, children: "Back" }),
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1272
|
+
"button",
|
|
1273
|
+
{
|
|
1274
|
+
type: "button",
|
|
1275
|
+
className: "uploader-camera-shutter",
|
|
1276
|
+
"aria-label": "Capture photo",
|
|
1277
|
+
disabled: status !== "live",
|
|
1278
|
+
onClick: capture,
|
|
1279
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-camera-shutter-ring", "aria-hidden": "true" })
|
|
1280
|
+
}
|
|
1281
|
+
),
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "uploader-camera-actions-spacer", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-btn-secondary", children: "Back" }) })
|
|
1283
|
+
] }) })
|
|
1284
|
+
]
|
|
1285
|
+
}
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/react/ProgressBar.tsx
|
|
1290
|
+
var import_react5 = require("react");
|
|
1291
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1292
|
+
function ProgressBar({ value, label, size = "sm", className, style }) {
|
|
1293
|
+
const clamped = Math.max(0, Math.min(100, value));
|
|
1294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1295
|
+
"div",
|
|
1296
|
+
{
|
|
1297
|
+
role: "progressbar",
|
|
1298
|
+
"aria-valuenow": clamped,
|
|
1299
|
+
"aria-valuemin": 0,
|
|
1300
|
+
"aria-valuemax": 100,
|
|
1301
|
+
"aria-label": label,
|
|
1302
|
+
"data-size": size,
|
|
1303
|
+
className: ["uploader-progress-bar", className ?? ""].filter(Boolean).join(" "),
|
|
1304
|
+
style,
|
|
1305
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1306
|
+
"div",
|
|
1307
|
+
{
|
|
1308
|
+
className: "uploader-progress-bar-fill",
|
|
1309
|
+
style: { width: `${clamped}%` }
|
|
1310
|
+
}
|
|
1311
|
+
)
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/react/FileQueueRow.tsx
|
|
1317
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1318
|
+
function statusLabel(entry) {
|
|
1319
|
+
switch (entry.state) {
|
|
1320
|
+
case "uploading":
|
|
1321
|
+
return entry.progress != null ? `${entry.progress}%` : "Uploading\u2026";
|
|
1322
|
+
case "done":
|
|
1323
|
+
return "Done";
|
|
1324
|
+
case "error":
|
|
1325
|
+
return entry.error ?? "Failed";
|
|
1326
|
+
default:
|
|
1327
|
+
return "Ready";
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function FileQueueRow({ entry, onRemove, onRetry, onEdit }) {
|
|
1331
|
+
const { id, file, state, progress, previewUrl } = entry;
|
|
1332
|
+
const isUploading = state === "uploading";
|
|
1333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: `uploader-file-item uploader-file-${state}`, children: [
|
|
1334
|
+
previewUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1335
|
+
"img",
|
|
1336
|
+
{
|
|
1337
|
+
src: previewUrl,
|
|
1338
|
+
alt: "",
|
|
1339
|
+
"aria-hidden": "true",
|
|
1340
|
+
className: "uploader-file-thumb"
|
|
1341
|
+
}
|
|
1342
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "uploader-file-thumb-placeholder", "aria-hidden": "true" }),
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uploader-file-info", children: [
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uploader-file-info-row", children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "uploader-file-name", children: file.name }),
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1347
|
+
"span",
|
|
1348
|
+
{
|
|
1349
|
+
className: "uploader-file-status",
|
|
1350
|
+
"data-state": state,
|
|
1351
|
+
role: state === "error" ? "alert" : void 0,
|
|
1352
|
+
children: statusLabel(entry)
|
|
1353
|
+
}
|
|
1354
|
+
)
|
|
1355
|
+
] }),
|
|
1356
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1357
|
+
ProgressBar,
|
|
1358
|
+
{
|
|
1359
|
+
value: isUploading && progress != null ? progress : state === "done" ? 100 : 0,
|
|
1360
|
+
label: `${file.name} upload progress`
|
|
1361
|
+
}
|
|
1362
|
+
)
|
|
1363
|
+
] }),
|
|
1364
|
+
state === "done" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "uploader-file-done-badge", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "9", height: "7", viewBox: "0 0 9 7", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1365
|
+
"path",
|
|
1366
|
+
{
|
|
1367
|
+
d: "M1 3.5L3.5 6L8 1",
|
|
1368
|
+
stroke: "currentColor",
|
|
1369
|
+
strokeWidth: "1.8",
|
|
1370
|
+
strokeLinecap: "round",
|
|
1371
|
+
strokeLinejoin: "round"
|
|
1372
|
+
}
|
|
1373
|
+
) }) }),
|
|
1374
|
+
!isUploading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uploader-file-actions", children: [
|
|
1375
|
+
onEdit && previewUrl && (state === "pending" || state === "error") && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1376
|
+
"button",
|
|
1377
|
+
{
|
|
1378
|
+
type: "button",
|
|
1379
|
+
className: "uploader-btn-edit",
|
|
1380
|
+
"aria-label": `Edit ${file.name}`,
|
|
1381
|
+
onClick: () => onEdit(id),
|
|
1382
|
+
children: "Edit"
|
|
1383
|
+
}
|
|
1384
|
+
),
|
|
1385
|
+
state === "error" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1386
|
+
"button",
|
|
1387
|
+
{
|
|
1388
|
+
type: "button",
|
|
1389
|
+
className: "uploader-btn-retry",
|
|
1390
|
+
"aria-label": `Retry uploading ${file.name}`,
|
|
1391
|
+
onClick: () => onRetry(id),
|
|
1392
|
+
children: "Retry"
|
|
1393
|
+
}
|
|
1394
|
+
),
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1396
|
+
"button",
|
|
1397
|
+
{
|
|
1398
|
+
type: "button",
|
|
1399
|
+
className: "uploader-btn-remove",
|
|
1400
|
+
"aria-label": `Remove ${file.name}`,
|
|
1401
|
+
onClick: () => onRemove(id),
|
|
1402
|
+
children: "\xD7"
|
|
1403
|
+
}
|
|
1404
|
+
)
|
|
1405
|
+
] })
|
|
1406
|
+
] });
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/react/theme.ts
|
|
1410
|
+
var VAR_MAP = {
|
|
1411
|
+
accent: "--uploader-accent",
|
|
1412
|
+
accentHover: "--uploader-accent-hover",
|
|
1413
|
+
accentSoft: "--uploader-accent-soft",
|
|
1414
|
+
accentRing: "--uploader-accent-ring",
|
|
1415
|
+
radius: "--uploader-radius",
|
|
1416
|
+
background: "--uploader-bg",
|
|
1417
|
+
surface: "--uploader-surface",
|
|
1418
|
+
surfaceHover: "--uploader-surface-hover",
|
|
1419
|
+
border: "--uploader-border",
|
|
1420
|
+
borderSubtle: "--uploader-border-subtle",
|
|
1421
|
+
text: "--uploader-text",
|
|
1422
|
+
textMuted: "--uploader-text-muted",
|
|
1423
|
+
textXMuted: "--uploader-text-xmuted",
|
|
1424
|
+
error: "--uploader-error",
|
|
1425
|
+
errorSoft: "--uploader-error-soft",
|
|
1426
|
+
success: "--uploader-success",
|
|
1427
|
+
shadowSm: "--uploader-shadow-sm",
|
|
1428
|
+
shadow: "--uploader-shadow",
|
|
1429
|
+
shadowLg: "--uploader-shadow-lg",
|
|
1430
|
+
font: "--uploader-font",
|
|
1431
|
+
transition: "--uploader-transition",
|
|
1432
|
+
zBackdrop: "--uploader-z-backdrop",
|
|
1433
|
+
zPanel: "--uploader-z-panel"
|
|
1434
|
+
};
|
|
1435
|
+
function themeToVars(theme) {
|
|
1436
|
+
if (!theme) return {};
|
|
1437
|
+
const vars = {};
|
|
1438
|
+
for (const key of Object.keys(theme)) {
|
|
1439
|
+
const value = theme[key];
|
|
1440
|
+
if (value === void 0 || value === null) continue;
|
|
1441
|
+
vars[VAR_MAP[key]] = String(value);
|
|
1442
|
+
}
|
|
1443
|
+
return vars;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/react/PickerOverlay.tsx
|
|
1447
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1448
|
+
function dialogTitle(isUploading, isDone, totalFiles, doneCount, errorCount, editingId, cameraOpen) {
|
|
1449
|
+
if (editingId) return "Edit Image";
|
|
1450
|
+
if (cameraOpen) return "Take a photo";
|
|
1451
|
+
if (isDone && errorCount === 0) return "All files uploaded";
|
|
1452
|
+
if (isDone && errorCount > 0) return `${doneCount} of ${totalFiles} uploaded`;
|
|
1453
|
+
if (isUploading) return `Uploading ${totalFiles} file${totalFiles !== 1 ? "s" : ""}`;
|
|
1454
|
+
return "Upload files";
|
|
1455
|
+
}
|
|
965
1456
|
function PickerOverlay({
|
|
966
1457
|
open,
|
|
967
1458
|
onClose,
|
|
@@ -973,14 +1464,15 @@ function PickerOverlay({
|
|
|
973
1464
|
onFileUploadFinished,
|
|
974
1465
|
onFileUploadFailed,
|
|
975
1466
|
onCancel,
|
|
1467
|
+
theme,
|
|
976
1468
|
className,
|
|
977
1469
|
style
|
|
978
1470
|
}) {
|
|
979
|
-
const titleId = (0,
|
|
980
|
-
const
|
|
981
|
-
const
|
|
982
|
-
const [
|
|
983
|
-
const [
|
|
1471
|
+
const titleId = (0, import_react6.useId)();
|
|
1472
|
+
const panelRef = (0, import_react6.useRef)(null);
|
|
1473
|
+
const [isDragOver, setIsDragOver] = (0, import_react6.useState)(false);
|
|
1474
|
+
const [editingId, setEditingId] = (0, import_react6.useState)(null);
|
|
1475
|
+
const [cameraOpen, setCameraOpen] = (0, import_react6.useState)(false);
|
|
984
1476
|
const { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone } = usePicker({
|
|
985
1477
|
apikey,
|
|
986
1478
|
apiUrl,
|
|
@@ -990,7 +1482,7 @@ function PickerOverlay({
|
|
|
990
1482
|
onFileUploadFinished,
|
|
991
1483
|
onFileUploadFailed
|
|
992
1484
|
});
|
|
993
|
-
(0,
|
|
1485
|
+
(0, import_react6.useEffect)(() => {
|
|
994
1486
|
if (!open) return;
|
|
995
1487
|
const handler = (e) => {
|
|
996
1488
|
if (e.key === "Escape") {
|
|
@@ -1001,7 +1493,7 @@ function PickerOverlay({
|
|
|
1001
1493
|
document.addEventListener("keydown", handler);
|
|
1002
1494
|
return () => document.removeEventListener("keydown", handler);
|
|
1003
1495
|
}, [open, onClose, onCancel]);
|
|
1004
|
-
(0,
|
|
1496
|
+
(0, import_react6.useEffect)(() => {
|
|
1005
1497
|
if (!open || !panelRef.current) return;
|
|
1006
1498
|
const panel = panelRef.current;
|
|
1007
1499
|
const focusable = panel.querySelectorAll(
|
|
@@ -1027,8 +1519,8 @@ function PickerOverlay({
|
|
|
1027
1519
|
};
|
|
1028
1520
|
document.addEventListener("keydown", trap);
|
|
1029
1521
|
return () => document.removeEventListener("keydown", trap);
|
|
1030
|
-
}, [open, files, editingId]);
|
|
1031
|
-
(0,
|
|
1522
|
+
}, [open, files, editingId, cameraOpen]);
|
|
1523
|
+
(0, import_react6.useEffect)(() => {
|
|
1032
1524
|
if (!open) return;
|
|
1033
1525
|
const prev = document.body.style.overflow;
|
|
1034
1526
|
document.body.style.overflow = "hidden";
|
|
@@ -1036,18 +1528,18 @@ function PickerOverlay({
|
|
|
1036
1528
|
document.body.style.overflow = prev;
|
|
1037
1529
|
};
|
|
1038
1530
|
}, [open]);
|
|
1039
|
-
const handleDragEnter = (0,
|
|
1531
|
+
const handleDragEnter = (0, import_react6.useCallback)((e) => {
|
|
1040
1532
|
e.preventDefault();
|
|
1041
1533
|
setIsDragOver(true);
|
|
1042
1534
|
}, []);
|
|
1043
|
-
const handleDragLeave = (0,
|
|
1535
|
+
const handleDragLeave = (0, import_react6.useCallback)((e) => {
|
|
1044
1536
|
e.preventDefault();
|
|
1045
1537
|
setIsDragOver(false);
|
|
1046
1538
|
}, []);
|
|
1047
|
-
const handleDragOver = (0,
|
|
1539
|
+
const handleDragOver = (0, import_react6.useCallback)((e) => {
|
|
1048
1540
|
e.preventDefault();
|
|
1049
1541
|
}, []);
|
|
1050
|
-
const handleDrop = (0,
|
|
1542
|
+
const handleDrop = (0, import_react6.useCallback)(
|
|
1051
1543
|
(e) => {
|
|
1052
1544
|
e.preventDefault();
|
|
1053
1545
|
setIsDragOver(false);
|
|
@@ -1057,58 +1549,64 @@ function PickerOverlay({
|
|
|
1057
1549
|
},
|
|
1058
1550
|
[addFiles]
|
|
1059
1551
|
);
|
|
1060
|
-
const
|
|
1061
|
-
const handleInputChange = (0, import_react3.useCallback)(
|
|
1062
|
-
(e) => {
|
|
1063
|
-
if (e.target.files && e.target.files.length > 0) {
|
|
1064
|
-
addFiles(e.target.files);
|
|
1065
|
-
e.target.value = "";
|
|
1066
|
-
}
|
|
1067
|
-
},
|
|
1068
|
-
[addFiles]
|
|
1069
|
-
);
|
|
1070
|
-
const handleClose = (0, import_react3.useCallback)(() => {
|
|
1552
|
+
const handleClose = (0, import_react6.useCallback)(() => {
|
|
1071
1553
|
onCancel?.();
|
|
1072
1554
|
onClose();
|
|
1073
1555
|
}, [onCancel, onClose]);
|
|
1074
1556
|
const hasPending = files.some((f) => f.state === "pending");
|
|
1557
|
+
const pendingCount = files.filter((f) => f.state === "pending").length;
|
|
1558
|
+
const doneCount = files.filter((f) => f.state === "done").length;
|
|
1559
|
+
const errorCount = files.filter((f) => f.state === "error").length;
|
|
1075
1560
|
const accept = pickerOptions?.accept?.join(",");
|
|
1561
|
+
const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1;
|
|
1076
1562
|
const editingEntry = files.find((f) => f.id === editingId) ?? null;
|
|
1563
|
+
const offerCamera = shouldOfferCamera(pickerOptions);
|
|
1564
|
+
const title = dialogTitle(
|
|
1565
|
+
isUploading,
|
|
1566
|
+
isDone,
|
|
1567
|
+
files.length,
|
|
1568
|
+
doneCount,
|
|
1569
|
+
errorCount,
|
|
1570
|
+
editingId,
|
|
1571
|
+
cameraOpen
|
|
1572
|
+
);
|
|
1077
1573
|
if (!open) return null;
|
|
1078
1574
|
return (
|
|
1079
1575
|
/* Backdrop — clicking outside the panel dismisses the modal */
|
|
1080
|
-
/* @__PURE__ */ (0,
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1081
1577
|
"div",
|
|
1082
1578
|
{
|
|
1083
1579
|
className: "uploader-backdrop",
|
|
1084
1580
|
"data-testid": "picker-backdrop",
|
|
1581
|
+
style: themeToVars(theme),
|
|
1085
1582
|
onClick: handleClose,
|
|
1086
|
-
children: /* @__PURE__ */ (0,
|
|
1583
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1087
1584
|
"div",
|
|
1088
1585
|
{
|
|
1089
1586
|
ref: panelRef,
|
|
1090
1587
|
role: "dialog",
|
|
1091
1588
|
"aria-modal": "true",
|
|
1092
1589
|
"aria-labelledby": titleId,
|
|
1590
|
+
"data-drag-over": isDragOver && !editingEntry && files.length === 0 ? "true" : void 0,
|
|
1093
1591
|
className: ["uploader-picker-panel", className ?? ""].filter(Boolean).join(" "),
|
|
1094
1592
|
style,
|
|
1095
1593
|
"data-testid": "picker-panel",
|
|
1096
1594
|
onClick: (e) => e.stopPropagation(),
|
|
1097
1595
|
children: [
|
|
1098
|
-
/* @__PURE__ */ (0,
|
|
1099
|
-
/* @__PURE__ */ (0,
|
|
1100
|
-
/* @__PURE__ */ (0,
|
|
1596
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "uploader-picker-header", children: [
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { id: titleId, className: "uploader-picker-title", children: title }),
|
|
1598
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1101
1599
|
"button",
|
|
1102
1600
|
{
|
|
1103
1601
|
type: "button",
|
|
1104
1602
|
className: "uploader-btn-close",
|
|
1105
1603
|
"aria-label": "Close picker",
|
|
1106
1604
|
onClick: handleClose,
|
|
1107
|
-
children: "\
|
|
1605
|
+
children: "\u2715"
|
|
1108
1606
|
}
|
|
1109
1607
|
)
|
|
1110
1608
|
] }),
|
|
1111
|
-
editingEntry ? /* @__PURE__ */ (0,
|
|
1609
|
+
editingEntry ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1112
1610
|
ImageEditor,
|
|
1113
1611
|
{
|
|
1114
1612
|
file: editingEntry.file,
|
|
@@ -1118,160 +1616,134 @@ function PickerOverlay({
|
|
|
1118
1616
|
setEditingId(null);
|
|
1119
1617
|
}
|
|
1120
1618
|
}
|
|
1121
|
-
) :
|
|
1122
|
-
/*
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
className: [
|
|
1126
|
-
"uploader-dropzone",
|
|
1127
|
-
isDragOver ? "uploader-drag-over" : ""
|
|
1128
|
-
].filter(Boolean).join(" "),
|
|
1129
|
-
role: "button",
|
|
1130
|
-
tabIndex: 0,
|
|
1131
|
-
"aria-label": "Drop files here or click to browse",
|
|
1132
|
-
onDragEnter: handleDragEnter,
|
|
1133
|
-
onDragLeave: handleDragLeave,
|
|
1134
|
-
onDragOver: handleDragOver,
|
|
1135
|
-
onDrop: handleDrop,
|
|
1136
|
-
onClick: openFileDialog,
|
|
1137
|
-
onKeyDown: (e) => {
|
|
1138
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
1139
|
-
e.preventDefault();
|
|
1140
|
-
openFileDialog();
|
|
1141
|
-
}
|
|
1142
|
-
},
|
|
1143
|
-
children: [
|
|
1144
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uploader-dropzone-icon", "aria-hidden": "true", children: "\u2191" }),
|
|
1145
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "uploader-dropzone-label", children: "Drag & drop files here" }),
|
|
1146
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1147
|
-
"button",
|
|
1148
|
-
{
|
|
1149
|
-
type: "button",
|
|
1150
|
-
className: "uploader-btn-browse",
|
|
1151
|
-
onClick: (e) => {
|
|
1152
|
-
e.stopPropagation();
|
|
1153
|
-
openFileDialog();
|
|
1154
|
-
},
|
|
1155
|
-
children: "Browse files"
|
|
1156
|
-
}
|
|
1157
|
-
)
|
|
1158
|
-
]
|
|
1159
|
-
}
|
|
1160
|
-
),
|
|
1161
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1162
|
-
"input",
|
|
1619
|
+
) : cameraOpen ? (
|
|
1620
|
+
/* ── Camera capture view ───────────────────────────────────────── */
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1622
|
+
CameraCapture,
|
|
1163
1623
|
{
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
"aria-hidden": "true",
|
|
1170
|
-
tabIndex: -1,
|
|
1171
|
-
onChange: handleInputChange
|
|
1172
|
-
}
|
|
1173
|
-
),
|
|
1174
|
-
files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "uploader-file-list", role: "list", "aria-label": "Selected files", children: files.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: `uploader-file-item uploader-file-${entry.state}`, children: [
|
|
1175
|
-
entry.previewUrl && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1176
|
-
"img",
|
|
1177
|
-
{
|
|
1178
|
-
src: entry.previewUrl,
|
|
1179
|
-
alt: "",
|
|
1180
|
-
"aria-hidden": "true",
|
|
1181
|
-
className: "uploader-file-thumb"
|
|
1624
|
+
facingMode: pickerOptions?.cameraFacingMode,
|
|
1625
|
+
onCancel: () => setCameraOpen(false),
|
|
1626
|
+
onCapture: (file) => {
|
|
1627
|
+
addFiles([file]);
|
|
1628
|
+
setCameraOpen(false);
|
|
1182
1629
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1630
|
+
}
|
|
1631
|
+
)
|
|
1632
|
+
) : isDone ? (
|
|
1633
|
+
/* ── Done state ─────────────────────────────────────────────────── */
|
|
1634
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "uploader-done-body", children: [
|
|
1635
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "uploader-done-subtitle", children: [
|
|
1636
|
+
files.length,
|
|
1637
|
+
" file",
|
|
1638
|
+
files.length !== 1 ? "s" : "",
|
|
1639
|
+
" delivered to your bucket"
|
|
1188
1640
|
] }),
|
|
1189
|
-
|
|
1190
|
-
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "uploader-file-list uploader-done-list", role: "list", "aria-label": "Uploaded files", children: files.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1642
|
+
FileQueueRow,
|
|
1191
1643
|
{
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
),
|
|
1198
|
-
/* @__PURE__ */ (0,
|
|
1199
|
-
entry.previewUrl && (entry.state === "pending" || entry.state === "error") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1200
|
-
"button",
|
|
1201
|
-
{
|
|
1202
|
-
type: "button",
|
|
1203
|
-
className: "uploader-btn-edit",
|
|
1204
|
-
"aria-label": `Edit ${entry.file.name}`,
|
|
1205
|
-
onClick: () => setEditingId(entry.id),
|
|
1206
|
-
children: "Edit"
|
|
1207
|
-
}
|
|
1208
|
-
),
|
|
1209
|
-
entry.state === "error" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1210
|
-
"button",
|
|
1211
|
-
{
|
|
1212
|
-
type: "button",
|
|
1213
|
-
className: "uploader-btn-retry",
|
|
1214
|
-
"aria-label": `Retry uploading ${entry.file.name}`,
|
|
1215
|
-
onClick: () => retryFile(entry.id),
|
|
1216
|
-
children: "Retry"
|
|
1217
|
-
}
|
|
1218
|
-
),
|
|
1219
|
-
entry.state !== "uploading" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1220
|
-
"button",
|
|
1221
|
-
{
|
|
1222
|
-
type: "button",
|
|
1223
|
-
className: "uploader-btn-remove",
|
|
1224
|
-
"aria-label": `Remove ${entry.file.name}`,
|
|
1225
|
-
onClick: () => removeFile(entry.id),
|
|
1226
|
-
children: "\xD7"
|
|
1227
|
-
}
|
|
1228
|
-
)
|
|
1229
|
-
] })
|
|
1230
|
-
] }, entry.id)) }),
|
|
1231
|
-
isUploading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "uploader-aggregate-progress", role: "status", "aria-live": "polite", children: [
|
|
1232
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("progress", { value: progress, max: 100, "aria-label": "Overall upload progress" }),
|
|
1233
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "aria-live": "polite", children: [
|
|
1234
|
-
progress,
|
|
1235
|
-
"%"
|
|
1236
|
-
] })
|
|
1237
|
-
] }),
|
|
1238
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "uploader-picker-footer", children: [
|
|
1239
|
-
hasPending && !isUploading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1644
|
+
entry,
|
|
1645
|
+
onRemove: removeFile,
|
|
1646
|
+
onRetry: retryFile
|
|
1647
|
+
},
|
|
1648
|
+
entry.id
|
|
1649
|
+
)) }),
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1240
1651
|
"button",
|
|
1241
1652
|
{
|
|
1242
1653
|
type: "button",
|
|
1243
|
-
className: "uploader-btn-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1654
|
+
className: "uploader-btn-primary uploader-btn-done-full",
|
|
1655
|
+
onClick: onClose,
|
|
1656
|
+
children: "Done"
|
|
1657
|
+
}
|
|
1658
|
+
)
|
|
1659
|
+
] })
|
|
1660
|
+
) : (
|
|
1661
|
+
/* ── Normal (idle / uploading / error) view ─────────────────────── */
|
|
1662
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1663
|
+
files.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1664
|
+
Dropzone,
|
|
1665
|
+
{
|
|
1666
|
+
onFiles: addFiles,
|
|
1667
|
+
isDragOver,
|
|
1668
|
+
onDragEnter: handleDragEnter,
|
|
1669
|
+
onDragLeave: handleDragLeave,
|
|
1670
|
+
onDragOver: handleDragOver,
|
|
1671
|
+
onDrop: handleDrop,
|
|
1672
|
+
accept,
|
|
1673
|
+
multiple,
|
|
1674
|
+
onCamera: offerCamera ? () => setCameraOpen(true) : void 0,
|
|
1675
|
+
hint: "PNG, JPG, PDF, ZIP \xB7 up to 50 MB"
|
|
1252
1676
|
}
|
|
1253
1677
|
),
|
|
1254
|
-
|
|
1255
|
-
"
|
|
1678
|
+
files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1679
|
+
"ul",
|
|
1256
1680
|
{
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1681
|
+
className: "uploader-file-list",
|
|
1682
|
+
role: "list",
|
|
1683
|
+
"aria-label": "Selected files",
|
|
1684
|
+
"aria-live": "polite",
|
|
1685
|
+
children: files.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1686
|
+
FileQueueRow,
|
|
1687
|
+
{
|
|
1688
|
+
entry,
|
|
1689
|
+
onRemove: removeFile,
|
|
1690
|
+
onRetry: retryFile,
|
|
1691
|
+
onEdit: setEditingId
|
|
1692
|
+
},
|
|
1693
|
+
entry.id
|
|
1694
|
+
))
|
|
1261
1695
|
}
|
|
1262
1696
|
),
|
|
1263
|
-
|
|
1264
|
-
"
|
|
1697
|
+
isUploading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1698
|
+
"div",
|
|
1265
1699
|
{
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1700
|
+
className: "uploader-aggregate-progress",
|
|
1701
|
+
role: "status",
|
|
1702
|
+
"aria-live": "polite",
|
|
1703
|
+
children: [
|
|
1704
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uploader-aggregate-progress-label", children: "Total progress" }),
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1706
|
+
ProgressBar,
|
|
1707
|
+
{
|
|
1708
|
+
value: progress,
|
|
1709
|
+
label: "Overall upload progress",
|
|
1710
|
+
size: "lg"
|
|
1711
|
+
}
|
|
1712
|
+
),
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "uploader-aggregate-progress-pct", children: [
|
|
1714
|
+
progress,
|
|
1715
|
+
"%"
|
|
1716
|
+
] })
|
|
1717
|
+
]
|
|
1271
1718
|
}
|
|
1272
|
-
)
|
|
1719
|
+
),
|
|
1720
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "uploader-picker-footer", children: [
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uploader-picker-footer-esc", children: "Esc to close" }),
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "uploader-picker-footer-actions", children: [
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1724
|
+
"button",
|
|
1725
|
+
{
|
|
1726
|
+
type: "button",
|
|
1727
|
+
className: "uploader-btn-secondary uploader-btn-cancel",
|
|
1728
|
+
disabled: isUploading,
|
|
1729
|
+
onClick: handleClose,
|
|
1730
|
+
children: "Cancel"
|
|
1731
|
+
}
|
|
1732
|
+
),
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1734
|
+
"button",
|
|
1735
|
+
{
|
|
1736
|
+
type: "button",
|
|
1737
|
+
className: "uploader-btn-primary uploader-btn-upload",
|
|
1738
|
+
disabled: isUploading || !hasPending,
|
|
1739
|
+
onClick: () => void upload(),
|
|
1740
|
+
children: isUploading ? "Uploading\u2026" : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? "s" : ""}` : ""}`
|
|
1741
|
+
}
|
|
1742
|
+
)
|
|
1743
|
+
] })
|
|
1744
|
+
] })
|
|
1273
1745
|
] })
|
|
1274
|
-
|
|
1746
|
+
)
|
|
1275
1747
|
]
|
|
1276
1748
|
}
|
|
1277
1749
|
)
|
|
@@ -1281,8 +1753,8 @@ function PickerOverlay({
|
|
|
1281
1753
|
}
|
|
1282
1754
|
|
|
1283
1755
|
// src/react/DropPane.tsx
|
|
1284
|
-
var
|
|
1285
|
-
var
|
|
1756
|
+
var import_react7 = require("react");
|
|
1757
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1286
1758
|
function DropPane({
|
|
1287
1759
|
apikey,
|
|
1288
1760
|
apiUrl,
|
|
@@ -1292,12 +1764,14 @@ function DropPane({
|
|
|
1292
1764
|
onFileUploadFinished,
|
|
1293
1765
|
onFileUploadFailed,
|
|
1294
1766
|
onCancel,
|
|
1767
|
+
hint = "or click to browse \xB7 max 50 MB",
|
|
1768
|
+
theme,
|
|
1295
1769
|
className,
|
|
1296
1770
|
style
|
|
1297
1771
|
}) {
|
|
1298
|
-
const
|
|
1299
|
-
const
|
|
1300
|
-
const
|
|
1772
|
+
const [isDragOver, setIsDragOver] = (0, import_react7.useState)(false);
|
|
1773
|
+
const [cameraOpen, setCameraOpen] = (0, import_react7.useState)(false);
|
|
1774
|
+
const rootStyle = { ...themeToVars(theme), ...style };
|
|
1301
1775
|
const { files, addFiles, removeFile, retryFile, upload, progress, isUploading, isDone } = usePicker({
|
|
1302
1776
|
apikey,
|
|
1303
1777
|
apiUrl,
|
|
@@ -1307,21 +1781,21 @@ function DropPane({
|
|
|
1307
1781
|
onFileUploadFinished,
|
|
1308
1782
|
onFileUploadFailed
|
|
1309
1783
|
});
|
|
1310
|
-
const handleDragEnter = (0,
|
|
1784
|
+
const handleDragEnter = (0, import_react7.useCallback)((e) => {
|
|
1311
1785
|
e.preventDefault();
|
|
1312
1786
|
e.stopPropagation();
|
|
1313
1787
|
setIsDragOver(true);
|
|
1314
1788
|
}, []);
|
|
1315
|
-
const handleDragLeave = (0,
|
|
1789
|
+
const handleDragLeave = (0, import_react7.useCallback)((e) => {
|
|
1316
1790
|
e.preventDefault();
|
|
1317
1791
|
e.stopPropagation();
|
|
1318
1792
|
setIsDragOver(false);
|
|
1319
1793
|
}, []);
|
|
1320
|
-
const handleDragOver = (0,
|
|
1794
|
+
const handleDragOver = (0, import_react7.useCallback)((e) => {
|
|
1321
1795
|
e.preventDefault();
|
|
1322
1796
|
e.stopPropagation();
|
|
1323
1797
|
}, []);
|
|
1324
|
-
const handleDrop = (0,
|
|
1798
|
+
const handleDrop = (0, import_react7.useCallback)(
|
|
1325
1799
|
(e) => {
|
|
1326
1800
|
e.preventDefault();
|
|
1327
1801
|
e.stopPropagation();
|
|
@@ -1332,141 +1806,126 @@ function DropPane({
|
|
|
1332
1806
|
},
|
|
1333
1807
|
[addFiles]
|
|
1334
1808
|
);
|
|
1335
|
-
const handleInputChange = (0, import_react4.useCallback)(
|
|
1336
|
-
(e) => {
|
|
1337
|
-
if (e.target.files && e.target.files.length > 0) {
|
|
1338
|
-
addFiles(e.target.files);
|
|
1339
|
-
e.target.value = "";
|
|
1340
|
-
}
|
|
1341
|
-
},
|
|
1342
|
-
[addFiles]
|
|
1343
|
-
);
|
|
1344
|
-
const openFileDialog = (0, import_react4.useCallback)(() => {
|
|
1345
|
-
inputRef.current?.click();
|
|
1346
|
-
}, []);
|
|
1347
1809
|
const hasPending = files.some((f) => f.state === "pending");
|
|
1810
|
+
const pendingCount = files.filter((f) => f.state === "pending").length;
|
|
1348
1811
|
const accept = pickerOptions?.accept?.join(",");
|
|
1349
|
-
|
|
1812
|
+
const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1;
|
|
1813
|
+
const offerCamera = shouldOfferCamera(pickerOptions);
|
|
1814
|
+
if (cameraOpen) {
|
|
1815
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1816
|
+
"div",
|
|
1817
|
+
{
|
|
1818
|
+
className: ["uploader-drop-pane", className ?? ""].filter(Boolean).join(" "),
|
|
1819
|
+
"data-testid": "drop-pane",
|
|
1820
|
+
style: rootStyle,
|
|
1821
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1822
|
+
CameraCapture,
|
|
1823
|
+
{
|
|
1824
|
+
facingMode: pickerOptions?.cameraFacingMode,
|
|
1825
|
+
onCancel: () => setCameraOpen(false),
|
|
1826
|
+
onCapture: (file) => {
|
|
1827
|
+
addFiles([file]);
|
|
1828
|
+
setCameraOpen(false);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
)
|
|
1832
|
+
}
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1350
1836
|
"div",
|
|
1351
1837
|
{
|
|
1352
|
-
className: ["uploader-drop-pane",
|
|
1353
|
-
|
|
1838
|
+
className: ["uploader-drop-pane", className ?? ""].filter(Boolean).join(" "),
|
|
1839
|
+
"data-drag-over": isDragOver ? "true" : void 0,
|
|
1354
1840
|
"data-testid": "drop-pane",
|
|
1841
|
+
style: rootStyle,
|
|
1355
1842
|
children: [
|
|
1356
|
-
/* @__PURE__ */ (0,
|
|
1357
|
-
|
|
1843
|
+
files.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1844
|
+
Dropzone,
|
|
1358
1845
|
{
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
tabIndex: 0,
|
|
1362
|
-
"aria-label": "Drop files here or click to browse",
|
|
1846
|
+
onFiles: addFiles,
|
|
1847
|
+
isDragOver,
|
|
1363
1848
|
onDragEnter: handleDragEnter,
|
|
1364
1849
|
onDragLeave: handleDragLeave,
|
|
1365
1850
|
onDragOver: handleDragOver,
|
|
1366
1851
|
onDrop: handleDrop,
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
openFileDialog();
|
|
1372
|
-
}
|
|
1373
|
-
},
|
|
1374
|
-
children: [
|
|
1375
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-dropzone-icon", "aria-hidden": "true", children: "\u2191" }),
|
|
1376
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "uploader-dropzone-label", children: [
|
|
1377
|
-
"Drag & drop files here or",
|
|
1378
|
-
" ",
|
|
1379
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: inputId, className: "uploader-browse-link", children: "browse" })
|
|
1380
|
-
] })
|
|
1381
|
-
]
|
|
1852
|
+
accept,
|
|
1853
|
+
multiple,
|
|
1854
|
+
onCamera: offerCamera ? () => setCameraOpen(true) : void 0,
|
|
1855
|
+
hint
|
|
1382
1856
|
}
|
|
1383
1857
|
),
|
|
1384
|
-
/* @__PURE__ */ (0,
|
|
1385
|
-
"
|
|
1858
|
+
files.length > 0 && isDragOver && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1859
|
+
"div",
|
|
1386
1860
|
{
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
className: "uploader-file-input",
|
|
1861
|
+
className: "uploader-drop-pane-drag-overlay",
|
|
1862
|
+
onDragEnter: handleDragEnter,
|
|
1863
|
+
onDragLeave: handleDragLeave,
|
|
1864
|
+
onDragOver: handleDragOver,
|
|
1865
|
+
onDrop: handleDrop,
|
|
1393
1866
|
"aria-hidden": "true",
|
|
1394
|
-
|
|
1395
|
-
onChange: handleInputChange
|
|
1867
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uploader-drop-pane-drag-label", children: "Drop to add files" })
|
|
1396
1868
|
}
|
|
1397
1869
|
),
|
|
1398
|
-
files.length > 0 && /* @__PURE__ */ (0,
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
),
|
|
1408
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "uploader-file-info", children: [
|
|
1409
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-file-name", children: entry.file.name }),
|
|
1410
|
-
entry.state === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-file-error", role: "alert", children: entry.error ?? "Upload failed" }),
|
|
1411
|
-
entry.state === "done" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "uploader-file-done", children: "Uploaded" })
|
|
1412
|
-
] }),
|
|
1413
|
-
entry.state === "uploading" && entry.progress != null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1414
|
-
"progress",
|
|
1415
|
-
{
|
|
1416
|
-
className: "uploader-file-progress",
|
|
1417
|
-
value: entry.progress,
|
|
1418
|
-
max: 100,
|
|
1419
|
-
"aria-label": `${entry.file.name} upload progress`
|
|
1420
|
-
}
|
|
1421
|
-
),
|
|
1422
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "uploader-file-actions", children: [
|
|
1423
|
-
entry.state === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1424
|
-
"button",
|
|
1425
|
-
{
|
|
1426
|
-
type: "button",
|
|
1427
|
-
className: "uploader-btn-retry",
|
|
1428
|
-
"aria-label": `Retry uploading ${entry.file.name}`,
|
|
1429
|
-
onClick: () => retryFile(entry.id),
|
|
1430
|
-
children: "Retry"
|
|
1431
|
-
}
|
|
1432
|
-
),
|
|
1433
|
-
entry.state !== "uploading" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1434
|
-
"button",
|
|
1870
|
+
files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1871
|
+
"ul",
|
|
1872
|
+
{
|
|
1873
|
+
className: "uploader-file-list uploader-drop-pane-file-list",
|
|
1874
|
+
role: "list",
|
|
1875
|
+
"aria-label": "Selected files",
|
|
1876
|
+
"aria-live": "polite",
|
|
1877
|
+
children: files.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1878
|
+
FileQueueRow,
|
|
1435
1879
|
{
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
"
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1880
|
+
entry,
|
|
1881
|
+
onRemove: removeFile,
|
|
1882
|
+
onRetry: retryFile
|
|
1883
|
+
},
|
|
1884
|
+
entry.id
|
|
1885
|
+
))
|
|
1886
|
+
}
|
|
1887
|
+
),
|
|
1888
|
+
isUploading && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1889
|
+
"div",
|
|
1890
|
+
{
|
|
1891
|
+
className: "uploader-aggregate-progress",
|
|
1892
|
+
role: "status",
|
|
1893
|
+
"aria-live": "polite",
|
|
1894
|
+
children: [
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1896
|
+
ProgressBar,
|
|
1897
|
+
{
|
|
1898
|
+
value: progress,
|
|
1899
|
+
label: "Overall upload progress",
|
|
1900
|
+
size: "lg"
|
|
1901
|
+
}
|
|
1902
|
+
),
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "uploader-aggregate-progress-pct", children: [
|
|
1904
|
+
progress,
|
|
1905
|
+
"%"
|
|
1906
|
+
] })
|
|
1907
|
+
]
|
|
1908
|
+
}
|
|
1909
|
+
),
|
|
1910
|
+
files.length > 0 && !isDone && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "uploader-drop-pane-actions", children: [
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1454
1912
|
"button",
|
|
1455
1913
|
{
|
|
1456
1914
|
type: "button",
|
|
1457
|
-
className: "uploader-btn-
|
|
1915
|
+
className: "uploader-btn-secondary uploader-btn-cancel",
|
|
1458
1916
|
disabled: isUploading,
|
|
1459
|
-
onClick:
|
|
1460
|
-
children: "
|
|
1917
|
+
onClick: onCancel,
|
|
1918
|
+
children: "Cancel"
|
|
1461
1919
|
}
|
|
1462
1920
|
),
|
|
1463
|
-
|
|
1921
|
+
hasPending && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1464
1922
|
"button",
|
|
1465
1923
|
{
|
|
1466
1924
|
type: "button",
|
|
1467
|
-
className: "uploader-btn-
|
|
1468
|
-
|
|
1469
|
-
|
|
1925
|
+
className: "uploader-btn-primary uploader-btn-upload",
|
|
1926
|
+
disabled: isUploading,
|
|
1927
|
+
onClick: () => void upload(),
|
|
1928
|
+
children: isUploading ? "Uploading\u2026" : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? "s" : ""}` : ""}`
|
|
1470
1929
|
}
|
|
1471
1930
|
)
|
|
1472
1931
|
] })
|
|
@@ -1476,6 +1935,7 @@ function DropPane({
|
|
|
1476
1935
|
}
|
|
1477
1936
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1478
1937
|
0 && (module.exports = {
|
|
1938
|
+
CameraCapture,
|
|
1479
1939
|
DropPane,
|
|
1480
1940
|
ImageEditor,
|
|
1481
1941
|
PickerOverlay,
|
|
@@ -1484,10 +1944,13 @@ function DropPane({
|
|
|
1484
1944
|
editImage,
|
|
1485
1945
|
flip,
|
|
1486
1946
|
flop,
|
|
1947
|
+
isCameraSupported,
|
|
1487
1948
|
output,
|
|
1488
1949
|
quality,
|
|
1489
1950
|
resize,
|
|
1490
1951
|
rotate,
|
|
1952
|
+
shouldOfferCamera,
|
|
1953
|
+
themeToVars,
|
|
1491
1954
|
transformUrl,
|
|
1492
1955
|
usePicker
|
|
1493
1956
|
});
|