UNPKG

@jonbell/react-radial-menu

Version:

React radial menu with sub menu support, animations and themes.

2 lines (1 loc) 16.3 kB
"use strict";var e=require("react");function t(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&t.indexOf(a)<0&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(a=Object.getOwnPropertySymbols(e);r<a.length;r++)t.indexOf(a[r])<0&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]])}return n}function n(e){var t,a,r="";if("string"==typeof e||"number"==typeof e)r+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(a=n(e[t]))&&(r&&(r+=" "),r+=a);else for(t in e)e[t]&&(r&&(r+=" "),r+=t);return r}function a(){for(var e,t,a=0,r="";a<arguments.length;)(e=arguments[a++])&&(t=n(e))&&(r&&(r+=" "),r+=t);return r}"function"==typeof SuppressedError&&SuppressedError;const r=e.createContext(null);function o(...e){return a(...e).split(" ").map((e=>`__rrm-${e}`)).join(" ")}function i(e,t,n,a,r){function o(e,t,n,r,o){const i=e+a,c=t+a,l=n+a,s=r+a;if(i>=o.objectX&&i<=o.objectX+o.objectWidth&&c>=o.objectY&&c<=o.objectY+o.objectHeight||l>=o.objectX&&l<=o.objectX+o.objectWidth&&s>=o.objectY&&s<=o.objectY+o.objectHeight)return!0;const u=o.objectX,_=o.objectX+o.objectWidth,d=o.objectY,m=o.objectY+o.objectHeight;if(i<=u&&l>=u){const e=c+(s-c)*(u-i)/(l-i);if(e>=d&&e<=m)return!0}if(i>=_&&l<=_){const e=c+(s-c)*(_-i)/(l-i);if(e>=d&&e<=m)return!0}if(c<=d&&s>=d){const e=i+(l-i)*(d-c)/(s-c);if(e>=u&&e<=_)return!0}if(c>=m&&s<=m){const e=i+(l-i)*(m-c)/(s-c);if(e>=u&&e<=_)return!0}return!1}let i=e;const c=Math.PI/180;let l=!1;for(;i<t&&!l;){o(n*Math.cos(i),n*Math.sin(i),a*Math.cos(i),a*Math.sin(i),r)?i+=c:l=!0}l||(i=e);const s=a*Math.cos(i),u=a*Math.sin(i),_=a*Math.cos(t),d=a*Math.sin(t),m=n*Math.cos(i),h=n*Math.sin(i),M=n*Math.cos(t),p=n*Math.sin(t),g=t-i<=180?0:1;return`\n M ${a} ${a}\n m ${m} ${h}\n l ${s-m} ${u-h}\n A ${a} ${a} 0 ${g} 1 ${_+a} ${d+a}\n l ${M-_} ${p-d}\n A ${n} ${n} 0 ${g} 0 ${a+m} ${a+h}\n `}function c(e,t,n){const a=.98*n,r=.95*n,o=a*Math.cos((e+t)/2)+n,i=a*Math.sin((e+t)/2)+n;return`${r*Math.cos((e+t)/2+Math.PI/60)+n},${r*Math.sin((e+t)/2+Math.PI/60)+n} ${o},${i} ${r*Math.cos((e+t)/2-Math.PI/60)+n},${r*Math.sin((e+t)/2-Math.PI/60)+n}`}function l(e,t,n,a,r,o){const i=o?20:300,c=o?20:40,l=t*a+t/2,s=n,u=Math.cos(l)*s+r,_=Math.sin(l)*s+r;return{objectX:l>Math.PI/2&&l<3*Math.PI/2?u-i:u,objectY:_,objectWidth:i,objectHeight:c}}!function(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css","top"===n&&a.firstChild?a.insertBefore(r,a.firstChild):a.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}}(':root {\n --__reactRadialMenu__menu-bgColor: #fff;\n --__reactRadialMenu__separator-color: rgba(0, 0, 0, 0.2);\n --__reactRadialMenu__item-color: #333;\n --__reactRadialMenu__zIndex: 666;\n --__reactRadialMenu__activeItem-color: #fff;\n --__reactRadialMenu__activeItem-bgColor: #3498db;\n --__reactRadialMenu__arrow-color: #6f6e77;\n --__reactRadialMenu__activeArrow-color: #fff;\n --__reactRadialMenu__animation-delay: 300ms;\n}\n/* --------------------------------- Themes --------------------------------- */\n.__rrm-light {\n --__reactRadialMenu__menu-bgColor: #fff;\n --__reactRadialMenu__separator-color: rgba(0, 0, 0, 0.2);\n --__reactRadialMenu__item-color: #333;\n}\n.__rrm-dark {\n --__reactRadialMenu__menu-bgColor: rgba(40, 40, 40, 0.98);\n --__reactRadialMenu__separator-color: #4c4c4c;\n --__reactRadialMenu__item-color: #fff;\n}\n/* ------------------------------- Animations --------------------------------*/\n.__rrm-menu.__rrm-closing.__rrm-fade,\n.__rrm-menu.__rrm-opening.__rrm-fade {\n opacity: 0;\n}\n.__rrm-menu.__rrm-opened.__rrm-fade {\n opacity: 1;\n}\n\n.__rrm-menu.__rrm-closing.__rrm-scale,\n.__rrm-menu.__rrm-opening.__rrm-scale {\n scale: 0;\n}\n.__rrm-menu.__rrm-opened.__rrm-scale {\n scale: 1;\n}\n\n.__rrm-menu.__rrm-closing.__rrm-rotate,\n.__rrm-menu.__rrm-opening.__rrm-rotate {\n rotate: -45deg;\n}\n.__rrm-menu.__rrm-opened.__rrm-rotate {\n rotate: 0deg;\n}\n/* ---------------------------------- Menu ---------------------------------- */\n.__rrm-menu {\n position: fixed;\n z-index: var(--__reactRadialMenu__zIndex);\n transition: all var(--__reactRadialMenu__animation-delay) ease;\n transform-origin: center;\n font-family: "Roboto", sans-serif;\n font-size: 1rem;\n overflow: visible;\n}\n\n.__rrm-menu g {\n cursor: pointer;\n}\n/* ---------------------------------- Base ---------------------------------- */\n.__rrm-base {\n transition: all var(--__reactRadialMenu__animation-delay) ease;\n fill: var(--__reactRadialMenu__menu-bgColor);\n stroke: var(--__reactRadialMenu__separator-color);\n}\n.__rrm-base.__rrm-active {\n fill: var(--__reactRadialMenu__activeItem-bgColor);\n}\n/* --------------------------------- Content -------------------------------- */\n.__rrm-content {\n justify-content: end;\n text-align: left;\n position: relative;\n display: inline-block;\n align-items: start;\n pointer-events: none;\n color: var(--__reactRadialMenu__item-color);\n overflow: hidden;\n text-overflow: ellipsis;\n background-color: var( --__reactRadialMenu__menu-bgColor);\n}\n.__rrm-content.__rrm-active {\n color: var(--__reactRadialMenu__activeItem-color);\n background-color: var(--__reactRadialMenu__activeItem-bgColor);\n}\n.__rrm-content.__rrm-isFlowLeft {\n text-align: right;\n right: 0;\n}\n/* ---------------------------------- Arrow --------------------------------- */\n.__rrm-arrow {\n stroke: var(--__reactRadialMenu__arrow-color);\n fill: none;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 2px;\n}\n.__rrm-arrow.__rrm-active {\n stroke: var(--__reactRadialMenu__activeArrow-color);\n}\n/* --------------------------------- Return --------------------------------- */\n.__rrm-return {\n stroke: var(--__reactRadialMenu__arrow-color);\n fill: none;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 4px;\n}\n.__rrm-return.__rrm-active {\n stroke: var(--__reactRadialMenu__activeArrow-color);\n}\n/* --------------------------------- No Bg --------------------------------- */\n.__rrm-no-bg > g > foreignObject {\n border: 1px solid var(--__reactRadialMenu__separator-color);\n border-radius: 50%;\n background-color: var(--__reactRadialMenu__menu-bgColor);\n}\n.__rrm-no-bg > g > foreignObject:has(.__rrm-active) {\n background-color: var(--__reactRadialMenu__activeItem-bgColor);\n}\n.__rrm-no-bg .__rrm-arrow {\n stroke: var(--__reactRadialMenu__arrow-color);\n fill: none;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 2px;\n}\n.__rrm-no-bg .__rrm-arrow.__rrm-active {\n stroke: var(--__reactRadialMenu__arrow-color);\n}\n');const s={activeMenuId:"0",deltaRadius:0,innerRadius:0,menuHeight:0,menuWidth:0,middleRadius:0,outerRadius:0,drawBackground:!0,hoverToOpen:!1,hoverToBackTimeout:800},u=n=>{var{__myMenuId:a,__angleStep:s,__index:u,itemView:_,data:d,onItemClick:m}=n,h=t(n,["__myMenuId","__angleStep","__index","itemView","data","onItemClick"]);const{data:M,changeMenu:p}=e.useContext(r),{innerRadius:g,outerRadius:v,middleRadius:f,deltaRadius:b}=M,[w,j]=e.useState(!1),E=s,R=u,y=a,{objectX:I,objectY:k,objectWidth:x,objectHeight:C}=e.useMemo((()=>l(0,E,f,R,v)),[b,E,f,R,v]),O=e.useMemo((()=>l(0,E,f,R-1,v)),[b,E,f,R,v]),$=I<0;return M.drawBackground?e.createElement("g",Object.assign({},h,{onMouseEnter:e=>{var t;null===(t=h.onMouseEnter)||void 0===t||t.call(h,e),j(!0),M.hoverToOpen&&p(y,e)},onMouseLeave:e=>{var t;null===(t=h.onMouseLeave)||void 0===t||t.call(h,e),j(!1)},onClick:e=>{e.preventDefault(),e.stopPropagation(),null==m||m(e,R,d),p(y,e)}}),e.createElement("path",{d:i(R*E,(R+1)*E,g,v,O),className:o("base",{active:w})}),e.createElement("foreignObject",{x:I,y:k,width:x,height:C,style:{overflow:"visible",textAlign:$?"right":"left",alignItems:$?"end":"start"}},e.createElement("div",{className:o("content",{active:w})},_)),e.createElement("polyline",{points:c(R*E,(R+1)*E,v),className:o("arrow",{active:w,isFlowLeft:$})})):e.createElement("g",Object.assign({},h),e.createElement("foreignObject",{style:{overflow:"visible",textAlign:$?"right":"left",alignItems:$?"end":"start"},onMouseEnter:e=>{var t;null===(t=h.onMouseEnter)||void 0===t||t.call(h,e),j(!0),M.hoverToOpen&&p(y,e)},onMouseLeave:e=>{var t;null===(t=h.onMouseLeave)||void 0===t||t.call(h,e),j(!1)},onClick:e=>{e.preventDefault(),e.stopPropagation(),null==m||m(e,R,d),p(y,e)},x:I,y:k,width:x,height:C},e.createElement("div",{className:o("content",{active:w,isFlowLeft:$})},_)),e.createElement("polyline",{points:c(R*E,(R+1)*E,v),className:o("arrow",{active:w})}))},_=n=>{var{position:a,onClick:i,__parentMenuId:c,onMouseEnter:l}=n,s=t(n,["position","onClick","__parentMenuId","onMouseEnter"]);const{data:u}=e.useContext(r),{innerRadius:_,outerRadius:d,activeMenuId:m}=u,[h,M]=e.useState(!1),p=e.useRef(null);let{startAngle:g,endAngle:v,objectX:f,objectY:b,objectWidth:w,objectHeight:j}=e.useMemo((()=>function(e,t,n){let a=0,r=0,o=0,i=0,c=0,l=0;switch(e){case"top":a=Math.PI/6+Math.PI,r=5*Math.PI/6+Math.PI,o=t/2,l=Math.sin(a)*t+n-o,c=Math.cos(a)*t+n,i=Math.cos(r)*t+n-c;break;case"bottom":a=Math.PI/6,r=5*Math.PI/6,o=t/2,l=Math.sin(r)*t+n,c=Math.cos(r)*t+n,i=Math.cos(a)*t+n-c;break;case"left":a=4*Math.PI/6,r=8*Math.PI/6,i=t/2,c=Math.cos(r)*t+n-i,l=Math.sin(r)*t+n,o=Math.sin(a)*t+n-l;break;case"right":a=10*Math.PI/6,r=2*Math.PI/6,i=t/2,c=Math.cos(a)*t+n,l=Math.sin(a)*t+n,o=Math.sin(r)*t+n-l;break;case"center":c=n-t,l=c,i=2*t,o=i;break;default:throw new Error(`Invalid position: ${e}`)}return{startAngle:a,endAngle:r,objectX:c,objectY:l,objectWidth:i,objectHeight:o}}(a,_,d)),[a,_,d]);const E=e.useCallback((e=>{M(!0),u.hoverToOpen&&(p.current=setTimeout((()=>{l(e)}),1e3))}),[l,u.hoverToOpen]),R=e.useCallback((()=>{M(!1),p.current&&(clearTimeout(p.current),p.current=null)}),[]);return e.useEffect((()=>()=>{p.current&&clearTimeout(p.current)}),[]),c!==m?e.createElement(e.Fragment,null):u.drawBackground?e.createElement("g",Object.assign({},s,{onMouseEnter:E,onMouseLeave:R,onClick:e=>{e.preventDefault(),e.stopPropagation(),i(e,a)}}),"center"!==a?e.createElement("path",{d:`M ${Math.cos(g)*_+d}\n ${Math.sin(g)*_+d}\n A ${_} ${_} 0 0 1 \n ${Math.cos(v)*_+d}\n ${Math.sin(v)*_+d}\n Z`,className:o("base",{active:h})}):e.createElement("circle",{cx:d,cy:d,r:_,className:o("base",{active:h})}),e.createElement("foreignObject",{x:f,y:b,width:w,height:j},e.createElement("div",{className:o("wrapper-return",{active:h})},s.children?s.children:e.createElement("svg",{className:o("return",{active:h}),width:.5*w+"px",height:.5*j+"px",viewBox:"0 0 48 48"},e.createElement("path",{d:"M12.9998 8L6 14L12.9998 21"}),e.createElement("path",{d:"M6 14H28.9938C35.8768 14 41.7221 19.6204 41.9904 26.5C42.2739 33.7696 36.2671 40 28.9938 40H11.9984"}))))):e.createElement("g",Object.assign({},s),e.createElement("foreignObject",{x:f,y:b,width:w,height:j,onMouseEnter:E,onMouseLeave:R,onClick:e=>{e.preventDefault(),e.stopPropagation(),i(e,a)}},e.createElement("div",{className:o("content",{active:h})},s.children?s.children:e.createElement("svg",{className:o("return",{active:h}),width:.5*w+"px",height:.5*j+"px",viewBox:"0 0 48 48"},e.createElement("path",{d:"M12.9998 8L6 14L12.9998 21"}),e.createElement("path",{d:"M6 14H28.9938C35.8768 14 41.7221 19.6204 41.9904 26.5C42.2739 33.7696 36.2671 40 28.9938 40H11.9984"})))))},d=({__myMenuId:t,__parentMenuId:n,displayPosition:a,children:o,displayView:i,onDisplayClick:c})=>{const{changeMenu:l}=e.useContext(r),s=t,u=n,d=e.Children.count(o);if(d<2)throw new Error("RadialMenu must have at least 2 children");const m=2*Math.PI/d;return e.createElement(e.Fragment,null,e.Children.map(o,((t,n)=>{if(e.isValidElement(t)){let a={__index:n,__angleStep:m,__parentMenuId:s};return e.cloneElement(t,a)}return t})),e.createElement(_,{__parentMenuId:s,position:a,onMouseEnter:e=>{l(u,e)},onClick:(e,t)=>{null==c||c(e,t),l(u,e)}},i))};exports.Menu=n=>{var{centerX:i,centerY:c,innerRadius:l,outerRadius:u,animationTimeout:_,show:d,animateSubMenuChange:m,animation:h,theme:M,drawBackground:p,hoverToOpen:g,hoverToBackTimeout:v}=n,f=t(n,["centerX","centerY","innerRadius","outerRadius","animationTimeout","show","animateSubMenuChange","animation","theme","drawBackground","hoverToOpen","hoverToBackTimeout"]);const[b,w]=e.useState(s),[j,E]=e.useState(i),[R,y]=e.useState(c);if(l>=u)throw new Error("RadialMenu's innerRadius must be less than outerRadius");const I=e.Children.count(f.children);if(I<2)throw new Error("RadialMenu must have at least 2 children");const k=2*Math.PI/I,x=2*(l+u)/3,C=u-l,O=2*u,$=O;_=e.useMemo((()=>_||0),[_]);e.useEffect((()=>{w((e=>({innerRadius:l,outerRadius:u,middleRadius:x,deltaRadius:C,menuWidth:O,menuHeight:$,activeMenuId:d?"0":e.activeMenuId,drawBackground:null==p||p,hoverToOpen:null!=g&&g,hoverToBackTimeout:null!=v?v:800})))}),[l,u,d,p,g]),e.useEffect((()=>{E(i),y(c)}),[i,c]);const[P,T]=e.useState("closed"),B=e.useCallback((()=>{document.documentElement.style.setProperty("--__reactRadialMenu__animation-delay",`${_}ms`),d?(T("opening"),setTimeout((()=>T("opened")),_)):(T("closing"),setTimeout((()=>T("closed")),_))}),[d,_]),S=e.useCallback(((e,t)=>{g&&(E(t.clientX),y(t.clientY)),m?(B(),setTimeout((()=>w((t=>Object.assign(Object.assign({},t),{activeMenuId:e})))),_)):w((t=>Object.assign(Object.assign({},t),{activeMenuId:e})))}),[B,m,g,O,$]);return e.useEffect((()=>{B()}),[d,B]),"closed"===P?e.createElement(e.Fragment,null):e.createElement(r.Provider,{value:{data:b,changeMenu:S}},e.createElement("svg",Object.assign({},f,{width:O,height:$,viewBox:`-3 -3 ${O+6} ${$+6}`,style:Object.assign(Object.assign({},f.style),{overflow:"visible",width:`${O}px`,height:`${$}px`,left:j-u+"px",top:R-u+"px"}),className:a(f.className,o("menu",P,h,M,!b.drawBackground&&"no-bg"))}),e.Children.map(f.children,((t,n)=>{if(e.isValidElement(t)){let a={__index:n,__angleStep:k,__parentMenuId:"0"};return e.cloneElement(t,a)}return t}))))},exports.MenuItem=n=>{var{__angleStep:a,__index:c,__parentMenuId:s,__isBackButton:u,data:_,onItemClick:d}=n,m=t(n,["__angleStep","__index","__parentMenuId","__isBackButton","data","onItemClick"]);const{data:h}=e.useContext(r),{innerRadius:M,outerRadius:p,middleRadius:g,deltaRadius:v,activeMenuId:f}=h,[b,w]=e.useState(!1),j=a,E=c,R=s,{objectX:y,objectY:I,objectWidth:k,objectHeight:x}=e.useMemo((()=>l(0,j,g,E,p,u)),[M,j,g,E,p,u]),C=e.useMemo((()=>l(0,j,g,E-1,p,u)),[M,j,g,E,p,u]);if(R!==f)return e.createElement(e.Fragment,null);const O=y<0;return h.drawBackground?e.createElement("g",Object.assign({},m,{onMouseEnter:e=>{var t;null===(t=m.onMouseEnter)||void 0===t||t.call(m,e),w(!0)},onMouseLeave:e=>{var t;null===(t=m.onMouseLeave)||void 0===t||t.call(m,e),w(!1)},onClick:e=>{e.preventDefault(),e.stopPropagation(),null==d||d(e,E,_)},onMouseUp:e=>{e.preventDefault(),e.stopPropagation(),null==d||d(e,E,_)}}),e.createElement("path",{d:i(E*j,(E+1)*j,M,p,C),className:o("base",{active:b})}),e.createElement("foreignObject",{x:y,y:I,width:k,height:x,style:{overflow:"visible",textAlign:O?"right":"left",alignItems:O?"end":"start"}},e.createElement("div",{className:o("content",{active:b,isFlowLeft:O})},m.children))):e.createElement("g",Object.assign({},m),e.createElement("foreignObject",{x:y,y:I,width:k,height:x,onMouseEnter:e=>{var t;null===(t=m.onMouseEnter)||void 0===t||t.call(m,e),w(!0)},onMouseLeave:e=>{var t;null===(t=m.onMouseLeave)||void 0===t||t.call(m,e),w(!1)},onClick:e=>{e.preventDefault(),e.stopPropagation(),null==d||d(e,E,_)}},e.createElement("div",{className:o("content",{active:b})},m.children)))},exports.SubMenu=n=>{const{data:a}=e.useContext(r),{activeMenuId:o}=a,i=`${n.__parentMenuId}-${n.__index}`,{__parentMenuId:c,displayPosition:l,children:s,displayView:_,onDisplayClick:m}=n,h=t(n,["__parentMenuId","displayPosition","children","displayView","onDisplayClick"]);return o===n.__parentMenuId?e.createElement(u,Object.assign({},h,{__myMenuId:i})):e.createElement(d,{__myMenuId:i,__parentMenuId:c,displayPosition:l,children:s,displayView:_,onDisplayClick:m})};