@vuu-ui/vuu-popups 0.8.28 → 0.8.29-debug
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.
- package/cjs/index.js +2219 -1
- package/cjs/index.js.map +4 -4
- package/esm/index.js +2209 -1
- package/esm/index.js.map +4 -4
- package/index.css +368 -1
- package/index.css.map +3 -3
- package/package.json +5 -5
- package/types/notifications/NotificationsCenter.d.ts +6 -0
- package/types/notifications/NotificationsProvider.d.ts +7 -26
- package/types/notifications/ToastNotification.d.ts +9 -0
- package/types/notifications/index.d.ts +2 -0
- package/types/notifications/notificationTypes.d.ts +7 -0
- package/LICENSE +0 -201
package/esm/index.js
CHANGED
|
@@ -1,2 +1,2210 @@
|
|
|
1
|
-
import Yt from"clsx";import{useThemeAttributes as jt}from"@vuu-ui/vuu-utils";import{useCallback as Qt,useLayoutEffect as Zt,useRef as Ke}from"react";import{Button as Rt,Text as Lt}from"@salt-ds/core";import Ht from"clsx";import{jsx as ke,jsxs as At}from"react/jsx-runtime";var It="vuuDialogHeader",Re=({hideCloseButton:e=!1,title:t,onClose:o,...n})=>At("div",{...n,className:Ht(It,"vuuToolbarProxy"),children:[ke(Lt,{className:"dialogHeader",children:t}),!e&&ke(Rt,{onClick:o,"data-align":"end","data-icon":"close",variant:"secondary"},"close")]});import St from"clsx";import Ot,{createElement as Ae}from"react";import G from"react-dom";import*as Le from"react-dom";import Nt from"clsx";var Dt=1,Bt=({className:e,dataMode:t,x:o=0,y:n=0,win:r=window})=>{let i=r.document.createElement("div");return i.className=Nt(`vuuPopup ${Dt++}`,e),i.style.cssText=`left:${o}px; top:${n}px;`,t&&(i.dataset.mode=t),r.document.body.appendChild(i),i},Vn=e=>Bt(e),He=(e,t,o,n,r)=>{t.style.cssText=`left:${o}px; top:${n}px;position: absolute;`,Le.render(e,t,r)};var S=!1,I=[],Ne=e=>(e==null?void 0:e.type)==="menu-action",De=e=>(e==null?void 0:e.type)==="click-away";function Y(e){if(e.key==="Esc"){if(I.length)Se();else if(S){let t=document.body.querySelector(".vuuDialog");t&&G.unmountComponentAtNode(t)}}}function Be(e){if(I.length){let t=document.body.querySelectorAll(".vuuPopup,#vuu-portal-root");for(let o=0;o<t.length;o++)if(t[o].contains(e.target))return;Se({mouseEvt:e,type:"click-away"})}}function Se(e){if(I.length===1)O.hidePopup(e,"anon","all");else if(I.length){let t=document.body.querySelectorAll(".vuuPopup");for(let o=0;o<t.length;o++)G.unmountComponentAtNode(t[o]);Oe("*")}}function $t(){S===!1&&(S=!0,window.addEventListener("keydown",Y,!0))}function Kt(){S&&(S=!1,window.removeEventListener("keydown",Y,!0))}function zt(e){I.indexOf(e)===-1&&(I.push(e),S===!1&&(window.addEventListener("keydown",Y,!0),window.addEventListener("click",Be,!0)))}function Oe(e){if(I.length){if(e==="*")I.length=0;else{let t=I.indexOf(e);t!==-1&&I.splice(t,1)}I.length===0&&S===!1&&(window.removeEventListener("keydown",Y,!0),window.removeEventListener("click",Be,!0))}}var Ft=({children:e,position:t,style:o})=>{let n=St("hwPopup","hwPopupContainer",t);return Ae("div",{className:n,style:o},e)},Wt=1,O=class e{static showPopup({group:t="all",name:o="anon",left:n=0,position:r="",right:i="auto",top:s=0,width:p="auto",component:l}){if(!l)throw Error("PopupService showPopup, no component supplied");typeof l.props.onClose=="function"?e.onClose=l.props.onClose:e.onClose=void 0,zt(o),document.addEventListener("keydown",e.escapeKeyListener,!0);let u=document.body.querySelector(".vuuPopup."+t);u===null&&(u=document.createElement("div"),u.className="vuuPopup "+t,document.body.appendChild(u));let a={width:p};He(Ae(Ft,{key:Wt++,position:r,style:a},l),u,n,s,()=>{e.keepWithinThePage(u,i)})}static escapeKeyListener(t){t.key==="Escape"&&e.hidePopup({type:"escape",event:t})}static hidePopup(t,o="anon",n="all"){var r;if(I.indexOf(o)!==-1){Oe(o);let i=document.body.querySelector(`.vuuPopup.${n}`);i&&G.unmountComponentAtNode(i)}document.removeEventListener("keydown",e.escapeKeyListener,!0),(r=e==null?void 0:e.onClose)==null||r.call(e,t?{...t,closedBy:"popup-service"}:void 0)}static keepWithinThePage(t,o="auto"){let n=t.querySelector(".vuuPopupContainer > *");if(n){let{top:r,left:i,width:s,height:p,right:l}=n.getBoundingClientRect(),u=window.innerWidth,c=window.innerHeight-(r+p);c<0&&(n.style.top=Math.round(r)+c+"px");let d=u-(i+s);if(d<0&&(n.style.left=Math.round(i)+d+"px"),typeof o=="number"&&o!==l){let f=o-l;n.style.left=i+f+"px"}}}},Ie=class e{static showDialog(t){let o=".vuuDialog",n=t.props.onClose;$t(),G.render(Ot.cloneElement(t,{container:o,onClose:()=>{e.closeDialog(),n&&n()}}),document.body.querySelector(o))}static closeDialog(){Kt();let t=document.body.querySelector(".vuuDialog");t&&G.unmountComponentAtNode(t)}};import Xt from"clsx";import{useCallback as qt,useLayoutEffect as Gt,useRef as Vt,useState as Ut}from"react";var V=(e,t,o,n,r,i)=>{let{bottom:s,height:p,left:l,right:u,top:a,width:c}=e.getBoundingClientRect();switch(t){case"below":return{left:l+o,top:s+n};case"right":return{left:u+o,top:a+n};case"below-center":return{left:l+c/2+o,top:s+n};case"below-right":return{left:l,minWidth:r,top:s+n};case"below-full-width":return{left:l+o,minWidth:r,top:s+n,width:c};case"center":return i?{left:c/2-i.width/2+o,top:p/2-i.height/2+n,visibility:"visible"}:{left:c/2+o,top:p/2+n,visibility:"hidden"};default:throw Error(`Popup getPositionRelativeToAnchor non-supported placement value ${t}`)}};var K=({anchorElement:e,minWidth:t,offsetLeft:o=0,offsetTop:n=0,placement:r,position:i})=>{let s=Vt(null),[p,l]=Ut(i);Gt(()=>{if(r==="absolute"&&i)l(i);else if(e.current&&r!=="auto"){let a=s.current===null?void 0:s.current.getBoundingClientRect(),c=V(e.current,r,o,n,t,a);l(c)}},[e,t,o,n,r,i]);let u=qt(a=>{if(s.current=a,a&&r==="center"&&e.current){let{height:c,width:d}=a.getBoundingClientRect();l(V(e.current,r,o,n,void 0,{height:c,width:d}))}},[e,o,n,r]);return{position:p,popupRef:r==="center"?u:void 0}};import{jsx as Jt}from"react/jsx-runtime";var $e=({children:e,className:t,anchorElement:o,minWidth:n,offsetLeft:r,offsetTop:i,placement:s,position:p})=>{let{popupRef:l,position:u}=K({anchorElement:o,minWidth:n,offsetLeft:r,offsetTop:i,placement:s,position:p});return u===void 0?null:Jt("div",{className:Xt("vuuPortal",t),ref:l,style:u,children:e})};import{jsx as ue,jsxs as to}from"react/jsx-runtime";var ze="vuuDialog",_t={current:document.body},eo={},Fe=({PopupProps:e=eo,children:t,className:o,isOpen:n=!1,onClose:r,style:i,title:s,hideCloseButton:p=!1,...l})=>{let{anchorElement:u=_t,offsetLeft:a=0,offsetTop:c=0,placement:d="auto"}=e,f=Ke(null),P=Ke(null),[g,y,E]=jt(),{position:T}=K({anchorElement:u,offsetLeft:a,offsetTop:c,placement:d}),C=Qt(()=>{r==null||r()},[r]);return Zt(()=>{if(f.current){if(n){f.current.showModal();let{left:M,top:v}=f.current.getBoundingClientRect();P.current&&(P.current.style.cssText=`left:-${M}px;position:absolute;top:-${v}px;`)}else f.current.close();if(d.endsWith("center")){let{width:M}=f.current.getBoundingClientRect();f.current.style.marginLeft=`-${M/2}px`}}},[n,d]),to("dialog",{...l,className:Yt(ze,g),"data-mode":E,onClose:C,id:"vuu-dialog",ref:f,style:{...i,...T},children:[ue(Re,{hideCloseButton:p,onClose:C,title:s}),ue("div",{className:`${ze}-body`,children:t}),ue("div",{id:"vuu-dialog-portal-root",ref:P})]})};import{useCallback as oo,useState as no}from"react";import{jsx as ro}from"react/jsx-runtime";var Hr=()=>{let[e,t]=no(),o=oo(()=>{t(void 0)},[]);return{dialog:e?ro(Fe,{className:"vuDialog",isOpen:!0,onClose:o,style:{maxHeight:500},title:e.title,hideCloseButton:e.hideCloseButton,children:e.content}):null,setDialogState:t}};import{useId as Wo}from"@vuu-ui/vuu-utils";import{useCallback as fe,useRef as st}from"react";import{useThemeAttributes as io}from"@vuu-ui/vuu-utils";import{useLayoutEffect as We,useRef as so,useState as uo}from"react";import{createPortal as ao}from"react-dom";function lo(e){return typeof e=="function"?e():e}var po=["vuu-dialog-portal-root","vuu-portal-root"],co=e=>{if(Array.isArray(e))for(let t of e){let o=document.getElementById(t);if(o)return o}else return document.getElementById(e);return null},z=({children:e,container:t=document.body,id:o=po,onRender:n,open:r=!0,themeAttributes:i})=>{var f;let[s,p]=uo(!1),l=so(null),u=(f=lo(t))!=null?f:document.body,[a,c,d]=io(i);return We(()=>{let P=co(o);P?l.current=P:(l.current=document.createElement("div"),l.current.id=typeof o=="string"?o:o.length>0?o.at(-1):"vuu-portal-root");let g=l.current;u.contains(g)||u.appendChild(g),g.classList.add(a,c),g.dataset.mode=d,p(!0)},[o,u,a,c,d]),We(()=>{requestAnimationFrame(()=>{n==null||n()})},[n]),r&&s&&l.current&&e?ao(e,l.current):null};import _e from"clsx";import et,{useLayoutEffect as ko,useMemo as Ro,useRef as Lo}from"react";import{useId as Ho}from"@vuu-ui/vuu-utils";import qe,{useCallback as mo,useMemo as fo}from"react";var ae=e=>e.type===j||!!e.props["data-group"],go=e=>{if(Array.isArray(e)&&le(e[0]))return e[0]},vo=(e,t,o,n=!1)=>{let{props:{children:r}}=e;return{childWithId:qe.cloneElement(e,{hasSeparator:n,id:`${t}`,key:t,children:o?go(r):r}),grandChildren:o?r:void 0}},Ge=(e,t)=>{let o=mo(()=>{let i=(s,p=t,l={},u={})=>{let a=l[p]=[],c=0,d=!1;return qe.Children.forEach(s,f=>{if(!le(f))if(f.type===Ve)d=!0;else{let P=ae(f),g=`${p}-${c}`,{props:{action:y,options:E}}=f,{childWithId:T,grandChildren:C}=vo(f,g,P,d);a.push(T),C?i(C,g,l,u):u[g]={action:y,options:E},c+=1,d=!1}}),[l,u]};return i(e)},[t,e]),[n,r]=fo(()=>o(),[o]);return[n,r]};import{useCallback as Q,useMemo as wo,useRef as pe,useState as Eo}from"react";var Ue=e=>e.closest("[data-root='true']")!==null,Xe=(e,t)=>{var o;return e.ariaHasPopup==="true"&&((o=e.dataset)==null?void 0:o.idx)===`${t}`||e.querySelector(`:scope > [data-index='${t}'][aria-haspopup='true']`)!==null};function bo(e,...t){let o=new Set(e);for(let n of t)for(let r of n)o.add(r);return o}var ho="Enter";var xo="Delete",Mo=new Set([ho,xo]),yo=new Set(["Tab"]),Po=new Set(["ArrowRight","ArrowLeft"]),Je=new Set(["Home","End","ArrowDown","ArrowUp"]),Ye=new Set(["Home","End","ArrowRight","ArrowLeft"]),Co=new Set(["F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"]),Jr=bo(Mo,Ye,Je,Po,Co,yo);var je=({key:e},t="vertical")=>(t==="vertical"?Je:Ye).has(e);import{isValidNumber as ce}from"@vuu-ui/vuu-utils";var Qe=({autoHighlightFirstItem:e=!1,count:t,defaultHighlightedIdx:o,highlightedIndex:n,onActivate:r,onHighlight:i,onCloseMenu:s,onOpenMenu:p})=>{var M;if(ce(n)&&ce(o))throw Error("useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx");let l=ce(n),u=pe((M=o!=null?o:n)!=null?M:e?0:-1),[,a]=Eo(null),c=Q(v=>{u.current=v,i==null||i(v),a({})},[i]),d=Q(v=>{v!==u.current&&(l||c(v))},[l,c]),f=pe(!0),P=pe(!1),g=v=>P.current=v,y=l?n:u.current,E=Q(v=>{let m=To(t,v.key,u.current);m!==u.current&&d(m)},[t,d]),T=Q(v=>{if(je(v))v.preventDefault(),v.stopPropagation(),f.current=!0,E(v);else if((v.key==="ArrowRight"||v.key==="Enter")&&Xe(v.target,y)){let b=v.target.querySelector(`:scope > [data-index='${y}']`);b&&(p==null||p(b,!0))}else v.key==="ArrowLeft"&&!Ue(v.target)?s(v,"close-child-menu"):v.key==="Enter"?(v.preventDefault(),v.stopPropagation(),r&&r(y)):v.key==="Tab"&&s(v,"tab-away")},[y,E,r,s,p]),C=wo(()=>({onFocus:()=>{y===-1&&c(0)},onKeyDown:T,onMouseDownCapture:()=>{f.current=!1,g(!0)},onMouseMove:()=>{f.current&&(f.current=!1)},onMouseLeave:()=>{f.current=!0,g(!1),d(-1)}}),[T,y,c,d]);return{focusVisible:f.current?y:-1,controlledHighlighting:l,highlightedIndex:y,setHighlightedIndex:d,listProps:C,setIgnoreFocus:g}};function To(e,t,o){return t==="ArrowUp"?o>0?o-1:o:o===null?0:o===e-1?o:o+1}import{Fragment as Do,jsx as F}from"react/jsx-runtime";var Ze="vuuMenuList",Ve=()=>F("li",{className:"vuuMenuItem-divider"}),j=()=>null,Z=({children:e,idx:t,options:o,...n})=>F("div",{...n,children:e}),tt=({children:e})=>F(Do,{children:e});tt.displayName="MenuItemLabel";Z.Label=tt;var Io=e=>et.isValidElement(e)&&typeof e.type!="string"&&"displayName"in e.type?e.type.displayName:void 0,le=e=>Io(e)==="MenuItemLabel",Ao=e=>e.props["data-icon"],de=({activatedByKeyboard:e,childMenuShowing:t,children:o,className:n,defaultHighlightedIdx:r,highlightedIdx:i,id:s,isRoot:p,listItemProps:l,onHighlightMenuItem:u,onActivate:a,onCloseMenu:c,openMenu:d,...f})=>{let P=Ho(s),g=Lo(null),y=Ro(()=>new Map,[]),E=x=>{var w;let h=(w=g.current)==null?void 0:w.querySelector(`:scope > [data-index='${x}']`);h!=null&&h.id&&(a==null||a(h.id))},{focusVisible:T,highlightedIndex:C,listProps:M}=Qe({count:et.Children.count(o),defaultHighlightedIdx:r,highlightedIndex:i,onActivate:E,onHighlight:u,onOpenMenu:d,onCloseMenu:c}),v=t==null?T:-1;ko(()=>{var x;t===void 0&&e&&((x=g.current)==null||x.focus())},[e,t]);let m=()=>C===void 0||C===-1?void 0:y.get(C);function b(){let x={...l,role:"menuitem"},h=(R,L,H)=>L?[F("span",{className:"vuuIconContainer","data-icon":H},"icon")].concat(R):R;function w(R,L,H,B){var Te;let{children:N,className:Ct,"data-icon":we,id:J,hasSeparator:wt,label:$,...Et}=L.props,se=ae(L),Ee=se&&t===J,Tt=Ee?`${P}-${J}`:void 0,kt=($!=null?$:typeof N=="string")?N:void 0;R.push(F(Z,{...Et,...x,...No(J,H,(Te=L.key)!=null?Te:J,C,v,Ct,wt),"aria-controls":Tt,"aria-haspopup":se||void 0,"aria-expanded":Ee||void 0,"aria-label":kt,children:h(se&&$!=null?$:N,B,we)}))}let k=[];if(o.length>0){let R=o.some(Ao);o.forEach((L,H)=>{w(k,L,H,R)})}return k}return F("div",{...f,...M,"aria-activedescendant":m(),className:_e(Ze,n,{[`${Ze}-childMenuShowing`]:t!==void 0}),"data-root":p||void 0,id:P,ref:g,role:"menu",children:b()})},No=(e,t,o,n,r,i,s)=>({id:`menuitem-${e}`,key:o!=null?o:t,"data-index":t,className:_e("vuuMenuItem",i,{"vuuMenuItem-separator":s,vuuHighlighted:t===n,focusVisible:r===t})});de.displayName="MenuList";import{useCallback as A,useMemo as Bo,useRef as _,useState as So}from"react";var me=e=>e==null?void 0:e.closest("[data-index],[aria-posinset]");var nt=(e,t,o)=>e.map((n,r)=>r===e.length-1?{...n,[o]:n[o]-t}:n),Oo=(e,t)=>nt(e,t,"left"),$o=(e,t)=>nt(e,t,"top"),Ko=(e,t)=>{let[o,n]=t.slice(-2),r=document.getElementById(`${e}-${n.id}`);if(r===null)throw Error(`useCascade.flipSides element with id ${n.id} not found`);let{width:i}=r.getBoundingClientRect();return t.map(s=>s===n?{...s,left:o.left-(i-2)}:s)},zo=(e,t)=>{let[{left:o,top:n}]=t.slice(-1),{offsetWidth:r,offsetTop:i}=e;return{left:o+r,top:i+n}},rt=(e,t)=>{let o=e.lastIndexOf("-");return e.startsWith("menuitem")?o>-1?e.slice(9,o):t:o>-1?e.slice(0,o):t},Fo=e=>e.slice(9),ot=({ariaExpanded:e,ariaHasPopup:t,id:o},n)=>{if(o.startsWith("menuitem"))return{hostMenuId:rt(o,n),targetMenuId:Fo(o),menuItemId:o,isGroup:t==="true",isOpen:e==="true"};throw Error(`getMenuItemDetails #${o} is not a menuitem`)},it=({id:e,onActivate:t,onMouseEnterItem:o,position:{x:n,y:r}})=>{let[,i]=So({}),s=_([{id:e,left:n,top:r}]),p=A(m=>s.current.findIndex(b=>b.id===m)!==-1,[]),l=A(m=>{let b=d.current[m];if(b===void 0)throw Error(`getOpenMenuState no entry for menu ${m}`);return b},[]),u=A(m=>{s.current=m,i({})},[]),a=_(),c=_(),d=_({[e]:"no-popup"}),f=A((m=e,b,x=null)=>{if(m===e&&x===null)u([{id:e,left:n,top:r}]);else{d.current[m]="popup-open";let h=document.getElementById(x);if(h!==null){let{left:w,top:k}=zo(h,s.current);u(s.current.concat({id:b,left:w,top:k}))}else throw Error(`openMenu no menuItem ${x}`)}},[e,n,r,u]),P=A(m=>{if(m===e)u([]);else{let b=s.current.slice(),x=b.pop();d.current[x.id]="no-popup";let h=b.at(-1);h&&(d.current[h.id]="no-popup"),u(b)}},[e,u]),g=A(m=>{let b=s.current.slice(),x=m.slice(9),{id:h}=b.at(-1);for(;b.length>1&&!x.startsWith(h);){let w=rt(h,e);b.pop(),d.current[h]="no-popup",d.current[w]="no-popup",{id:h}=b[b.length-1]}b.length<s.current.length&&u(b)},[e,u]),y=A(()=>{a.current&&(clearTimeout(a.current),a.current=void 0)},[]),E=A((m,b,x,h=300)=>{y(),a.current=window.setTimeout(()=>{g(x),d.current[m]="popup-open",d.current[b]="no-popup",f(m,b,x)},h)},[y,g,f]),T=A((m,b,x)=>{d.current[b]="pending-close",c.current=window.setTimeout(()=>{g(x)},400)},[g]),C=A(()=>{let{current:m}=s,b=m.at(-1),x=b?document.getElementById(b.id):void 0;if(x){let{right:h,bottom:w}=x.getBoundingClientRect(),{clientHeight:k,clientWidth:R}=document.body;if(h>R){let L=m.length>1?Ko(e,m):Oo(m,h-R);u(L)}else if(w>k){let L=$o(m,w-k);u(L)}typeof x.tabIndex=="number"&&x.focus()}},[e,u]),M=A((m,b=!1)=>{let{hostMenuId:x,targetMenuId:h,menuItemId:w,isGroup:k,isOpen:R}=ot(m,e),{current:{[x]:L}}=d,H=b?0:void 0;if(L==="no-popup"&&k)d.current[x]="popup-pending",E(x,h,w,H);else if(L==="popup-pending"&&!k)d.current[x]="no-popup",clearTimeout(a.current),a.current=void 0;else if(L==="popup-pending"&&k)clearTimeout(a.current),E(x,h,w,H);else if(L==="popup-open")if(p(h)){let B=l(h);switch(g(w),B){case"pending-close":clearTimeout(c.current),c.current=void 0,d.current[h]="no-popup",y();break;default:}}else{let[B,N]=s.current.slice(-2);B.id===x&&d.current[N.id]!=="pending-close"?(T(x,N.id,w),k&&!R&&E(x,h,w,H)):B.id===x&&k&&w!==N.id&&d.current[N.id]==="pending-close"||k?E(x,h,w,H):d.current[N.id]!=="pending-close"&&g(w)}L==="pending-close"&&(y(),clearTimeout(c.current),c.current=void 0,d.current[x]="popup-open")},[y,g,l,p,e,T,E]),v=Bo(()=>({onMouseEnter:m=>{let b=me(m.target);M(b),o(m,b.id)},onClick:m=>{let b=me(m.target),{isGroup:x,menuItemId:h}=ot(b,e);x?M(b):t(h)}}),[t,o,e,M]);return{closeMenu:P,handleRender:C,listItemProps:v,openMenu:M,openMenus:s.current}};import{Fragment as Go,jsx as at}from"react/jsx-runtime";import{createElement as ut}from"react";var qo=()=>{},ge=({PortalProps:e,activatedByKeyboard:t,children:o,className:n,id:r,onClose:i=()=>{},position:s={x:0,y:0},style:p,...l})=>{let u=Wo(r),a=st(qo),[c,d]=Ge(o,u),f=st(t),P=fe(()=>{f.current=!1},[]),g=fe(h=>{let w=h.slice(9),{action:k,options:R}=d[w];a.current(u),i({type:"menu-action",menuId:k,options:R})},[d,u,i]),{closeMenu:y,listItemProps:E,openMenu:T,openMenus:C,handleRender:M}=it({id:`${u}`,onActivate:g,onMouseEnterItem:P,position:s});a.current=y;let v=fe((h,w)=>{f.current=!0,y(),w==="tab-away"&&i({event:h,type:"tab-away"})},[y,i]),m=()=>{},b=C.length-1,x=h=>{if(!(h>=b)){let{id:w}=C[h+1];return w}};return at(Go,{children:C.map(({id:h,left:w,top:k},R,L)=>{let H=x(R);return ut(z,{...e,key:R,onRender:M},at($e,{anchorElement:{current:document.body},placement:"absolute",position:{left:w,top:k},children:ut(de,{...l,activatedByKeyboard:f.current,childMenuShowing:H,className:n,id:h,isRoot:R===0,key:R,listItemProps:E,onActivate:g,onHighlightMenuItem:m,onCloseMenu:v,openMenu:T,style:p,tabIndex:R===L.length-1?0:void 0},c[h])}))})})};ge.displayName="ContextMenu";import{createContext as Vo,useCallback as Uo,useMemo as Xo}from"react";import{jsx as ve}from"react/jsx-runtime";var ee=Vo(null),Jo=({children:e,context:t,menuActionHandler:o,menuBuilder:n})=>{let r=Xo(()=>t!=null&&t.menuBuilders&&n?t.menuBuilders.concat(n):n?[n]:(t==null?void 0:t.menuBuilders)||[],[t,n]),i=Uo(s=>{var p;if(o!=null&&o(s)||(p=t==null?void 0:t.menuActionHandler)!=null&&p.call(t,s))return!0},[t,o]);return ve(ee.Provider,{value:{menuActionHandler:i,menuBuilders:r},children:e})},Ki=({children:e,label:t,menuActionHandler:o,menuBuilder:n})=>ve(ee.Consumer,{children:r=>ve(Jo,{context:r,label:t,menuActionHandler:o,menuBuilder:n,children:e})});import{isGroupMenuItemDescriptor as Yo,useThemeAttributes as jo}from"@vuu-ui/vuu-utils";import{cloneElement as Qo,useCallback as be,useContext as Zo,useMemo as _o}from"react";import{jsx as he}from"react/jsx-runtime";var lt=(e,t)=>{let o=Zo(ee),[n,r,i]=jo(),s=_o(()=>({themeClass:n,densityClass:r,dataMode:i}),[i,r,n]),p=be((a,c,d)=>{let f=[];for(let P of a)f=f.concat(P(c,d));return f},[]),l=be((a,c,{ContextMenuProps:d,contextMenu:f,...P})=>{var y,E;if((y=a.stopPropagation)==null||y.call(a),(E=a.preventDefault)==null||E.call(a),f)return tn({x:a.clientX,y:a.clientY},f);let g=[];if(e&&g.push(e),o&&Array.isArray(o==null?void 0:o.menuBuilders)&&o.menuBuilders.length>0&&g.push(...o.menuBuilders),g.length>0){let T=p(g,c,P),C=M=>(t==null?void 0:t(M))===!0?!0:o==null?void 0:o.menuActionHandler(M);T.length&&C&&on(a,T,C,{PortalProps:{themeAttributes:s},...d})}else console.warn("useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)")},[p,o,t,e,s]),u=be(()=>{console.log("hide context menu")},[]);return[l,u]},en={},tn=(e,t)=>{O.showPopup({focus:!0,left:0,top:0,component:Qo(t,{position:e})})},on=(e,t,o,{position:n,...r}=en)=>{let i=u=>{let a=(c,d)=>Yo(c)?he(j,{label:c.label,children:c.children.map(a)},d):he(Z,{action:c.action,className:c.className,"data-icon":c.icon,options:c.options,children:c.label},d);return u.map(a)},s=u=>{var a;if(Ne(u)){if((u==null?void 0:u.closedBy)==="popup-service")return;o(u),O.hidePopup(u)}(a=r==null?void 0:r.onClose)==null||a.call(r,u)},p=n!=null?n:{x:e.clientX,y:e.clientY},l=he(ge,{...r,onClose:s,position:p,children:i(t)});O.showPopup({left:0,top:0,component:l,focus:!0})};import{Icon as rn,IconButton as sn}from"@vuu-ui/vuu-ui-controls";import{useId as un}from"@vuu-ui/vuu-utils";import{Button as an}from"@salt-ds/core";import dt from"clsx";import{useCallback as te,useRef as pt,useState as nn}from"react";var ct=({anchorElement:e,id:t,menuActionHandler:o,menuBuilder:n,menuClassName:r,menuLocation:i,menuOptions:s,onMenuClose:p,onMenuOpen:l,popupPlacement:u,tabIndex:a})=>{let[c,d]=nn(!1),f=pt(!1),P=pt(null),g=te(m=>{d(m),m&&(l==null||l())},[l]),[y]=lt(n,o),E=te(m=>{console.log("menu Open ",{el:m})},[]),T=te(m=>{console.log("onClose"),g(!1),De(m)?(m.mouseEvt.target===P.current&&(f.current=!0),p==null||p(m)):requestAnimationFrame(()=>{var b;p==null||p(m),a!==-1&&(m==null?void 0:m.type)!=="tab-away"&&((b=P.current)==null||b.focus())})},[p,g,a]),C=te(m=>{var b;if(f.current)f.current=!1;else{let x=(b=e==null?void 0:e.current)!=null?b:P.current;if(x){let{left:h,top:w,width:k}=V(x,u,0,0);g(!0),y(m,i,{ContextMenuProps:{className:r,id:`${t}-menu`,onClose:T,openMenu:E,position:{x:h,y:w},style:{width:k?k-2:void 0}},...s})}}},[e,T,E,t,r,i,s,u,g,y]);return{ariaAttributes:{"aria-controls":c?`${t}-menu`:void 0,"aria-expanded":c,"aria-haspopup":"menu"},buttonProps:{id:t,onClick:C,tabIndex:a},menuOpen:c,rootRef:P}};import{jsx as mt,jsxs as ln}from"react/jsx-runtime";var xe="vuuPopupMenu",vs=({anchorElement:e,className:t,disabled:o=!1,label:n,icon:r=n?"chevron-down":"more-vert",id:i,menuActionHandler:s,menuBuilder:p,menuClassName:l,menuLocation:u="header",menuOptions:a,onMenuClose:c,onMenuOpen:d,popupPlacement:f="below-right",tabIndex:P=0,variant:g="secondary",...y})=>{let E=un(i),{ariaAttributes:T,buttonProps:C,menuOpen:M,rootRef:v}=ct({anchorElement:e,id:E,menuActionHandler:s,menuBuilder:p,menuClassName:l,menuLocation:u,onMenuClose:c,onMenuOpen:d,menuOptions:a,popupPlacement:f,tabIndex:P});return n?ln(an,{...y,...T,...C,className:dt(xe,t,`${xe}-withCaption`,{"saltButton-active":M}),disabled:o,ref:v,variant:"secondary",children:[r?mt(rn,{name:r}):null,n]}):r?mt(sn,{...y,...T,...C,className:dt(xe,t,{"saltButton-active":M}),disabled:o,icon:r,ref:v,variant:g}):(console.error("PopupMenu must have a label or an icon (or both)"),null)};import{useThemeAttributes as pn}from"@vuu-ui/vuu-utils";import{Button as ft}from"@salt-ds/core";import cn from"clsx";import{useLayoutEffect as dn,useRef as gt}from"react";import{jsx as U,jsxs as vt}from"react/jsx-runtime";var W="vuuPrompt",mn={current:document.body},fn={},Ls=({PopupProps:e=fn,cancelButtonLabel:t="Cancel",confirmButtonLabel:o="Confirm",icon:n,onCancel:r,onConfirm:i,style:s,text:p,title:l,variant:u="info",...a})=>{let{anchorElement:c=mn,offsetLeft:d=0,offsetTop:f=0,placement:P="below"}=e,[g,y,E]=pn(),{position:T}=K({anchorElement:c,offsetLeft:d,offsetTop:f,placement:P}),C=gt(null),M=gt(null);return dn(()=>{if(C.current&&(C.current.showModal(),M.current&&M.current.focus(),P.endsWith("center"))){let{width:v}=C.current.getBoundingClientRect();C.current.style.marginLeft=`-${v/2}px`}},[P]),U("dialog",{...a,className:cn(W,`${W}-${u}`,g),"data-mode":E,ref:C,style:{...s,...T},children:vt("form",{className:`${W}-form`,children:[U("div",{className:`${W}-header`,"data-icon":n,children:l}),U("div",{className:`${W}-text`,children:p}),vt("div",{className:`${W}-buttonBar`,children:[U(ft,{onClick:r,variant:"secondary",children:t}),U(ft,{onClick:i,ref:M,value:"default",children:o})]})]})})};import{useCallback as gn,useLayoutEffect as vn}from"react";var oe=12,bn=(e,t)=>t<e.top,hn=(e,t)=>document.body.clientHeight-e.bottom>t,xn=e=>Array.isArray(e)?e.length===0?[void 0,e]:[e[0],e.slice(1)]:[e,[]],bt=({anchorElement:e,offsetLeft:t=0,offsetTop:o=0,placement:n})=>{let r=gn(i=>{if(i&&e.current){let s=e.current.getBoundingClientRect(),{height:p,width:l}=i.getBoundingClientRect(),u,a=n;do switch([u,a]=xn(a),u){case"above":if(bn(s,p+oe)){let c=(l-s.width)/2;i.style.cssText=`left:${s.left-c}px;top:${s.top-p-oe}px;opacity: 1;`,i.dataset.align="above";return}break;case"below":if(hn(s,p+oe)){let c=(l-s.width)/2;i.style.cssText=`left:${s.left-c}px;top:${s.bottom+oe}px;opacity: 1;`,i.dataset.align="below";return}break;case"right":console.log("place the fucker right");break;case"left":console.log("place the fucker left");break;default:console.warn(`unklnown tooltip placement ${n}`)}while(u)}},[e,n]);return vn(()=>{},[e,t,o,n]),r};import Mn from"clsx";import{jsx as ye}from"react/jsx-runtime";var Me="vuuTooltip",qs=({anchorElement:e,children:t,className:o,id:n,onMouseEnter:r,onMouseLeave:i,placement:s,status:p,style:l})=>{let u=bt({anchorElement:e,placement:s});return ye(z,{children:ye("div",{className:Mn(Me,o,"vuuHidden",{[`${Me}-error`]:p==="error"}),id:n,ref:u,style:{...l,left:0,top:0},children:ye("span",{className:`${Me}-content`,onMouseEnter:r,onMouseLeave:i,children:t})})})};import{queryClosest as yn,useId as Pn}from"@vuu-ui/vuu-utils";import{useCallback as D,useRef as X,useState as Cn}from"react";var js=({anchorQuery:e="*",id:t,placement:o="right",tooltipContent:n})=>{let r=X(),i=X(!1),s=X(null),p=X(),l=X(),[u,a]=Cn(),c=Pn(t),d=D(M=>{var v;M.key==="Escape"&&((v=r.current)==null||v.call(r))},[]);r.current=D(()=>{a(void 0),document.removeEventListener("keydown",d)},[d]);let f=D(()=>{window.clearTimeout(l.current)},[]),P=D(()=>{var M;(M=r.current)==null||M.call(r)},[]),g=D((M=0)=>{p.current?(window.clearTimeout(p.current),p.current=void 0):r.current&&(M===0?r.current():l.current=window.setTimeout(r.current,M))},[]),y=D((M=s)=>{let{current:v}=M;v&&(a({anchorElement:M,children:n,id:`${c}-tooltip`,onMouseEnter:f,onMouseLeave:P,placement:o}),document.addEventListener("keydown",d)),p.current=void 0,g(i.current?3e3:1e3)},[d,f,P,g,c,o,n]),E=D(M=>{i.current=!0;let v=yn(M.target,e);v&&(console.log(`el ${v.classList}`),s.current=v,p.current=window.setTimeout(y,800))},[e,y]),T=D(()=>{i.current=!1,g(200)},[g]);return{anchorProps:{"aria-describedby":`${c}-tooltip`,onMouseEnter:E,onMouseLeave:T},hideTooltip:g,showTooltip:y,tooltipProps:u}};import wn,{useState as xt,useContext as En,useCallback as Tn,useEffect as kn}from"react";import ht from"clsx";import{getUniqueId as Rn}from"@vuu-ui/vuu-utils";import{jsx as q,jsxs as Ce}from"react/jsx-runtime";var Ln=60,Mt=6e3,Pe=1e3,Hn=300,yt=56,re=300,In=10,An=10,ie=50,ne="vuuToastNotifications",Nn=(r=>(r.Info="info",r.Success="success",r.Warning="warning",r.Error="error",r))(Nn||{}),Pt=wn.createContext({notify:()=>"have you forgotten to provide a NotificationProvider?"}),iu=e=>{let[t,o]=xt([]),n=Tn(r=>{let i={...r,id:Rn()};o(s=>[...s,i]),setTimeout(()=>{o(s=>s.filter(p=>p!==i))},Mt+Pe*2)},[]);return Ce(Pt.Provider,{value:{notify:n},children:[q("div",{style:{width:re+ie+An},children:t.map((r,i)=>q(Dn,{top:Ln+(yt+In)*i,notification:r},r.id))}),e.children]})},su=()=>En(Pt),Dn=e=>{let{top:t,notification:o,animated:n=!0}=e,[r,i]=xt(-re-ie);return kn(()=>{setTimeout(()=>i(ie)),n&&setTimeout(()=>i(-re-ie),Mt+Pe)},[n]),q(z,{children:Ce("div",{className:ht(`${ne}-toast`,o.type),style:{height:yt,right:r,width:re,top:t,transition:n?`right ${Pe}ms, top ${Hn}ms `:"none"},children:[q("div",{className:ht(`${ne}-toastIcon`,`${o.type}-icon`)}),Ce("div",{className:`${ne}-toastContent`,children:[q("strong",{className:`${ne}-toastHeader`,children:o.header}),q("div",{children:o.body})]})]})})};export{ge as ContextMenu,ee as ContextMenuContext,Ki as ContextMenuProvider,Fe as Dialog,Re as DialogHeader,Ie as DialogService,Z as MenuItem,j as MenuItemGroup,de as MenuList,Nn as NotificationLevel,Pt as NotificationsContext,iu as NotificationsProvider,$e as PopupComponent,vs as PopupMenu,O as PopupService,z as Portal,Ls as Prompt,Ve as Separator,Dn as ToastNotification,qs as Tooltip,Vn as createContainer,le as isMenuItemLabel,De as reasonIsClickAway,Ne as reasonIsMenuAction,He as renderPortal,K as useAnchoredPosition,lt as useContextMenu,Hr as useDialog,su as useNotifications,js as useTooltip};
|
|
1
|
+
// src/dialog/Dialog.tsx
|
|
2
|
+
import cx5 from "clsx";
|
|
3
|
+
import { useThemeAttributes } from "@vuu-ui/vuu-utils";
|
|
4
|
+
import { useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/dialog-header/DialogHeader.tsx
|
|
7
|
+
import { Button, Text } from "@salt-ds/core";
|
|
8
|
+
import cx from "clsx";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
var classBase = "vuuDialogHeader";
|
|
11
|
+
var DialogHeader = ({
|
|
12
|
+
hideCloseButton = false,
|
|
13
|
+
title,
|
|
14
|
+
onClose,
|
|
15
|
+
...htmlAttributes
|
|
16
|
+
}) => {
|
|
17
|
+
return /* @__PURE__ */ jsxs("div", { ...htmlAttributes, className: cx(classBase, "vuuToolbarProxy"), children: [
|
|
18
|
+
/* @__PURE__ */ jsx(Text, { className: "dialogHeader", children: title }),
|
|
19
|
+
!hideCloseButton && /* @__PURE__ */ jsx(
|
|
20
|
+
Button,
|
|
21
|
+
{
|
|
22
|
+
onClick: onClose,
|
|
23
|
+
"data-align": "end",
|
|
24
|
+
"data-icon": "close",
|
|
25
|
+
variant: "secondary"
|
|
26
|
+
},
|
|
27
|
+
"close"
|
|
28
|
+
)
|
|
29
|
+
] });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/popup/popup-service.ts
|
|
33
|
+
import cx3 from "clsx";
|
|
34
|
+
import React, {
|
|
35
|
+
createElement
|
|
36
|
+
} from "react";
|
|
37
|
+
import ReactDOM2 from "react-dom";
|
|
38
|
+
|
|
39
|
+
// src/portal-deprecated/render-portal.tsx
|
|
40
|
+
import * as ReactDOM from "react-dom";
|
|
41
|
+
import cx2 from "clsx";
|
|
42
|
+
var containerId = 1;
|
|
43
|
+
var getPortalContainer = ({
|
|
44
|
+
className,
|
|
45
|
+
dataMode,
|
|
46
|
+
x = 0,
|
|
47
|
+
y = 0,
|
|
48
|
+
win = window
|
|
49
|
+
}) => {
|
|
50
|
+
const el = win.document.createElement("div");
|
|
51
|
+
el.className = cx2(`vuuPopup ${containerId++}`, className);
|
|
52
|
+
el.style.cssText = `left:${x}px; top:${y}px;`;
|
|
53
|
+
if (dataMode) {
|
|
54
|
+
el.dataset.mode = dataMode;
|
|
55
|
+
}
|
|
56
|
+
win.document.body.appendChild(el);
|
|
57
|
+
return el;
|
|
58
|
+
};
|
|
59
|
+
var createContainer = (props) => getPortalContainer(props);
|
|
60
|
+
var renderPortal = (component, container, x, y, onRender) => {
|
|
61
|
+
container.style.cssText = `left:${x}px; top:${y}px;position: absolute;`;
|
|
62
|
+
ReactDOM.render(component, container, onRender);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/popup/popup-service.ts
|
|
66
|
+
var _dialogOpen = false;
|
|
67
|
+
var _popups = [];
|
|
68
|
+
var reasonIsMenuAction = (reason) => (reason == null ? void 0 : reason.type) === "menu-action";
|
|
69
|
+
var reasonIsClickAway = (reason) => (reason == null ? void 0 : reason.type) === "click-away";
|
|
70
|
+
function specialKeyHandler(e) {
|
|
71
|
+
if (e.key === "Esc") {
|
|
72
|
+
if (_popups.length) {
|
|
73
|
+
closeAllPopups();
|
|
74
|
+
} else if (_dialogOpen) {
|
|
75
|
+
const dialogRoot = document.body.querySelector(".vuuDialog");
|
|
76
|
+
if (dialogRoot) {
|
|
77
|
+
ReactDOM2.unmountComponentAtNode(dialogRoot);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function outsideClickHandler(e) {
|
|
83
|
+
if (_popups.length) {
|
|
84
|
+
const popupContainers = document.body.querySelectorAll(
|
|
85
|
+
".vuuPopup,#vuu-portal-root"
|
|
86
|
+
);
|
|
87
|
+
for (let i = 0; i < popupContainers.length; i++) {
|
|
88
|
+
if (popupContainers[i].contains(e.target)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
closeAllPopups({ mouseEvt: e, type: "click-away" });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function closeAllPopups(reason) {
|
|
96
|
+
if (_popups.length === 1) {
|
|
97
|
+
PopupService.hidePopup(reason, "anon", "all");
|
|
98
|
+
} else if (_popups.length) {
|
|
99
|
+
const popupContainers = document.body.querySelectorAll(".vuuPopup");
|
|
100
|
+
for (let i = 0; i < popupContainers.length; i++) {
|
|
101
|
+
ReactDOM2.unmountComponentAtNode(popupContainers[i]);
|
|
102
|
+
}
|
|
103
|
+
popupClosed("*");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function dialogOpened() {
|
|
107
|
+
if (_dialogOpen === false) {
|
|
108
|
+
_dialogOpen = true;
|
|
109
|
+
window.addEventListener("keydown", specialKeyHandler, true);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function dialogClosed() {
|
|
113
|
+
if (_dialogOpen) {
|
|
114
|
+
_dialogOpen = false;
|
|
115
|
+
window.removeEventListener("keydown", specialKeyHandler, true);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function popupOpened(name) {
|
|
119
|
+
if (_popups.indexOf(name) === -1) {
|
|
120
|
+
_popups.push(name);
|
|
121
|
+
if (_dialogOpen === false) {
|
|
122
|
+
window.addEventListener("keydown", specialKeyHandler, true);
|
|
123
|
+
window.addEventListener("click", outsideClickHandler, true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function popupClosed(name) {
|
|
128
|
+
if (_popups.length) {
|
|
129
|
+
if (name === "*") {
|
|
130
|
+
_popups.length = 0;
|
|
131
|
+
} else {
|
|
132
|
+
const pos = _popups.indexOf(name);
|
|
133
|
+
if (pos !== -1) {
|
|
134
|
+
_popups.splice(pos, 1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (_popups.length === 0 && _dialogOpen === false) {
|
|
138
|
+
window.removeEventListener("keydown", specialKeyHandler, true);
|
|
139
|
+
window.removeEventListener("click", outsideClickHandler, true);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
var PopupComponent = ({
|
|
144
|
+
children,
|
|
145
|
+
position,
|
|
146
|
+
style
|
|
147
|
+
}) => {
|
|
148
|
+
const className = cx3("hwPopup", "hwPopupContainer", position);
|
|
149
|
+
return createElement("div", { className, style }, children);
|
|
150
|
+
};
|
|
151
|
+
var incrementingKey = 1;
|
|
152
|
+
var PopupService = class _PopupService {
|
|
153
|
+
static showPopup({
|
|
154
|
+
group = "all",
|
|
155
|
+
name = "anon",
|
|
156
|
+
left = 0,
|
|
157
|
+
position = "",
|
|
158
|
+
right = "auto",
|
|
159
|
+
top = 0,
|
|
160
|
+
width = "auto",
|
|
161
|
+
component
|
|
162
|
+
}) {
|
|
163
|
+
if (!component) {
|
|
164
|
+
throw Error(`PopupService showPopup, no component supplied`);
|
|
165
|
+
}
|
|
166
|
+
if (typeof component.props.onClose === "function") {
|
|
167
|
+
_PopupService.onClose = component.props.onClose;
|
|
168
|
+
} else {
|
|
169
|
+
_PopupService.onClose = void 0;
|
|
170
|
+
}
|
|
171
|
+
popupOpened(name);
|
|
172
|
+
document.addEventListener("keydown", _PopupService.escapeKeyListener, true);
|
|
173
|
+
let el = document.body.querySelector(".vuuPopup." + group);
|
|
174
|
+
if (el === null) {
|
|
175
|
+
el = document.createElement("div");
|
|
176
|
+
el.className = "vuuPopup " + group;
|
|
177
|
+
document.body.appendChild(el);
|
|
178
|
+
}
|
|
179
|
+
const style = { width };
|
|
180
|
+
renderPortal(
|
|
181
|
+
createElement(
|
|
182
|
+
PopupComponent,
|
|
183
|
+
{ key: incrementingKey++, position, style },
|
|
184
|
+
component
|
|
185
|
+
),
|
|
186
|
+
el,
|
|
187
|
+
left,
|
|
188
|
+
top,
|
|
189
|
+
() => {
|
|
190
|
+
_PopupService.keepWithinThePage(el, right);
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
static escapeKeyListener(evt) {
|
|
195
|
+
if (evt.key === "Escape") {
|
|
196
|
+
_PopupService.hidePopup({ type: "escape", event: evt });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
static hidePopup(reason, name = "anon", group = "all") {
|
|
200
|
+
var _a;
|
|
201
|
+
if (_popups.indexOf(name) !== -1) {
|
|
202
|
+
popupClosed(name);
|
|
203
|
+
const popupRoot = document.body.querySelector(`.vuuPopup.${group}`);
|
|
204
|
+
if (popupRoot) {
|
|
205
|
+
ReactDOM2.unmountComponentAtNode(popupRoot);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
document.removeEventListener(
|
|
209
|
+
"keydown",
|
|
210
|
+
_PopupService.escapeKeyListener,
|
|
211
|
+
true
|
|
212
|
+
);
|
|
213
|
+
(_a = _PopupService == null ? void 0 : _PopupService.onClose) == null ? void 0 : _a.call(
|
|
214
|
+
_PopupService,
|
|
215
|
+
reason ? {
|
|
216
|
+
...reason,
|
|
217
|
+
closedBy: "popup-service"
|
|
218
|
+
} : void 0
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
static keepWithinThePage(el, right = "auto") {
|
|
222
|
+
const target = el.querySelector(".vuuPopupContainer > *");
|
|
223
|
+
if (target) {
|
|
224
|
+
const {
|
|
225
|
+
top,
|
|
226
|
+
left,
|
|
227
|
+
width,
|
|
228
|
+
height,
|
|
229
|
+
right: currentRight
|
|
230
|
+
} = target.getBoundingClientRect();
|
|
231
|
+
const w = window.innerWidth;
|
|
232
|
+
const h = window.innerHeight;
|
|
233
|
+
const overflowH = h - (top + height);
|
|
234
|
+
if (overflowH < 0) {
|
|
235
|
+
target.style.top = Math.round(top) + overflowH + "px";
|
|
236
|
+
}
|
|
237
|
+
const overflowW = w - (left + width);
|
|
238
|
+
if (overflowW < 0) {
|
|
239
|
+
target.style.left = Math.round(left) + overflowW + "px";
|
|
240
|
+
}
|
|
241
|
+
if (typeof right === "number" && right !== currentRight) {
|
|
242
|
+
const adjustment = right - currentRight;
|
|
243
|
+
target.style.left = left + adjustment + "px";
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var DialogService = class _DialogService {
|
|
249
|
+
static showDialog(dialog) {
|
|
250
|
+
const containerEl = ".vuuDialog";
|
|
251
|
+
const onClose = dialog.props.onClose;
|
|
252
|
+
dialogOpened();
|
|
253
|
+
ReactDOM2.render(
|
|
254
|
+
React.cloneElement(dialog, {
|
|
255
|
+
container: containerEl,
|
|
256
|
+
onClose: () => {
|
|
257
|
+
_DialogService.closeDialog();
|
|
258
|
+
if (onClose) {
|
|
259
|
+
onClose();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}),
|
|
263
|
+
document.body.querySelector(containerEl)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
static closeDialog() {
|
|
267
|
+
dialogClosed();
|
|
268
|
+
const dialogRoot = document.body.querySelector(".vuuDialog");
|
|
269
|
+
if (dialogRoot) {
|
|
270
|
+
ReactDOM2.unmountComponentAtNode(dialogRoot);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// src/popup/Popup.tsx
|
|
276
|
+
import cx4 from "clsx";
|
|
277
|
+
|
|
278
|
+
// src/popup/useAnchoredPosition.ts
|
|
279
|
+
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
280
|
+
|
|
281
|
+
// src/popup/getPositionRelativeToAnchor.ts
|
|
282
|
+
var getPositionRelativeToAnchor = (anchorElement, placement, offsetLeft, offsetTop, minWidth, dimensions) => {
|
|
283
|
+
const { bottom, height, left, right, top, width } = anchorElement.getBoundingClientRect();
|
|
284
|
+
switch (placement) {
|
|
285
|
+
case "below":
|
|
286
|
+
return { left: left + offsetLeft, top: bottom + offsetTop };
|
|
287
|
+
case "right":
|
|
288
|
+
return { left: right + offsetLeft, top: top + offsetTop };
|
|
289
|
+
case "below-center":
|
|
290
|
+
return { left: left + width / 2 + offsetLeft, top: bottom + offsetTop };
|
|
291
|
+
case "below-right":
|
|
292
|
+
return { left, minWidth, top: bottom + offsetTop };
|
|
293
|
+
case "below-full-width":
|
|
294
|
+
return {
|
|
295
|
+
left: left + offsetLeft,
|
|
296
|
+
minWidth,
|
|
297
|
+
top: bottom + offsetTop,
|
|
298
|
+
width
|
|
299
|
+
};
|
|
300
|
+
case "center":
|
|
301
|
+
if (dimensions) {
|
|
302
|
+
return {
|
|
303
|
+
left: width / 2 - dimensions.width / 2 + offsetLeft,
|
|
304
|
+
top: height / 2 - dimensions.height / 2 + offsetTop,
|
|
305
|
+
visibility: "visible"
|
|
306
|
+
};
|
|
307
|
+
} else {
|
|
308
|
+
return {
|
|
309
|
+
left: width / 2 + offsetLeft,
|
|
310
|
+
top: height / 2 + offsetTop,
|
|
311
|
+
visibility: "hidden"
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
default:
|
|
315
|
+
throw Error(
|
|
316
|
+
`Popup getPositionRelativeToAnchor non-supported placement value ${placement}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// src/popup/useAnchoredPosition.ts
|
|
322
|
+
var useAnchoredPosition = ({
|
|
323
|
+
anchorElement,
|
|
324
|
+
minWidth,
|
|
325
|
+
offsetLeft = 0,
|
|
326
|
+
offsetTop = 0,
|
|
327
|
+
placement,
|
|
328
|
+
position: positionProp
|
|
329
|
+
}) => {
|
|
330
|
+
const popupRef = useRef(null);
|
|
331
|
+
const [position, setPosition] = useState(positionProp);
|
|
332
|
+
useLayoutEffect(() => {
|
|
333
|
+
if (placement === "absolute" && positionProp) {
|
|
334
|
+
setPosition(positionProp);
|
|
335
|
+
} else if (anchorElement.current && placement !== "auto") {
|
|
336
|
+
const dimensions = popupRef.current === null ? void 0 : popupRef.current.getBoundingClientRect();
|
|
337
|
+
const position2 = getPositionRelativeToAnchor(
|
|
338
|
+
anchorElement.current,
|
|
339
|
+
placement,
|
|
340
|
+
offsetLeft,
|
|
341
|
+
offsetTop,
|
|
342
|
+
minWidth,
|
|
343
|
+
dimensions
|
|
344
|
+
);
|
|
345
|
+
setPosition(position2);
|
|
346
|
+
}
|
|
347
|
+
}, [anchorElement, minWidth, offsetLeft, offsetTop, placement, positionProp]);
|
|
348
|
+
const popupCallbackRef = useCallback(
|
|
349
|
+
(el) => {
|
|
350
|
+
popupRef.current = el;
|
|
351
|
+
if (el && placement === "center" && anchorElement.current) {
|
|
352
|
+
const { height, width } = el.getBoundingClientRect();
|
|
353
|
+
setPosition(
|
|
354
|
+
getPositionRelativeToAnchor(
|
|
355
|
+
anchorElement.current,
|
|
356
|
+
placement,
|
|
357
|
+
offsetLeft,
|
|
358
|
+
offsetTop,
|
|
359
|
+
void 0,
|
|
360
|
+
{ height, width }
|
|
361
|
+
)
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
[anchorElement, offsetLeft, offsetTop, placement]
|
|
366
|
+
);
|
|
367
|
+
return {
|
|
368
|
+
position,
|
|
369
|
+
popupRef: placement === "center" ? popupCallbackRef : void 0
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// src/popup/Popup.tsx
|
|
374
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
375
|
+
var PopupComponent2 = ({
|
|
376
|
+
children,
|
|
377
|
+
className,
|
|
378
|
+
anchorElement,
|
|
379
|
+
minWidth,
|
|
380
|
+
offsetLeft,
|
|
381
|
+
offsetTop,
|
|
382
|
+
placement,
|
|
383
|
+
position: positionProp
|
|
384
|
+
}) => {
|
|
385
|
+
const { popupRef, position } = useAnchoredPosition({
|
|
386
|
+
anchorElement,
|
|
387
|
+
minWidth,
|
|
388
|
+
offsetLeft,
|
|
389
|
+
offsetTop,
|
|
390
|
+
placement,
|
|
391
|
+
position: positionProp
|
|
392
|
+
});
|
|
393
|
+
return position === void 0 ? null : /* @__PURE__ */ jsx2("div", { className: cx4(`vuuPortal`, className), ref: popupRef, style: position, children });
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/dialog/Dialog.tsx
|
|
397
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
398
|
+
var classBase2 = "vuuDialog";
|
|
399
|
+
var AnchorBody = { current: document.body };
|
|
400
|
+
var EMPTY_PROPS = {};
|
|
401
|
+
var Dialog = ({
|
|
402
|
+
PopupProps = EMPTY_PROPS,
|
|
403
|
+
children,
|
|
404
|
+
className,
|
|
405
|
+
isOpen = false,
|
|
406
|
+
onClose,
|
|
407
|
+
style,
|
|
408
|
+
title,
|
|
409
|
+
hideCloseButton = false,
|
|
410
|
+
...htmlAttributes
|
|
411
|
+
}) => {
|
|
412
|
+
const {
|
|
413
|
+
anchorElement = AnchorBody,
|
|
414
|
+
offsetLeft = 0,
|
|
415
|
+
offsetTop = 0,
|
|
416
|
+
placement = "auto"
|
|
417
|
+
} = PopupProps;
|
|
418
|
+
const rootRef = useRef2(null);
|
|
419
|
+
const portalRef = useRef2(null);
|
|
420
|
+
const [themeClass, _, dataMode] = useThemeAttributes();
|
|
421
|
+
const { position } = useAnchoredPosition({
|
|
422
|
+
anchorElement,
|
|
423
|
+
offsetLeft,
|
|
424
|
+
offsetTop,
|
|
425
|
+
placement
|
|
426
|
+
});
|
|
427
|
+
const close = useCallback2(() => {
|
|
428
|
+
onClose == null ? void 0 : onClose();
|
|
429
|
+
}, [onClose]);
|
|
430
|
+
useLayoutEffect2(() => {
|
|
431
|
+
if (rootRef.current) {
|
|
432
|
+
if (isOpen) {
|
|
433
|
+
rootRef.current.showModal();
|
|
434
|
+
const { left, top } = rootRef.current.getBoundingClientRect();
|
|
435
|
+
if (portalRef.current) {
|
|
436
|
+
portalRef.current.style.cssText = `left:-${left}px;position:absolute;top:-${top}px;`;
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
rootRef.current.close();
|
|
440
|
+
}
|
|
441
|
+
if (placement.endsWith("center")) {
|
|
442
|
+
const { width } = rootRef.current.getBoundingClientRect();
|
|
443
|
+
rootRef.current.style.marginLeft = `-${width / 2}px`;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}, [isOpen, placement]);
|
|
447
|
+
return /* @__PURE__ */ jsxs2(
|
|
448
|
+
"dialog",
|
|
449
|
+
{
|
|
450
|
+
...htmlAttributes,
|
|
451
|
+
className: cx5(classBase2, themeClass),
|
|
452
|
+
"data-mode": dataMode,
|
|
453
|
+
onClose: close,
|
|
454
|
+
id: "vuu-dialog",
|
|
455
|
+
ref: rootRef,
|
|
456
|
+
style: { ...style, ...position },
|
|
457
|
+
children: [
|
|
458
|
+
/* @__PURE__ */ jsx3(
|
|
459
|
+
DialogHeader,
|
|
460
|
+
{
|
|
461
|
+
hideCloseButton,
|
|
462
|
+
onClose: close,
|
|
463
|
+
title
|
|
464
|
+
}
|
|
465
|
+
),
|
|
466
|
+
/* @__PURE__ */ jsx3("div", { className: `${classBase2}-body`, children }),
|
|
467
|
+
/* @__PURE__ */ jsx3("div", { id: "vuu-dialog-portal-root", ref: portalRef })
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/dialog/useDialog.tsx
|
|
474
|
+
import { useCallback as useCallback3, useState as useState2 } from "react";
|
|
475
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
476
|
+
var useDialog = () => {
|
|
477
|
+
const [dialogState, setDialogState] = useState2();
|
|
478
|
+
const handleClose = useCallback3(() => {
|
|
479
|
+
setDialogState(void 0);
|
|
480
|
+
}, []);
|
|
481
|
+
const dialog = dialogState ? /* @__PURE__ */ jsx4(
|
|
482
|
+
Dialog,
|
|
483
|
+
{
|
|
484
|
+
className: "vuDialog",
|
|
485
|
+
isOpen: true,
|
|
486
|
+
onClose: handleClose,
|
|
487
|
+
style: { maxHeight: 500 },
|
|
488
|
+
title: dialogState.title,
|
|
489
|
+
hideCloseButton: dialogState.hideCloseButton,
|
|
490
|
+
children: dialogState.content
|
|
491
|
+
}
|
|
492
|
+
) : null;
|
|
493
|
+
return {
|
|
494
|
+
dialog,
|
|
495
|
+
setDialogState
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/menu/ContextMenu.tsx
|
|
500
|
+
import { useId as useId2 } from "@vuu-ui/vuu-utils";
|
|
501
|
+
import { useCallback as useCallback7, useRef as useRef7 } from "react";
|
|
502
|
+
|
|
503
|
+
// src/portal/Portal.tsx
|
|
504
|
+
import { useThemeAttributes as useThemeAttributes2 } from "@vuu-ui/vuu-utils";
|
|
505
|
+
import { useLayoutEffect as useLayoutEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
506
|
+
import { createPortal } from "react-dom";
|
|
507
|
+
function getContainer(container) {
|
|
508
|
+
return typeof container === "function" ? container() : container;
|
|
509
|
+
}
|
|
510
|
+
var DEFAULT_ID = ["vuu-dialog-portal-root", "vuu-portal-root"];
|
|
511
|
+
var getFirstAvailableElementById = (id) => {
|
|
512
|
+
if (Array.isArray(id)) {
|
|
513
|
+
for (const i of id) {
|
|
514
|
+
const element = document.getElementById(i);
|
|
515
|
+
if (element) {
|
|
516
|
+
return element;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
return document.getElementById(id);
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
};
|
|
524
|
+
var Portal = ({
|
|
525
|
+
children,
|
|
526
|
+
container: containerProp = document.body,
|
|
527
|
+
id = DEFAULT_ID,
|
|
528
|
+
onRender,
|
|
529
|
+
open = true,
|
|
530
|
+
themeAttributes
|
|
531
|
+
}) => {
|
|
532
|
+
var _a;
|
|
533
|
+
const [mounted, setMounted] = useState3(false);
|
|
534
|
+
const portalRef = useRef3(null);
|
|
535
|
+
const container = (_a = getContainer(containerProp)) != null ? _a : document.body;
|
|
536
|
+
const [themeClass, densityClass, dataMode] = useThemeAttributes2(themeAttributes);
|
|
537
|
+
useLayoutEffect3(() => {
|
|
538
|
+
const root = getFirstAvailableElementById(id);
|
|
539
|
+
if (root) {
|
|
540
|
+
portalRef.current = root;
|
|
541
|
+
} else {
|
|
542
|
+
portalRef.current = document.createElement("div");
|
|
543
|
+
portalRef.current.id = typeof id === "string" ? id : id.length > 0 ? id.at(-1) : "vuu-portal-root";
|
|
544
|
+
}
|
|
545
|
+
const el = portalRef.current;
|
|
546
|
+
if (!container.contains(el)) {
|
|
547
|
+
container.appendChild(el);
|
|
548
|
+
}
|
|
549
|
+
el.classList.add(themeClass, densityClass);
|
|
550
|
+
el.dataset.mode = dataMode;
|
|
551
|
+
setMounted(true);
|
|
552
|
+
}, [id, container, themeClass, densityClass, dataMode]);
|
|
553
|
+
useLayoutEffect3(() => {
|
|
554
|
+
requestAnimationFrame(() => {
|
|
555
|
+
onRender == null ? void 0 : onRender();
|
|
556
|
+
});
|
|
557
|
+
}, [onRender]);
|
|
558
|
+
if (open && mounted && portalRef.current && children) {
|
|
559
|
+
return createPortal(children, portalRef.current);
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/menu/MenuList.tsx
|
|
565
|
+
import cx6 from "clsx";
|
|
566
|
+
import React3, {
|
|
567
|
+
useLayoutEffect as useLayoutEffect4,
|
|
568
|
+
useMemo as useMemo3,
|
|
569
|
+
useRef as useRef5
|
|
570
|
+
} from "react";
|
|
571
|
+
import { useId } from "@vuu-ui/vuu-utils";
|
|
572
|
+
|
|
573
|
+
// src/menu/use-items-with-ids-next.ts
|
|
574
|
+
import React2, { useCallback as useCallback4, useMemo } from "react";
|
|
575
|
+
var isMenuItemGroup = (child) => child.type === MenuItemGroup || !!child.props["data-group"];
|
|
576
|
+
var getLabelFromChildren = (children) => {
|
|
577
|
+
if (Array.isArray(children) && isMenuItemLabel(children[0])) {
|
|
578
|
+
return children[0];
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
var assignId = (child, path, group, hasSeparator = false) => {
|
|
582
|
+
const {
|
|
583
|
+
props: { children }
|
|
584
|
+
} = child;
|
|
585
|
+
return {
|
|
586
|
+
childWithId: React2.cloneElement(child, {
|
|
587
|
+
hasSeparator,
|
|
588
|
+
id: `${path}`,
|
|
589
|
+
key: path,
|
|
590
|
+
children: group ? getLabelFromChildren(children) : children
|
|
591
|
+
}),
|
|
592
|
+
grandChildren: group ? children : void 0
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
var useItemsWithIdsNext = (childrenProp, rootId) => {
|
|
596
|
+
const normalizeChildren = useCallback4(() => {
|
|
597
|
+
const collectChildren = (children, path = rootId, menus2 = {}, actions2 = {}) => {
|
|
598
|
+
const list = menus2[path] = [];
|
|
599
|
+
let idx = 0;
|
|
600
|
+
let hasSeparator = false;
|
|
601
|
+
React2.Children.forEach(children, (child) => {
|
|
602
|
+
if (isMenuItemLabel(child)) {
|
|
603
|
+
} else if (child.type === Separator) {
|
|
604
|
+
hasSeparator = true;
|
|
605
|
+
} else {
|
|
606
|
+
const hasChildItems = isMenuItemGroup(child);
|
|
607
|
+
const childPath = `${path}-${idx}`;
|
|
608
|
+
const {
|
|
609
|
+
props: { action, options }
|
|
610
|
+
} = child;
|
|
611
|
+
const { childWithId, grandChildren } = assignId(
|
|
612
|
+
child,
|
|
613
|
+
childPath,
|
|
614
|
+
hasChildItems,
|
|
615
|
+
hasSeparator
|
|
616
|
+
);
|
|
617
|
+
list.push(childWithId);
|
|
618
|
+
if (grandChildren) {
|
|
619
|
+
collectChildren(grandChildren, childPath, menus2, actions2);
|
|
620
|
+
} else {
|
|
621
|
+
actions2[childPath] = { action, options };
|
|
622
|
+
}
|
|
623
|
+
idx += 1;
|
|
624
|
+
hasSeparator = false;
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
return [menus2, actions2];
|
|
628
|
+
};
|
|
629
|
+
return collectChildren(childrenProp);
|
|
630
|
+
}, [rootId, childrenProp]);
|
|
631
|
+
const [menus, actions] = useMemo(
|
|
632
|
+
() => normalizeChildren(),
|
|
633
|
+
[normalizeChildren]
|
|
634
|
+
);
|
|
635
|
+
return [menus, actions];
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/menu/use-keyboard-navigation.ts
|
|
639
|
+
import {
|
|
640
|
+
useCallback as useCallback5,
|
|
641
|
+
useMemo as useMemo2,
|
|
642
|
+
useRef as useRef4,
|
|
643
|
+
useState as useState4
|
|
644
|
+
} from "react";
|
|
645
|
+
|
|
646
|
+
// src/menu/utils.ts
|
|
647
|
+
var isRoot = (el) => el.closest(`[data-root='true']`) !== null;
|
|
648
|
+
var hasPopup = (el, idx) => {
|
|
649
|
+
var _a;
|
|
650
|
+
return el.ariaHasPopup === "true" && ((_a = el.dataset) == null ? void 0 : _a.idx) === `${idx}` || el.querySelector(`:scope > [data-index='${idx}'][aria-haspopup='true']`) !== null;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/menu/key-code.ts
|
|
654
|
+
function union(set1, ...sets) {
|
|
655
|
+
const result = new Set(set1);
|
|
656
|
+
for (const set of sets) {
|
|
657
|
+
for (const element of set) {
|
|
658
|
+
result.add(element);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
var Enter = "Enter";
|
|
664
|
+
var Delete = "Delete";
|
|
665
|
+
var actionKeys = /* @__PURE__ */ new Set([Enter, Delete]);
|
|
666
|
+
var focusKeys = /* @__PURE__ */ new Set(["Tab"]);
|
|
667
|
+
var arrowLeftRightKeys = /* @__PURE__ */ new Set(["ArrowRight", "ArrowLeft"]);
|
|
668
|
+
var verticalNavigationKeys = /* @__PURE__ */ new Set(["Home", "End", "ArrowDown", "ArrowUp"]);
|
|
669
|
+
var horizontalNavigationKeys = /* @__PURE__ */ new Set([
|
|
670
|
+
"Home",
|
|
671
|
+
"End",
|
|
672
|
+
"ArrowRight",
|
|
673
|
+
"ArrowLeft"
|
|
674
|
+
]);
|
|
675
|
+
var functionKeys = /* @__PURE__ */ new Set([
|
|
676
|
+
"F1",
|
|
677
|
+
"F2",
|
|
678
|
+
"F3",
|
|
679
|
+
"F4",
|
|
680
|
+
"F5",
|
|
681
|
+
"F6",
|
|
682
|
+
"F7",
|
|
683
|
+
"F8",
|
|
684
|
+
"F9",
|
|
685
|
+
"F10",
|
|
686
|
+
"F11",
|
|
687
|
+
"F12"
|
|
688
|
+
]);
|
|
689
|
+
var specialKeys = union(
|
|
690
|
+
actionKeys,
|
|
691
|
+
horizontalNavigationKeys,
|
|
692
|
+
verticalNavigationKeys,
|
|
693
|
+
arrowLeftRightKeys,
|
|
694
|
+
functionKeys,
|
|
695
|
+
focusKeys
|
|
696
|
+
);
|
|
697
|
+
var isNavigationKey = ({ key }, orientation = "vertical") => {
|
|
698
|
+
const navigationKeys = orientation === "vertical" ? verticalNavigationKeys : horizontalNavigationKeys;
|
|
699
|
+
return navigationKeys.has(key);
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// src/menu/use-keyboard-navigation.ts
|
|
703
|
+
import { isValidNumber } from "@vuu-ui/vuu-utils";
|
|
704
|
+
var useKeyboardNavigation = ({
|
|
705
|
+
autoHighlightFirstItem = false,
|
|
706
|
+
count,
|
|
707
|
+
defaultHighlightedIdx,
|
|
708
|
+
highlightedIndex: highlightedIndexProp,
|
|
709
|
+
onActivate,
|
|
710
|
+
onHighlight,
|
|
711
|
+
// onKeyDown,
|
|
712
|
+
onCloseMenu,
|
|
713
|
+
onOpenMenu
|
|
714
|
+
}) => {
|
|
715
|
+
var _a;
|
|
716
|
+
if (isValidNumber(highlightedIndexProp) && isValidNumber(defaultHighlightedIdx)) {
|
|
717
|
+
throw Error(
|
|
718
|
+
"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx"
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
const controlledHighlighting = isValidNumber(highlightedIndexProp);
|
|
722
|
+
const highlightedIndexRef = useRef4(
|
|
723
|
+
(_a = defaultHighlightedIdx != null ? defaultHighlightedIdx : highlightedIndexProp) != null ? _a : autoHighlightFirstItem ? 0 : -1
|
|
724
|
+
);
|
|
725
|
+
const [, forceRender] = useState4(null);
|
|
726
|
+
const setHighlightedIdx = useCallback5(
|
|
727
|
+
(idx) => {
|
|
728
|
+
highlightedIndexRef.current = idx;
|
|
729
|
+
onHighlight == null ? void 0 : onHighlight(idx);
|
|
730
|
+
forceRender({});
|
|
731
|
+
},
|
|
732
|
+
[onHighlight]
|
|
733
|
+
);
|
|
734
|
+
const setHighlightedIndex = useCallback5(
|
|
735
|
+
(idx) => {
|
|
736
|
+
if (idx !== highlightedIndexRef.current) {
|
|
737
|
+
if (!controlledHighlighting) {
|
|
738
|
+
setHighlightedIdx(idx);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
[controlledHighlighting, setHighlightedIdx]
|
|
743
|
+
);
|
|
744
|
+
const keyBoardNavigation = useRef4(true);
|
|
745
|
+
const ignoreFocus = useRef4(false);
|
|
746
|
+
const setIgnoreFocus = (value) => ignoreFocus.current = value;
|
|
747
|
+
const highlightedIndex = controlledHighlighting ? highlightedIndexProp : highlightedIndexRef.current;
|
|
748
|
+
const navigateChildldItems = useCallback5(
|
|
749
|
+
(e) => {
|
|
750
|
+
const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);
|
|
751
|
+
if (nextIdx !== highlightedIndexRef.current) {
|
|
752
|
+
setHighlightedIndex(nextIdx);
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
[count, setHighlightedIndex]
|
|
756
|
+
);
|
|
757
|
+
const handleKeyDown = useCallback5(
|
|
758
|
+
(e) => {
|
|
759
|
+
if (isNavigationKey(e)) {
|
|
760
|
+
e.preventDefault();
|
|
761
|
+
e.stopPropagation();
|
|
762
|
+
keyBoardNavigation.current = true;
|
|
763
|
+
navigateChildldItems(e);
|
|
764
|
+
} else if ((e.key === "ArrowRight" || e.key === "Enter") && hasPopup(e.target, highlightedIndex)) {
|
|
765
|
+
const menuEl = e.target;
|
|
766
|
+
const menuItemEl = menuEl.querySelector(
|
|
767
|
+
`:scope > [data-index='${highlightedIndex}']`
|
|
768
|
+
);
|
|
769
|
+
if (menuItemEl) {
|
|
770
|
+
onOpenMenu == null ? void 0 : onOpenMenu(menuItemEl, true);
|
|
771
|
+
}
|
|
772
|
+
} else if (e.key === "ArrowLeft" && !isRoot(e.target)) {
|
|
773
|
+
onCloseMenu(e, "close-child-menu");
|
|
774
|
+
} else if (e.key === "Enter") {
|
|
775
|
+
e.preventDefault();
|
|
776
|
+
e.stopPropagation();
|
|
777
|
+
onActivate && onActivate(highlightedIndex);
|
|
778
|
+
} else if (e.key === "Tab") {
|
|
779
|
+
onCloseMenu(e, "tab-away");
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
[
|
|
783
|
+
highlightedIndex,
|
|
784
|
+
navigateChildldItems,
|
|
785
|
+
onActivate,
|
|
786
|
+
onCloseMenu,
|
|
787
|
+
onOpenMenu
|
|
788
|
+
]
|
|
789
|
+
);
|
|
790
|
+
const listProps = useMemo2(
|
|
791
|
+
() => ({
|
|
792
|
+
onFocus: () => {
|
|
793
|
+
if (highlightedIndex === -1) {
|
|
794
|
+
setHighlightedIdx(0);
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
onKeyDown: handleKeyDown,
|
|
798
|
+
onMouseDownCapture: () => {
|
|
799
|
+
keyBoardNavigation.current = false;
|
|
800
|
+
setIgnoreFocus(true);
|
|
801
|
+
},
|
|
802
|
+
// onMouseEnter would seem less expensive but it misses some cases
|
|
803
|
+
onMouseMove: () => {
|
|
804
|
+
if (keyBoardNavigation.current) {
|
|
805
|
+
keyBoardNavigation.current = false;
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
onMouseLeave: () => {
|
|
809
|
+
keyBoardNavigation.current = true;
|
|
810
|
+
setIgnoreFocus(false);
|
|
811
|
+
setHighlightedIndex(-1);
|
|
812
|
+
}
|
|
813
|
+
}),
|
|
814
|
+
[handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex]
|
|
815
|
+
);
|
|
816
|
+
return {
|
|
817
|
+
focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,
|
|
818
|
+
controlledHighlighting,
|
|
819
|
+
highlightedIndex,
|
|
820
|
+
setHighlightedIndex,
|
|
821
|
+
// keyBoardNavigation,
|
|
822
|
+
listProps,
|
|
823
|
+
setIgnoreFocus
|
|
824
|
+
};
|
|
825
|
+
};
|
|
826
|
+
function nextItemIdx(count, key, idx) {
|
|
827
|
+
if (key === "ArrowUp") {
|
|
828
|
+
if (idx > 0) {
|
|
829
|
+
return idx - 1;
|
|
830
|
+
} else {
|
|
831
|
+
return idx;
|
|
832
|
+
}
|
|
833
|
+
} else {
|
|
834
|
+
if (idx === null) {
|
|
835
|
+
return 0;
|
|
836
|
+
} else if (idx === count - 1) {
|
|
837
|
+
return idx;
|
|
838
|
+
} else {
|
|
839
|
+
return idx + 1;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/menu/MenuList.tsx
|
|
845
|
+
import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
|
|
846
|
+
var classBase3 = "vuuMenuList";
|
|
847
|
+
var Separator = () => /* @__PURE__ */ jsx5("li", { className: "vuuMenuItem-divider" });
|
|
848
|
+
var MenuItemGroup = () => null;
|
|
849
|
+
var MenuItem = ({
|
|
850
|
+
children,
|
|
851
|
+
idx,
|
|
852
|
+
options,
|
|
853
|
+
...props
|
|
854
|
+
}) => {
|
|
855
|
+
return /* @__PURE__ */ jsx5("div", { ...props, children });
|
|
856
|
+
};
|
|
857
|
+
var MenuItemLabel = ({ children }) => /* @__PURE__ */ jsx5(Fragment, { children });
|
|
858
|
+
MenuItemLabel.displayName = "MenuItemLabel";
|
|
859
|
+
MenuItem.Label = MenuItemLabel;
|
|
860
|
+
var getDisplayName = (item) => React3.isValidElement(item) && typeof item.type !== "string" && "displayName" in item.type ? item.type.displayName : void 0;
|
|
861
|
+
var isMenuItemLabel = (item) => getDisplayName(item) === "MenuItemLabel";
|
|
862
|
+
var hasIcon = (child) => child.props["data-icon"];
|
|
863
|
+
var MenuList = ({
|
|
864
|
+
activatedByKeyboard,
|
|
865
|
+
childMenuShowing,
|
|
866
|
+
children,
|
|
867
|
+
className,
|
|
868
|
+
defaultHighlightedIdx,
|
|
869
|
+
highlightedIdx: highlightedIdxProp,
|
|
870
|
+
id: idProp,
|
|
871
|
+
isRoot: isRoot2,
|
|
872
|
+
listItemProps,
|
|
873
|
+
onHighlightMenuItem,
|
|
874
|
+
onActivate,
|
|
875
|
+
onCloseMenu,
|
|
876
|
+
openMenu: onOpenMenu,
|
|
877
|
+
...props
|
|
878
|
+
}) => {
|
|
879
|
+
const id = useId(idProp);
|
|
880
|
+
const root = useRef5(null);
|
|
881
|
+
const mapIdxToId = useMemo3(() => /* @__PURE__ */ new Map(), []);
|
|
882
|
+
const handleActivate = (idx) => {
|
|
883
|
+
var _a;
|
|
884
|
+
const el = (_a = root.current) == null ? void 0 : _a.querySelector(`:scope > [data-index='${idx}']`);
|
|
885
|
+
(el == null ? void 0 : el.id) && (onActivate == null ? void 0 : onActivate(el.id));
|
|
886
|
+
};
|
|
887
|
+
const { focusVisible, highlightedIndex, listProps } = useKeyboardNavigation({
|
|
888
|
+
count: React3.Children.count(children),
|
|
889
|
+
defaultHighlightedIdx,
|
|
890
|
+
highlightedIndex: highlightedIdxProp,
|
|
891
|
+
onActivate: handleActivate,
|
|
892
|
+
onHighlight: onHighlightMenuItem,
|
|
893
|
+
onOpenMenu,
|
|
894
|
+
onCloseMenu
|
|
895
|
+
});
|
|
896
|
+
const appliedFocusVisible = childMenuShowing == void 0 ? focusVisible : -1;
|
|
897
|
+
useLayoutEffect4(() => {
|
|
898
|
+
var _a;
|
|
899
|
+
if (childMenuShowing === void 0 && activatedByKeyboard) {
|
|
900
|
+
(_a = root.current) == null ? void 0 : _a.focus();
|
|
901
|
+
}
|
|
902
|
+
}, [activatedByKeyboard, childMenuShowing]);
|
|
903
|
+
const getActiveDescendant = () => highlightedIndex === void 0 || highlightedIndex === -1 ? void 0 : mapIdxToId.get(highlightedIndex);
|
|
904
|
+
function renderContent() {
|
|
905
|
+
const propsCommonToAllListItems = {
|
|
906
|
+
...listItemProps,
|
|
907
|
+
role: "menuitem"
|
|
908
|
+
};
|
|
909
|
+
const maybeIcon = (childElement, withIcon, iconName) => withIcon ? [
|
|
910
|
+
/* @__PURE__ */ jsx5(
|
|
911
|
+
"span",
|
|
912
|
+
{
|
|
913
|
+
className: "vuuIconContainer",
|
|
914
|
+
"data-icon": iconName
|
|
915
|
+
},
|
|
916
|
+
"icon"
|
|
917
|
+
)
|
|
918
|
+
].concat(childElement) : childElement;
|
|
919
|
+
function addClonedChild(list, child, idx, withIcon) {
|
|
920
|
+
var _a;
|
|
921
|
+
const {
|
|
922
|
+
children: children2,
|
|
923
|
+
className: className2,
|
|
924
|
+
"data-icon": iconName,
|
|
925
|
+
id: itemId,
|
|
926
|
+
hasSeparator,
|
|
927
|
+
label,
|
|
928
|
+
...props2
|
|
929
|
+
} = child.props;
|
|
930
|
+
const hasSubMenu = isMenuItemGroup(child);
|
|
931
|
+
const subMenuShowing = hasSubMenu && childMenuShowing === itemId;
|
|
932
|
+
const ariaControls = subMenuShowing ? `${id}-${itemId}` : void 0;
|
|
933
|
+
const ariaLabel = (label != null ? label : typeof children2 === "string") ? children2 : void 0;
|
|
934
|
+
list.push(
|
|
935
|
+
/* @__PURE__ */ jsx5(
|
|
936
|
+
MenuItem,
|
|
937
|
+
{
|
|
938
|
+
...props2,
|
|
939
|
+
...propsCommonToAllListItems,
|
|
940
|
+
...getMenuItemProps(
|
|
941
|
+
itemId,
|
|
942
|
+
idx,
|
|
943
|
+
(_a = child.key) != null ? _a : itemId,
|
|
944
|
+
highlightedIndex,
|
|
945
|
+
appliedFocusVisible,
|
|
946
|
+
className2,
|
|
947
|
+
hasSeparator
|
|
948
|
+
),
|
|
949
|
+
"aria-controls": ariaControls,
|
|
950
|
+
"aria-haspopup": hasSubMenu || void 0,
|
|
951
|
+
"aria-expanded": subMenuShowing || void 0,
|
|
952
|
+
"aria-label": ariaLabel,
|
|
953
|
+
children: hasSubMenu ? maybeIcon(label != null ? label : children2, withIcon, iconName) : maybeIcon(children2, withIcon, iconName)
|
|
954
|
+
}
|
|
955
|
+
)
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
const listItems = [];
|
|
959
|
+
if (children.length > 0) {
|
|
960
|
+
const withIcon = children.some(hasIcon);
|
|
961
|
+
children.forEach((child, idx) => {
|
|
962
|
+
addClonedChild(listItems, child, idx, withIcon);
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
return listItems;
|
|
966
|
+
}
|
|
967
|
+
return /* @__PURE__ */ jsx5(
|
|
968
|
+
"div",
|
|
969
|
+
{
|
|
970
|
+
...props,
|
|
971
|
+
...listProps,
|
|
972
|
+
"aria-activedescendant": getActiveDescendant(),
|
|
973
|
+
className: cx6(classBase3, className, {
|
|
974
|
+
[`${classBase3}-childMenuShowing`]: childMenuShowing !== void 0
|
|
975
|
+
}),
|
|
976
|
+
"data-root": isRoot2 || void 0,
|
|
977
|
+
id,
|
|
978
|
+
ref: root,
|
|
979
|
+
role: "menu",
|
|
980
|
+
children: renderContent()
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
};
|
|
984
|
+
var getMenuItemProps = (itemId, idx, key, highlightedIdx, focusVisible, className, hasSeparator) => ({
|
|
985
|
+
id: `menuitem-${itemId}`,
|
|
986
|
+
key: key != null ? key : idx,
|
|
987
|
+
"data-index": idx,
|
|
988
|
+
className: cx6("vuuMenuItem", className, {
|
|
989
|
+
"vuuMenuItem-separator": hasSeparator,
|
|
990
|
+
vuuHighlighted: idx === highlightedIdx,
|
|
991
|
+
focusVisible: focusVisible === idx
|
|
992
|
+
})
|
|
993
|
+
});
|
|
994
|
+
MenuList.displayName = "MenuList";
|
|
995
|
+
|
|
996
|
+
// src/menu/use-cascade.ts
|
|
997
|
+
import {
|
|
998
|
+
useCallback as useCallback6,
|
|
999
|
+
useMemo as useMemo4,
|
|
1000
|
+
useRef as useRef6,
|
|
1001
|
+
useState as useState5
|
|
1002
|
+
} from "react";
|
|
1003
|
+
|
|
1004
|
+
// src/menu/list-dom-utils.ts
|
|
1005
|
+
var closestListItem = (el) => el == null ? void 0 : el.closest("[data-index],[aria-posinset]");
|
|
1006
|
+
|
|
1007
|
+
// src/menu/use-cascade.ts
|
|
1008
|
+
var nudge = (menus, distance, pos) => {
|
|
1009
|
+
return menus.map(
|
|
1010
|
+
(m, i) => i === menus.length - 1 ? {
|
|
1011
|
+
...m,
|
|
1012
|
+
[pos]: m[pos] - distance
|
|
1013
|
+
} : m
|
|
1014
|
+
);
|
|
1015
|
+
};
|
|
1016
|
+
var nudgeLeft = (menus, distance) => nudge(menus, distance, "left");
|
|
1017
|
+
var nudgeUp = (menus, distance) => nudge(menus, distance, "top");
|
|
1018
|
+
var flipSides = (id, menus) => {
|
|
1019
|
+
const [parentMenu, menu] = menus.slice(-2);
|
|
1020
|
+
const el = document.getElementById(`${id}-${menu.id}`);
|
|
1021
|
+
if (el === null) {
|
|
1022
|
+
throw Error(`useCascade.flipSides element with id ${menu.id} not found`);
|
|
1023
|
+
}
|
|
1024
|
+
const { width } = el.getBoundingClientRect();
|
|
1025
|
+
return menus.map(
|
|
1026
|
+
(m) => m === menu ? {
|
|
1027
|
+
...m,
|
|
1028
|
+
left: parentMenu.left - (width - 2)
|
|
1029
|
+
} : m
|
|
1030
|
+
);
|
|
1031
|
+
};
|
|
1032
|
+
var getPosition = (el, openMenus) => {
|
|
1033
|
+
const [{ left, top: menuTop }] = openMenus.slice(-1);
|
|
1034
|
+
const { offsetWidth: width, offsetTop: top } = el;
|
|
1035
|
+
return { left: left + width, top: top + menuTop };
|
|
1036
|
+
};
|
|
1037
|
+
var getHostMenuId = (id, rootId) => {
|
|
1038
|
+
const pos = id.lastIndexOf("-");
|
|
1039
|
+
if (id.startsWith("menuitem")) {
|
|
1040
|
+
return pos > -1 ? id.slice(9, pos) : rootId;
|
|
1041
|
+
} else {
|
|
1042
|
+
return pos > -1 ? id.slice(0, pos) : rootId;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
var getTargetMenuId = (id) => id.slice(9);
|
|
1046
|
+
var getMenuItemDetails = ({ ariaExpanded, ariaHasPopup, id }, rootId) => {
|
|
1047
|
+
if (id.startsWith("menuitem")) {
|
|
1048
|
+
return {
|
|
1049
|
+
hostMenuId: getHostMenuId(id, rootId),
|
|
1050
|
+
targetMenuId: getTargetMenuId(id),
|
|
1051
|
+
menuItemId: id,
|
|
1052
|
+
isGroup: ariaHasPopup === "true",
|
|
1053
|
+
isOpen: ariaExpanded === "true"
|
|
1054
|
+
};
|
|
1055
|
+
} else {
|
|
1056
|
+
throw Error(`getMenuItemDetails #${id} is not a menuitem`);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
var useCascade = ({
|
|
1060
|
+
id: rootId,
|
|
1061
|
+
onActivate,
|
|
1062
|
+
onMouseEnterItem,
|
|
1063
|
+
position: { x: posX, y: posY }
|
|
1064
|
+
}) => {
|
|
1065
|
+
const [, forceRefresh] = useState5({});
|
|
1066
|
+
const openMenus = useRef6([
|
|
1067
|
+
{ id: rootId, left: posX, top: posY }
|
|
1068
|
+
]);
|
|
1069
|
+
const menuIsOpen = useCallback6(
|
|
1070
|
+
(menuId) => openMenus.current.findIndex((menu) => menu.id === menuId) !== -1,
|
|
1071
|
+
[]
|
|
1072
|
+
);
|
|
1073
|
+
const getOpenMenuStatus = useCallback6((menuId) => {
|
|
1074
|
+
const state = menuState.current[menuId];
|
|
1075
|
+
if (state === void 0) {
|
|
1076
|
+
throw Error(`getOpenMenuState no entry for menu ${menuId}`);
|
|
1077
|
+
}
|
|
1078
|
+
return state;
|
|
1079
|
+
}, []);
|
|
1080
|
+
const setOpenMenus = useCallback6((menus) => {
|
|
1081
|
+
openMenus.current = menus;
|
|
1082
|
+
forceRefresh({});
|
|
1083
|
+
}, []);
|
|
1084
|
+
const menuOpenPendingTimeout = useRef6();
|
|
1085
|
+
const menuClosePendingTimeout = useRef6();
|
|
1086
|
+
const menuState = useRef6({ [rootId]: "no-popup" });
|
|
1087
|
+
const openMenu = useCallback6(
|
|
1088
|
+
(hostMenuId = rootId, targetMenuId, itemId = null) => {
|
|
1089
|
+
if (hostMenuId === rootId && itemId === null) {
|
|
1090
|
+
setOpenMenus([{ id: rootId, left: posX, top: posY }]);
|
|
1091
|
+
} else {
|
|
1092
|
+
menuState.current[hostMenuId] = "popup-open";
|
|
1093
|
+
const el = document.getElementById(itemId);
|
|
1094
|
+
if (el !== null) {
|
|
1095
|
+
const { left, top } = getPosition(el, openMenus.current);
|
|
1096
|
+
setOpenMenus(
|
|
1097
|
+
openMenus.current.concat({ id: targetMenuId, left, top })
|
|
1098
|
+
);
|
|
1099
|
+
} else {
|
|
1100
|
+
throw Error(`openMenu no menuItem ${itemId}`);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
[rootId, posX, posY, setOpenMenus]
|
|
1105
|
+
);
|
|
1106
|
+
const closeMenu = useCallback6(
|
|
1107
|
+
(menuId) => {
|
|
1108
|
+
if (menuId === rootId) {
|
|
1109
|
+
setOpenMenus([]);
|
|
1110
|
+
} else {
|
|
1111
|
+
const menus = openMenus.current.slice();
|
|
1112
|
+
const lastMenu = menus.pop();
|
|
1113
|
+
menuState.current[lastMenu.id] = "no-popup";
|
|
1114
|
+
const parentMenu = menus.at(-1);
|
|
1115
|
+
if (parentMenu) {
|
|
1116
|
+
menuState.current[parentMenu.id] = "no-popup";
|
|
1117
|
+
}
|
|
1118
|
+
setOpenMenus(menus);
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
[rootId, setOpenMenus]
|
|
1122
|
+
);
|
|
1123
|
+
const closeMenus = useCallback6(
|
|
1124
|
+
(menuItemId) => {
|
|
1125
|
+
const menus = openMenus.current.slice();
|
|
1126
|
+
const menuItemMenuId = menuItemId.slice(9);
|
|
1127
|
+
let { id: lastMenuId } = menus.at(-1);
|
|
1128
|
+
while (menus.length > 1 && !menuItemMenuId.startsWith(lastMenuId)) {
|
|
1129
|
+
const parentMenuId = getHostMenuId(lastMenuId, rootId);
|
|
1130
|
+
menus.pop();
|
|
1131
|
+
menuState.current[lastMenuId] = "no-popup";
|
|
1132
|
+
menuState.current[parentMenuId] = "no-popup";
|
|
1133
|
+
({ id: lastMenuId } = menus[menus.length - 1]);
|
|
1134
|
+
}
|
|
1135
|
+
if (menus.length < openMenus.current.length) {
|
|
1136
|
+
setOpenMenus(menus);
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
[rootId, setOpenMenus]
|
|
1140
|
+
);
|
|
1141
|
+
const clearAnyScheduledOpenTasks = useCallback6(() => {
|
|
1142
|
+
if (menuOpenPendingTimeout.current) {
|
|
1143
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
1144
|
+
menuOpenPendingTimeout.current = void 0;
|
|
1145
|
+
}
|
|
1146
|
+
}, []);
|
|
1147
|
+
const scheduleOpen = useCallback6(
|
|
1148
|
+
(hostMenuId, targetMenuId, menuItemId, delay = 300) => {
|
|
1149
|
+
clearAnyScheduledOpenTasks();
|
|
1150
|
+
menuOpenPendingTimeout.current = window.setTimeout(() => {
|
|
1151
|
+
closeMenus(menuItemId);
|
|
1152
|
+
menuState.current[hostMenuId] = "popup-open";
|
|
1153
|
+
menuState.current[targetMenuId] = "no-popup";
|
|
1154
|
+
openMenu(hostMenuId, targetMenuId, menuItemId);
|
|
1155
|
+
}, delay);
|
|
1156
|
+
},
|
|
1157
|
+
[clearAnyScheduledOpenTasks, closeMenus, openMenu]
|
|
1158
|
+
);
|
|
1159
|
+
const scheduleClose = useCallback6(
|
|
1160
|
+
(hostMenuId, openMenuId, itemId) => {
|
|
1161
|
+
menuState.current[openMenuId] = "pending-close";
|
|
1162
|
+
menuClosePendingTimeout.current = window.setTimeout(() => {
|
|
1163
|
+
closeMenus(itemId);
|
|
1164
|
+
}, 400);
|
|
1165
|
+
},
|
|
1166
|
+
[closeMenus]
|
|
1167
|
+
);
|
|
1168
|
+
const handleRender = useCallback6(() => {
|
|
1169
|
+
const { current: menus } = openMenus;
|
|
1170
|
+
const menu = menus.at(-1);
|
|
1171
|
+
const el = menu ? document.getElementById(menu.id) : void 0;
|
|
1172
|
+
if (el) {
|
|
1173
|
+
const { right, bottom } = el.getBoundingClientRect();
|
|
1174
|
+
const { clientHeight, clientWidth } = document.body;
|
|
1175
|
+
if (right > clientWidth) {
|
|
1176
|
+
const newMenus = menus.length > 1 ? flipSides(rootId, menus) : nudgeLeft(menus, right - clientWidth);
|
|
1177
|
+
setOpenMenus(newMenus);
|
|
1178
|
+
} else if (bottom > clientHeight) {
|
|
1179
|
+
const newMenus = nudgeUp(menus, bottom - clientHeight);
|
|
1180
|
+
setOpenMenus(newMenus);
|
|
1181
|
+
}
|
|
1182
|
+
if (typeof el.tabIndex === "number") {
|
|
1183
|
+
el.focus();
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}, [rootId, setOpenMenus]);
|
|
1187
|
+
const triggerChildMenu = useCallback6(
|
|
1188
|
+
(menuItemEl, immediate = false) => {
|
|
1189
|
+
const { hostMenuId, targetMenuId, menuItemId, isGroup, isOpen } = getMenuItemDetails(menuItemEl, rootId);
|
|
1190
|
+
const {
|
|
1191
|
+
current: { [hostMenuId]: state }
|
|
1192
|
+
} = menuState;
|
|
1193
|
+
const delay = immediate ? 0 : void 0;
|
|
1194
|
+
if (state === "no-popup" && isGroup) {
|
|
1195
|
+
menuState.current[hostMenuId] = "popup-pending";
|
|
1196
|
+
scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);
|
|
1197
|
+
} else if (state === "popup-pending" && !isGroup) {
|
|
1198
|
+
menuState.current[hostMenuId] = "no-popup";
|
|
1199
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
1200
|
+
menuOpenPendingTimeout.current = void 0;
|
|
1201
|
+
} else if (state === "popup-pending" && isGroup) {
|
|
1202
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
1203
|
+
scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);
|
|
1204
|
+
} else if (state === "popup-open") {
|
|
1205
|
+
if (menuIsOpen(targetMenuId)) {
|
|
1206
|
+
const menuStatus = getOpenMenuStatus(targetMenuId);
|
|
1207
|
+
closeMenus(menuItemId);
|
|
1208
|
+
switch (menuStatus) {
|
|
1209
|
+
case "pending-close":
|
|
1210
|
+
clearTimeout(menuClosePendingTimeout.current);
|
|
1211
|
+
menuClosePendingTimeout.current = void 0;
|
|
1212
|
+
menuState.current[targetMenuId] = "no-popup";
|
|
1213
|
+
clearAnyScheduledOpenTasks();
|
|
1214
|
+
break;
|
|
1215
|
+
default:
|
|
1216
|
+
}
|
|
1217
|
+
} else {
|
|
1218
|
+
const [parentOfLastOpenedMenu, lastOpenedMenu] = openMenus.current.slice(-2);
|
|
1219
|
+
if (parentOfLastOpenedMenu.id === hostMenuId && menuState.current[lastOpenedMenu.id] !== "pending-close") {
|
|
1220
|
+
scheduleClose(hostMenuId, lastOpenedMenu.id, menuItemId);
|
|
1221
|
+
if (isGroup && !isOpen) {
|
|
1222
|
+
scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);
|
|
1223
|
+
}
|
|
1224
|
+
} else if (parentOfLastOpenedMenu.id === hostMenuId && isGroup && menuItemId !== lastOpenedMenu.id && menuState.current[lastOpenedMenu.id] === "pending-close") {
|
|
1225
|
+
scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);
|
|
1226
|
+
} else if (isGroup) {
|
|
1227
|
+
scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);
|
|
1228
|
+
} else if (!(menuState.current[lastOpenedMenu.id] === "pending-close")) {
|
|
1229
|
+
closeMenus(menuItemId);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (state === "pending-close") {
|
|
1234
|
+
clearAnyScheduledOpenTasks();
|
|
1235
|
+
clearTimeout(menuClosePendingTimeout.current);
|
|
1236
|
+
menuClosePendingTimeout.current = void 0;
|
|
1237
|
+
menuState.current[hostMenuId] = "popup-open";
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
[
|
|
1241
|
+
clearAnyScheduledOpenTasks,
|
|
1242
|
+
closeMenus,
|
|
1243
|
+
getOpenMenuStatus,
|
|
1244
|
+
menuIsOpen,
|
|
1245
|
+
rootId,
|
|
1246
|
+
scheduleClose,
|
|
1247
|
+
scheduleOpen
|
|
1248
|
+
]
|
|
1249
|
+
);
|
|
1250
|
+
const listItemProps = useMemo4(
|
|
1251
|
+
() => ({
|
|
1252
|
+
onMouseEnter: (evt) => {
|
|
1253
|
+
const menuItemEl = closestListItem(evt.target);
|
|
1254
|
+
triggerChildMenu(menuItemEl);
|
|
1255
|
+
onMouseEnterItem(evt, menuItemEl.id);
|
|
1256
|
+
},
|
|
1257
|
+
onClick: (evt) => {
|
|
1258
|
+
const listItemEl = closestListItem(evt.target);
|
|
1259
|
+
const { isGroup, menuItemId } = getMenuItemDetails(listItemEl, rootId);
|
|
1260
|
+
if (isGroup) {
|
|
1261
|
+
triggerChildMenu(listItemEl);
|
|
1262
|
+
} else {
|
|
1263
|
+
onActivate(menuItemId);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}),
|
|
1267
|
+
[onActivate, onMouseEnterItem, rootId, triggerChildMenu]
|
|
1268
|
+
);
|
|
1269
|
+
return {
|
|
1270
|
+
closeMenu,
|
|
1271
|
+
handleRender,
|
|
1272
|
+
listItemProps,
|
|
1273
|
+
openMenu: triggerChildMenu,
|
|
1274
|
+
openMenus: openMenus.current
|
|
1275
|
+
};
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// src/menu/ContextMenu.tsx
|
|
1279
|
+
import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
|
|
1280
|
+
import { createElement as createElement2 } from "react";
|
|
1281
|
+
var noop = () => void 0;
|
|
1282
|
+
var ContextMenu = ({
|
|
1283
|
+
PortalProps: PortalProps2,
|
|
1284
|
+
activatedByKeyboard,
|
|
1285
|
+
children: childrenProp,
|
|
1286
|
+
className,
|
|
1287
|
+
id: idProp,
|
|
1288
|
+
onClose = () => void 0,
|
|
1289
|
+
position = { x: 0, y: 0 },
|
|
1290
|
+
style,
|
|
1291
|
+
...menuListProps
|
|
1292
|
+
}) => {
|
|
1293
|
+
const id = useId2(idProp);
|
|
1294
|
+
const closeMenuRef = useRef7(noop);
|
|
1295
|
+
const [menus, actions] = useItemsWithIdsNext(childrenProp, id);
|
|
1296
|
+
const navigatingWithKeyboard = useRef7(activatedByKeyboard);
|
|
1297
|
+
const handleMouseEnterItem = useCallback7(() => {
|
|
1298
|
+
navigatingWithKeyboard.current = false;
|
|
1299
|
+
}, []);
|
|
1300
|
+
const handleActivate = useCallback7(
|
|
1301
|
+
(menuItemId) => {
|
|
1302
|
+
const actionId = menuItemId.slice(9);
|
|
1303
|
+
const { action, options } = actions[actionId];
|
|
1304
|
+
closeMenuRef.current(id);
|
|
1305
|
+
onClose({
|
|
1306
|
+
type: "menu-action",
|
|
1307
|
+
menuId: action,
|
|
1308
|
+
options
|
|
1309
|
+
});
|
|
1310
|
+
},
|
|
1311
|
+
[actions, id, onClose]
|
|
1312
|
+
);
|
|
1313
|
+
const {
|
|
1314
|
+
closeMenu,
|
|
1315
|
+
listItemProps,
|
|
1316
|
+
openMenu: onOpenMenu,
|
|
1317
|
+
openMenus,
|
|
1318
|
+
handleRender
|
|
1319
|
+
} = useCascade({
|
|
1320
|
+
// FIXME
|
|
1321
|
+
id: `${id}`,
|
|
1322
|
+
onActivate: handleActivate,
|
|
1323
|
+
onMouseEnterItem: handleMouseEnterItem,
|
|
1324
|
+
position
|
|
1325
|
+
});
|
|
1326
|
+
closeMenuRef.current = closeMenu;
|
|
1327
|
+
const handleCloseMenu = useCallback7(
|
|
1328
|
+
(evt, reason) => {
|
|
1329
|
+
navigatingWithKeyboard.current = true;
|
|
1330
|
+
closeMenu();
|
|
1331
|
+
if (reason === "tab-away") {
|
|
1332
|
+
onClose({
|
|
1333
|
+
event: evt,
|
|
1334
|
+
type: "tab-away"
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
[closeMenu, onClose]
|
|
1339
|
+
);
|
|
1340
|
+
const handleHighlightMenuItem = () => {
|
|
1341
|
+
};
|
|
1342
|
+
const lastMenu = openMenus.length - 1;
|
|
1343
|
+
const getChildMenuId = (i) => {
|
|
1344
|
+
if (i >= lastMenu) {
|
|
1345
|
+
return void 0;
|
|
1346
|
+
} else {
|
|
1347
|
+
const { id: id2 } = openMenus[i + 1];
|
|
1348
|
+
return id2;
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
return /* @__PURE__ */ jsx6(Fragment2, { children: openMenus.map(({ id: menuId, left, top }, i, all) => {
|
|
1352
|
+
const childMenuId = getChildMenuId(i);
|
|
1353
|
+
return /* @__PURE__ */ createElement2(Portal, { ...PortalProps2, key: i, onRender: handleRender }, /* @__PURE__ */ jsx6(
|
|
1354
|
+
PopupComponent2,
|
|
1355
|
+
{
|
|
1356
|
+
anchorElement: { current: document.body },
|
|
1357
|
+
placement: "absolute",
|
|
1358
|
+
position: { left, top },
|
|
1359
|
+
children: /* @__PURE__ */ createElement2(
|
|
1360
|
+
MenuList,
|
|
1361
|
+
{
|
|
1362
|
+
...menuListProps,
|
|
1363
|
+
activatedByKeyboard: navigatingWithKeyboard.current,
|
|
1364
|
+
childMenuShowing: childMenuId,
|
|
1365
|
+
className,
|
|
1366
|
+
id: menuId,
|
|
1367
|
+
isRoot: i === 0,
|
|
1368
|
+
key: i,
|
|
1369
|
+
listItemProps,
|
|
1370
|
+
onActivate: handleActivate,
|
|
1371
|
+
onHighlightMenuItem: handleHighlightMenuItem,
|
|
1372
|
+
onCloseMenu: handleCloseMenu,
|
|
1373
|
+
openMenu: onOpenMenu,
|
|
1374
|
+
style,
|
|
1375
|
+
tabIndex: i === all.length - 1 ? 0 : void 0
|
|
1376
|
+
},
|
|
1377
|
+
menus[menuId]
|
|
1378
|
+
)
|
|
1379
|
+
}
|
|
1380
|
+
));
|
|
1381
|
+
}) });
|
|
1382
|
+
};
|
|
1383
|
+
ContextMenu.displayName = "ContextMenu";
|
|
1384
|
+
|
|
1385
|
+
// src/menu/context-menu-provider.tsx
|
|
1386
|
+
import { createContext, useCallback as useCallback8, useMemo as useMemo5 } from "react";
|
|
1387
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1388
|
+
var ContextMenuContext = createContext(
|
|
1389
|
+
null
|
|
1390
|
+
);
|
|
1391
|
+
var Provider = ({
|
|
1392
|
+
children,
|
|
1393
|
+
context,
|
|
1394
|
+
menuActionHandler,
|
|
1395
|
+
menuBuilder
|
|
1396
|
+
}) => {
|
|
1397
|
+
const menuBuilders = useMemo5(() => {
|
|
1398
|
+
if ((context == null ? void 0 : context.menuBuilders) && menuBuilder) {
|
|
1399
|
+
return context.menuBuilders.concat(menuBuilder);
|
|
1400
|
+
} else if (menuBuilder) {
|
|
1401
|
+
return [menuBuilder];
|
|
1402
|
+
} else {
|
|
1403
|
+
return (context == null ? void 0 : context.menuBuilders) || [];
|
|
1404
|
+
}
|
|
1405
|
+
}, [context, menuBuilder]);
|
|
1406
|
+
const handleMenuAction = useCallback8(
|
|
1407
|
+
(reason) => {
|
|
1408
|
+
var _a;
|
|
1409
|
+
if (menuActionHandler == null ? void 0 : menuActionHandler(reason)) {
|
|
1410
|
+
return true;
|
|
1411
|
+
}
|
|
1412
|
+
if ((_a = context == null ? void 0 : context.menuActionHandler) == null ? void 0 : _a.call(context, reason)) {
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
[context, menuActionHandler]
|
|
1417
|
+
);
|
|
1418
|
+
return /* @__PURE__ */ jsx7(
|
|
1419
|
+
ContextMenuContext.Provider,
|
|
1420
|
+
{
|
|
1421
|
+
value: {
|
|
1422
|
+
menuActionHandler: handleMenuAction,
|
|
1423
|
+
menuBuilders
|
|
1424
|
+
},
|
|
1425
|
+
children
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1428
|
+
};
|
|
1429
|
+
var ContextMenuProvider = ({
|
|
1430
|
+
children,
|
|
1431
|
+
label,
|
|
1432
|
+
menuActionHandler,
|
|
1433
|
+
menuBuilder
|
|
1434
|
+
}) => {
|
|
1435
|
+
return /* @__PURE__ */ jsx7(ContextMenuContext.Consumer, { children: (parentContext) => /* @__PURE__ */ jsx7(
|
|
1436
|
+
Provider,
|
|
1437
|
+
{
|
|
1438
|
+
context: parentContext,
|
|
1439
|
+
label,
|
|
1440
|
+
menuActionHandler,
|
|
1441
|
+
menuBuilder,
|
|
1442
|
+
children
|
|
1443
|
+
}
|
|
1444
|
+
) });
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
// src/menu/useContextMenu.tsx
|
|
1448
|
+
import {
|
|
1449
|
+
isGroupMenuItemDescriptor,
|
|
1450
|
+
useThemeAttributes as useThemeAttributes3
|
|
1451
|
+
} from "@vuu-ui/vuu-utils";
|
|
1452
|
+
import { cloneElement, useCallback as useCallback9, useContext, useMemo as useMemo6 } from "react";
|
|
1453
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1454
|
+
var useContextMenu = (menuBuilder, menuActionHandler) => {
|
|
1455
|
+
const ctx = useContext(ContextMenuContext);
|
|
1456
|
+
const [themeClass, densityClass, dataMode] = useThemeAttributes3();
|
|
1457
|
+
const themeAttributes = useMemo6(
|
|
1458
|
+
() => ({
|
|
1459
|
+
themeClass,
|
|
1460
|
+
densityClass,
|
|
1461
|
+
dataMode
|
|
1462
|
+
}),
|
|
1463
|
+
[dataMode, densityClass, themeClass]
|
|
1464
|
+
);
|
|
1465
|
+
const buildMenuOptions = useCallback9(
|
|
1466
|
+
(menuBuilders, location, options) => {
|
|
1467
|
+
let results = [];
|
|
1468
|
+
for (const menuBuilder2 of menuBuilders) {
|
|
1469
|
+
results = results.concat(menuBuilder2(location, options));
|
|
1470
|
+
}
|
|
1471
|
+
return results;
|
|
1472
|
+
},
|
|
1473
|
+
[]
|
|
1474
|
+
);
|
|
1475
|
+
const handleShowContextMenu = useCallback9(
|
|
1476
|
+
(e, location, { ContextMenuProps: ContextMenuProps2, contextMenu, ...options }) => {
|
|
1477
|
+
var _a, _b;
|
|
1478
|
+
(_a = e.stopPropagation) == null ? void 0 : _a.call(e);
|
|
1479
|
+
(_b = e.preventDefault) == null ? void 0 : _b.call(e);
|
|
1480
|
+
if (contextMenu) {
|
|
1481
|
+
return showContextMenuComponent(
|
|
1482
|
+
{
|
|
1483
|
+
x: e.clientX,
|
|
1484
|
+
y: e.clientY
|
|
1485
|
+
},
|
|
1486
|
+
contextMenu
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
const menuBuilders = [];
|
|
1490
|
+
if (menuBuilder) {
|
|
1491
|
+
menuBuilders.push(menuBuilder);
|
|
1492
|
+
}
|
|
1493
|
+
if (ctx && Array.isArray(ctx == null ? void 0 : ctx.menuBuilders) && ctx.menuBuilders.length > 0) {
|
|
1494
|
+
menuBuilders.push(...ctx.menuBuilders);
|
|
1495
|
+
}
|
|
1496
|
+
if (menuBuilders.length > 0) {
|
|
1497
|
+
const menuItemDescriptors = buildMenuOptions(
|
|
1498
|
+
menuBuilders,
|
|
1499
|
+
location,
|
|
1500
|
+
options
|
|
1501
|
+
);
|
|
1502
|
+
const menuHandler = (action) => {
|
|
1503
|
+
if ((menuActionHandler == null ? void 0 : menuActionHandler(action)) === true) {
|
|
1504
|
+
return true;
|
|
1505
|
+
} else {
|
|
1506
|
+
return ctx == null ? void 0 : ctx.menuActionHandler(action);
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
if (menuItemDescriptors.length && menuHandler) {
|
|
1510
|
+
showContextMenu(e, menuItemDescriptors, menuHandler, {
|
|
1511
|
+
PortalProps: {
|
|
1512
|
+
themeAttributes
|
|
1513
|
+
},
|
|
1514
|
+
...ContextMenuProps2
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
} else {
|
|
1518
|
+
console.warn(
|
|
1519
|
+
"useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)"
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
[buildMenuOptions, ctx, menuActionHandler, menuBuilder, themeAttributes]
|
|
1524
|
+
);
|
|
1525
|
+
const hideContextMenu = useCallback9(() => {
|
|
1526
|
+
console.log("hide context menu");
|
|
1527
|
+
}, []);
|
|
1528
|
+
return [handleShowContextMenu, hideContextMenu];
|
|
1529
|
+
};
|
|
1530
|
+
var NO_OPTIONS = {};
|
|
1531
|
+
var showContextMenuComponent = (position, contextMenu) => {
|
|
1532
|
+
PopupService.showPopup({
|
|
1533
|
+
focus: true,
|
|
1534
|
+
left: 0,
|
|
1535
|
+
top: 0,
|
|
1536
|
+
component: cloneElement(contextMenu, { position })
|
|
1537
|
+
});
|
|
1538
|
+
};
|
|
1539
|
+
var showContextMenu = (e, menuDescriptors, handleContextMenuAction, {
|
|
1540
|
+
position: positionProp,
|
|
1541
|
+
...contextMenuProps
|
|
1542
|
+
} = NO_OPTIONS) => {
|
|
1543
|
+
const menuItems = (menuDescriptors2) => {
|
|
1544
|
+
const fromDescriptor = (menuItem, i) => isGroupMenuItemDescriptor(menuItem) ? /* @__PURE__ */ jsx8(MenuItemGroup, { label: menuItem.label, children: menuItem.children.map(fromDescriptor) }, i) : /* @__PURE__ */ jsx8(
|
|
1545
|
+
MenuItem,
|
|
1546
|
+
{
|
|
1547
|
+
action: menuItem.action,
|
|
1548
|
+
className: menuItem.className,
|
|
1549
|
+
"data-icon": menuItem.icon,
|
|
1550
|
+
options: menuItem.options,
|
|
1551
|
+
children: menuItem.label
|
|
1552
|
+
},
|
|
1553
|
+
i
|
|
1554
|
+
);
|
|
1555
|
+
return menuDescriptors2.map(fromDescriptor);
|
|
1556
|
+
};
|
|
1557
|
+
const handleClose = (reason) => {
|
|
1558
|
+
var _a;
|
|
1559
|
+
if (reasonIsMenuAction(reason)) {
|
|
1560
|
+
if ((reason == null ? void 0 : reason.closedBy) === "popup-service") {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
handleContextMenuAction(reason);
|
|
1564
|
+
PopupService.hidePopup(reason);
|
|
1565
|
+
}
|
|
1566
|
+
(_a = contextMenuProps == null ? void 0 : contextMenuProps.onClose) == null ? void 0 : _a.call(contextMenuProps, reason);
|
|
1567
|
+
};
|
|
1568
|
+
const position = positionProp != null ? positionProp : {
|
|
1569
|
+
x: e.clientX,
|
|
1570
|
+
y: e.clientY
|
|
1571
|
+
};
|
|
1572
|
+
const component = /* @__PURE__ */ jsx8(
|
|
1573
|
+
ContextMenu,
|
|
1574
|
+
{
|
|
1575
|
+
...contextMenuProps,
|
|
1576
|
+
onClose: handleClose,
|
|
1577
|
+
position,
|
|
1578
|
+
children: menuItems(menuDescriptors)
|
|
1579
|
+
}
|
|
1580
|
+
);
|
|
1581
|
+
PopupService.showPopup({ left: 0, top: 0, component, focus: true });
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
// src/popup-menu/PopupMenu.tsx
|
|
1585
|
+
import { Icon, IconButton } from "@vuu-ui/vuu-ui-controls";
|
|
1586
|
+
import { useId as useId3 } from "@vuu-ui/vuu-utils";
|
|
1587
|
+
import { Button as Button2 } from "@salt-ds/core";
|
|
1588
|
+
import cx7 from "clsx";
|
|
1589
|
+
|
|
1590
|
+
// src/popup-menu/usePopupMenu.ts
|
|
1591
|
+
import {
|
|
1592
|
+
useCallback as useCallback10,
|
|
1593
|
+
useRef as useRef8,
|
|
1594
|
+
useState as useState6
|
|
1595
|
+
} from "react";
|
|
1596
|
+
var usePopupMenu = ({
|
|
1597
|
+
anchorElement,
|
|
1598
|
+
id,
|
|
1599
|
+
menuActionHandler,
|
|
1600
|
+
menuBuilder,
|
|
1601
|
+
menuClassName,
|
|
1602
|
+
menuLocation,
|
|
1603
|
+
menuOptions,
|
|
1604
|
+
onMenuClose,
|
|
1605
|
+
onMenuOpen,
|
|
1606
|
+
popupPlacement,
|
|
1607
|
+
tabIndex
|
|
1608
|
+
}) => {
|
|
1609
|
+
const [menuOpen, _setMenuOpen] = useState6(false);
|
|
1610
|
+
const suppressShowMenuRef = useRef8(false);
|
|
1611
|
+
const rootRef = useRef8(null);
|
|
1612
|
+
const setMenuOpen = useCallback10(
|
|
1613
|
+
(isOpen) => {
|
|
1614
|
+
_setMenuOpen(isOpen);
|
|
1615
|
+
if (isOpen) {
|
|
1616
|
+
onMenuOpen == null ? void 0 : onMenuOpen();
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
[onMenuOpen]
|
|
1620
|
+
);
|
|
1621
|
+
const [showContextMenu2] = useContextMenu(menuBuilder, menuActionHandler);
|
|
1622
|
+
const handleOpenMenu = useCallback10((el) => {
|
|
1623
|
+
console.log(`menu Open `, {
|
|
1624
|
+
el
|
|
1625
|
+
});
|
|
1626
|
+
}, []);
|
|
1627
|
+
const handleMenuClose = useCallback10(
|
|
1628
|
+
(reason) => {
|
|
1629
|
+
console.log("onClose");
|
|
1630
|
+
setMenuOpen(false);
|
|
1631
|
+
if (reasonIsClickAway(reason)) {
|
|
1632
|
+
const target = reason.mouseEvt.target;
|
|
1633
|
+
if (target === rootRef.current) {
|
|
1634
|
+
suppressShowMenuRef.current = true;
|
|
1635
|
+
}
|
|
1636
|
+
onMenuClose == null ? void 0 : onMenuClose(reason);
|
|
1637
|
+
} else {
|
|
1638
|
+
requestAnimationFrame(() => {
|
|
1639
|
+
var _a;
|
|
1640
|
+
onMenuClose == null ? void 0 : onMenuClose(reason);
|
|
1641
|
+
if (tabIndex !== -1 && (reason == null ? void 0 : reason.type) !== "tab-away") {
|
|
1642
|
+
(_a = rootRef.current) == null ? void 0 : _a.focus();
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
},
|
|
1647
|
+
[onMenuClose, setMenuOpen, tabIndex]
|
|
1648
|
+
);
|
|
1649
|
+
const showMenu = useCallback10(
|
|
1650
|
+
(e) => {
|
|
1651
|
+
var _a;
|
|
1652
|
+
if (suppressShowMenuRef.current) {
|
|
1653
|
+
suppressShowMenuRef.current = false;
|
|
1654
|
+
} else {
|
|
1655
|
+
const anchorEl = (_a = anchorElement == null ? void 0 : anchorElement.current) != null ? _a : rootRef.current;
|
|
1656
|
+
if (anchorEl) {
|
|
1657
|
+
const {
|
|
1658
|
+
left: x,
|
|
1659
|
+
top: y,
|
|
1660
|
+
width
|
|
1661
|
+
} = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);
|
|
1662
|
+
setMenuOpen(true);
|
|
1663
|
+
showContextMenu2(e, menuLocation, {
|
|
1664
|
+
ContextMenuProps: {
|
|
1665
|
+
className: menuClassName,
|
|
1666
|
+
id: `${id}-menu`,
|
|
1667
|
+
onClose: handleMenuClose,
|
|
1668
|
+
openMenu: handleOpenMenu,
|
|
1669
|
+
position: {
|
|
1670
|
+
x,
|
|
1671
|
+
y
|
|
1672
|
+
},
|
|
1673
|
+
style: { width: width ? width - 2 : void 0 }
|
|
1674
|
+
},
|
|
1675
|
+
...menuOptions
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
},
|
|
1680
|
+
[
|
|
1681
|
+
anchorElement,
|
|
1682
|
+
handleMenuClose,
|
|
1683
|
+
handleOpenMenu,
|
|
1684
|
+
id,
|
|
1685
|
+
menuClassName,
|
|
1686
|
+
menuLocation,
|
|
1687
|
+
menuOptions,
|
|
1688
|
+
popupPlacement,
|
|
1689
|
+
setMenuOpen,
|
|
1690
|
+
showContextMenu2
|
|
1691
|
+
]
|
|
1692
|
+
);
|
|
1693
|
+
const ariaAttributes = {
|
|
1694
|
+
"aria-controls": menuOpen ? `${id}-menu` : void 0,
|
|
1695
|
+
"aria-expanded": menuOpen,
|
|
1696
|
+
"aria-haspopup": "menu"
|
|
1697
|
+
};
|
|
1698
|
+
const buttonProps = {
|
|
1699
|
+
id,
|
|
1700
|
+
onClick: showMenu,
|
|
1701
|
+
tabIndex
|
|
1702
|
+
};
|
|
1703
|
+
return { ariaAttributes, buttonProps, menuOpen, rootRef };
|
|
1704
|
+
};
|
|
1705
|
+
|
|
1706
|
+
// src/popup-menu/PopupMenu.tsx
|
|
1707
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1708
|
+
var classBase4 = "vuuPopupMenu";
|
|
1709
|
+
var PopupMenu = ({
|
|
1710
|
+
anchorElement,
|
|
1711
|
+
className,
|
|
1712
|
+
disabled = false,
|
|
1713
|
+
label,
|
|
1714
|
+
icon: icon2 = label ? "chevron-down" : "more-vert",
|
|
1715
|
+
id: idProp,
|
|
1716
|
+
menuActionHandler,
|
|
1717
|
+
menuBuilder,
|
|
1718
|
+
menuClassName,
|
|
1719
|
+
menuLocation = "header",
|
|
1720
|
+
menuOptions,
|
|
1721
|
+
onMenuClose,
|
|
1722
|
+
onMenuOpen,
|
|
1723
|
+
popupPlacement = "below-right",
|
|
1724
|
+
tabIndex = 0,
|
|
1725
|
+
variant = "secondary",
|
|
1726
|
+
...htmlAttributes
|
|
1727
|
+
}) => {
|
|
1728
|
+
const id = useId3(idProp);
|
|
1729
|
+
const { ariaAttributes, buttonProps, menuOpen, rootRef } = usePopupMenu({
|
|
1730
|
+
anchorElement,
|
|
1731
|
+
id,
|
|
1732
|
+
menuActionHandler,
|
|
1733
|
+
menuBuilder,
|
|
1734
|
+
menuClassName,
|
|
1735
|
+
menuLocation,
|
|
1736
|
+
onMenuClose,
|
|
1737
|
+
onMenuOpen,
|
|
1738
|
+
menuOptions,
|
|
1739
|
+
popupPlacement,
|
|
1740
|
+
tabIndex
|
|
1741
|
+
});
|
|
1742
|
+
if (label) {
|
|
1743
|
+
return /* @__PURE__ */ jsxs3(
|
|
1744
|
+
Button2,
|
|
1745
|
+
{
|
|
1746
|
+
...htmlAttributes,
|
|
1747
|
+
...ariaAttributes,
|
|
1748
|
+
...buttonProps,
|
|
1749
|
+
className: cx7(classBase4, className, `${classBase4}-withCaption`, {
|
|
1750
|
+
"saltButton-active": menuOpen
|
|
1751
|
+
}),
|
|
1752
|
+
disabled,
|
|
1753
|
+
ref: rootRef,
|
|
1754
|
+
variant: "secondary",
|
|
1755
|
+
children: [
|
|
1756
|
+
icon2 ? /* @__PURE__ */ jsx9(Icon, { name: icon2 }) : null,
|
|
1757
|
+
label
|
|
1758
|
+
]
|
|
1759
|
+
}
|
|
1760
|
+
);
|
|
1761
|
+
} else if (icon2) {
|
|
1762
|
+
return /* @__PURE__ */ jsx9(
|
|
1763
|
+
IconButton,
|
|
1764
|
+
{
|
|
1765
|
+
...htmlAttributes,
|
|
1766
|
+
...ariaAttributes,
|
|
1767
|
+
...buttonProps,
|
|
1768
|
+
className: cx7(classBase4, className, {
|
|
1769
|
+
"saltButton-active": menuOpen
|
|
1770
|
+
}),
|
|
1771
|
+
disabled,
|
|
1772
|
+
icon: icon2,
|
|
1773
|
+
ref: rootRef,
|
|
1774
|
+
variant
|
|
1775
|
+
}
|
|
1776
|
+
);
|
|
1777
|
+
} else {
|
|
1778
|
+
console.error("PopupMenu must have a label or an icon (or both)");
|
|
1779
|
+
return null;
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
|
|
1783
|
+
// src/prompt/Prompt.tsx
|
|
1784
|
+
import { useThemeAttributes as useThemeAttributes4 } from "@vuu-ui/vuu-utils";
|
|
1785
|
+
import { Button as Button3 } from "@salt-ds/core";
|
|
1786
|
+
import cx8 from "clsx";
|
|
1787
|
+
import { useLayoutEffect as useLayoutEffect5, useRef as useRef9 } from "react";
|
|
1788
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1789
|
+
var classBase5 = "vuuPrompt";
|
|
1790
|
+
var AnchorBody2 = { current: document.body };
|
|
1791
|
+
var EMPTY_PROPS2 = {};
|
|
1792
|
+
var Prompt = ({
|
|
1793
|
+
PopupProps = EMPTY_PROPS2,
|
|
1794
|
+
cancelButtonLabel = "Cancel",
|
|
1795
|
+
confirmButtonLabel = "Confirm",
|
|
1796
|
+
icon: icon2,
|
|
1797
|
+
onCancel,
|
|
1798
|
+
onConfirm,
|
|
1799
|
+
style,
|
|
1800
|
+
text,
|
|
1801
|
+
title,
|
|
1802
|
+
variant = "info",
|
|
1803
|
+
...htmlAttributes
|
|
1804
|
+
}) => {
|
|
1805
|
+
const {
|
|
1806
|
+
anchorElement = AnchorBody2,
|
|
1807
|
+
offsetLeft = 0,
|
|
1808
|
+
offsetTop = 0,
|
|
1809
|
+
placement = "below"
|
|
1810
|
+
} = PopupProps;
|
|
1811
|
+
const [themeClass, _, dataMode] = useThemeAttributes4();
|
|
1812
|
+
const { position } = useAnchoredPosition({
|
|
1813
|
+
anchorElement,
|
|
1814
|
+
offsetLeft,
|
|
1815
|
+
offsetTop,
|
|
1816
|
+
placement
|
|
1817
|
+
});
|
|
1818
|
+
const rootRef = useRef9(null);
|
|
1819
|
+
const confirmRef = useRef9(null);
|
|
1820
|
+
useLayoutEffect5(() => {
|
|
1821
|
+
if (rootRef.current) {
|
|
1822
|
+
rootRef.current.showModal();
|
|
1823
|
+
if (confirmRef.current) {
|
|
1824
|
+
confirmRef.current.focus();
|
|
1825
|
+
}
|
|
1826
|
+
if (placement.endsWith("center")) {
|
|
1827
|
+
const { width } = rootRef.current.getBoundingClientRect();
|
|
1828
|
+
rootRef.current.style.marginLeft = `-${width / 2}px`;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}, [placement]);
|
|
1832
|
+
return /* @__PURE__ */ jsx10(
|
|
1833
|
+
"dialog",
|
|
1834
|
+
{
|
|
1835
|
+
...htmlAttributes,
|
|
1836
|
+
className: cx8(classBase5, `${classBase5}-${variant}`, themeClass),
|
|
1837
|
+
"data-mode": dataMode,
|
|
1838
|
+
ref: rootRef,
|
|
1839
|
+
style: { ...style, ...position },
|
|
1840
|
+
children: /* @__PURE__ */ jsxs4("form", { className: `${classBase5}-form`, children: [
|
|
1841
|
+
/* @__PURE__ */ jsx10("div", { className: `${classBase5}-header`, "data-icon": icon2, children: title }),
|
|
1842
|
+
/* @__PURE__ */ jsx10("div", { className: `${classBase5}-text`, children: text }),
|
|
1843
|
+
/* @__PURE__ */ jsxs4("div", { className: `${classBase5}-buttonBar`, children: [
|
|
1844
|
+
/* @__PURE__ */ jsx10(Button3, { onClick: onCancel, variant: "secondary", children: cancelButtonLabel }),
|
|
1845
|
+
/* @__PURE__ */ jsx10(Button3, { onClick: onConfirm, ref: confirmRef, value: "default", children: confirmButtonLabel })
|
|
1846
|
+
] })
|
|
1847
|
+
] })
|
|
1848
|
+
}
|
|
1849
|
+
);
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
// src/tooltip/useAnchoredPosition.ts
|
|
1853
|
+
import { useCallback as useCallback11, useLayoutEffect as useLayoutEffect6 } from "react";
|
|
1854
|
+
var pointerSize = 12;
|
|
1855
|
+
var roomAbove = (anchor, height) => height < anchor.top;
|
|
1856
|
+
var roomBelow = (anchor, height) => document.body.clientHeight - anchor.bottom > height;
|
|
1857
|
+
var getNextPlacement = (placement) => {
|
|
1858
|
+
if (Array.isArray(placement)) {
|
|
1859
|
+
if (placement.length === 0) {
|
|
1860
|
+
return [void 0, placement];
|
|
1861
|
+
} else {
|
|
1862
|
+
return [placement[0], placement.slice(1)];
|
|
1863
|
+
}
|
|
1864
|
+
} else {
|
|
1865
|
+
return [placement, []];
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
var useAnchoredPosition2 = ({
|
|
1869
|
+
anchorElement,
|
|
1870
|
+
offsetLeft = 0,
|
|
1871
|
+
offsetTop = 0,
|
|
1872
|
+
placement
|
|
1873
|
+
}) => {
|
|
1874
|
+
const ref = useCallback11(
|
|
1875
|
+
(el) => {
|
|
1876
|
+
if (el && anchorElement.current) {
|
|
1877
|
+
const anchor = anchorElement.current.getBoundingClientRect();
|
|
1878
|
+
const { height, width } = el.getBoundingClientRect();
|
|
1879
|
+
let nextPlacement;
|
|
1880
|
+
let placements = placement;
|
|
1881
|
+
do {
|
|
1882
|
+
[nextPlacement, placements] = getNextPlacement(placements);
|
|
1883
|
+
switch (nextPlacement) {
|
|
1884
|
+
case "above":
|
|
1885
|
+
if (roomAbove(anchor, height + pointerSize)) {
|
|
1886
|
+
const midDiff = (width - anchor.width) / 2;
|
|
1887
|
+
el.style.cssText = `left:${anchor.left - midDiff}px;top:${anchor.top - height - pointerSize}px;opacity: 1;`;
|
|
1888
|
+
el.dataset.align = "above";
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
break;
|
|
1892
|
+
case "below":
|
|
1893
|
+
if (roomBelow(anchor, height + pointerSize)) {
|
|
1894
|
+
const midDiff = (width - anchor.width) / 2;
|
|
1895
|
+
el.style.cssText = `left:${anchor.left - midDiff}px;top:${anchor.bottom + pointerSize}px;opacity: 1;`;
|
|
1896
|
+
el.dataset.align = "below";
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
break;
|
|
1900
|
+
case "right":
|
|
1901
|
+
console.log("place the fucker right");
|
|
1902
|
+
break;
|
|
1903
|
+
case "left":
|
|
1904
|
+
console.log("place the fucker left");
|
|
1905
|
+
break;
|
|
1906
|
+
default:
|
|
1907
|
+
console.warn(`unklnown tooltip placement ${placement}`);
|
|
1908
|
+
}
|
|
1909
|
+
} while (nextPlacement);
|
|
1910
|
+
}
|
|
1911
|
+
},
|
|
1912
|
+
[anchorElement, placement]
|
|
1913
|
+
);
|
|
1914
|
+
useLayoutEffect6(() => {
|
|
1915
|
+
}, [anchorElement, offsetLeft, offsetTop, placement]);
|
|
1916
|
+
return ref;
|
|
1917
|
+
};
|
|
1918
|
+
|
|
1919
|
+
// src/tooltip/Tooltip.tsx
|
|
1920
|
+
import cx9 from "clsx";
|
|
1921
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1922
|
+
var classBase6 = "vuuTooltip";
|
|
1923
|
+
var Tooltip = ({
|
|
1924
|
+
anchorElement,
|
|
1925
|
+
children,
|
|
1926
|
+
className,
|
|
1927
|
+
id,
|
|
1928
|
+
onMouseEnter,
|
|
1929
|
+
onMouseLeave,
|
|
1930
|
+
placement: placementProp,
|
|
1931
|
+
status,
|
|
1932
|
+
style: styleProp
|
|
1933
|
+
}) => {
|
|
1934
|
+
const ref = useAnchoredPosition2({
|
|
1935
|
+
anchorElement,
|
|
1936
|
+
placement: placementProp
|
|
1937
|
+
});
|
|
1938
|
+
return /* @__PURE__ */ jsx11(Portal, { children: /* @__PURE__ */ jsx11(
|
|
1939
|
+
"div",
|
|
1940
|
+
{
|
|
1941
|
+
className: cx9(classBase6, className, "vuuHidden", {
|
|
1942
|
+
[`${classBase6}-error`]: status === "error"
|
|
1943
|
+
}),
|
|
1944
|
+
id,
|
|
1945
|
+
ref,
|
|
1946
|
+
style: { ...styleProp, left: 0, top: 0 },
|
|
1947
|
+
children: /* @__PURE__ */ jsx11(
|
|
1948
|
+
"span",
|
|
1949
|
+
{
|
|
1950
|
+
className: `${classBase6}-content`,
|
|
1951
|
+
onMouseEnter,
|
|
1952
|
+
onMouseLeave,
|
|
1953
|
+
children
|
|
1954
|
+
}
|
|
1955
|
+
)
|
|
1956
|
+
}
|
|
1957
|
+
) });
|
|
1958
|
+
};
|
|
1959
|
+
|
|
1960
|
+
// src/tooltip/useTooltip.ts
|
|
1961
|
+
import { queryClosest, useId as useId4 } from "@vuu-ui/vuu-utils";
|
|
1962
|
+
import { useCallback as useCallback12, useRef as useRef10, useState as useState7 } from "react";
|
|
1963
|
+
var useTooltip = ({
|
|
1964
|
+
anchorQuery = "*",
|
|
1965
|
+
id: idProp,
|
|
1966
|
+
placement = "right",
|
|
1967
|
+
tooltipContent
|
|
1968
|
+
}) => {
|
|
1969
|
+
const hideTooltipRef = useRef10();
|
|
1970
|
+
const isHoveringRef = useRef10(false);
|
|
1971
|
+
const anchorElementRef = useRef10(null);
|
|
1972
|
+
const mouseEnterTimerRef = useRef10();
|
|
1973
|
+
const mouseLeaveTimerRef = useRef10();
|
|
1974
|
+
const [tooltipProps, setTooltipProps] = useState7();
|
|
1975
|
+
const id = useId4(idProp);
|
|
1976
|
+
const escapeListener = useCallback12((evt) => {
|
|
1977
|
+
var _a;
|
|
1978
|
+
if (evt.key === "Escape") {
|
|
1979
|
+
(_a = hideTooltipRef.current) == null ? void 0 : _a.call(hideTooltipRef);
|
|
1980
|
+
}
|
|
1981
|
+
}, []);
|
|
1982
|
+
hideTooltipRef.current = useCallback12(() => {
|
|
1983
|
+
setTooltipProps(void 0);
|
|
1984
|
+
document.removeEventListener("keydown", escapeListener);
|
|
1985
|
+
}, [escapeListener]);
|
|
1986
|
+
const handleMouseEnterTooltip = useCallback12(() => {
|
|
1987
|
+
window.clearTimeout(mouseLeaveTimerRef.current);
|
|
1988
|
+
}, []);
|
|
1989
|
+
const handleMouseLeaveTooltip = useCallback12(() => {
|
|
1990
|
+
var _a;
|
|
1991
|
+
(_a = hideTooltipRef.current) == null ? void 0 : _a.call(hideTooltipRef);
|
|
1992
|
+
}, []);
|
|
1993
|
+
const hideTooltip = useCallback12((defer = 0) => {
|
|
1994
|
+
if (mouseEnterTimerRef.current) {
|
|
1995
|
+
window.clearTimeout(mouseEnterTimerRef.current);
|
|
1996
|
+
mouseEnterTimerRef.current = void 0;
|
|
1997
|
+
} else if (hideTooltipRef.current) {
|
|
1998
|
+
if (defer === 0) {
|
|
1999
|
+
hideTooltipRef.current();
|
|
2000
|
+
} else {
|
|
2001
|
+
mouseLeaveTimerRef.current = window.setTimeout(
|
|
2002
|
+
hideTooltipRef.current,
|
|
2003
|
+
defer
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}, []);
|
|
2008
|
+
const showTooltip = useCallback12(
|
|
2009
|
+
(ref = anchorElementRef) => {
|
|
2010
|
+
const { current: anchorEl } = ref;
|
|
2011
|
+
if (anchorEl) {
|
|
2012
|
+
setTooltipProps({
|
|
2013
|
+
anchorElement: ref,
|
|
2014
|
+
children: tooltipContent,
|
|
2015
|
+
id: `${id}-tooltip`,
|
|
2016
|
+
onMouseEnter: handleMouseEnterTooltip,
|
|
2017
|
+
onMouseLeave: handleMouseLeaveTooltip,
|
|
2018
|
+
placement
|
|
2019
|
+
});
|
|
2020
|
+
document.addEventListener("keydown", escapeListener);
|
|
2021
|
+
}
|
|
2022
|
+
mouseEnterTimerRef.current = void 0;
|
|
2023
|
+
hideTooltip(isHoveringRef.current ? 3e3 : 1e3);
|
|
2024
|
+
},
|
|
2025
|
+
[
|
|
2026
|
+
escapeListener,
|
|
2027
|
+
handleMouseEnterTooltip,
|
|
2028
|
+
handleMouseLeaveTooltip,
|
|
2029
|
+
hideTooltip,
|
|
2030
|
+
id,
|
|
2031
|
+
placement,
|
|
2032
|
+
tooltipContent
|
|
2033
|
+
]
|
|
2034
|
+
);
|
|
2035
|
+
const handleMouseEnter = useCallback12(
|
|
2036
|
+
(evt) => {
|
|
2037
|
+
isHoveringRef.current = true;
|
|
2038
|
+
const el = queryClosest(evt.target, anchorQuery);
|
|
2039
|
+
if (el) {
|
|
2040
|
+
console.log(`el ${el.classList}`);
|
|
2041
|
+
anchorElementRef.current = el;
|
|
2042
|
+
mouseEnterTimerRef.current = window.setTimeout(showTooltip, 800);
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
[anchorQuery, showTooltip]
|
|
2046
|
+
);
|
|
2047
|
+
const handleMouseLeave = useCallback12(() => {
|
|
2048
|
+
isHoveringRef.current = false;
|
|
2049
|
+
hideTooltip(200);
|
|
2050
|
+
}, [hideTooltip]);
|
|
2051
|
+
const anchorProps = {
|
|
2052
|
+
"aria-describedby": `${id}-tooltip`,
|
|
2053
|
+
onMouseEnter: handleMouseEnter,
|
|
2054
|
+
onMouseLeave: handleMouseLeave
|
|
2055
|
+
};
|
|
2056
|
+
return {
|
|
2057
|
+
anchorProps,
|
|
2058
|
+
hideTooltip,
|
|
2059
|
+
showTooltip,
|
|
2060
|
+
tooltipProps
|
|
2061
|
+
};
|
|
2062
|
+
};
|
|
2063
|
+
|
|
2064
|
+
// src/notifications/NotificationsProvider.tsx
|
|
2065
|
+
import React4, { useContext as useContext2 } from "react";
|
|
2066
|
+
|
|
2067
|
+
// src/notifications/NotificationsCenter.tsx
|
|
2068
|
+
import { useMemo as useMemo7, useState as useState9 } from "react";
|
|
2069
|
+
import { getUniqueId } from "@vuu-ui/vuu-utils";
|
|
2070
|
+
|
|
2071
|
+
// src/notifications/ToastNotification.tsx
|
|
2072
|
+
import { Icon as Icon2 } from "@vuu-ui/vuu-ui-controls";
|
|
2073
|
+
import cx10 from "clsx";
|
|
2074
|
+
import { useEffect, useState as useState8 } from "react";
|
|
2075
|
+
import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2076
|
+
var toastWidth = 300;
|
|
2077
|
+
var toastContainerRightPadding = 50;
|
|
2078
|
+
var toastDisplayDuration = 6e3;
|
|
2079
|
+
var horizontalTransitionDuration = 1e3;
|
|
2080
|
+
var toastHeight = 56;
|
|
2081
|
+
var verticalTransitionDuration = 300;
|
|
2082
|
+
var classBase7 = "vuuToastNotification";
|
|
2083
|
+
var icon = {
|
|
2084
|
+
error: "error",
|
|
2085
|
+
info: "info-circle",
|
|
2086
|
+
success: "tick",
|
|
2087
|
+
warning: "warn-triangle"
|
|
2088
|
+
};
|
|
2089
|
+
var ToastNotification = (props) => {
|
|
2090
|
+
const { top, notification, animated = true } = props;
|
|
2091
|
+
const [right, setRight] = useState8(-toastWidth - toastContainerRightPadding);
|
|
2092
|
+
useEffect(() => {
|
|
2093
|
+
setTimeout(() => setRight(toastContainerRightPadding));
|
|
2094
|
+
if (animated) {
|
|
2095
|
+
setTimeout(
|
|
2096
|
+
() => setRight(-toastWidth - toastContainerRightPadding),
|
|
2097
|
+
toastDisplayDuration + horizontalTransitionDuration
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
}, [animated]);
|
|
2101
|
+
return /* @__PURE__ */ jsx12(Portal, { children: /* @__PURE__ */ jsxs5(
|
|
2102
|
+
"div",
|
|
2103
|
+
{
|
|
2104
|
+
className: cx10(classBase7, `${classBase7}-${notification.type}`),
|
|
2105
|
+
style: {
|
|
2106
|
+
height: toastHeight,
|
|
2107
|
+
right,
|
|
2108
|
+
width: toastWidth,
|
|
2109
|
+
top,
|
|
2110
|
+
transition: animated ? `right ${horizontalTransitionDuration}ms, top ${verticalTransitionDuration}ms ` : "none"
|
|
2111
|
+
},
|
|
2112
|
+
children: [
|
|
2113
|
+
/* @__PURE__ */ jsx12(Icon2, { name: icon[notification.type] }),
|
|
2114
|
+
/* @__PURE__ */ jsxs5("div", { className: `${classBase7}-toastContent`, children: [
|
|
2115
|
+
/* @__PURE__ */ jsx12("strong", { className: `${classBase7}-toastHeader`, children: notification.header }),
|
|
2116
|
+
/* @__PURE__ */ jsx12("div", { children: notification.body })
|
|
2117
|
+
] })
|
|
2118
|
+
]
|
|
2119
|
+
}
|
|
2120
|
+
) });
|
|
2121
|
+
};
|
|
2122
|
+
|
|
2123
|
+
// src/notifications/NotificationsCenter.tsx
|
|
2124
|
+
import { Fragment as Fragment3, jsx as jsx13 } from "react/jsx-runtime";
|
|
2125
|
+
var toastOffsetTop = 60;
|
|
2126
|
+
var toastDisplayDuration2 = 6e3;
|
|
2127
|
+
var horizontalTransitionDuration2 = 1e3;
|
|
2128
|
+
var toastHeight2 = 56;
|
|
2129
|
+
var toastContainerContentGap = 10;
|
|
2130
|
+
var NotificationsCenter = ({
|
|
2131
|
+
notificationsContext
|
|
2132
|
+
}) => {
|
|
2133
|
+
const [notifications, setNotifications] = useState9([]);
|
|
2134
|
+
useMemo7(() => {
|
|
2135
|
+
notificationsContext.setNotify((notification) => {
|
|
2136
|
+
const newNotification = {
|
|
2137
|
+
...notification,
|
|
2138
|
+
id: getUniqueId()
|
|
2139
|
+
};
|
|
2140
|
+
setNotifications((prev) => prev.concat(newNotification));
|
|
2141
|
+
setTimeout(() => {
|
|
2142
|
+
setNotifications((prev) => prev.filter((n) => n !== newNotification));
|
|
2143
|
+
}, toastDisplayDuration2 + horizontalTransitionDuration2 * 2);
|
|
2144
|
+
});
|
|
2145
|
+
}, [notificationsContext]);
|
|
2146
|
+
return /* @__PURE__ */ jsx13(Fragment3, { children: notifications.map((notification, i) => /* @__PURE__ */ jsx13(
|
|
2147
|
+
ToastNotification,
|
|
2148
|
+
{
|
|
2149
|
+
top: toastOffsetTop + (toastHeight2 + toastContainerContentGap) * i,
|
|
2150
|
+
notification
|
|
2151
|
+
},
|
|
2152
|
+
notification.id
|
|
2153
|
+
)) });
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2156
|
+
// src/notifications/NotificationsProvider.tsx
|
|
2157
|
+
import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2158
|
+
var NotificationsContextObject = class {
|
|
2159
|
+
constructor() {
|
|
2160
|
+
this.notify = () => "have you forgotten to provide a NotificationsCenter?";
|
|
2161
|
+
this.setNotify = (dispatcher) => {
|
|
2162
|
+
this.notify = dispatcher;
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2166
|
+
var NotificationsContext = React4.createContext(
|
|
2167
|
+
new NotificationsContextObject()
|
|
2168
|
+
);
|
|
2169
|
+
var NotificationsProvider = (props) => {
|
|
2170
|
+
const context = useContext2(NotificationsContext);
|
|
2171
|
+
return /* @__PURE__ */ jsxs6(NotificationsContext.Provider, { value: context, children: [
|
|
2172
|
+
/* @__PURE__ */ jsx14(NotificationsCenter, { notificationsContext: context }),
|
|
2173
|
+
props.children
|
|
2174
|
+
] });
|
|
2175
|
+
};
|
|
2176
|
+
var useNotifications = () => {
|
|
2177
|
+
const { notify } = useContext2(NotificationsContext);
|
|
2178
|
+
return notify;
|
|
2179
|
+
};
|
|
2180
|
+
export {
|
|
2181
|
+
ContextMenu,
|
|
2182
|
+
ContextMenuContext,
|
|
2183
|
+
ContextMenuProvider,
|
|
2184
|
+
Dialog,
|
|
2185
|
+
DialogHeader,
|
|
2186
|
+
DialogService,
|
|
2187
|
+
MenuItem,
|
|
2188
|
+
MenuItemGroup,
|
|
2189
|
+
MenuList,
|
|
2190
|
+
NotificationsProvider,
|
|
2191
|
+
PopupComponent2 as PopupComponent,
|
|
2192
|
+
PopupMenu,
|
|
2193
|
+
PopupService,
|
|
2194
|
+
Portal,
|
|
2195
|
+
Prompt,
|
|
2196
|
+
Separator,
|
|
2197
|
+
ToastNotification,
|
|
2198
|
+
Tooltip,
|
|
2199
|
+
createContainer,
|
|
2200
|
+
isMenuItemLabel,
|
|
2201
|
+
reasonIsClickAway,
|
|
2202
|
+
reasonIsMenuAction,
|
|
2203
|
+
renderPortal,
|
|
2204
|
+
useAnchoredPosition,
|
|
2205
|
+
useContextMenu,
|
|
2206
|
+
useDialog,
|
|
2207
|
+
useNotifications,
|
|
2208
|
+
useTooltip
|
|
2209
|
+
};
|
|
2
2210
|
//# sourceMappingURL=index.js.map
|