tiptap-extension-resizable-image
Version:
Resizable image extension for Tiptap
3 lines • 10.5 kB
JavaScript
;var react$1=require('react'),jsxRuntime=require('react/jsx-runtime'),react=require('@tiptap/react'),core=require('@tiptap/core'),state=require('@tiptap/pm/state');var F=Object.defineProperty,K=Object.defineProperties;var j=Object.getOwnPropertyDescriptors;var k=Object.getOwnPropertySymbols;var B=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable;var S=(e,r,t)=>r in e?F(e,r,{enumerable:true,configurable:true,writable:true,value:t}):e[r]=t,w=(e,r)=>{for(var t in r||(r={}))B.call(r,t)&&S(e,t,r[t]);if(k)for(var t of k(r))_.call(r,t)&&S(e,t,r[t]);return e},I=(e,r)=>K(e,j(r));var V=(e,r,t)=>new Promise((n,l)=>{var c=p=>{try{d(t.next(p));}catch(h){l(h);}},g=p=>{try{d(t.throw(p));}catch(h){l(h);}},d=p=>p.done?n(p.value):Promise.resolve(p.value).then(c,g);d((t=t.apply(e,r)).next());});var J=e=>{let{updateAttributes:r,node:{attrs:t},editor:n,extension:l}=e,{captionProps:c}=l.options,g=react$1.useRef(null);react$1.useEffect(()=>{var f;l.options.withCaption||t.caption===""&&((f=g.current)==null||f.focus());},[t.caption,l.options.withCaption]);let d=f=>{if(!g.current)return;let{innerText:b,innerHTML:z}=f.target,x=(b==null?void 0:b.replaceAll(`
`,""))==="",H=z.replaceAll("<div>",`
`).replaceAll("</div>","").replaceAll("<br>",""),L=x?"":H;g.current.innerHTML=L,r({caption:L});},p=f=>{f.preventDefault();let b=f.clipboardData.getData("text/plain"),z=window.getSelection();if(z&&b){let x=z.getRangeAt(0);z.deleteFromDocument(),x.insertNode(document.createTextNode(b)),x.setStart(x.endContainer,x.endOffset);}},h=f=>{if((f.ctrlKey||f.metaKey)&&f.key==="a"){f.preventDefault();let b=window.getSelection();if(b){let z=b.getRangeAt(0);z.selectNodeContents(f.currentTarget),b.addRange(z);}}};return jsxRuntime.jsx("span",I(w({},c),{ref:g,className:`caption ${(c==null?void 0:c.className)||""}`,contentEditable:n.isEditable,onBlur:d,onPaste:p,onKeyDown:h,dangerouslySetInnerHTML:{__html:t.caption||""}}))},N=J;function v(e,r,t){return Math.min(Math.max(e,r),t)}var a={east:1,north:8,south:2,west:4};function D({editor:e,imageRef:r,minWidth:t,maxWidth:n,minHeight:l,maxHeight:c,keepRatio:g,onResizeEnd:d,onResizeStart:p}){let h=react$1.useRef(null),f=react$1.useRef({currentHeight:0,currentWidth:0,direction:0,isResizing:false,ratio:0,startHeight:0,startWidth:0,startX:0,startY:0}),b=e.view.dom,z=o=>{let s=o===a.east||o===a.west,i=o===a.north||o===a.south,M=o&a.north&&o&a.west||o&a.south&&o&a.east,y=s?"ew":i?"ns":M?"nwse":"nesw";b!==null&&b.style.setProperty("cursor",`${y}-resize`,"important"),document.body!==null&&(document.body.style.setProperty("cursor",`${y}-resize`,"important"),document.body.style.setProperty("-webkit-user-select","none","important"));},x=()=>{b!==null&&b.style.removeProperty("cursor"),document.body!==null&&(document.body.style.removeProperty("cursor"),document.body.style.removeProperty("-webkit-user-select"));},H=(o,s)=>{if(!e.isEditable)return;let i=r.current,M=h.current;if(i!==null&&M!==null){o.preventDefault();let{width:y,height:u}=i.getBoundingClientRect(),m=f.current;m.startWidth=y,m.startHeight=u,m.ratio=y/u,m.currentWidth=y,m.currentHeight=u,m.startX=o.clientX,m.startY=o.clientY,m.isResizing=true,m.direction=s,z(s),p==null||p(),M.classList.add("image-control-wrapper--resizing"),i.style.height=`${u}px`,i.style.width=`${y}px`,document.addEventListener("pointermove",L),document.addEventListener("pointerup",T);}},L=o=>{let s=r.current,i=f.current,M=i.direction&(a.east|a.west),y=i.direction&(a.south|a.north);if(!(s===null||!i.isResizing)){if(g){let u=0,m=0;if(M){let R=Math.floor(i.startX-o.clientX);R=i.direction&a.east?-R:R,u=v(i.startWidth+R,t,n),m=u/i.ratio;}else {let R=Math.floor(i.startY-o.clientY);R=i.direction&a.south?-R:R,m=v(i.startHeight+R,l,c),u=m*i.ratio;}if(u<t||u>n||m<l||m>c)return;s.style.width=`${u}px`,s.style.height=`${m}px`,s.style.maxWidth=`${u}px`,i.currentHeight=m,i.currentWidth=u;return}if(M&&y){let u=Math.floor(i.startX-o.clientX);u=i.direction&a.east?-u:u;let m=Math.floor(i.startY-o.clientY);m=i.direction&a.south?-m:m;let R=v(i.startWidth+u,t,n),C=v(i.startHeight+m,l,c);s.style.width=`${R}px`,s.style.height=`${C}px`,s.style.maxWidth=`${R}px`,i.currentHeight=C,i.currentWidth=R;}else if(y){let u=Math.floor(i.startY-o.clientY);u=i.direction&a.south?-u:u;let m=v(i.startHeight+u,l,c);s.style.height=`${m}px`,i.currentHeight=m;}else {let u=Math.floor(i.startX-o.clientX);u=i.direction&a.east?-u:u;let m=v(i.startWidth+u,t,n);s.style.width=`${m}px`,s.style.maxWidth=`${m}px`,i.currentWidth=m;}}},T=()=>{let o=r.current,s=f.current,i=h.current;if(o!==null&&i!==null&&s.isResizing){let M=s.currentWidth,y=s.currentHeight;s.startWidth=0,s.startHeight=0,s.ratio=0,s.startX=0,s.startY=0,s.currentWidth=0,s.currentHeight=0,s.isResizing=false,i.classList.remove("image-control-wrapper--resizing"),x(),d(M,y),document.removeEventListener("pointermove",L),document.removeEventListener("pointerup",T);}};return jsxRuntime.jsxs("div",{ref:h,children:[jsxRuntime.jsx("div",{className:"image-resizer image-resizer-n",onPointerDown:o=>{H(o,a.north);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-ne",onPointerDown:o=>{H(o,a.north|a.east);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-e",onPointerDown:o=>{H(o,a.east);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-se",onPointerDown:o=>{H(o,a.south|a.east);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-s",onPointerDown:o=>{H(o,a.south);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-sw",onPointerDown:o=>{H(o,a.south|a.west);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-w",onPointerDown:o=>{H(o,a.west);}}),jsxRuntime.jsx("div",{className:"image-resizer image-resizer-nw",onPointerDown:o=>{H(o,a.north|a.west);}})]})}var te=e=>{let{updateAttributes:r,node:t,extension:n,editor:l}=e,c=react$1.useRef(null),g=t.attrs,d=n.options,p=!l.isEditable,{width:h,height:f,caption:b}=g,z=g["data-keep-ratio"],x=d.withCaption||typeof b=="string",H=!z&&{width:h,height:f},L=I(w({},g),{style:I(w({},H||{}),{maxWidth:h})}),T=(s,i)=>{z&&c.current&&(c.current.style.width="",c.current.style.height=""),r({width:s,height:i});},o=s=>{var i;(i=d.onContextMenu)==null||i.call(d,s,e);};return p?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("img",w({},L)),x&&b&&jsxRuntime.jsx(N,w({},e))]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("img",I(w({},L),{ref:c,onContextMenu:o})),x&&jsxRuntime.jsx(N,w({},e)),jsxRuntime.jsx(D,{editor:l,imageRef:c,minWidth:d.minWidth,maxWidth:d.maxWidth,minHeight:d.minHeight,maxHeight:d.maxHeight,keepRatio:z,onResizeEnd:T})]})},W=te;var ie=e=>jsxRuntime.jsx(react.NodeViewWrapper,{className:"image-component","data-drag-handle":true,children:jsxRuntime.jsx(W,w({},e))}),A=ie;var ce=/(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/,de=e=>new Promise((r,t)=>{let n=new Image,l=URL.createObjectURL(e);n.onload=()=>{r({width:n.width,height:n.height}),URL.revokeObjectURL(l);},n.onerror=()=>{URL.revokeObjectURL(l),t(new Error("Failed to load image"));},n.src=l;}),O=(e,r,t)=>V(null,null,function*(){var d;if(!e["data-keep-ratio"])return Promise.resolve(e);let{width:n,height:l}=yield de(r),c=(d=e.width)!=null?d:t.defaultWidth,g=l/n;return I(w({},e),{width:c,height:c*g})}),ue=core.Node.create({name:"image",group:"inline",inline:true,draggable:true,atom:true,addOptions(){return {HTMLAttributes:{},defaultHeight:500,defaultWidth:500,minWidth:100,maxWidth:16384,minHeight:100,maxHeight:1/0,allowBase64:true}},addAttributes(){return {src:{default:""},alt:{default:""},title:{default:""},width:{default:this.options.defaultWidth},height:{default:this.options.defaultHeight},"data-keep-ratio":{parseHTML:e=>e.getAttribute("data-keep-ratio")!=="false",renderHTML(e){return e["data-keep-ratio"]?{style:[`max-width: ${e.width}px`].join(";"),"data-keep-ratio":"true"}:{}},default:true},className:{parseHTML:e=>e.getAttribute("class"),renderHTML:e=>({class:e.className}),default:""},caption:{parseHTML:e=>{var r,t;return (t=(r=e.parentElement)==null?void 0:r.querySelector("span"))==null?void 0:t.textContent},renderHTML(){return null},default:void 0}}},parseHTML(){return [{tag:this.options.allowBase64?"img[src]":'img[src]:not([src^="data:"])'},{tag:"span > img + span",ignore:true}]},renderHTML({HTMLAttributes:e,node:r}){var d,p;let t=document.createElement("span");t.classList.add("node-image");let n=document.createElement("span");n.classList.add("image-component");let l=document.createElement("img"),c=core.mergeAttributes(this.options.HTMLAttributes,e);Object.keys(c).forEach(h=>{h!=="caption"&&l.setAttribute(h,c[h]);}),n.appendChild(l);let g=r.attrs;if(g.caption){let h=document.createElement("span");h.classList.add("caption",...((p=(d=this.options.captionProps)==null?void 0:d.className)==null?void 0:p.split(" "))||""),h.innerHTML=g.caption,n.appendChild(h);}return t.appendChild(n),t},addCommands(){return {setResizableImage:(e,r,t)=>({commands:n})=>n.insertContentAt(r||this.editor.state.selection.head,{type:this.name,attrs:e},t)}},addInputRules(){return [core.nodeInputRule({find:ce,type:this.type,getAttributes:e=>{let[,,r,t,n]=e;return {src:t,alt:r,title:n}}})]},addProseMirrorPlugins(){return [new state.Plugin({key:new state.PluginKey(this.name),props:{handlePaste:(e,r,t)=>{var g,d,p;let n=(g=r.clipboardData)==null?void 0:g.files;if(((d=t.content.firstChild)==null?void 0:d.type.name)===this.name)return this.editor.commands.setResizableImage(w({},t.content.firstChild.attrs)),true;let l=!((p=r.clipboardData)!=null&&p.getData("text/html"));if(!n||n.length===0||!this.options.onUpload||!l)return false;let c=this.editor.state.selection.head;for(let h of n)this.options.onUpload(h).then(f=>O(f,h,this.options)).then(f=>{this.editor.chain().focus().setResizableImage(f,c,{updateSelection:false}).run();});return true},handleDrop:(e,r)=>{var c,g;r.preventDefault();let t=(c=r.dataTransfer)==null?void 0:c.files,n=!((g=r.dataTransfer)!=null&&g.getData("text/html"));if(!t||t.length===0||!this.options.onUpload||!n)return false;let l=e.posAtCoords({left:r.clientX,top:r.clientY});for(let d of t)this.options.onUpload(d).then(p=>O(p,d,this.options)).then(p=>{this.editor.chain().focus().setResizableImage(p,l==null?void 0:l.pos,{updateSelection:false}).run();});}}})]},addNodeView(){return react.ReactNodeViewRenderer(e=>A(e),{className:"node-image"})}});exports.CaptionInput=N;exports.ImageResizer=D;exports.ResizableImage=ue;exports.ResizableImageComponent=W;exports.ResizableImageNodeView=A;