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