@sandemo/easy-annotator 0.1.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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/components/AnnotatorModal.d.ts +13 -0
  4. package/dist/components/AnnotatorModal.d.ts.map +1 -0
  5. package/dist/components/EasyAnnotatorUpload.d.ts +3 -0
  6. package/dist/components/EasyAnnotatorUpload.d.ts.map +1 -0
  7. package/dist/components/FilePreviewList.d.ts +10 -0
  8. package/dist/components/FilePreviewList.d.ts.map +1 -0
  9. package/dist/components/ImageAnnotator.d.ts +17 -0
  10. package/dist/components/ImageAnnotator.d.ts.map +1 -0
  11. package/dist/components/UploadDropzone.d.ts +10 -0
  12. package/dist/components/UploadDropzone.d.ts.map +1 -0
  13. package/dist/easy-annotator.cjs +2 -0
  14. package/dist/easy-annotator.cjs.map +1 -0
  15. package/dist/easy-annotator.css +1 -0
  16. package/dist/easy-annotator.d.ts +2 -0
  17. package/dist/easy-annotator.js +709 -0
  18. package/dist/easy-annotator.js.map +1 -0
  19. package/dist/hooks/useEasyAnnotator.d.ts +29 -0
  20. package/dist/hooks/useEasyAnnotator.d.ts.map +1 -0
  21. package/dist/hooks/useObjectUrls.d.ts +5 -0
  22. package/dist/hooks/useObjectUrls.d.ts.map +1 -0
  23. package/dist/i18n/labels.d.ts +5 -0
  24. package/dist/i18n/labels.d.ts.map +1 -0
  25. package/dist/index.d.ts +12 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/markerjs3-DI_cywfl.js +2669 -0
  28. package/dist/markerjs3-DI_cywfl.js.map +1 -0
  29. package/dist/markerjs3-DiaiYpgs.cjs +57 -0
  30. package/dist/markerjs3-DiaiYpgs.cjs.map +1 -0
  31. package/dist/services/defaultPayloadBuilder.d.ts +3 -0
  32. package/dist/services/defaultPayloadBuilder.d.ts.map +1 -0
  33. package/dist/styles.cjs +2 -0
  34. package/dist/styles.cjs.map +1 -0
  35. package/dist/styles.d.ts +1 -0
  36. package/dist/styles.d.ts.map +1 -0
  37. package/dist/styles.js +2 -0
  38. package/dist/styles.js.map +1 -0
  39. package/dist/types/index.d.ts +95 -0
  40. package/dist/types/index.d.ts.map +1 -0
  41. package/dist/utils/errors.d.ts +8 -0
  42. package/dist/utils/errors.d.ts.map +1 -0
  43. package/dist/utils/file.d.ts +10 -0
  44. package/dist/utils/file.d.ts.map +1 -0
  45. package/dist/utils/id.d.ts +2 -0
  46. package/dist/utils/id.d.ts.map +1 -0
  47. package/dist/utils/image.d.ts +3 -0
  48. package/dist/utils/image.d.ts.map +1 -0
  49. package/package.json +72 -0
@@ -0,0 +1,709 @@
1
+ import { jsxs as k, jsx as s } from "react/jsx-runtime";
2
+ import { useRef as I, useCallback as w, useEffect as B, useState as z, useMemo as X } from "react";
3
+ const W = [
4
+ "image/jpeg",
5
+ "image/jpg",
6
+ "image/png",
7
+ "image/webp",
8
+ "application/pdf",
9
+ "application/msword",
10
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
11
+ ], Y = 10 * 1024 * 1024, Z = 5, te = /* @__PURE__ */ new Set(["image/jpeg", "image/jpg", "image/png", "image/webp"]);
12
+ function ne(e) {
13
+ return te.has(e.toLowerCase());
14
+ }
15
+ function ae(e) {
16
+ const t = e.type.toLowerCase();
17
+ if (ne(t)) return "image";
18
+ if (t === "application/pdf") return "pdf";
19
+ if (t === "application/msword") return "doc";
20
+ if (t === "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
21
+ return "docx";
22
+ const a = e.name.toLowerCase().split(".").pop() ?? "";
23
+ return ["jpg", "jpeg", "png", "webp"].includes(a) ? "image" : a === "pdf" ? "pdf" : a === "doc" ? "doc" : a === "docx" ? "docx" : "doc";
24
+ }
25
+ function re(e, t) {
26
+ if (t.length === 0) return !0;
27
+ const a = e.type.toLowerCase();
28
+ if (a && t.some((m) => m.toLowerCase() === a)) return !0;
29
+ const o = e.name.toLowerCase().split(".").pop() ?? "", r = {
30
+ jpg: "image/jpeg",
31
+ jpeg: "image/jpeg",
32
+ png: "image/png",
33
+ webp: "image/webp",
34
+ pdf: "application/pdf",
35
+ doc: "application/msword",
36
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
37
+ }[o];
38
+ return r ? t.includes(r) : !1;
39
+ }
40
+ function q(e) {
41
+ return Math.round(e / (1024 * 1024) * 10) / 10;
42
+ }
43
+ function oe(e) {
44
+ const t = e.lastIndexOf(".");
45
+ return t <= 0 ? `${e}-annotated.png` : `${e.slice(0, t)}-annotated.png`;
46
+ }
47
+ const J = {
48
+ title: "Upload",
49
+ dropzoneText: "Drag & drop files here, or",
50
+ dropzoneButton: "browse",
51
+ annotate: "Annotate",
52
+ remove: "Remove",
53
+ upload: "Upload",
54
+ uploading: "Uploading…",
55
+ uploaded: "Uploaded",
56
+ retry: "Retry",
57
+ cancel: "Cancel",
58
+ save: "Save",
59
+ errorFileType: (e) => `File type not allowed: ${e}`,
60
+ errorFileTooLarge: (e, t) => `File too large: ${e} (max ${t} MB)`,
61
+ errorFileCount: (e) => `Maximum ${e} files`,
62
+ errorAnnotationFailed: "Failed to save annotation.",
63
+ errorUploadFailed: "Upload failed. Please retry.",
64
+ modalTitle: "Annotate image",
65
+ loadingAnnotator: "Loading annotator…",
66
+ filesSelected: (e) => `${e} file${e === 1 ? "" : "s"} selected`
67
+ }, Fe = {
68
+ title: "Unggah",
69
+ dropzoneText: "Seret & letakkan file di sini, atau",
70
+ dropzoneButton: "pilih file",
71
+ annotate: "Anotasi",
72
+ remove: "Hapus",
73
+ upload: "Unggah",
74
+ uploading: "Mengunggah…",
75
+ uploaded: "Terunggah",
76
+ retry: "Coba Lagi",
77
+ cancel: "Batal",
78
+ save: "Simpan",
79
+ errorFileType: (e) => `Tipe file tidak diizinkan: ${e}`,
80
+ errorFileTooLarge: (e, t) => `File terlalu besar: ${e} (maks ${t} MB)`,
81
+ errorFileCount: (e) => `Maksimal ${e} file`,
82
+ errorAnnotationFailed: "Gagal menyimpan anotasi.",
83
+ errorUploadFailed: "Unggah gagal. Silakan coba lagi.",
84
+ modalTitle: "Anotasi gambar",
85
+ loadingAnnotator: "Memuat anotator…",
86
+ filesSelected: (e) => `${e} file dipilih`
87
+ };
88
+ function ie(e) {
89
+ return e ? { ...J, ...e } : J;
90
+ }
91
+ function le(e, t) {
92
+ return { code: "FILE_TYPE_REJECTED", file: e, reason: t };
93
+ }
94
+ function se(e, t) {
95
+ return { code: "FILE_TOO_LARGE", file: e, maxBytes: t };
96
+ }
97
+ function ce(e) {
98
+ return { code: "FILE_COUNT_EXCEEDED", maxFiles: e };
99
+ }
100
+ function Q(e) {
101
+ return { code: "ANNOTATION_FAILED", cause: e };
102
+ }
103
+ function de(e, t) {
104
+ return { code: "UPLOAD_FAILED", cause: t, file: e };
105
+ }
106
+ function V(...e) {
107
+ var a;
108
+ (typeof process < "u" && ((a = process == null ? void 0 : process.env) != null && a.NODE_ENV) ? process.env.NODE_ENV : "development") !== "production" && console.warn("[easy-annotator]", ...e);
109
+ }
110
+ function ue() {
111
+ return typeof crypto < "u" && "randomUUID" in crypto ? crypto.randomUUID() : `ea_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
112
+ }
113
+ function pe() {
114
+ const e = I(/* @__PURE__ */ new Set()), t = w((o) => {
115
+ const l = URL.createObjectURL(o);
116
+ return e.current.add(l), l;
117
+ }, []), a = w((o) => {
118
+ o && e.current.has(o) && (URL.revokeObjectURL(o), e.current.delete(o));
119
+ }, []);
120
+ return B(() => {
121
+ const o = e.current;
122
+ return () => {
123
+ o.forEach((l) => URL.revokeObjectURL(l)), o.clear();
124
+ };
125
+ }, []), { create: t, revoke: a };
126
+ }
127
+ function me(e) {
128
+ const {
129
+ accept: t = W,
130
+ maxFileSize: a = Y,
131
+ maxFiles: o = Z,
132
+ multiple: l = !0,
133
+ ownerType: r,
134
+ ownerId: m,
135
+ fieldKey: c,
136
+ uploadHandler: y,
137
+ onError: b,
138
+ onUploaded: F,
139
+ onChange: U
140
+ } = e, [_, n] = z([]), [T, S] = z(!1), { create: d, revoke: u } = pe(), L = I(U), g = I(b), A = I(F);
141
+ B(() => {
142
+ L.current = U, g.current = b, A.current = F;
143
+ }, [U, b, F]), B(() => {
144
+ var p;
145
+ (p = L.current) == null || p.call(L, _);
146
+ }, [_]);
147
+ const O = w(
148
+ (p) => {
149
+ const h = Array.from(p);
150
+ n((f) => {
151
+ var E, M, K;
152
+ const v = [...f], i = Math.max(0, o - f.length);
153
+ let N = !1;
154
+ for (const C of h) {
155
+ if (!l && v.length >= 1) break;
156
+ if (v.length >= f.length + i) {
157
+ N = !0;
158
+ break;
159
+ }
160
+ if (!re(C, t)) {
161
+ (E = g.current) == null || E.call(g, le(C, `MIME ${C.type || "unknown"}`));
162
+ continue;
163
+ }
164
+ if (C.size > a) {
165
+ (M = g.current) == null || M.call(g, se(C, a));
166
+ continue;
167
+ }
168
+ const H = ae(C), ee = H === "image" ? d(C) : void 0;
169
+ v.push({
170
+ id: ue(),
171
+ originalFile: C,
172
+ fileType: H,
173
+ mimeType: C.type || "application/octet-stream",
174
+ originalFilename: C.name,
175
+ previewUrl: ee,
176
+ status: "idle"
177
+ });
178
+ }
179
+ return (N || h.length > i) && ((K = g.current) == null || K.call(g, ce(o))), v;
180
+ });
181
+ },
182
+ [t, d, a, o, l]
183
+ ), P = w(
184
+ (p) => {
185
+ n((h) => {
186
+ const f = h.find((v) => v.id === p);
187
+ return f && (u(f.previewUrl), u(f.annotatedPreviewUrl)), h.filter((v) => v.id !== p);
188
+ });
189
+ },
190
+ [u]
191
+ ), $ = w(
192
+ (p, h, f) => {
193
+ n(
194
+ (v) => v.map((i) => i.id !== p ? i : (u(i.annotatedPreviewUrl), {
195
+ ...i,
196
+ annotatedFile: h,
197
+ annotationState: f,
198
+ annotatedPreviewUrl: d(h)
199
+ }))
200
+ );
201
+ },
202
+ [d, u]
203
+ ), R = w(
204
+ (p) => {
205
+ n(
206
+ (h) => h.map((f) => {
207
+ if (f.id !== p) return f;
208
+ u(f.annotatedPreviewUrl);
209
+ const { annotatedFile: v, annotationState: i, annotatedPreviewUrl: N, ...E } = f;
210
+ return { ...E };
211
+ })
212
+ );
213
+ },
214
+ [u]
215
+ ), D = w(
216
+ (p, h, f) => {
217
+ n(
218
+ (v) => v.map((i) => i.id === p ? { ...i, status: h, errorMessage: f } : i)
219
+ );
220
+ },
221
+ []
222
+ ), j = w(
223
+ (p) => ({
224
+ ownerType: r,
225
+ ownerId: m,
226
+ fieldKey: c,
227
+ originalFile: p.originalFile,
228
+ annotatedFile: p.annotatedFile,
229
+ annotationState: p.annotationState,
230
+ fileType: p.fileType,
231
+ mimeType: p.mimeType,
232
+ originalFilename: p.originalFilename
233
+ }),
234
+ [c, m, r]
235
+ ), x = w(async () => {
236
+ var f, v;
237
+ S(!0);
238
+ const p = await new Promise((i) => {
239
+ n((N) => {
240
+ const E = N.filter((M) => M.status === "idle" || M.status === "error");
241
+ return i(E), N.map(
242
+ (M) => E.find((K) => K.id === M.id) ? { ...M, status: "uploading", errorMessage: void 0 } : M
243
+ );
244
+ });
245
+ }), h = [];
246
+ for (const i of p)
247
+ try {
248
+ const N = await y(j(i));
249
+ h.push(N), D(i.id, "done");
250
+ } catch (N) {
251
+ const E = N instanceof Error ? N.message : "Upload failed";
252
+ D(i.id, "error", E), (f = g.current) == null || f.call(g, de(i, N), i);
253
+ }
254
+ return S(!1), h.length > 0 && ((v = A.current) == null || v.call(A, h)), h;
255
+ }, [j, D, y]), G = X(
256
+ () => ({
257
+ total: _.length,
258
+ remainingSlots: Math.max(0, o - _.length),
259
+ maxFileSizeMb: q(a)
260
+ }),
261
+ [_.length, a, o]
262
+ );
263
+ return {
264
+ files: _,
265
+ isUploading: T,
266
+ stats: G,
267
+ addFiles: O,
268
+ removeFile: P,
269
+ setAnnotation: $,
270
+ clearAnnotation: R,
271
+ uploadAll: x
272
+ };
273
+ }
274
+ function ge({
275
+ accept: e,
276
+ multiple: t,
277
+ disabled: a = !1,
278
+ labels: o,
279
+ onFiles: l
280
+ }) {
281
+ const r = I(null), [m, c] = z(!1), y = w(() => {
282
+ var n;
283
+ a || (n = r.current) == null || n.click();
284
+ }, [a]), b = w(
285
+ (n) => {
286
+ n.target.files && n.target.files.length > 0 && l(n.target.files), n.target.value = "";
287
+ },
288
+ [l]
289
+ ), F = w(
290
+ (n) => {
291
+ n.preventDefault(), c(!1), !a && n.dataTransfer.files && n.dataTransfer.files.length > 0 && l(n.dataTransfer.files);
292
+ },
293
+ [a, l]
294
+ ), U = w(
295
+ (n) => {
296
+ n.preventDefault(), a || c(!0);
297
+ },
298
+ [a]
299
+ ), _ = w((n) => {
300
+ n.preventDefault(), c(!1);
301
+ }, []);
302
+ return /* @__PURE__ */ k(
303
+ "div",
304
+ {
305
+ className: `ea-dropzone${m ? " ea-dropzone--active" : ""}${a ? " ea-dropzone--disabled" : ""}`,
306
+ role: "button",
307
+ tabIndex: a ? -1 : 0,
308
+ "aria-disabled": a,
309
+ onClick: y,
310
+ onKeyDown: (n) => {
311
+ (n.key === "Enter" || n.key === " ") && (n.preventDefault(), y());
312
+ },
313
+ onDrop: F,
314
+ onDragOver: U,
315
+ onDragLeave: _,
316
+ children: [
317
+ /* @__PURE__ */ k("p", { className: "ea-dropzone__text", children: [
318
+ o.dropzoneText,
319
+ " ",
320
+ /* @__PURE__ */ s("span", { className: "ea-dropzone__button", children: o.dropzoneButton })
321
+ ] }),
322
+ /* @__PURE__ */ s(
323
+ "input",
324
+ {
325
+ ref: r,
326
+ type: "file",
327
+ accept: e.join(","),
328
+ multiple: t,
329
+ onChange: b,
330
+ className: "ea-dropzone__input",
331
+ disabled: a,
332
+ "aria-hidden": "true",
333
+ tabIndex: -1
334
+ }
335
+ )
336
+ ]
337
+ }
338
+ );
339
+ }
340
+ function fe(e) {
341
+ switch (e) {
342
+ case "pdf":
343
+ return "PDF";
344
+ case "doc":
345
+ return "DOC";
346
+ case "docx":
347
+ return "DOCX";
348
+ default:
349
+ return "IMG";
350
+ }
351
+ }
352
+ function he({
353
+ files: e,
354
+ labels: t,
355
+ onAnnotate: a,
356
+ onRemove: o,
357
+ disabled: l = !1
358
+ }) {
359
+ return e.length === 0 ? null : /* @__PURE__ */ s("ul", { className: "ea-list", role: "list", children: e.map((r) => {
360
+ const m = r.fileType === "image", c = r.annotatedPreviewUrl ?? r.previewUrl;
361
+ return /* @__PURE__ */ k("li", { className: "ea-list__item", "data-status": r.status, children: [
362
+ /* @__PURE__ */ s("div", { className: "ea-list__thumb", children: m && c ? /* @__PURE__ */ s("img", { src: c, alt: r.originalFilename }) : /* @__PURE__ */ s("span", { className: "ea-list__icon", children: fe(r.fileType) }) }),
363
+ /* @__PURE__ */ k("div", { className: "ea-list__meta", children: [
364
+ /* @__PURE__ */ s("p", { className: "ea-list__name", title: r.originalFilename, children: r.originalFilename }),
365
+ /* @__PURE__ */ k("p", { className: "ea-list__status", children: [
366
+ r.status === "uploading" && t.uploading,
367
+ r.status === "done" && t.uploaded,
368
+ r.status === "error" && (r.errorMessage ?? t.errorUploadFailed),
369
+ r.status === "idle" && r.annotatedFile && "✓ annotated"
370
+ ] })
371
+ ] }),
372
+ /* @__PURE__ */ k("div", { className: "ea-list__actions", children: [
373
+ m && /* @__PURE__ */ s(
374
+ "button",
375
+ {
376
+ type: "button",
377
+ className: "ea-btn ea-btn--ghost",
378
+ onClick: () => a(r.id),
379
+ disabled: l || r.status === "uploading",
380
+ children: t.annotate
381
+ }
382
+ ),
383
+ /* @__PURE__ */ s(
384
+ "button",
385
+ {
386
+ type: "button",
387
+ className: "ea-btn ea-btn--danger",
388
+ onClick: () => o(r.id),
389
+ disabled: l || r.status === "uploading",
390
+ "aria-label": `${t.remove} ${r.originalFilename}`,
391
+ children: t.remove
392
+ }
393
+ )
394
+ ] })
395
+ ] }, r.id);
396
+ }) });
397
+ }
398
+ function we({
399
+ src: e,
400
+ originalFilename: t,
401
+ initialState: a,
402
+ labels: o,
403
+ onReady: l,
404
+ onError: r
405
+ }) {
406
+ const m = I(null), c = I(null), y = I(null), b = I(null), [F, U] = z(!0), [_, n] = z(null);
407
+ return B(() => {
408
+ let T = !1;
409
+ async function S() {
410
+ try {
411
+ const d = await import("./markerjs3-DI_cywfl.js");
412
+ if (T) return;
413
+ const u = c.current, L = m.current;
414
+ if (!u || !L || (!u.complete || u.naturalWidth === 0) && (await new Promise((A, O) => {
415
+ u.addEventListener("load", () => A(), { once: !0 }), u.addEventListener("error", () => O(new Error("Image failed to load")), {
416
+ once: !0
417
+ });
418
+ }), T))
419
+ return;
420
+ const g = new d.MarkerArea();
421
+ if (L.appendChild(g), g.targetImage = u, a)
422
+ try {
423
+ g.restoreState(a, !1);
424
+ } catch (A) {
425
+ V("Failed to restore annotation state:", A);
426
+ }
427
+ y.current = g, b.current = d.Renderer, U(!1), l == null || l({
428
+ save: async () => {
429
+ const A = y.current, O = b.current, P = c.current;
430
+ if (!A || !O || !P)
431
+ throw new Error("Annotator is not ready.");
432
+ const $ = A.getState(), R = new O();
433
+ R.targetImage = P;
434
+ const D = await R.rasterize($);
435
+ return { file: await ve(
436
+ D,
437
+ oe(t)
438
+ ), state: $ };
439
+ }
440
+ });
441
+ } catch (d) {
442
+ if (T) return;
443
+ U(!1), n(o.errorAnnotationFailed), V("Failed to load marker.js:", d), r == null || r(Q(d));
444
+ }
445
+ }
446
+ return S(), () => {
447
+ var u;
448
+ T = !0;
449
+ const d = y.current;
450
+ d && ((u = m.current) != null && u.contains(d)) && m.current.removeChild(d), y.current = null, b.current = null;
451
+ };
452
+ }, [e]), /* @__PURE__ */ k("div", { className: "ea-annotator", children: [
453
+ /* @__PURE__ */ s("div", { className: "ea-annotator__stage", ref: m, children: /* @__PURE__ */ s(
454
+ "img",
455
+ {
456
+ ref: c,
457
+ src: e,
458
+ alt: "",
459
+ className: "ea-annotator__img",
460
+ crossOrigin: "anonymous"
461
+ }
462
+ ) }),
463
+ F && /* @__PURE__ */ s("div", { className: "ea-annotator__loading", children: o.loadingAnnotator }),
464
+ _ && /* @__PURE__ */ s("div", { className: "ea-annotator__error", children: _ })
465
+ ] });
466
+ }
467
+ async function ve(e, t) {
468
+ const o = await (await fetch(e)).blob();
469
+ return new File([o], t, { type: o.type || "image/png" });
470
+ }
471
+ function ye({
472
+ open: e,
473
+ src: t,
474
+ originalFilename: a,
475
+ initialState: o,
476
+ labels: l,
477
+ onSave: r,
478
+ onCancel: m,
479
+ onError: c
480
+ }) {
481
+ const y = I(null), b = I(null), [F, U] = z(!1), _ = w(async () => {
482
+ if (y.current) {
483
+ U(!0);
484
+ try {
485
+ const { file: n, state: T } = await y.current.save();
486
+ r(n, T);
487
+ } catch (n) {
488
+ c == null || c(n);
489
+ } finally {
490
+ U(!1);
491
+ }
492
+ }
493
+ }, [c, r]);
494
+ return B(() => {
495
+ var d;
496
+ if (!e) return;
497
+ const n = document.activeElement;
498
+ (d = b.current) == null || d.focus();
499
+ const T = (u) => {
500
+ u.key === "Escape" && m();
501
+ };
502
+ document.addEventListener("keydown", T);
503
+ const S = document.body.style.overflow;
504
+ return document.body.style.overflow = "hidden", () => {
505
+ var u;
506
+ document.removeEventListener("keydown", T), document.body.style.overflow = S, (u = n == null ? void 0 : n.focus) == null || u.call(n);
507
+ };
508
+ }, [m, e]), !e || !t ? null : /* @__PURE__ */ s(
509
+ "div",
510
+ {
511
+ className: "ea-modal",
512
+ role: "dialog",
513
+ "aria-modal": "true",
514
+ "aria-label": l.modalTitle,
515
+ onClick: (n) => {
516
+ n.target === n.currentTarget && m();
517
+ },
518
+ children: /* @__PURE__ */ k("div", { className: "ea-modal__dialog", ref: b, tabIndex: -1, children: [
519
+ /* @__PURE__ */ k("header", { className: "ea-modal__header", children: [
520
+ /* @__PURE__ */ s("h2", { className: "ea-modal__title", children: l.modalTitle }),
521
+ /* @__PURE__ */ s(
522
+ "button",
523
+ {
524
+ type: "button",
525
+ className: "ea-btn ea-btn--ghost",
526
+ onClick: m,
527
+ "aria-label": l.cancel,
528
+ children: "×"
529
+ }
530
+ )
531
+ ] }),
532
+ /* @__PURE__ */ s("div", { className: "ea-modal__body", children: /* @__PURE__ */ s(
533
+ we,
534
+ {
535
+ src: t,
536
+ originalFilename: a,
537
+ initialState: o,
538
+ labels: l,
539
+ onReady: (n) => {
540
+ y.current = n;
541
+ },
542
+ onError: c
543
+ }
544
+ ) }),
545
+ /* @__PURE__ */ k("footer", { className: "ea-modal__footer", children: [
546
+ /* @__PURE__ */ s(
547
+ "button",
548
+ {
549
+ type: "button",
550
+ className: "ea-btn ea-btn--ghost",
551
+ onClick: m,
552
+ disabled: F,
553
+ children: l.cancel
554
+ }
555
+ ),
556
+ /* @__PURE__ */ s(
557
+ "button",
558
+ {
559
+ type: "button",
560
+ className: "ea-btn ea-btn--primary",
561
+ onClick: _,
562
+ disabled: F,
563
+ children: F ? l.uploading : l.save
564
+ }
565
+ )
566
+ ] })
567
+ ] })
568
+ }
569
+ );
570
+ }
571
+ function Ue(e) {
572
+ const {
573
+ title: t,
574
+ ownerType: a,
575
+ ownerId: o,
576
+ fieldKey: l,
577
+ uploadHandler: r,
578
+ onUploaded: m,
579
+ onError: c,
580
+ onChange: y,
581
+ accept: b = W,
582
+ maxFileSize: F = Y,
583
+ maxFiles: U = Z,
584
+ multiple: _ = !0,
585
+ labels: n,
586
+ className: T,
587
+ disabled: S = !1
588
+ } = e, d = X(() => ie(n), [n]), u = t ?? d.title, {
589
+ files: L,
590
+ isUploading: g,
591
+ stats: A,
592
+ addFiles: O,
593
+ removeFile: P,
594
+ setAnnotation: $,
595
+ uploadAll: R
596
+ } = me({
597
+ accept: b,
598
+ maxFileSize: F,
599
+ maxFiles: U,
600
+ multiple: _,
601
+ ownerType: a,
602
+ ownerId: o,
603
+ fieldKey: l,
604
+ uploadHandler: r,
605
+ onError: c,
606
+ onUploaded: m,
607
+ onChange: y
608
+ }), [D, j] = z(null), x = X(
609
+ () => L.find((i) => i.id === D) ?? null,
610
+ [D, L]
611
+ ), G = w((i) => {
612
+ j(i);
613
+ }, []), p = w(() => {
614
+ j(null);
615
+ }, []), h = w(
616
+ (i, N) => {
617
+ D && $(D, i, N), j(null);
618
+ },
619
+ [D, $]
620
+ ), f = w(
621
+ (i) => {
622
+ c == null || c(Q(i));
623
+ },
624
+ [c]
625
+ ), v = !S && !g && L.some((i) => i.status === "idle" || i.status === "error");
626
+ return /* @__PURE__ */ k("section", { className: `ea-root${T ? ` ${T}` : ""}`, children: [
627
+ /* @__PURE__ */ k("header", { className: "ea-header", children: [
628
+ /* @__PURE__ */ s("h2", { className: "ea-title", children: u }),
629
+ /* @__PURE__ */ k("p", { className: "ea-subtitle", children: [
630
+ d.filesSelected(A.total),
631
+ " · max ",
632
+ q(F),
633
+ " MB · up to ",
634
+ U,
635
+ " ",
636
+ "files"
637
+ ] })
638
+ ] }),
639
+ /* @__PURE__ */ s(
640
+ ge,
641
+ {
642
+ accept: b,
643
+ multiple: _,
644
+ disabled: S || A.remainingSlots === 0,
645
+ labels: d,
646
+ onFiles: O
647
+ }
648
+ ),
649
+ /* @__PURE__ */ s(
650
+ he,
651
+ {
652
+ files: L,
653
+ labels: d,
654
+ onAnnotate: G,
655
+ onRemove: P,
656
+ disabled: S
657
+ }
658
+ ),
659
+ L.length > 0 && /* @__PURE__ */ s("div", { className: "ea-actions", children: /* @__PURE__ */ s(
660
+ "button",
661
+ {
662
+ type: "button",
663
+ className: "ea-btn ea-btn--primary",
664
+ onClick: () => {
665
+ R();
666
+ },
667
+ disabled: !v,
668
+ children: g ? d.uploading : d.upload
669
+ }
670
+ ) }),
671
+ /* @__PURE__ */ s(
672
+ ye,
673
+ {
674
+ open: !!x,
675
+ src: (x == null ? void 0 : x.previewUrl) ?? null,
676
+ originalFilename: (x == null ? void 0 : x.originalFilename) ?? "",
677
+ initialState: x == null ? void 0 : x.annotationState,
678
+ labels: d,
679
+ onSave: h,
680
+ onCancel: p,
681
+ onError: f
682
+ }
683
+ )
684
+ ] });
685
+ }
686
+ function Te(e) {
687
+ const t = new FormData();
688
+ return t.append("owner_type", e.ownerType), t.append("owner_id", String(e.ownerId)), t.append("field_key", e.fieldKey), t.append("original_file", e.originalFile, e.originalFile.name), e.annotatedFile && t.append("annotated_file", e.annotatedFile, e.annotatedFile.name), e.annotationState && t.append("annotation_state", JSON.stringify(e.annotationState)), t.append("file_type", e.fileType), t.append("mime_type", e.mimeType), t.append("original_filename", e.originalFilename), t;
689
+ }
690
+ export {
691
+ ye as AnnotatorModal,
692
+ W as DEFAULT_ACCEPT,
693
+ Z as DEFAULT_MAX_FILES,
694
+ Y as DEFAULT_MAX_FILE_SIZE,
695
+ Ue as EasyAnnotatorUpload,
696
+ he as FilePreviewList,
697
+ we as ImageAnnotator,
698
+ ge as UploadDropzone,
699
+ Te as buildFormDataPayload,
700
+ q as bytesToMb,
701
+ ae as classifyFile,
702
+ J as enLabels,
703
+ Fe as idLabels,
704
+ re as isAccepted,
705
+ ie as mergeLabels,
706
+ me as useEasyAnnotator,
707
+ pe as useObjectUrls
708
+ };
709
+ //# sourceMappingURL=easy-annotator.js.map