@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sandemo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @sandemo/easy-annotator
2
+
3
+ Reusable React/Next.js component for **file upload with image annotation**. Drop it into any form, point it at your own backend via `uploadHandler`, done.
4
+
5
+ - Image annotation before upload (powered by [marker.js 3](https://markerjs.com/), lazy-loaded)
6
+ - Backend-agnostic — works with S3, MinIO, R2, Laravel, Go Fiber, Next.js API routes, anything
7
+ - TypeScript-first, Next.js-friendly (App Router + Pages Router)
8
+ - PDF / DOC / DOCX passthrough (annotation skipped automatically)
9
+ - ~150 KB gzipped, zero runtime config
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @sandemo/easy-annotator
15
+ ```
16
+
17
+ ## Minimal usage
18
+
19
+ ```tsx
20
+ 'use client'
21
+
22
+ import { EasyAnnotatorUpload, buildFormDataPayload } from '@sandemo/easy-annotator'
23
+ import '@sandemo/easy-annotator/styles.css'
24
+
25
+ const uploadHandler = async (payload) => {
26
+ const formData = buildFormDataPayload(payload)
27
+ const res = await fetch('/api/evidences', { method: 'POST', body: formData })
28
+ if (!res.ok) throw new Error('Upload failed')
29
+ return res.json()
30
+ }
31
+
32
+ export default function MyForm() {
33
+ return (
34
+ <EasyAnnotatorUpload
35
+ title="Upload Lampiran"
36
+ ownerType="surat_rekomendasi"
37
+ ownerId={123}
38
+ fieldKey="hasil_pengamatan"
39
+ uploadHandler={uploadHandler}
40
+ onUploaded={(items) => console.log(items)}
41
+ />
42
+ )
43
+ }
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ - [Usage / quickstart](./docs/usage.md)
49
+ - [Next.js integration](./docs/nextjs.md)
50
+ - [Upload handler contract](./docs/upload-handler.md)
51
+ - [API reference](./docs/api-reference.md)
52
+
53
+ ## Contributing
54
+
55
+ Issues and pull requests are welcome at [github.com/sandemoit/easy-annotator](https://github.com/sandemoit/easy-annotator).
56
+
57
+ ## License
58
+
59
+ [MIT](./LICENSE) © Sandemo
@@ -0,0 +1,13 @@
1
+ import { LabelOverrides } from '../types';
2
+ export interface AnnotatorModalProps {
3
+ open: boolean;
4
+ src: string | null;
5
+ originalFilename: string;
6
+ initialState?: object;
7
+ labels: LabelOverrides;
8
+ onSave: (annotatedFile: File, annotationState: object) => void;
9
+ onCancel: () => void;
10
+ onError?: (err: unknown) => void;
11
+ }
12
+ export declare function AnnotatorModal({ open, src, originalFilename, initialState, labels, onSave, onCancel, onError, }: AnnotatorModalProps): import("react/jsx-runtime").JSX.Element | null;
13
+ //# sourceMappingURL=AnnotatorModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnnotatorModal.d.ts","sourceRoot":"","sources":["../../src/components/AnnotatorModal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,GAAG,EACH,gBAAgB,EAChB,YAAY,EACZ,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GACR,EAAE,mBAAmB,kDA6FrB"}
@@ -0,0 +1,3 @@
1
+ import { EasyAnnotatorUploadProps } from '../types';
2
+ export declare function EasyAnnotatorUpload(props: EasyAnnotatorUploadProps): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=EasyAnnotatorUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EasyAnnotatorUpload.d.ts","sourceRoot":"","sources":["../../src/components/EasyAnnotatorUpload.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AASxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAqIlE"}
@@ -0,0 +1,10 @@
1
+ import { EasyAnnotatorFile, LabelOverrides } from '../types';
2
+ export interface FilePreviewListProps {
3
+ files: EasyAnnotatorFile[];
4
+ labels: LabelOverrides;
5
+ onAnnotate: (id: string) => void;
6
+ onRemove: (id: string) => void;
7
+ disabled?: boolean;
8
+ }
9
+ export declare function FilePreviewList({ files, labels, onAnnotate, onRemove, disabled, }: FilePreviewListProps): import("react/jsx-runtime").JSX.Element | null;
10
+ //# sourceMappingURL=FilePreviewList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePreviewList.d.ts","sourceRoot":"","sources":["../../src/components/FilePreviewList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEjE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAeD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,MAAM,EACN,UAAU,EACV,QAAQ,EACR,QAAgB,GACjB,EAAE,oBAAoB,kDAsDtB"}
@@ -0,0 +1,17 @@
1
+ import { LabelOverrides } from '../types';
2
+ export interface ImageAnnotatorHandle {
3
+ save: () => Promise<{
4
+ file: File;
5
+ state: object;
6
+ }>;
7
+ }
8
+ export interface ImageAnnotatorProps {
9
+ src: string;
10
+ originalFilename: string;
11
+ initialState?: object;
12
+ labels: LabelOverrides;
13
+ onReady?: (handle: ImageAnnotatorHandle) => void;
14
+ onError?: (err: unknown) => void;
15
+ }
16
+ export declare function ImageAnnotator({ src, originalFilename, initialState, labels, onReady, onError, }: ImageAnnotatorProps): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=ImageAnnotator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageAnnotator.d.ts","sourceRoot":"","sources":["../../src/components/ImageAnnotator.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAI9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAChD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAmBD,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,gBAAgB,EAChB,YAAY,EACZ,MAAM,EACN,OAAO,EACP,OAAO,GACR,EAAE,mBAAmB,2CAwGrB"}
@@ -0,0 +1,10 @@
1
+ import { LabelOverrides } from '../types';
2
+ export interface UploadDropzoneProps {
3
+ accept: string[];
4
+ multiple: boolean;
5
+ disabled?: boolean;
6
+ labels: LabelOverrides;
7
+ onFiles: (files: FileList | File[]) => void;
8
+ }
9
+ export declare function UploadDropzone({ accept, multiple, disabled, labels, onFiles, }: UploadDropzoneProps): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=UploadDropzone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UploadDropzone.d.ts","sourceRoot":"","sources":["../../src/components/UploadDropzone.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI,CAAA;CAC5C;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,QAAgB,EAChB,MAAM,EACN,OAAO,GACR,EAAE,mBAAmB,2CAgFrB"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),a=require("react"),B=["image/jpeg","image/jpg","image/png","image/webp","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"],X=10*1024*1024,K=5,re=new Set(["image/jpeg","image/jpg","image/png","image/webp"]);function se(e){return re.has(e.toLowerCase())}function J(e){const t=e.type.toLowerCase();if(se(t))return"image";if(t==="application/pdf")return"pdf";if(t==="application/msword")return"doc";if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document")return"docx";const o=e.name.toLowerCase().split(".").pop()??"";return["jpg","jpeg","png","webp"].includes(o)?"image":o==="pdf"?"pdf":o==="doc"?"doc":o==="docx"?"docx":"doc"}function V(e,t){if(t.length===0)return!0;const o=e.type.toLowerCase();if(o&&t.some(f=>f.toLowerCase()===o))return!0;const i=e.name.toLowerCase().split(".").pop()??"",s={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}[i];return s?t.includes(s):!1}function G(e){return Math.round(e/(1024*1024)*10)/10}function ie(e){const t=e.lastIndexOf(".");return t<=0?`${e}-annotated.png`:`${e.slice(0,t)}-annotated.png`}const z={title:"Upload",dropzoneText:"Drag & drop files here, or",dropzoneButton:"browse",annotate:"Annotate",remove:"Remove",upload:"Upload",uploading:"Uploading…",uploaded:"Uploaded",retry:"Retry",cancel:"Cancel",save:"Save",errorFileType:e=>`File type not allowed: ${e}`,errorFileTooLarge:(e,t)=>`File too large: ${e} (max ${t} MB)`,errorFileCount:e=>`Maximum ${e} files`,errorAnnotationFailed:"Failed to save annotation.",errorUploadFailed:"Upload failed. Please retry.",modalTitle:"Annotate image",loadingAnnotator:"Loading annotator…",filesSelected:e=>`${e} file${e===1?"":"s"} selected`},le={title:"Unggah",dropzoneText:"Seret & letakkan file di sini, atau",dropzoneButton:"pilih file",annotate:"Anotasi",remove:"Hapus",upload:"Unggah",uploading:"Mengunggah…",uploaded:"Terunggah",retry:"Coba Lagi",cancel:"Batal",save:"Simpan",errorFileType:e=>`Tipe file tidak diizinkan: ${e}`,errorFileTooLarge:(e,t)=>`File terlalu besar: ${e} (maks ${t} MB)`,errorFileCount:e=>`Maksimal ${e} file`,errorAnnotationFailed:"Gagal menyimpan anotasi.",errorUploadFailed:"Unggah gagal. Silakan coba lagi.",modalTitle:"Anotasi gambar",loadingAnnotator:"Memuat anotator…",filesSelected:e=>`${e} file dipilih`};function W(e){return e?{...z,...e}:z}function ce(e,t){return{code:"FILE_TYPE_REJECTED",file:e,reason:t}}function de(e,t){return{code:"FILE_TOO_LARGE",file:e,maxBytes:t}}function ue(e){return{code:"FILE_COUNT_EXCEEDED",maxFiles:e}}function Z(e){return{code:"ANNOTATION_FAILED",cause:e}}function pe(e,t){return{code:"UPLOAD_FAILED",cause:t,file:e}}function H(...e){var o;(typeof process<"u"&&((o=process==null?void 0:process.env)!=null&&o.NODE_ENV)?process.env.NODE_ENV:"development")!=="production"&&console.warn("[easy-annotator]",...e)}function me(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`ea_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function Y(){const e=a.useRef(new Set),t=a.useCallback(i=>{const c=URL.createObjectURL(i);return e.current.add(c),c},[]),o=a.useCallback(i=>{i&&e.current.has(i)&&(URL.revokeObjectURL(i),e.current.delete(i))},[]);return a.useEffect(()=>{const i=e.current;return()=>{i.forEach(c=>URL.revokeObjectURL(c)),i.clear()}},[]),{create:t,revoke:o}}function Q(e){const{accept:t=B,maxFileSize:o=X,maxFiles:i=K,multiple:c=!0,ownerType:s,ownerId:f,fieldKey:d,uploadHandler:_,onError:x,onUploaded:w,onChange:F}=e,[v,n]=a.useState([]),[j,L]=a.useState(!1),{create:u,revoke:p}=Y(),A=a.useRef(F),g=a.useRef(x),U=a.useRef(w);a.useEffect(()=>{A.current=F,g.current=x,U.current=w},[F,x,w]),a.useEffect(()=>{var m;(m=A.current)==null||m.call(A,v)},[v]);const D=a.useCallback(m=>{const b=Array.from(m);n(h=>{var E,N,R;const y=[...h],l=Math.max(0,i-h.length);let k=!1;for(const S of b){if(!c&&y.length>=1)break;if(y.length>=h.length+l){k=!0;break}if(!V(S,t)){(E=g.current)==null||E.call(g,ce(S,`MIME ${S.type||"unknown"}`));continue}if(S.size>o){(N=g.current)==null||N.call(g,de(S,o));continue}const q=J(S),oe=q==="image"?u(S):void 0;y.push({id:me(),originalFile:S,fileType:q,mimeType:S.type||"application/octet-stream",originalFilename:S.name,previewUrl:oe,status:"idle"})}return(k||b.length>l)&&((R=g.current)==null||R.call(g,ue(i))),y})},[t,u,o,i,c]),O=a.useCallback(m=>{n(b=>{const h=b.find(y=>y.id===m);return h&&(p(h.previewUrl),p(h.annotatedPreviewUrl)),b.filter(y=>y.id!==m)})},[p]),I=a.useCallback((m,b,h)=>{n(y=>y.map(l=>l.id!==m?l:(p(l.annotatedPreviewUrl),{...l,annotatedFile:b,annotationState:h,annotatedPreviewUrl:u(b)})))},[u,p]),P=a.useCallback(m=>{n(b=>b.map(h=>{if(h.id!==m)return h;p(h.annotatedPreviewUrl);const{annotatedFile:y,annotationState:l,annotatedPreviewUrl:k,...E}=h;return{...E}}))},[p]),T=a.useCallback((m,b,h)=>{n(y=>y.map(l=>l.id===m?{...l,status:b,errorMessage:h}:l))},[]),M=a.useCallback(m=>({ownerType:s,ownerId:f,fieldKey:d,originalFile:m.originalFile,annotatedFile:m.annotatedFile,annotationState:m.annotationState,fileType:m.fileType,mimeType:m.mimeType,originalFilename:m.originalFilename}),[d,f,s]),C=a.useCallback(async()=>{var h,y;L(!0);const m=await new Promise(l=>{n(k=>{const E=k.filter(N=>N.status==="idle"||N.status==="error");return l(E),k.map(N=>E.find(R=>R.id===N.id)?{...N,status:"uploading",errorMessage:void 0}:N)})}),b=[];for(const l of m)try{const k=await _(M(l));b.push(k),T(l.id,"done")}catch(k){const E=k instanceof Error?k.message:"Upload failed";T(l.id,"error",E),(h=g.current)==null||h.call(g,pe(l,k),l)}return L(!1),b.length>0&&((y=U.current)==null||y.call(U,b)),b},[M,T,_]),$=a.useMemo(()=>({total:v.length,remainingSlots:Math.max(0,i-v.length),maxFileSizeMb:G(o)}),[v.length,o,i]);return{files:v,isUploading:j,stats:$,addFiles:D,removeFile:O,setAnnotation:I,clearAnnotation:P,uploadAll:C}}function ee({accept:e,multiple:t,disabled:o=!1,labels:i,onFiles:c}){const s=a.useRef(null),[f,d]=a.useState(!1),_=a.useCallback(()=>{var n;o||(n=s.current)==null||n.click()},[o]),x=a.useCallback(n=>{n.target.files&&n.target.files.length>0&&c(n.target.files),n.target.value=""},[c]),w=a.useCallback(n=>{n.preventDefault(),d(!1),!o&&n.dataTransfer.files&&n.dataTransfer.files.length>0&&c(n.dataTransfer.files)},[o,c]),F=a.useCallback(n=>{n.preventDefault(),o||d(!0)},[o]),v=a.useCallback(n=>{n.preventDefault(),d(!1)},[]);return r.jsxs("div",{className:`ea-dropzone${f?" ea-dropzone--active":""}${o?" ea-dropzone--disabled":""}`,role:"button",tabIndex:o?-1:0,"aria-disabled":o,onClick:_,onKeyDown:n=>{(n.key==="Enter"||n.key===" ")&&(n.preventDefault(),_())},onDrop:w,onDragOver:F,onDragLeave:v,children:[r.jsxs("p",{className:"ea-dropzone__text",children:[i.dropzoneText," ",r.jsx("span",{className:"ea-dropzone__button",children:i.dropzoneButton})]}),r.jsx("input",{ref:s,type:"file",accept:e.join(","),multiple:t,onChange:x,className:"ea-dropzone__input",disabled:o,"aria-hidden":"true",tabIndex:-1})]})}function fe(e){switch(e){case"pdf":return"PDF";case"doc":return"DOC";case"docx":return"DOCX";default:return"IMG"}}function te({files:e,labels:t,onAnnotate:o,onRemove:i,disabled:c=!1}){return e.length===0?null:r.jsx("ul",{className:"ea-list",role:"list",children:e.map(s=>{const f=s.fileType==="image",d=s.annotatedPreviewUrl??s.previewUrl;return r.jsxs("li",{className:"ea-list__item","data-status":s.status,children:[r.jsx("div",{className:"ea-list__thumb",children:f&&d?r.jsx("img",{src:d,alt:s.originalFilename}):r.jsx("span",{className:"ea-list__icon",children:fe(s.fileType)})}),r.jsxs("div",{className:"ea-list__meta",children:[r.jsx("p",{className:"ea-list__name",title:s.originalFilename,children:s.originalFilename}),r.jsxs("p",{className:"ea-list__status",children:[s.status==="uploading"&&t.uploading,s.status==="done"&&t.uploaded,s.status==="error"&&(s.errorMessage??t.errorUploadFailed),s.status==="idle"&&s.annotatedFile&&"✓ annotated"]})]}),r.jsxs("div",{className:"ea-list__actions",children:[f&&r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:()=>o(s.id),disabled:c||s.status==="uploading",children:t.annotate}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--danger",onClick:()=>i(s.id),disabled:c||s.status==="uploading","aria-label":`${t.remove} ${s.originalFilename}`,children:t.remove})]})]},s.id)})})}function ne({src:e,originalFilename:t,initialState:o,labels:i,onReady:c,onError:s}){const f=a.useRef(null),d=a.useRef(null),_=a.useRef(null),x=a.useRef(null),[w,F]=a.useState(!0),[v,n]=a.useState(null);return a.useEffect(()=>{let j=!1;async function L(){try{const u=await Promise.resolve().then(()=>require("./markerjs3-DiaiYpgs.cjs"));if(j)return;const p=d.current,A=f.current;if(!p||!A||(!p.complete||p.naturalWidth===0)&&(await new Promise((U,D)=>{p.addEventListener("load",()=>U(),{once:!0}),p.addEventListener("error",()=>D(new Error("Image failed to load")),{once:!0})}),j))return;const g=new u.MarkerArea;if(A.appendChild(g),g.targetImage=p,o)try{g.restoreState(o,!1)}catch(U){H("Failed to restore annotation state:",U)}_.current=g,x.current=u.Renderer,F(!1),c==null||c({save:async()=>{const U=_.current,D=x.current,O=d.current;if(!U||!D||!O)throw new Error("Annotator is not ready.");const I=U.getState(),P=new D;P.targetImage=O;const T=await P.rasterize(I);return{file:await ge(T,ie(t)),state:I}}})}catch(u){if(j)return;F(!1),n(i.errorAnnotationFailed),H("Failed to load marker.js:",u),s==null||s(Z(u))}}return L(),()=>{var p;j=!0;const u=_.current;u&&((p=f.current)!=null&&p.contains(u))&&f.current.removeChild(u),_.current=null,x.current=null}},[e]),r.jsxs("div",{className:"ea-annotator",children:[r.jsx("div",{className:"ea-annotator__stage",ref:f,children:r.jsx("img",{ref:d,src:e,alt:"",className:"ea-annotator__img",crossOrigin:"anonymous"})}),w&&r.jsx("div",{className:"ea-annotator__loading",children:i.loadingAnnotator}),v&&r.jsx("div",{className:"ea-annotator__error",children:v})]})}async function ge(e,t){const i=await(await fetch(e)).blob();return new File([i],t,{type:i.type||"image/png"})}function ae({open:e,src:t,originalFilename:o,initialState:i,labels:c,onSave:s,onCancel:f,onError:d}){const _=a.useRef(null),x=a.useRef(null),[w,F]=a.useState(!1),v=a.useCallback(async()=>{if(_.current){F(!0);try{const{file:n,state:j}=await _.current.save();s(n,j)}catch(n){d==null||d(n)}finally{F(!1)}}},[d,s]);return a.useEffect(()=>{var u;if(!e)return;const n=document.activeElement;(u=x.current)==null||u.focus();const j=p=>{p.key==="Escape"&&f()};document.addEventListener("keydown",j);const L=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{var p;document.removeEventListener("keydown",j),document.body.style.overflow=L,(p=n==null?void 0:n.focus)==null||p.call(n)}},[f,e]),!e||!t?null:r.jsx("div",{className:"ea-modal",role:"dialog","aria-modal":"true","aria-label":c.modalTitle,onClick:n=>{n.target===n.currentTarget&&f()},children:r.jsxs("div",{className:"ea-modal__dialog",ref:x,tabIndex:-1,children:[r.jsxs("header",{className:"ea-modal__header",children:[r.jsx("h2",{className:"ea-modal__title",children:c.modalTitle}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:f,"aria-label":c.cancel,children:"×"})]}),r.jsx("div",{className:"ea-modal__body",children:r.jsx(ne,{src:t,originalFilename:o,initialState:i,labels:c,onReady:n=>{_.current=n},onError:d})}),r.jsxs("footer",{className:"ea-modal__footer",children:[r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:f,disabled:w,children:c.cancel}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--primary",onClick:v,disabled:w,children:w?c.uploading:c.save})]})]})})}function he(e){const{title:t,ownerType:o,ownerId:i,fieldKey:c,uploadHandler:s,onUploaded:f,onError:d,onChange:_,accept:x=B,maxFileSize:w=X,maxFiles:F=K,multiple:v=!0,labels:n,className:j,disabled:L=!1}=e,u=a.useMemo(()=>W(n),[n]),p=t??u.title,{files:A,isUploading:g,stats:U,addFiles:D,removeFile:O,setAnnotation:I,uploadAll:P}=Q({accept:x,maxFileSize:w,maxFiles:F,multiple:v,ownerType:o,ownerId:i,fieldKey:c,uploadHandler:s,onError:d,onUploaded:f,onChange:_}),[T,M]=a.useState(null),C=a.useMemo(()=>A.find(l=>l.id===T)??null,[T,A]),$=a.useCallback(l=>{M(l)},[]),m=a.useCallback(()=>{M(null)},[]),b=a.useCallback((l,k)=>{T&&I(T,l,k),M(null)},[T,I]),h=a.useCallback(l=>{d==null||d(Z(l))},[d]),y=!L&&!g&&A.some(l=>l.status==="idle"||l.status==="error");return r.jsxs("section",{className:`ea-root${j?` ${j}`:""}`,children:[r.jsxs("header",{className:"ea-header",children:[r.jsx("h2",{className:"ea-title",children:p}),r.jsxs("p",{className:"ea-subtitle",children:[u.filesSelected(U.total)," · max ",G(w)," MB · up to ",F," ","files"]})]}),r.jsx(ee,{accept:x,multiple:v,disabled:L||U.remainingSlots===0,labels:u,onFiles:D}),r.jsx(te,{files:A,labels:u,onAnnotate:$,onRemove:O,disabled:L}),A.length>0&&r.jsx("div",{className:"ea-actions",children:r.jsx("button",{type:"button",className:"ea-btn ea-btn--primary",onClick:()=>{P()},disabled:!y,children:g?u.uploading:u.upload})}),r.jsx(ae,{open:!!C,src:(C==null?void 0:C.previewUrl)??null,originalFilename:(C==null?void 0:C.originalFilename)??"",initialState:C==null?void 0:C.annotationState,labels:u,onSave:b,onCancel:m,onError:h})]})}function be(e){const t=new FormData;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}exports.AnnotatorModal=ae;exports.DEFAULT_ACCEPT=B;exports.DEFAULT_MAX_FILES=K;exports.DEFAULT_MAX_FILE_SIZE=X;exports.EasyAnnotatorUpload=he;exports.FilePreviewList=te;exports.ImageAnnotator=ne;exports.UploadDropzone=ee;exports.buildFormDataPayload=be;exports.bytesToMb=G;exports.classifyFile=J;exports.enLabels=z;exports.idLabels=le;exports.isAccepted=V;exports.mergeLabels=W;exports.useEasyAnnotator=Q;exports.useObjectUrls=Y;
2
+ //# sourceMappingURL=easy-annotator.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"easy-annotator.cjs","sources":["../src/utils/file.ts","../src/i18n/labels.ts","../src/utils/errors.ts","../src/utils/id.ts","../src/hooks/useObjectUrls.ts","../src/hooks/useEasyAnnotator.ts","../src/components/UploadDropzone.tsx","../src/components/FilePreviewList.tsx","../src/components/ImageAnnotator.tsx","../src/components/AnnotatorModal.tsx","../src/components/EasyAnnotatorUpload.tsx","../src/services/defaultPayloadBuilder.ts"],"sourcesContent":["import type { EasyAnnotatorFileType } from '../types'\n\nexport const DEFAULT_ACCEPT: string[] = [\n 'image/jpeg',\n 'image/jpg',\n 'image/png',\n 'image/webp',\n 'application/pdf',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n]\n\nexport const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024\nexport const DEFAULT_MAX_FILES = 5\n\nconst IMAGE_MIMES = new Set(['image/jpeg', 'image/jpg', 'image/png', 'image/webp'])\n\nexport function isImageMime(mime: string): boolean {\n return IMAGE_MIMES.has(mime.toLowerCase())\n}\n\nexport function classifyFile(file: File): EasyAnnotatorFileType {\n const mime = file.type.toLowerCase()\n if (isImageMime(mime)) return 'image'\n if (mime === 'application/pdf') return 'pdf'\n if (mime === 'application/msword') return 'doc'\n if (\n mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'\n ) {\n return 'docx'\n }\n // Fall back to extension when MIME is empty (common for some browsers/OSes).\n const ext = file.name.toLowerCase().split('.').pop() ?? ''\n if (['jpg', 'jpeg', 'png', 'webp'].includes(ext)) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (ext === 'doc') return 'doc'\n if (ext === 'docx') return 'docx'\n return 'doc'\n}\n\nexport function isAccepted(file: File, accept: string[]): boolean {\n if (accept.length === 0) return true\n const mime = file.type.toLowerCase()\n if (mime && accept.some((a) => a.toLowerCase() === mime)) return true\n const ext = file.name.toLowerCase().split('.').pop() ?? ''\n const extMimeMap: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n }\n const fallback = extMimeMap[ext]\n return fallback ? accept.includes(fallback) : false\n}\n\nexport function bytesToMb(bytes: number): number {\n return Math.round((bytes / (1024 * 1024)) * 10) / 10\n}\n\nexport function withAnnotatedFilename(originalName: string): string {\n const lastDot = originalName.lastIndexOf('.')\n if (lastDot <= 0) return `${originalName}-annotated.png`\n const base = originalName.slice(0, lastDot)\n return `${base}-annotated.png`\n}\n","import type { LabelOverrides } from '../types'\n\nexport const enLabels: LabelOverrides = {\n title: 'Upload',\n dropzoneText: 'Drag & drop files here, or',\n dropzoneButton: 'browse',\n annotate: 'Annotate',\n remove: 'Remove',\n upload: 'Upload',\n uploading: 'Uploading…',\n uploaded: 'Uploaded',\n retry: 'Retry',\n cancel: 'Cancel',\n save: 'Save',\n errorFileType: (filename) => `File type not allowed: ${filename}`,\n errorFileTooLarge: (filename, maxMb) => `File too large: ${filename} (max ${maxMb} MB)`,\n errorFileCount: (max) => `Maximum ${max} files`,\n errorAnnotationFailed: 'Failed to save annotation.',\n errorUploadFailed: 'Upload failed. Please retry.',\n modalTitle: 'Annotate image',\n loadingAnnotator: 'Loading annotator…',\n filesSelected: (count) => `${count} file${count === 1 ? '' : 's'} selected`,\n}\n\nexport const idLabels: LabelOverrides = {\n title: 'Unggah',\n dropzoneText: 'Seret & letakkan file di sini, atau',\n dropzoneButton: 'pilih file',\n annotate: 'Anotasi',\n remove: 'Hapus',\n upload: 'Unggah',\n uploading: 'Mengunggah…',\n uploaded: 'Terunggah',\n retry: 'Coba Lagi',\n cancel: 'Batal',\n save: 'Simpan',\n errorFileType: (filename) => `Tipe file tidak diizinkan: ${filename}`,\n errorFileTooLarge: (filename, maxMb) =>\n `File terlalu besar: ${filename} (maks ${maxMb} MB)`,\n errorFileCount: (max) => `Maksimal ${max} file`,\n errorAnnotationFailed: 'Gagal menyimpan anotasi.',\n errorUploadFailed: 'Unggah gagal. Silakan coba lagi.',\n modalTitle: 'Anotasi gambar',\n loadingAnnotator: 'Memuat anotator…',\n filesSelected: (count) => `${count} file dipilih`,\n}\n\nexport function mergeLabels(overrides?: Partial<LabelOverrides>): LabelOverrides {\n if (!overrides) return enLabels\n return { ...enLabels, ...overrides }\n}\n","import type { EasyAnnotatorError, EasyAnnotatorFile } from '../types'\n\nexport function fileTypeRejected(file: File, reason: string): EasyAnnotatorError {\n return { code: 'FILE_TYPE_REJECTED', file, reason }\n}\n\nexport function fileTooLarge(file: File, maxBytes: number): EasyAnnotatorError {\n return { code: 'FILE_TOO_LARGE', file, maxBytes }\n}\n\nexport function fileCountExceeded(maxFiles: number): EasyAnnotatorError {\n return { code: 'FILE_COUNT_EXCEEDED', maxFiles }\n}\n\nexport function annotationFailed(cause?: unknown): EasyAnnotatorError {\n return { code: 'ANNOTATION_FAILED', cause }\n}\n\nexport function uploadFailed(file: EasyAnnotatorFile, cause?: unknown): EasyAnnotatorError {\n return { code: 'UPLOAD_FAILED', cause, file }\n}\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined\n\nexport function devWarn(...args: unknown[]): void {\n const env =\n typeof process !== 'undefined' && process?.env?.NODE_ENV\n ? process.env.NODE_ENV\n : 'development'\n if (env !== 'production') {\n // eslint-disable-next-line no-console\n console.warn('[easy-annotator]', ...args)\n }\n}\n","export function createId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID()\n }\n return `ea_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nexport function useObjectUrls() {\n const urlsRef = useRef<Set<string>>(new Set())\n\n const create = useCallback((blob: Blob): string => {\n const url = URL.createObjectURL(blob)\n urlsRef.current.add(url)\n return url\n }, [])\n\n const revoke = useCallback((url: string | undefined): void => {\n if (!url) return\n if (urlsRef.current.has(url)) {\n URL.revokeObjectURL(url)\n urlsRef.current.delete(url)\n }\n }, [])\n\n useEffect(() => {\n const urls = urlsRef.current\n return () => {\n urls.forEach((url) => URL.revokeObjectURL(url))\n urls.clear()\n }\n }, [])\n\n return { create, revoke }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n EasyAnnotatorError,\n EasyAnnotatorFile,\n EasyAnnotatorUploadHandler,\n EasyAnnotatorUploadPayload,\n EasyAnnotatorUploadedResult,\n} from '../types'\nimport {\n DEFAULT_ACCEPT,\n DEFAULT_MAX_FILES,\n DEFAULT_MAX_FILE_SIZE,\n bytesToMb,\n classifyFile,\n isAccepted,\n} from '../utils/file'\nimport {\n fileCountExceeded,\n fileTooLarge,\n fileTypeRejected,\n uploadFailed,\n} from '../utils/errors'\nimport { createId } from '../utils/id'\nimport { useObjectUrls } from './useObjectUrls'\n\nexport interface UseEasyAnnotatorOptions {\n accept?: string[]\n maxFileSize?: number\n maxFiles?: number\n multiple?: boolean\n ownerType: string\n ownerId: string | number\n fieldKey: string\n uploadHandler: EasyAnnotatorUploadHandler\n onError?: (err: EasyAnnotatorError, file?: EasyAnnotatorFile) => void\n onUploaded?: (items: EasyAnnotatorUploadedResult[]) => void\n onChange?: (files: EasyAnnotatorFile[]) => void\n}\n\nexport function useEasyAnnotator(options: UseEasyAnnotatorOptions) {\n const {\n accept = DEFAULT_ACCEPT,\n maxFileSize = DEFAULT_MAX_FILE_SIZE,\n maxFiles = DEFAULT_MAX_FILES,\n multiple = true,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onError,\n onUploaded,\n onChange,\n } = options\n\n const [files, setFiles] = useState<EasyAnnotatorFile[]>([])\n const [isUploading, setIsUploading] = useState(false)\n const { create: createObjectUrl, revoke: revokeObjectUrl } = useObjectUrls()\n\n // Keep latest callbacks in refs so they don't force effect re-runs.\n const onChangeRef = useRef(onChange)\n const onErrorRef = useRef(onError)\n const onUploadedRef = useRef(onUploaded)\n useEffect(() => {\n onChangeRef.current = onChange\n onErrorRef.current = onError\n onUploadedRef.current = onUploaded\n }, [onChange, onError, onUploaded])\n\n useEffect(() => {\n onChangeRef.current?.(files)\n }, [files])\n\n const addFiles = useCallback(\n (incoming: FileList | File[]) => {\n const arr = Array.from(incoming)\n setFiles((prev) => {\n const next = [...prev]\n const remainingSlots = Math.max(0, maxFiles - prev.length)\n let countExceeded = false\n\n for (const raw of arr) {\n if (!multiple && next.length >= 1) break\n if (next.length >= prev.length + remainingSlots) {\n countExceeded = true\n break\n }\n if (!isAccepted(raw, accept)) {\n onErrorRef.current?.(fileTypeRejected(raw, `MIME ${raw.type || 'unknown'}`))\n continue\n }\n if (raw.size > maxFileSize) {\n onErrorRef.current?.(fileTooLarge(raw, maxFileSize))\n continue\n }\n const fileType = classifyFile(raw)\n const previewUrl = fileType === 'image' ? createObjectUrl(raw) : undefined\n next.push({\n id: createId(),\n originalFile: raw,\n fileType,\n mimeType: raw.type || 'application/octet-stream',\n originalFilename: raw.name,\n previewUrl,\n status: 'idle',\n })\n }\n\n if (countExceeded || arr.length > remainingSlots) {\n onErrorRef.current?.(fileCountExceeded(maxFiles))\n }\n return next\n })\n },\n [accept, createObjectUrl, maxFileSize, maxFiles, multiple],\n )\n\n const removeFile = useCallback(\n (id: string) => {\n setFiles((prev) => {\n const target = prev.find((f) => f.id === id)\n if (target) {\n revokeObjectUrl(target.previewUrl)\n revokeObjectUrl(target.annotatedPreviewUrl)\n }\n return prev.filter((f) => f.id !== id)\n })\n },\n [revokeObjectUrl],\n )\n\n const setAnnotation = useCallback(\n (id: string, annotatedFile: File, annotationState: object) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n revokeObjectUrl(f.annotatedPreviewUrl)\n return {\n ...f,\n annotatedFile,\n annotationState,\n annotatedPreviewUrl: createObjectUrl(annotatedFile),\n }\n }),\n )\n },\n [createObjectUrl, revokeObjectUrl],\n )\n\n const clearAnnotation = useCallback(\n (id: string) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n revokeObjectUrl(f.annotatedPreviewUrl)\n const { annotatedFile: _af, annotationState: _as, annotatedPreviewUrl: _ap, ...rest } = f\n void _af\n void _as\n void _ap\n return { ...rest }\n }),\n )\n },\n [revokeObjectUrl],\n )\n\n const setStatus = useCallback(\n (id: string, status: EasyAnnotatorFile['status'], errorMessage?: string) => {\n setFiles((prev) =>\n prev.map((f) => (f.id === id ? { ...f, status, errorMessage } : f)),\n )\n },\n [],\n )\n\n const buildPayload = useCallback(\n (file: EasyAnnotatorFile): EasyAnnotatorUploadPayload => ({\n ownerType,\n ownerId,\n fieldKey,\n originalFile: file.originalFile,\n annotatedFile: file.annotatedFile,\n annotationState: file.annotationState,\n fileType: file.fileType,\n mimeType: file.mimeType,\n originalFilename: file.originalFilename,\n }),\n [fieldKey, ownerId, ownerType],\n )\n\n const uploadAll = useCallback(async () => {\n setIsUploading(true)\n const targets = (await new Promise<EasyAnnotatorFile[]>((resolve) => {\n setFiles((prev) => {\n const toUpload = prev.filter((f) => f.status === 'idle' || f.status === 'error')\n resolve(toUpload)\n return prev.map((f) =>\n toUpload.find((t) => t.id === f.id)\n ? { ...f, status: 'uploading', errorMessage: undefined }\n : f,\n )\n })\n })) as EasyAnnotatorFile[]\n\n const results: EasyAnnotatorUploadedResult[] = []\n\n for (const file of targets) {\n try {\n const result = await uploadHandler(buildPayload(file))\n results.push(result)\n setStatus(file.id, 'done')\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Upload failed'\n setStatus(file.id, 'error', message)\n onErrorRef.current?.(uploadFailed(file, err), file)\n }\n }\n\n setIsUploading(false)\n if (results.length > 0) onUploadedRef.current?.(results)\n return results\n }, [buildPayload, setStatus, uploadHandler])\n\n const stats = useMemo(\n () => ({\n total: files.length,\n remainingSlots: Math.max(0, maxFiles - files.length),\n maxFileSizeMb: bytesToMb(maxFileSize),\n }),\n [files.length, maxFileSize, maxFiles],\n )\n\n return {\n files,\n isUploading,\n stats,\n addFiles,\n removeFile,\n setAnnotation,\n clearAnnotation,\n uploadAll,\n }\n}\n","import { useCallback, useRef, useState, type DragEvent, type ChangeEvent } from 'react'\nimport type { LabelOverrides } from '../types'\n\nexport interface UploadDropzoneProps {\n accept: string[]\n multiple: boolean\n disabled?: boolean\n labels: LabelOverrides\n onFiles: (files: FileList | File[]) => void\n}\n\nexport function UploadDropzone({\n accept,\n multiple,\n disabled = false,\n labels,\n onFiles,\n}: UploadDropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const [isDragging, setIsDragging] = useState(false)\n\n const openPicker = useCallback(() => {\n if (disabled) return\n inputRef.current?.click()\n }, [disabled])\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n onFiles(e.target.files)\n }\n e.target.value = ''\n },\n [onFiles],\n )\n\n const handleDrop = useCallback(\n (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n setIsDragging(false)\n if (disabled) return\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n onFiles(e.dataTransfer.files)\n }\n },\n [disabled, onFiles],\n )\n\n const handleDragOver = useCallback(\n (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n if (!disabled) setIsDragging(true)\n },\n [disabled],\n )\n\n const handleDragLeave = useCallback((e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n setIsDragging(false)\n }, [])\n\n return (\n <div\n className={`ea-dropzone${isDragging ? ' ea-dropzone--active' : ''}${\n disabled ? ' ea-dropzone--disabled' : ''\n }`}\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n onClick={openPicker}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openPicker()\n }\n }}\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n >\n <p className=\"ea-dropzone__text\">\n {labels.dropzoneText}{' '}\n <span className=\"ea-dropzone__button\">{labels.dropzoneButton}</span>\n </p>\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept.join(',')}\n multiple={multiple}\n onChange={handleChange}\n className=\"ea-dropzone__input\"\n disabled={disabled}\n aria-hidden=\"true\"\n tabIndex={-1}\n />\n </div>\n )\n}\n","import type { EasyAnnotatorFile, LabelOverrides } from '../types'\n\nexport interface FilePreviewListProps {\n files: EasyAnnotatorFile[]\n labels: LabelOverrides\n onAnnotate: (id: string) => void\n onRemove: (id: string) => void\n disabled?: boolean\n}\n\nfunction fileIcon(type: EasyAnnotatorFile['fileType']): string {\n switch (type) {\n case 'pdf':\n return 'PDF'\n case 'doc':\n return 'DOC'\n case 'docx':\n return 'DOCX'\n default:\n return 'IMG'\n }\n}\n\nexport function FilePreviewList({\n files,\n labels,\n onAnnotate,\n onRemove,\n disabled = false,\n}: FilePreviewListProps) {\n if (files.length === 0) return null\n\n return (\n <ul className=\"ea-list\" role=\"list\">\n {files.map((file) => {\n const isImage = file.fileType === 'image'\n const thumb = file.annotatedPreviewUrl ?? file.previewUrl\n return (\n <li key={file.id} className=\"ea-list__item\" data-status={file.status}>\n <div className=\"ea-list__thumb\">\n {isImage && thumb ? (\n <img src={thumb} alt={file.originalFilename} />\n ) : (\n <span className=\"ea-list__icon\">{fileIcon(file.fileType)}</span>\n )}\n </div>\n <div className=\"ea-list__meta\">\n <p className=\"ea-list__name\" title={file.originalFilename}>\n {file.originalFilename}\n </p>\n <p className=\"ea-list__status\">\n {file.status === 'uploading' && labels.uploading}\n {file.status === 'done' && labels.uploaded}\n {file.status === 'error' && (file.errorMessage ?? labels.errorUploadFailed)}\n {file.status === 'idle' && file.annotatedFile && '✓ annotated'}\n </p>\n </div>\n <div className=\"ea-list__actions\">\n {isImage && (\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={() => onAnnotate(file.id)}\n disabled={disabled || file.status === 'uploading'}\n >\n {labels.annotate}\n </button>\n )}\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--danger\"\n onClick={() => onRemove(file.id)}\n disabled={disabled || file.status === 'uploading'}\n aria-label={`${labels.remove} ${file.originalFilename}`}\n >\n {labels.remove}\n </button>\n </div>\n </li>\n )\n })}\n </ul>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport type { LabelOverrides } from '../types'\nimport { withAnnotatedFilename } from '../utils/file'\nimport { annotationFailed, devWarn } from '../utils/errors'\n\nexport interface ImageAnnotatorHandle {\n save: () => Promise<{ file: File; state: object }>\n}\n\nexport interface ImageAnnotatorProps {\n src: string\n originalFilename: string\n initialState?: object\n labels: LabelOverrides\n onReady?: (handle: ImageAnnotatorHandle) => void\n onError?: (err: unknown) => void\n}\n\n// Local minimal interfaces so we don't leak marker.js types through our public .d.ts.\ntype MarkerAreaLike = HTMLElement & {\n targetImage: HTMLImageElement | undefined\n getState: () => object\n restoreState: (state: object, addUndoStep?: boolean) => void\n}\n\ntype RendererLike = {\n targetImage: HTMLImageElement | undefined\n rasterize: (state: object, targetCanvas?: HTMLCanvasElement) => Promise<string>\n}\n\ntype MarkerJsModule = {\n MarkerArea: new () => MarkerAreaLike\n Renderer: new () => RendererLike\n}\n\nexport function ImageAnnotator({\n src,\n originalFilename,\n initialState,\n labels,\n onReady,\n onError,\n}: ImageAnnotatorProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const imgRef = useRef<HTMLImageElement>(null)\n const markerAreaRef = useRef<MarkerAreaLike | null>(null)\n const rendererCtorRef = useRef<(new () => RendererLike) | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let disposed = false\n\n async function init() {\n try {\n const mod = (await import('@markerjs/markerjs3')) as unknown as MarkerJsModule\n if (disposed) return\n const img = imgRef.current\n const container = containerRef.current\n if (!img || !container) return\n\n // Wait for the <img> to have natural dimensions before handing it to MarkerArea.\n if (!img.complete || img.naturalWidth === 0) {\n await new Promise<void>((resolve, reject) => {\n img.addEventListener('load', () => resolve(), { once: true })\n img.addEventListener('error', () => reject(new Error('Image failed to load')), {\n once: true,\n })\n })\n if (disposed) return\n }\n\n const markerArea = new mod.MarkerArea()\n // marker.js 3's MarkerArea is a custom element — append, then set the target image.\n container.appendChild(markerArea)\n markerArea.targetImage = img\n\n if (initialState) {\n try {\n markerArea.restoreState(initialState, false)\n } catch (restoreErr) {\n devWarn('Failed to restore annotation state:', restoreErr)\n }\n }\n\n markerAreaRef.current = markerArea\n rendererCtorRef.current = mod.Renderer\n setLoading(false)\n\n onReady?.({\n save: async () => {\n const area = markerAreaRef.current\n const RendererCtor = rendererCtorRef.current\n const targetImg = imgRef.current\n if (!area || !RendererCtor || !targetImg) {\n throw new Error('Annotator is not ready.')\n }\n const state = area.getState()\n const renderer = new RendererCtor()\n renderer.targetImage = targetImg\n const dataUrl = await renderer.rasterize(state)\n const file = await dataUrlToFile(\n dataUrl,\n withAnnotatedFilename(originalFilename),\n )\n return { file, state }\n },\n })\n } catch (err) {\n if (disposed) return\n setLoading(false)\n setError(labels.errorAnnotationFailed)\n devWarn('Failed to load marker.js:', err)\n onError?.(annotationFailed(err))\n }\n }\n\n void init()\n\n return () => {\n disposed = true\n const area = markerAreaRef.current\n if (area && containerRef.current?.contains(area)) {\n containerRef.current.removeChild(area)\n }\n markerAreaRef.current = null\n rendererCtorRef.current = null\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [src])\n\n return (\n <div className=\"ea-annotator\">\n <div className=\"ea-annotator__stage\" ref={containerRef}>\n <img\n ref={imgRef}\n src={src}\n alt=\"\"\n className=\"ea-annotator__img\"\n crossOrigin=\"anonymous\"\n />\n </div>\n {loading && <div className=\"ea-annotator__loading\">{labels.loadingAnnotator}</div>}\n {error && <div className=\"ea-annotator__error\">{error}</div>}\n </div>\n )\n}\n\nasync function dataUrlToFile(dataUrl: string, filename: string): Promise<File> {\n const res = await fetch(dataUrl)\n const blob = await res.blob()\n return new File([blob], filename, { type: blob.type || 'image/png' })\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { LabelOverrides } from '../types'\nimport { ImageAnnotator, type ImageAnnotatorHandle } from './ImageAnnotator'\n\nexport interface AnnotatorModalProps {\n open: boolean\n src: string | null\n originalFilename: string\n initialState?: object\n labels: LabelOverrides\n onSave: (annotatedFile: File, annotationState: object) => void\n onCancel: () => void\n onError?: (err: unknown) => void\n}\n\nexport function AnnotatorModal({\n open,\n src,\n originalFilename,\n initialState,\n labels,\n onSave,\n onCancel,\n onError,\n}: AnnotatorModalProps) {\n const handleRef = useRef<ImageAnnotatorHandle | null>(null)\n const dialogRef = useRef<HTMLDivElement>(null)\n const [saving, setSaving] = useState(false)\n\n const handleSave = useCallback(async () => {\n if (!handleRef.current) return\n setSaving(true)\n try {\n const { file, state } = await handleRef.current.save()\n onSave(file, state)\n } catch (err) {\n onError?.(err)\n } finally {\n setSaving(false)\n }\n }, [onError, onSave])\n\n useEffect(() => {\n if (!open) return\n const previousActive = document.activeElement as HTMLElement | null\n dialogRef.current?.focus()\n\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancel()\n }\n document.addEventListener('keydown', handleKey)\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.removeEventListener('keydown', handleKey)\n document.body.style.overflow = previousOverflow\n previousActive?.focus?.()\n }\n }, [onCancel, open])\n\n if (!open || !src) return null\n\n return (\n <div\n className=\"ea-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={labels.modalTitle}\n onClick={(e) => {\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <div className=\"ea-modal__dialog\" ref={dialogRef} tabIndex={-1}>\n <header className=\"ea-modal__header\">\n <h2 className=\"ea-modal__title\">{labels.modalTitle}</h2>\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={onCancel}\n aria-label={labels.cancel}\n >\n ×\n </button>\n </header>\n <div className=\"ea-modal__body\">\n <ImageAnnotator\n src={src}\n originalFilename={originalFilename}\n initialState={initialState}\n labels={labels}\n onReady={(h) => {\n handleRef.current = h\n }}\n onError={onError}\n />\n </div>\n <footer className=\"ea-modal__footer\">\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={onCancel}\n disabled={saving}\n >\n {labels.cancel}\n </button>\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--primary\"\n onClick={handleSave}\n disabled={saving}\n >\n {saving ? labels.uploading : labels.save}\n </button>\n </footer>\n </div>\n </div>\n )\n}\n","import { useCallback, useMemo, useState } from 'react'\nimport type { EasyAnnotatorUploadProps } from '../types'\nimport { DEFAULT_ACCEPT, DEFAULT_MAX_FILES, DEFAULT_MAX_FILE_SIZE, bytesToMb } from '../utils/file'\nimport { mergeLabels } from '../i18n/labels'\nimport { useEasyAnnotator } from '../hooks/useEasyAnnotator'\nimport { UploadDropzone } from './UploadDropzone'\nimport { FilePreviewList } from './FilePreviewList'\nimport { AnnotatorModal } from './AnnotatorModal'\nimport { annotationFailed } from '../utils/errors'\n\nexport function EasyAnnotatorUpload(props: EasyAnnotatorUploadProps) {\n const {\n title,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onUploaded,\n onError,\n onChange,\n accept = DEFAULT_ACCEPT,\n maxFileSize = DEFAULT_MAX_FILE_SIZE,\n maxFiles = DEFAULT_MAX_FILES,\n multiple = true,\n labels: labelOverrides,\n className,\n disabled = false,\n } = props\n\n const labels = useMemo(() => mergeLabels(labelOverrides), [labelOverrides])\n const resolvedTitle = title ?? labels.title\n\n const {\n files,\n isUploading,\n stats,\n addFiles,\n removeFile,\n setAnnotation,\n uploadAll,\n } = useEasyAnnotator({\n accept,\n maxFileSize,\n maxFiles,\n multiple,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onError,\n onUploaded,\n onChange,\n })\n\n const [activeAnnotateId, setActiveAnnotateId] = useState<string | null>(null)\n const activeFile = useMemo(\n () => files.find((f) => f.id === activeAnnotateId) ?? null,\n [activeAnnotateId, files],\n )\n\n const handleAnnotate = useCallback((id: string) => {\n setActiveAnnotateId(id)\n }, [])\n\n const handleCancelAnnotate = useCallback(() => {\n setActiveAnnotateId(null)\n }, [])\n\n const handleSaveAnnotation = useCallback(\n (annotatedFile: File, annotationState: object) => {\n if (activeAnnotateId) {\n setAnnotation(activeAnnotateId, annotatedFile, annotationState)\n }\n setActiveAnnotateId(null)\n },\n [activeAnnotateId, setAnnotation],\n )\n\n const handleAnnotatorError = useCallback(\n (err: unknown) => {\n onError?.(annotationFailed(err))\n },\n [onError],\n )\n\n const canUpload =\n !disabled &&\n !isUploading &&\n files.some((f) => f.status === 'idle' || f.status === 'error')\n\n return (\n <section className={`ea-root${className ? ` ${className}` : ''}`}>\n <header className=\"ea-header\">\n <h2 className=\"ea-title\">{resolvedTitle}</h2>\n <p className=\"ea-subtitle\">\n {labels.filesSelected(stats.total)} · max {bytesToMb(maxFileSize)} MB · up to {maxFiles}{' '}\n files\n </p>\n </header>\n\n <UploadDropzone\n accept={accept}\n multiple={multiple}\n disabled={disabled || stats.remainingSlots === 0}\n labels={labels}\n onFiles={addFiles}\n />\n\n <FilePreviewList\n files={files}\n labels={labels}\n onAnnotate={handleAnnotate}\n onRemove={removeFile}\n disabled={disabled}\n />\n\n {files.length > 0 && (\n <div className=\"ea-actions\">\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--primary\"\n onClick={() => {\n void uploadAll()\n }}\n disabled={!canUpload}\n >\n {isUploading ? labels.uploading : labels.upload}\n </button>\n </div>\n )}\n\n <AnnotatorModal\n open={Boolean(activeFile)}\n src={activeFile?.previewUrl ?? null}\n originalFilename={activeFile?.originalFilename ?? ''}\n initialState={activeFile?.annotationState}\n labels={labels}\n onSave={handleSaveAnnotation}\n onCancel={handleCancelAnnotate}\n onError={handleAnnotatorError}\n />\n </section>\n )\n}\n","import type { EasyAnnotatorUploadPayload } from '../types'\n\nexport function buildFormDataPayload(payload: EasyAnnotatorUploadPayload): FormData {\n const fd = new FormData()\n fd.append('owner_type', payload.ownerType)\n fd.append('owner_id', String(payload.ownerId))\n fd.append('field_key', payload.fieldKey)\n fd.append('original_file', payload.originalFile, payload.originalFile.name)\n if (payload.annotatedFile) {\n fd.append('annotated_file', payload.annotatedFile, payload.annotatedFile.name)\n }\n if (payload.annotationState) {\n fd.append('annotation_state', JSON.stringify(payload.annotationState))\n }\n fd.append('file_type', payload.fileType)\n fd.append('mime_type', payload.mimeType)\n fd.append('original_filename', payload.originalFilename)\n return fd\n}\n"],"names":["DEFAULT_ACCEPT","DEFAULT_MAX_FILE_SIZE","DEFAULT_MAX_FILES","IMAGE_MIMES","isImageMime","mime","classifyFile","file","ext","isAccepted","accept","a","fallback","bytesToMb","bytes","withAnnotatedFilename","originalName","lastDot","enLabels","filename","maxMb","max","count","idLabels","mergeLabels","overrides","fileTypeRejected","reason","fileTooLarge","maxBytes","fileCountExceeded","maxFiles","annotationFailed","cause","uploadFailed","devWarn","args","_a","createId","useObjectUrls","urlsRef","useRef","create","useCallback","blob","url","revoke","useEffect","urls","useEasyAnnotator","options","maxFileSize","multiple","ownerType","ownerId","fieldKey","uploadHandler","onError","onUploaded","onChange","files","setFiles","useState","isUploading","setIsUploading","createObjectUrl","revokeObjectUrl","onChangeRef","onErrorRef","onUploadedRef","addFiles","incoming","arr","prev","next","remainingSlots","countExceeded","raw","_b","fileType","previewUrl","_c","removeFile","id","target","f","setAnnotation","annotatedFile","annotationState","clearAnnotation","_af","_as","_ap","rest","setStatus","status","errorMessage","buildPayload","uploadAll","targets","resolve","toUpload","t","results","result","err","message","stats","useMemo","UploadDropzone","disabled","labels","onFiles","inputRef","isDragging","setIsDragging","openPicker","handleChange","e","handleDrop","handleDragOver","handleDragLeave","jsxs","jsx","fileIcon","type","FilePreviewList","onAnnotate","onRemove","isImage","thumb","ImageAnnotator","src","originalFilename","initialState","onReady","containerRef","imgRef","markerAreaRef","rendererCtorRef","loading","setLoading","error","setError","disposed","init","mod","img","container","reject","markerArea","restoreErr","area","RendererCtor","targetImg","state","renderer","dataUrl","dataUrlToFile","AnnotatorModal","open","onSave","onCancel","handleRef","dialogRef","saving","setSaving","handleSave","previousActive","handleKey","previousOverflow","h","EasyAnnotatorUpload","props","title","labelOverrides","className","resolvedTitle","activeAnnotateId","setActiveAnnotateId","activeFile","handleAnnotate","handleCancelAnnotate","handleSaveAnnotation","handleAnnotatorError","canUpload","buildFormDataPayload","payload","fd"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ .ea-root{display:flex;flex-direction:column;gap:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#1f2937}.ea-header{display:flex;flex-direction:column;gap:2px}.ea-title{margin:0;font-size:1rem;font-weight:600}.ea-subtitle{margin:0;font-size:.8rem;color:#6b7280}.ea-dropzone{position:relative;display:flex;align-items:center;justify-content:center;padding:24px;border:2px dashed #d1d5db;border-radius:8px;background:#f9fafb;cursor:pointer;transition:border-color .12s ease,background-color .12s ease;outline:none}.ea-dropzone:focus-visible{border-color:#2563eb;box-shadow:0 0 0 3px #2563eb40}.ea-dropzone--active{border-color:#2563eb;background:#eff6ff}.ea-dropzone--disabled{opacity:.6;cursor:not-allowed}.ea-dropzone__text{margin:0;font-size:.875rem;color:#4b5563;text-align:center}.ea-dropzone__button{color:#2563eb;font-weight:600;text-decoration:underline}.ea-dropzone__input{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none}.ea-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.ea-list__item{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #e5e7eb;border-radius:6px;background:#fff}.ea-list__item[data-status=error]{border-color:#fca5a5;background:#fef2f2}.ea-list__item[data-status=done]{border-color:#86efac;background:#f0fdf4}.ea-list__thumb{flex:0 0 48px;width:48px;height:48px;border-radius:4px;background:#f3f4f6;display:flex;align-items:center;justify-content:center;overflow:hidden}.ea-list__thumb img{width:100%;height:100%;object-fit:cover}.ea-list__icon{font-size:.7rem;font-weight:700;color:#6b7280;letter-spacing:.05em}.ea-list__meta{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}.ea-list__name{margin:0;font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ea-list__status{margin:0;font-size:.75rem;color:#6b7280}.ea-list__actions{display:flex;gap:6px;flex-shrink:0}.ea-btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid transparent;border-radius:6px;padding:6px 12px;font-size:.8125rem;font-weight:500;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}.ea-btn:disabled{opacity:.55;cursor:not-allowed}.ea-btn--primary{background:#2563eb;color:#fff}.ea-btn--primary:hover:not(:disabled){background:#1d4ed8}.ea-btn--ghost{background:transparent;border-color:#d1d5db;color:#1f2937}.ea-btn--ghost:hover:not(:disabled){background:#f3f4f6}.ea-btn--danger{background:transparent;border-color:#fca5a5;color:#b91c1c}.ea-btn--danger:hover:not(:disabled){background:#fef2f2}.ea-actions{display:flex;justify-content:flex-end}.ea-modal{position:fixed;top:0;right:0;bottom:0;left:0;background:#0f172a8c;display:flex;align-items:center;justify-content:center;z-index:9999;padding:16px}.ea-modal__dialog{background:#fff;border-radius:10px;width:min(960px,100%);max-height:90vh;display:flex;flex-direction:column;box-shadow:0 20px 40px #00000040;outline:none}.ea-modal__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #e5e7eb}.ea-modal__title{margin:0;font-size:1rem;font-weight:600}.ea-modal__body{flex:1 1 auto;min-height:0;padding:12px;background:#f3f4f6;overflow:hidden}.ea-modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid #e5e7eb}.ea-annotator{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.ea-annotator__stage{position:relative;max-width:100%;max-height:100%;display:flex;align-items:center;justify-content:center}.ea-annotator__img{max-width:100%;max-height:70vh;display:block;object-fit:contain}.ea-annotator__loading,.ea-annotator__error{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#fffc;font-size:.875rem;color:#374151}.ea-annotator__error{color:#b91c1c;background:#fef2f2f2}
@@ -0,0 +1,2 @@
1
+ export * from './index'
2
+ export {}