UNPKG

@shapeshop/react

Version:

A TS hook and component to capture user mouse/stylus inputs to generate svg shapes and paths

41 lines (37 loc) 4.82 kB
import { useRef, useEffect, useCallback } from 'react'; var e,t,s;!function(e){e.Create="CREATE",e.Set="SET";}(e||(e={}));class n{constructor(){this.db={},this.listeners=[];}set(t,s){const n=this.db[t];this.db=Object.assign(Object.assign({},this.db),{[t]:s}),Object.freeze(this.db),this._onChange(void 0===n?e.Create:e.Set,t,s,n);}get(e){return this.db[e]}listen(e){this.listeners.push(e);}_onChange(e,t,s,n){this.listeners.forEach((i=>{i({action:e,prop:t,val:s,previousVal:n});}));}}!function(e){e.Rect="RECT",e.Circle="CIRCLE",e.Line="LINE",e.Path="PATH",e._ellipse="ELLIPSE",e._polyline="POLYLINE",e._polygon="POLYGON";}(t||(t={})),function(e){e.Shapes="shapes",e.ShapeType="shapeType",e.IsDrawing="isDrawing";}(s||(s={}));class i{constructor(e){this._shapes=()=>this.store.get(s.Shapes),this._setShapes=e=>this.store.set(s.Shapes,e),this._isDrawing=()=>this.store.get(s.IsDrawing),this._setDrawing=e=>this.store.set(s.IsDrawing,e),this._shapeType=()=>this.store.get(s.ShapeType),this._setShapeType=e=>this.store.set(s.ShapeType,e),this.handleMouseDown=e=>{if(0===e.button)try{const t={points:[h(e,this.el)],type:this._shapeType()};this._setDrawing(!0),this._setShapes([...this._shapes(),t]);}catch(e){if(e instanceof Error&&"REF_IS_NULL"===e.message)return void console.warn("ref is null")}},this.handleMouseMove=e=>{if(this._isDrawing())try{const s=h(e,this.el),n=this._shapes(),i=n.length-1,o=Object.assign({},n[i]),a=this._shapeType();a===t.Path&&(o.points=[...o.points,s]),a!==t.Rect&&a!==t.Line||(o.points=[o.points[0],s]);const r=[...n];r[i]=o,this._setShapes(r);}catch(e){if(e instanceof Error&&"REF_IS_NULL"===e.message)return void console.warn("ref is null")}},this.handleMouseUp=e=>{0===e.button&&this._setDrawing(!1);},this.setShapeType=e=>{this._setShapeType(e);},this.storeListener=({action:e,prop:t,val:s,previousVal:n})=>{var i;this.onCanvasUpdate&&(null===(i=this.onCanvasUpdate)||void 0===i||i.call(this,{action:e,prop:t,val:s,previousVal:n}));},this.destroy=()=>{console.log("Destroying"),this.el.removeEventListener("mousedown",this.handleMouseDown),this.el.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp);},console.log("ShapeShop Engine is on");const{el:i,defaultShape:o}=e;this.el=i,this.el.addEventListener("mousedown",this.handleMouseDown),this.el.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp),this.store=new n,this.store.listen(this.storeListener),this._setDrawing(!1),this._setShapes([]),this._setShapeType(o),this.onCanvasUpdate=e.onCanvasUpdate;}}function h(e,t){const s=t.getBoundingClientRect();return [e.clientX-s.left,e.clientY-s.top]}const o=(e,t)=>`${e.reduce(((e,s,n,i)=>0===n?`M ${s[0]},${s[1]}`:`${e} ${t(s,n,i)}`),"")}`;var a,r;const p=(e=>(t,s,n)=>{const[i,h]=e(n[s-1],n[s-2],t),[o,a]=e(t,n[s-1],n[s+1],!0);return `C ${i},${h} ${o},${a} ${t[0]},${t[1]}`})((a=(e,t)=>{const s=t[0]-e[0],n=t[1]-e[1];return {length:Math.sqrt(Math.pow(s,2)+Math.pow(n,2)),angle:Math.atan2(n,s)}},r=.2,(e,t,s,n)=>{const i=a(t||e,s||e),h=i.angle+(n?Math.PI:0),o=i.length*r;return [e[0]+Math.cos(h)*o,e[1]+Math.sin(h)*o]})),l=e=>o(e,p),c=e=>{const t=e[0][0],s=e[0][1],n=e[1][0]-t;let i=e[1][1]-s,h=n,o=s,a=t;return i<0&&(i=Math.abs(i),o=s-i),h<0&&(h=Math.abs(h),a=t-h),{x:a,y:o,width:h,height:i}},u=e=>({x1:e[0][0],y1:e[0][1],x2:e[1][0],y2:e[1][1]}); function useShapeShop(ref, shape, onCanvasUpdate) { const shapeshopRef = useRef(); useEffect(() => { return () => shapeshopRef.current.destroy(); }, []); useEffect(() => { if (!!shape && !!shapeshopRef.current) { shapeshopRef.current.setShapeType(shape); } }, [shape]); const _onCanvasUpdate = useCallback((update) => { if (onCanvasUpdate !== undefined) { onCanvasUpdate(update); } }, [onCanvasUpdate]); useEffect(() => { const element = ref.current; if (isHtmlElment(element)) { shapeshopRef.current = new i({ el: castToHtmlElement(ref.current), defaultShape: shape, onCanvasUpdate: _onCanvasUpdate }); } }, [ref, _onCanvasUpdate]); } function castToHtmlElement(obj) { if (isHtmlElment(obj)) { return obj; } throw new Error("NOT_HTML_ELEMENT"); } function isHtmlElment(obj) { return (obj === null || obj === void 0 ? void 0 : obj.onmousedown) !== undefined && (obj === null || obj === void 0 ? void 0 : obj.onmouseup) !== undefined && (obj === null || obj === void 0 ? void 0 : obj.onmousemove) !== undefined; } export { s as CanvasState, i as Engine, i as ShapeShop, t as ShapeType, p as bezierCommandCalc, u as generateLineData, l as generatePathData, c as generateRectData, o as svgPath, useShapeShop }; //# sourceMappingURL=index.js.map