UNPKG

@bonhomie/cloudinary-super-uploader

Version:

A powerful React + Node Cloudinary toolkit with drag & drop, browser compression, EXIF checks, duplicate detection, signed uploads, and more.

2 lines (1 loc) 6.39 kB
var K=Object.create;var O=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var R=(o,t)=>{for(var a in t)O(o,a,{get:t[a],enumerable:!0})},q=(o,t,a,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of V(t))!Z.call(o,e)&&e!==a&&O(o,e,{get:()=>t[e],enumerable:!(i=Q(t,e))||i.enumerable});return o};var W=(o,t,a)=>(a=o!=null?K(Y(o)):{},q(t||!o||!o.__esModule?O(a,"default",{value:o,enumerable:!0}):a,o)),ee=o=>q(O({},"__esModule",{value:!0}),o);var re={};R(re,{CloudinaryUploader:()=>P,buildThumbnailUrl:()=>C,compressImageBrowser:()=>N,getExifBrowser:()=>z,hashBrowser:()=>T,useCloudinaryUpload:()=>S,validateDimensions:()=>$});module.exports=ee(re);var s=W(require("react"),1);var h=require("react");function N(o,t=2e3,a=2e3){return new Promise(i=>{let e=new Image,d=new FileReader;d.onload=l=>{e.src=l.target.result},e.onload=()=>{let{width:l,height:p}=e,f=Math.min(t/l,a/p,1);l=l*f,p=p*f;let g=document.createElement("canvas");g.width=l,g.height=p,g.getContext("2d").drawImage(e,0,0,l,p),g.toBlob(b=>{let D=new File([b],o.name,{type:"image/jpeg",lastModified:Date.now()});i({success:!0,file:D})},"image/jpeg",.8)},d.onerror=()=>i({success:!1,error:"FileReader failed"}),d.readAsDataURL(o)})}var X=W(require("exif-parser"),1);function z(o){return new Promise(t=>{let a=new FileReader;a.onload=i=>{try{let e=i.target.result,l=X.default.create(e).parse();t({success:!0,data:l.tags})}catch(e){t({success:!1,error:e.message})}},a.onerror=()=>t({success:!1,error:"EXIF read failed"}),a.readAsArrayBuffer(o)})}async function T(o){try{let t=await o.arrayBuffer(),a=await crypto.subtle.digest("SHA-1",t);return{success:!0,hash:Array.from(new Uint8Array(a)).map(d=>d.toString(16).padStart(2,"0")).join("")}}catch(t){return{success:!1,error:t.message}}}function $(o,t=600,a=600){return new Promise(i=>{let e=new Image,d=new FileReader;d.onload=l=>{e.src=l.target.result},e.onload=()=>{e.width<t||e.height<a?i({success:!1,error:`Image too small: ${e.width}x${e.height} (min ${t}x${a})`}):i({success:!0,width:e.width,height:e.height})},e.onerror=()=>i({success:!1,error:"Invalid image"}),d.readAsDataURL(o)})}function C(o,t={}){if(!o||typeof o!="string")return o;let{width:a=400,height:i=400,crop:e="fill",quality:d="auto",format:l="auto"}=t;if(!o.includes("/upload/"))return o;let p=`c_${e},w_${a},h_${i},q_${d},f_${l}`;return o.replace("/upload/",`/upload/${p}/`)}function S(o={}){let{uploadUrl:t,maxWidth:a=2e3,maxHeight:i=2e3,minWidth:e=600,minHeight:d=600,maxFiles:l=10,allowedTypes:p=["image/jpeg","image/png","image/webp"],autoCompress:f=!0,maxAgeDays:g=365}=o,[m,b]=(0,h.useState)([]),[D,I]=(0,h.useState)(0),[F,A]=(0,h.useState)(!1),[j,y]=(0,h.useState)([]),_=(0,h.useCallback)(async k=>{let U=Array.from(k),r=[];if(m.length+U.length>l){y(n=>[...n,"Too many files"]);return}for(let n of U){if(!p.includes(n.type)){y(u=>[...u,`Invalid file type: ${n.type}`]);continue}let x=await $(n,e,d);if(!x.success){y(u=>[...u,x.error]);continue}let L=await z(n),v=await T(n);if(m.some(u=>u.hash===v.hash)){y(u=>[...u,"Duplicate image skipped"]);continue}let B=n;if(f){let u=await N(n,a,i);u.success&&(B=u.file)}r.push({file:B,hash:v.hash,exif:L})}if(r.length===0)return;A(!0),I(0);let c=[];for(let n=0;n<r.length;n++){let{file:x,hash:L,exif:v}=r[n],B=new FormData;B.append("file",x);let w=await(await fetch(t,{method:"POST",body:B})).json();if(w.success){let E=[];if(v.success&&v.data.DateTimeOriginal){let J=new Date(v.data.DateTimeOriginal*1e3);(Date.now()-J.getTime())/(1e3*60*60*24)>g&&E.push("Old photo")}let G=w.data.thumbnail||C(w.data.url,{width:400,height:400});c.push({url:w.data.url,thumbnail:G,publicId:w.data.publicId,width:w.data.width,height:w.data.height,hash:L,exif:v.data||null,warnings:E})}else y(E=>[...E,w.error||"Upload error"]);I(Math.round((n+1)/r.length*100))}b(n=>[...n,...c]),A(!1)},[m,t,p,f,a,i,e,d,l,g]),H=(0,h.useCallback)(k=>{b(U=>U.filter(r=>r.publicId!==k))},[]),M=(0,h.useCallback)(k=>{b(k)},[]);return{images:m,progress:D,uploading:F,errors:j,upload:_,removeImage:H,reorderImages:M}}function P(o){let{images:t,progress:a,uploading:i,errors:e,upload:d,removeImage:l,reorderImages:p}=S(o),f=(0,s.useRef)(null),[g,m]=(0,s.useState)(!1),[b,D]=(0,s.useState)(null),[I,F]=(0,s.useState)(null),A=r=>()=>{F(r)},j=r=>c=>{if(c.preventDefault(),I===null||I===r)return;let n=[...t],[x]=n.splice(I,1);n.splice(r,0,x),p(n),F(r)},y=r=>{r.preventDefault(),F(null)};return(0,s.useEffect)(()=>{if(!e||e.length===0)return;let r=e[e.length-1];D(r);let c=setTimeout(()=>D(null),4e3);return()=>clearTimeout(c)},[e]),s.default.createElement("div",{className:"bon-cloud-container"},b&&s.default.createElement("div",{className:"bon-toast"},b),s.default.createElement("div",{className:`bon-dropzone ${g?"bon-dropzone--active":""}`,onDragOver:r=>{r.preventDefault(),m(!0)},onDragLeave:r=>{r.preventDefault(),m(!1)},onDrop:r=>{r.preventDefault(),m(!1);let c=r.dataTransfer.files;d(c)},onClick:()=>{f.current&&f.current.click()}},s.default.createElement("p",{className:"bon-dropzone-text"},"Drag & Drop or ",s.default.createElement("span",{className:"bon-dropzone-highlight"},"Click")," ","to Upload"),s.default.createElement("input",{ref:f,type:"file",multiple:!0,hidden:!0,onChange:r=>{let c=r.target.files;d(c)},accept:"image/*"})),i&&s.default.createElement("div",{className:"bon-progress"},s.default.createElement("div",{className:"bon-bar",style:{width:`${a}%`}})),e.length>0&&s.default.createElement("div",{className:"bon-errors"},e.map((r,c)=>s.default.createElement("p",{key:c,className:"bon-error-item"},r))),s.default.createElement("div",{className:"bon-grid",onDragOver:r=>r.preventDefault(),onDrop:y},t.map((r,c)=>s.default.createElement("div",{key:r.publicId||c,className:"bon-item",draggable:!0,onDragStart:A(c),onDragEnter:j(c)},s.default.createElement("img",{src:r.thumbnail||r.url,alt:"",className:"bon-item-img"}),r.warnings&&r.warnings.length>0&&s.default.createElement("div",{className:"bon-item-warnings"},r.warnings.map((n,x)=>s.default.createElement("span",{key:x,className:"bon-warning-badge"},n))),s.default.createElement("button",{type:"button",onClick:()=>l(r.publicId),className:"bon-remove"},"\u2715")))))}0&&(module.exports={CloudinaryUploader,buildThumbnailUrl,compressImageBrowser,getExifBrowser,hashBrowser,useCloudinaryUpload,validateDimensions});