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