@vuu-ui/vuu-popups 0.6.21 → 0.6.22-debug
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +1311 -1
- package/cjs/index.js.map +2 -2
- package/esm/index.js +1296 -1
- package/esm/index.js.map +2 -2
- package/index.css +150 -1
- package/index.css.map +1 -1
- package/package.json +2 -2
package/esm/index.js
CHANGED
|
@@ -1,2 +1,1297 @@
|
|
|
1
|
-
import{Scrim as nt,Toolbar as ot,ToolbarButton as rt}from"@heswell/salt-lab";import{Text as st}from"@salt-ds/core";import it from"classnames";import{useCallback as fe,useRef as ut,useState as ge}from"react";import{useLayoutEffect as de,useMemo as tt}from"react";import*as me from"react-dom";import*as ae from"react-dom";import{SaltProvider as Ye}from"@salt-ds/core";import{jsx as et}from"react/jsx-runtime";var Ze=1,_e=(e=0,t=0,n=window)=>{let o=n.document.createElement("div");return o.className="vuuPopup "+Ze++,o.style.cssText=`left:${e}px; top:${t}px;`,n.document.body.appendChild(o),o},je=(e,t)=>_e(e,t),F=(e,t,n,o,r)=>{t.style.cssText=`left:${n}px; top:${o}px;position: absolute;`,ae.render(et(Ye,{applyClassesTo:"child",children:e}),t,r)},pe=je;var G=function({children:t,x:n=0,y:o=0,onRender:r}){let c=tt(()=>pe(),[]);return de(()=>{F(t,c,n,o,r)},[t,r,c,n,o]),de(()=>()=>{var i;c&&(me.unmountComponentAtNode(c),c.classList.contains("vuuPopup")&&((i=c.parentElement)==null||i.removeChild(c)))},[c]),null};var un=e=>{let t=getComputedStyle(document.body).getPropertyValue("--installed-themes");document.body.style.setProperty("--installed-themes",`${t} ${e}`)};import{jsx as W,jsxs as Me}from"react/jsx-runtime";var Z="vuuDialog",In=({children:e,className:t,isOpen:n=!1,onClose:o,title:r,...c})=>{let i=ut(null),[u]=ge(0),[a]=ge(0),d=fe(()=>{o==null||o()},[o]),l=fe(()=>{},[]);return n?W(G,{onRender:l,x:u,y:a,children:W(nt,{className:`${Z}-scrim`,open:n,children:Me("div",{...c,className:it(Z,t),ref:i,children:[Me(ot,{className:`${Z}-header`,children:[W(st,{children:r}),W(rt,{onClick:d,"data-align-end":!0,"data-icon":"close"},"close")]}),e]})})}):null};import{useIdMemo as Kt}from"@salt-ds/core";import{useCallback as ne,useRef as Se}from"react";import vt,{useLayoutEffect as yt,useMemo as It,useRef as Ct}from"react";import Le from"classnames";import{useIdMemo as wt}from"@salt-ds/core";import{useCallback as q,useMemo as gt,useRef as _,useState as Mt}from"react";var he=e=>e.closest("[data-root='true']")!==null,xe=(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 ct(e,...t){let n=new Set(e);for(let o of t)for(let r of o)n.add(r);return n}var lt="Enter";var at="Delete",pt=new Set([lt,at]),dt=new Set(["Tab"]),mt=new Set(["ArrowRight","ArrowLeft"]),be=new Set(["Home","End","ArrowDown","ArrowUp"]),ve=new Set(["Home","End","ArrowRight","ArrowLeft"]),ft=new Set(["F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"]),Rn=ct(pt,ve,be,mt,ft,dt);var ye=({key:e},t="vertical")=>(t==="vertical"?be:ve).has(e);var Ie=({autoHighlightFirstItem:e=!1,count:t,highlightedIndex:n,onActivate:o,onHighlight:r,onCloseMenu:c,onOpenMenu:i})=>{let u=_((n!=null?n:e)?0:-1),[,a]=Mt(null),d=n!==void 0,l=q(s=>{u.current=s,r==null||r(s),a({})},[r]),h=q(s=>{s!==u.current&&(d||l(s))},[d,l]),f=_(!0),C=_(!1),v=s=>C.current=s,g=d?n:u.current,E=q(s=>{let m=ht(t,s.key,u.current);m!==u.current&&h(m)},[t,h]),R=q(s=>{ye(s)?(s.preventDefault(),s.stopPropagation(),f.current=!0,E(s)):(s.key==="ArrowRight"||s.key==="Enter")&&xe(s.target,g)?i(g):s.key==="ArrowLeft"&&!he(s.target)?c(g):s.key==="Enter"&&o&&o(g)},[g,E,o,c,i]),L=gt(()=>({onFocus:()=>{g===-1&&l(0)},onKeyDown:R,onMouseDownCapture:()=>{f.current=!1,v(!0)},onMouseMove:()=>{f.current&&(f.current=!1)},onMouseLeave:()=>{f.current=!0,v(!1),h(-1)}}),[g,h,E,o,c,i,l]);return{focusVisible:f.current?g:-1,controlledHighlighting:d,highlightedIndex:g,setHighlightedIndex:h,listProps:L,setIgnoreFocus:v}};function ht(e,t,n){return t==="ArrowUp"?n>0?n-1:n:n===null?0:n===e-1?n:n+1}import Ce,{useCallback as xt,useMemo as bt}from"react";var j=e=>e.type===V||!!e.props["data-group"],we=e=>{let t=xt(()=>{let r=(i,u="root",a={},d={})=>{let l=a[u]=[],h=0,f=!1;return Ce.Children.forEach(i,C=>{if(C.type===Ee)f=!0;else{let v=j(C),g=u==="root"?`${h}`:`${u}.${h}`,{props:{action:E,options:R}}=C,{childWithId:L,grandChildren:s}=c(C,g,v,f);l.push(L),s?r(s,g,a,d):d[g]={action:E,options:R},h+=1,f=!1}}),[a,d]},c=(i,u,a,d=!1)=>{let{props:{children:l}}=i;return{childWithId:Ce.cloneElement(i,{hasSeparator:d,id:`${u}`,key:u,children:a?void 0:l}),grandChildren:a?l:void 0}};return r(e)},[e]),[n,o]=bt(()=>t(),[t]);return[n,o]};import{jsx as K}from"react/jsx-runtime";var Pe="vuuMenuList",Ee=()=>K("li",{className:"vuuMenuItem-divider"}),V=()=>null,ee=({children:e,idx:t,...n})=>K("div",{...n,children:e}),Et=e=>e.props["data-icon"],Re=({activatedByKeyboard:e,childMenuShowing:t=-1,children:n,className:o,highlightedIdx:r,id:c,isRoot:i,listItemProps:u,menuId:a,onHighlightMenuItem:d,onActivate:l,onCloseMenu:h,onOpenMenu:f,...C})=>{let v=wt(c),g=Ct(null),E=It(()=>new Map,[]),R=y=>{var b;let M=(b=g.current)==null?void 0:b.querySelector(`:scope > [data-idx='${y}']`);M!=null&&M.id&&(f==null||f(M.id))},L=y=>{var b;let M=(b=g.current)==null?void 0:b.querySelector(`:scope > [data-idx='${y}']`);M!=null&&M.id&&(l==null||l(M.id))},{focusVisible:s,highlightedIndex:m,listProps:p}=Ie({count:vt.Children.count(n),highlightedIndex:r,onActivate:L,onHighlight:d,onOpenMenu:R,onCloseMenu:h}),x=t==-1?s:-1;return yt(()=>{var y;t===-1&&e&&((y=g.current)==null||y.focus())},[e,t]),K("div",{...C,...p,"aria-activedescendant":(()=>m===void 0||m===-1?void 0:E.get(m))(),className:Le(Pe,o,{[`${Pe}-childMenuShowing`]:t!==-1}),"data-root":i||void 0,id:`${v}-${a}`,ref:g,role:"menu",tabIndex:0,children:k()});function k(){let y={...u,role:"menuitem"},M=(I,D,$)=>D?[K("span",{className:"vuuIconContainer","data-icon":$},"icon")].concat(I):I;function b(I,D,$,ie){var le;let{children:Ve,className:Ue,"data-icon":ue,id:X,hasSeparator:ze,label:Je,...Qe}=D.props,Y=j(D),ce=Y&&t===$,Xe=ce?`${v}-${X}`:void 0;I.push(K(ee,{...Qe,...y,...Pt(`${v}-${a}`,X,$,(le=D.key)!=null?le:X,m,x,Ue,ze),"aria-controls":Xe,"aria-haspopup":Y||void 0,"aria-expanded":ce||void 0,children:M(Y?Je:Ve,ie,ue)}))}let P=[];if(n.length>0){let I=n.some(Et);n.forEach((D,$)=>{b(P,D,$,I)})}return P}},Pt=(e,t,n,o,r,c,i,u)=>({id:`${e}-${t}`,key:o!=null?o:n,"data-idx":n,"data-highlighted":n===r||void 0,className:Le("vuuMenuItem",i,{"vuuMenuItem-separator":u,focusVisible:c===n})});Re.displayName="MenuList";var He=Re;import{useCallback as A,useMemo as Lt,useRef as N,useState as Rt}from"react";function ke(e){if(e){let t=e.dataset.idx;if(t)return parseInt(t,10);if(e.ariaPosInSet)return parseInt(e.ariaPosInSet,10)-1}}var te=e=>e==null?void 0:e.closest("[data-idx],[aria-posinset]");var Te=(e,t,n)=>e.map((o,r)=>r===e.length-1?{...o,[n]:o[n]-t}:o),Ht=(e,t)=>Te(e,t,"left"),kt=(e,t)=>Te(e,t,"top"),Tt=(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:c}=r.getBoundingClientRect();return t.map(i=>i===o?{...i,left:n.left-(c-2)}:i)},Dt=(e,t)=>{let[{left:n,top:o}]=t.slice(-1),{offsetWidth:r,offsetTop:c}=e;return{left:n+r,top:c+o}},O=e=>{let t=e.lastIndexOf("-");return t===-1?e:e.slice(t+1)},U=e=>{let t=O(e),n=t.lastIndexOf(".");return n>-1?t.slice(0,n):"root"},At=e=>{let t=0,n=e.indexOf(".",0);for(;n!==-1;)t+=1,n=e.indexOf(".",n+1);return t},St=e=>({menuId:U(e.id),itemId:O(e.id),isGroup:e.ariaHasPopup==="true",isOpen:e.ariaExpanded==="true",level:At(e.id)}),De=({id:e,onActivate:t,onMouseEnterItem:n,position:{x:o,y:r}})=>{let[,c]=Rt({}),i=N([{id:"root",left:o,top:r}]),u=A(s=>{i.current=s,c({})},[]),a=N(),d=N(),l=N({root:"no-popup"}),h=N(0),f=A((s="root",m=null,p=null)=>{if(s==="root"&&m===null)u([{id:"root",left:o,top:r}]);else{l.current[s]="popup-open";let w=(p?p.ownerDocument:document).getElementById(`${e}-${s}-${m}`),{left:k,top:y}=Dt(w,i.current);u(i.current.concat({id:m,left:k,top:y}))}},[e,o,r,u]),C=A(s=>{u(s==="root"?[]:i.current.slice(0,-1))},[u]),v=A((s,m)=>{let p=i.current.slice(),{id:x}=p[p.length-1];for(;p.length>1&&!m.startsWith(x);){let w=U(x);p.pop(),l.current[x]="no-popup",l.current[w]="no-popup",{id:x}=p[p.length-1]}p.length<i.current.length&&u(p)},[u]),g=A((s,m,p)=>{a.current&&clearTimeout(a.current),a.current=window.setTimeout(()=>{console.log(`scheduleOpen timed out opening ${m}`),v(s,m),l.current[s]="popup-open",l.current[m]="no-popup",f(s,m,p)},400)},[v,f]),E=A((s,m,p)=>{console.log(`scheduleClose openMenuId ${s} menuId ${m} itemId ${p}`),l.current[s]="pending-close",d.current=window.setTimeout(()=>{v(m,p)},400)},[v]),R=A(()=>{let{current:s}=i,[m]=s.slice(-1),p=document.getElementById(`${e}-${m.id}`);if(p){let{right:x,bottom:w}=p.getBoundingClientRect(),{clientHeight:k,clientWidth:y}=document.body;if(x>y){let M=s.length>1?Tt(e,s):Ht(s,x-y);u(M)}else if(w>k){let M=kt(s,w-k);u(M)}}},[e,u]),L=Lt(()=>({onMouseEnter:s=>{let m=te(s.target),{menuId:p,itemId:x,isGroup:w,isOpen:k,level:y}=St(m),M=h.current===y,{current:{[p]:b}}=l;if(h.current=y,b==="no-popup"&&w)l.current[p]="popup-pending",g(p,x,m);else if(b==="popup-pending"&&!w)l.current[p]="no-popup",clearTimeout(a.current),a.current=void 0;else if(b==="popup-pending"&&w)clearTimeout(a.current),g(p,x,m);else if(b==="popup-open"){let[{id:P},{id:I}]=i.current.slice(-2);P===p&&l.current[I]!=="pending-close"&&M?(E(I,p,x),w&&!k&&g(p,x,m)):P===p&&w&&x!==I&&l.current[I]==="pending-close"?g(p,x,m):w?(v(p,x),g(p,x,m)):l.current[I]==="pending-close"&&M||v(p,x)}b==="pending-close"&&(a.current&&(clearTimeout(a.current),a.current=void 0),clearTimeout(d.current),d.current=void 0,l.current[p]="popup-open"),n(s,x)},onClick:s=>{let m=s.target,p=te(m),x=ke(p);console.log(`list item click [${x}] hasPopup ${p.ariaHasPopup}`),p.ariaHasPopup==="true"?p.ariaExpanded!=="true"&&f(x):t(O(p.id))}}),[v,t,n,f,E,g]);return{closeMenu:C,handleRender:R,listItemProps:L,openMenu:f,openMenus:i.current}};import{useEffect as $t}from"react";var Ae=({containerClassName:e,isOpen:t,onClose:n})=>{$t(()=>{let o;return t&&(o=r=>{r.target.closest(`.${e}`)===null&&(n==null||n("root"))},document.body.addEventListener("click",o,!0)),()=>{o&&document.body.removeEventListener("click",o,!0)}},[e,t,n])};import{Fragment as Bt,jsx as $e}from"react/jsx-runtime";import{createElement as Ot}from"react";var Nt=()=>{},oe=({activatedByKeyboard:e,children:t,className:n,id:o,onClose:r=()=>{},position:c={x:0,y:0},style:i,...u})=>{let a=Kt(o),d=Se(Nt),[l,h]=we(t),f=Se(e),C=ne(()=>{f.current=!1},[]),v=ne(M=>{let{action:b,options:P}=h[M];d.current("root"),r(b,P)},[h,r]),{closeMenu:g,listItemProps:E,openMenu:R,openMenus:L,handleRender:s}=De({id:a,onActivate:v,onMouseEnterItem:C,position:c});d.current=g,console.log({openMenus:L});let m=ne(()=>{g(),r()},[g,r]);Ae({containerClassName:"vuuMenuList",onClose:m,isOpen:L.length>0});let p=M=>{let b=O(M),P=U(b);f.current=!0,R(P,b)},x=()=>{f.current=!0,g()},w=()=>{},k=L.length-1,y=M=>{if(M>=k)return-1;{let{id:b}=L[M+1],P=b.lastIndexOf(".");return parseInt(P===-1?b:b.slice(-P),10)}};return $e(Bt,{children:L.map(({id:M,left:b,top:P},I)=>{let D=y(I);return $e(G,{x:b,y:P,onRender:s,children:Ot(He,{...u,activatedByKeyboard:f.current,childMenuShowing:D,className:n,id:a,menuId:M,isRoot:I===0,key:I,listItemProps:E,onActivate:v,onHighlightMenuItem:w,onCloseMenu:x,onOpenMenu:p,style:i},l[M])},I)})})};oe.displayName="ContextMenu";import{createContext as Ft,useCallback as Gt,useMemo as Wt}from"react";import{jsx as re}from"react/jsx-runtime";var z=Ft(null),Ke=e=>e!==void 0&&"children"in e,qt=({children:e,context:t,menuActionHandler:n,menuBuilder:o})=>{let r=Wt(()=>t!=null&&t.menuBuilders&&o?t.menuBuilders.concat(o):o?[o]:(t==null?void 0:t.menuBuilders)||[],[t,o]),c=Gt((i,u)=>{var a;if(n!=null&&n(i,u)||(a=t==null?void 0:t.menuActionHandler)!=null&&a.call(t,i,u))return!0},[t,n]);return re(z.Provider,{value:{menuActionHandler:c,menuBuilders:r},children:e})},yo=({children:e,label:t,menuActionHandler:n,menuBuilder:o})=>re(z.Consumer,{children:r=>re(qt,{context:r,label:t,menuActionHandler:n,menuBuilder:o,children:e})});import{useCallback as qe,useContext as Zt}from"react";import Vt from"classnames";import Oe,{createElement as Be,useEffect as Ut,useRef as Ne}from"react";import B from"react-dom";var S=!1,H=[];function Q(e){if(e.key==="Esc"){if(H.length)Ge();else if(S){let t=document.body.querySelector(".vuuDialog");t&&B.unmountComponentAtNode(t)}}}function Fe(e){if(H.length){let t=document.body.querySelectorAll(".vuuPopup");for(let n=0;n<t.length;n++)if(t[n].contains(e.target))return;Ge()}}function Ge(){if(H.length){let e=document.body.querySelectorAll(".vuuPopup");for(let t=0;t<e.length;t++)B.unmountComponentAtNode(e[t]);We("*")}}function zt(){S===!1&&(S=!0,window.addEventListener("keydown",Q,!0))}function Jt(){S&&(S=!1,window.removeEventListener("keydown",Q,!0))}function Qt(e){H.indexOf(e)===-1&&(H.push(e),S===!1&&(window.addEventListener("keydown",Q,!0),window.addEventListener("click",Fe,!0)))}function We(e){if(H.length){if(e==="*")H.length=0;else{let t=H.indexOf(e);t!==-1&&H.splice(t,1)}H.length===0&&S===!1&&(window.removeEventListener("keydown",Q,!0),window.removeEventListener("click",Fe,!0))}}var Xt=({children:e,position:t,style:n})=>{let o=Vt("hwPopup","hwPopupContainer",t);return Be("div",{className:o,style:n},e)},Yt=1,T=class{static showPopup({name:t="anon",group:n="all",position:o="",left:r=0,right:c="auto",top:i=0,width:u="auto",component:a}){if(!a)throw Error("PopupService showPopup, no component supplied");Qt(t);let d=document.body.querySelector(".vuuPopup."+n);d===null&&(d=document.createElement("div"),d.className="vuuPopup "+n,document.body.appendChild(d));let l={width:u};F(Be(Xt,{key:Yt++,position:o,style:l},a),d,r,i,()=>{T.keepWithinThePage(d,c)})}static hidePopup(t="anon",n="all"){if(H.indexOf(t)!==-1){We(t);let o=document.body.querySelector(`.vuuPopup.${n}`);o&&B.unmountComponentAtNode(o)}}static keepWithinThePage(t,n="auto"){let o=t.querySelector(".vuuPopupContainer > *");if(o){let{top:r,left:c,width:i,height:u,right:a}=o.getBoundingClientRect(),d=window.innerWidth,h=window.innerHeight-(r+u);h<0&&(o.style.top=Math.round(r)+h+"px");let f=d-(c+i);if(f<0&&(o.style.left=Math.round(c)+f+"px"),typeof n=="number"&&n!==a){let C=n-a;o.style.left=c+C+"px"}}}},J=class{static showDialog(t){let n=".vuuDialog",o=t.props.onClose;zt(),B.render(Oe.cloneElement(t,{container:n,onClose:()=>{J.closeDialog(),o&&o()}}),document.body.querySelector(n))}static closeDialog(){Jt();let t=document.body.querySelector(".vuuDialog");t&&B.unmountComponentAtNode(t)}},So=e=>{let t=Ne(),n=Ne(null),o=(r,c)=>{let{name:i,group:u,depth:a,width:d}=r,l,h;if(t.current&&(window.clearTimeout(t.current),t.current=void 0),r.close===!0)T.hidePopup(i,u);else{let{position:f,children:C}=r,{left:v,top:g,width:E,bottom:R}=c;f==="below"?(l=v,h=R):f==="above"&&(l=v,h=g),t.current=window.setTimeout(()=>{T.showPopup({name:i,group:u,depth:a,position:f,left:l,top:h,width:d||E,component:C})},10)}};return Ut(()=>{if(n.current){let r=n.current.parentElement,c=r==null?void 0:r.getBoundingClientRect();c&&o(e,c)}return()=>{T.hidePopup(e.name,e.group)}},[e]),Oe.createElement("div",{className:"popup-proxy",ref:n})};import{jsx as se}from"react/jsx-runtime";var Jo=()=>{let e=Zt(z),t=qe((o,r,c)=>{let i=[];for(let u of o)i=i.concat(u(r,c));return i},[]);return qe((o,r,c)=>{var a;o.stopPropagation(),o.preventDefault();let i=(a=e==null?void 0:e.menuBuilders)!=null?a:[],u=t(i,r,c);console.log({menuItemDescriptors:u}),u.length&&(e!=null&&e.menuActionHandler)&&(console.log(`showContextMenu ${r}`,{options:c}),_t(o,u,e.menuActionHandler))},[t,e])},_t=(e,t,n)=>{let{clientX:o,clientY:r}=e,u=se(oe,{onClose:(a,d)=>{a&&(n(a,d),T.hidePopup())},position:{x:o,y:r},children:(a=>{let d=(l,h)=>Ke(l)?se(V,{label:l.label,children:l.children.map(d)},h):se(ee,{action:l.action,"data-icon":l.icon,options:l.options,children:l.label},h);return a.map(d)})(t)});T.showPopup({left:0,top:0,component:u})};export{oe as ContextMenu,z as ContextMenuContext,yo as ContextMenuProvider,In as Dialog,J as DialogService,ee as MenuItem,V as MenuItemGroup,So as Popup,T as PopupService,G as Portal,Ee as Separator,pe as createContainer,un as installTheme,Ke as isGroupMenuItemDescriptor,F as renderPortal,Jo as useContextMenu};
|
|
1
|
+
// src/dialog/Dialog.tsx
|
|
2
|
+
import { Scrim, Toolbar, ToolbarButton } from "@heswell/salt-lab";
|
|
3
|
+
import { Text } from "@salt-ds/core";
|
|
4
|
+
import cx from "classnames";
|
|
5
|
+
import { useCallback, useRef, useState } from "react";
|
|
6
|
+
|
|
7
|
+
// src/portal/Portal.tsx
|
|
8
|
+
import { useLayoutEffect, useMemo } from "react";
|
|
9
|
+
import * as ReactDOM2 from "react-dom";
|
|
10
|
+
|
|
11
|
+
// src/portal/render-portal.tsx
|
|
12
|
+
import * as ReactDOM from "react-dom";
|
|
13
|
+
import { SaltProvider } from "@salt-ds/core";
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
|
+
var containerId = 1;
|
|
16
|
+
var getPortalContainer = (x = 0, y = 0, win = window) => {
|
|
17
|
+
const el = win.document.createElement("div");
|
|
18
|
+
el.className = "vuuPopup " + containerId++;
|
|
19
|
+
el.style.cssText = `left:${x}px; top:${y}px;`;
|
|
20
|
+
win.document.body.appendChild(el);
|
|
21
|
+
return el;
|
|
22
|
+
};
|
|
23
|
+
var createDOMContainer = (x, y) => getPortalContainer(x, y);
|
|
24
|
+
var renderPortal = (component, container, x, y, onRender) => {
|
|
25
|
+
container.style.cssText = `left:${x}px; top:${y}px;position: absolute;`;
|
|
26
|
+
ReactDOM.render(
|
|
27
|
+
/* @__PURE__ */ jsx(SaltProvider, { applyClassesTo: "child", children: component }),
|
|
28
|
+
container,
|
|
29
|
+
onRender
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
var createContainer = createDOMContainer;
|
|
33
|
+
|
|
34
|
+
// src/portal/Portal.tsx
|
|
35
|
+
var Portal = function Portal2({
|
|
36
|
+
children,
|
|
37
|
+
x = 0,
|
|
38
|
+
y = 0,
|
|
39
|
+
onRender
|
|
40
|
+
}) {
|
|
41
|
+
const renderContainer = useMemo(() => {
|
|
42
|
+
return createContainer();
|
|
43
|
+
}, []);
|
|
44
|
+
useLayoutEffect(() => {
|
|
45
|
+
renderPortal(children, renderContainer, x, y, onRender);
|
|
46
|
+
}, [children, onRender, renderContainer, x, y]);
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
return () => {
|
|
49
|
+
var _a;
|
|
50
|
+
if (renderContainer) {
|
|
51
|
+
ReactDOM2.unmountComponentAtNode(renderContainer);
|
|
52
|
+
if (renderContainer.classList.contains("vuuPopup")) {
|
|
53
|
+
(_a = renderContainer.parentElement) == null ? void 0 : _a.removeChild(renderContainer);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}, [renderContainer]);
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/portal/portal-utils.ts
|
|
62
|
+
var installTheme = (themeId) => {
|
|
63
|
+
const installedThemes = getComputedStyle(document.body).getPropertyValue(
|
|
64
|
+
"--installed-themes"
|
|
65
|
+
);
|
|
66
|
+
document.body.style.setProperty(
|
|
67
|
+
"--installed-themes",
|
|
68
|
+
`${installedThemes} ${themeId}`
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/dialog/Dialog.tsx
|
|
73
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
74
|
+
var classBase = "vuuDialog";
|
|
75
|
+
var Dialog = ({
|
|
76
|
+
children,
|
|
77
|
+
className,
|
|
78
|
+
isOpen = false,
|
|
79
|
+
onClose,
|
|
80
|
+
title,
|
|
81
|
+
...props
|
|
82
|
+
}) => {
|
|
83
|
+
const root = useRef(null);
|
|
84
|
+
const [posX] = useState(0);
|
|
85
|
+
const [posY] = useState(0);
|
|
86
|
+
const close = useCallback(() => {
|
|
87
|
+
onClose == null ? void 0 : onClose();
|
|
88
|
+
}, [onClose]);
|
|
89
|
+
const handleRender = useCallback(() => {
|
|
90
|
+
}, []);
|
|
91
|
+
if (!isOpen) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return /* @__PURE__ */ jsx2(Portal, { onRender: handleRender, x: posX, y: posY, children: /* @__PURE__ */ jsx2(Scrim, { className: `${classBase}-scrim`, open: isOpen, children: /* @__PURE__ */ jsxs("div", { ...props, className: cx(classBase, className), ref: root, children: [
|
|
95
|
+
/* @__PURE__ */ jsxs(Toolbar, { className: `${classBase}-header`, children: [
|
|
96
|
+
/* @__PURE__ */ jsx2(Text, { children: title }),
|
|
97
|
+
/* @__PURE__ */ jsx2(
|
|
98
|
+
ToolbarButton,
|
|
99
|
+
{
|
|
100
|
+
onClick: close,
|
|
101
|
+
"data-align-end": true,
|
|
102
|
+
"data-icon": "close"
|
|
103
|
+
},
|
|
104
|
+
"close"
|
|
105
|
+
)
|
|
106
|
+
] }),
|
|
107
|
+
children
|
|
108
|
+
] }) }) });
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/menu/ContextMenu.tsx
|
|
112
|
+
import { useIdMemo as useId2 } from "@salt-ds/core";
|
|
113
|
+
import { useCallback as useCallback5, useRef as useRef5 } from "react";
|
|
114
|
+
|
|
115
|
+
// src/menu/MenuList.tsx
|
|
116
|
+
import React2, {
|
|
117
|
+
useLayoutEffect as useLayoutEffect2,
|
|
118
|
+
useMemo as useMemo4,
|
|
119
|
+
useRef as useRef3
|
|
120
|
+
} from "react";
|
|
121
|
+
import cx2 from "classnames";
|
|
122
|
+
import { useIdMemo as useId } from "@salt-ds/core";
|
|
123
|
+
|
|
124
|
+
// src/menu/use-keyboard-navigation.ts
|
|
125
|
+
import {
|
|
126
|
+
useCallback as useCallback2,
|
|
127
|
+
useMemo as useMemo2,
|
|
128
|
+
useRef as useRef2,
|
|
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-idx='${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
|
+
var useKeyboardNavigation = ({
|
|
190
|
+
autoHighlightFirstItem = false,
|
|
191
|
+
count,
|
|
192
|
+
highlightedIndex: highlightedIndexProp,
|
|
193
|
+
onActivate,
|
|
194
|
+
onHighlight,
|
|
195
|
+
// onKeyDown,
|
|
196
|
+
onCloseMenu,
|
|
197
|
+
onOpenMenu
|
|
198
|
+
}) => {
|
|
199
|
+
const highlightedIndexRef = useRef2(
|
|
200
|
+
(highlightedIndexProp != null ? highlightedIndexProp : autoHighlightFirstItem) ? 0 : -1
|
|
201
|
+
);
|
|
202
|
+
const [, forceRender] = useState2(null);
|
|
203
|
+
const controlledHighlighting = highlightedIndexProp !== void 0;
|
|
204
|
+
const setHighlightedIdx = useCallback2(
|
|
205
|
+
(idx) => {
|
|
206
|
+
highlightedIndexRef.current = idx;
|
|
207
|
+
onHighlight == null ? void 0 : onHighlight(idx);
|
|
208
|
+
forceRender({});
|
|
209
|
+
},
|
|
210
|
+
[onHighlight]
|
|
211
|
+
);
|
|
212
|
+
const setHighlightedIndex = useCallback2(
|
|
213
|
+
(idx) => {
|
|
214
|
+
if (idx !== highlightedIndexRef.current) {
|
|
215
|
+
if (!controlledHighlighting) {
|
|
216
|
+
setHighlightedIdx(idx);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
[controlledHighlighting, setHighlightedIdx]
|
|
221
|
+
);
|
|
222
|
+
const keyBoardNavigation = useRef2(true);
|
|
223
|
+
const ignoreFocus = useRef2(false);
|
|
224
|
+
const setIgnoreFocus = (value) => ignoreFocus.current = value;
|
|
225
|
+
const highlightedIndex = controlledHighlighting ? highlightedIndexProp : highlightedIndexRef.current;
|
|
226
|
+
const navigateChildldItems = useCallback2(
|
|
227
|
+
(e) => {
|
|
228
|
+
const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);
|
|
229
|
+
if (nextIdx !== highlightedIndexRef.current) {
|
|
230
|
+
setHighlightedIndex(nextIdx);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
[count, setHighlightedIndex]
|
|
234
|
+
);
|
|
235
|
+
const handleKeyDown = useCallback2(
|
|
236
|
+
(e) => {
|
|
237
|
+
if (isNavigationKey(e)) {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
e.stopPropagation();
|
|
240
|
+
keyBoardNavigation.current = true;
|
|
241
|
+
navigateChildldItems(e);
|
|
242
|
+
} else if ((e.key === "ArrowRight" || e.key === "Enter") && hasPopup(e.target, highlightedIndex)) {
|
|
243
|
+
onOpenMenu(highlightedIndex);
|
|
244
|
+
} else if (e.key === "ArrowLeft" && !isRoot(e.target)) {
|
|
245
|
+
onCloseMenu(highlightedIndex);
|
|
246
|
+
} else if (e.key === "Enter") {
|
|
247
|
+
onActivate && onActivate(highlightedIndex);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
[
|
|
251
|
+
highlightedIndex,
|
|
252
|
+
navigateChildldItems,
|
|
253
|
+
onActivate,
|
|
254
|
+
onCloseMenu,
|
|
255
|
+
onOpenMenu
|
|
256
|
+
]
|
|
257
|
+
);
|
|
258
|
+
const listProps = useMemo2(
|
|
259
|
+
() => ({
|
|
260
|
+
onFocus: () => {
|
|
261
|
+
if (highlightedIndex === -1) {
|
|
262
|
+
setHighlightedIdx(0);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
onKeyDown: handleKeyDown,
|
|
266
|
+
onMouseDownCapture: () => {
|
|
267
|
+
keyBoardNavigation.current = false;
|
|
268
|
+
setIgnoreFocus(true);
|
|
269
|
+
},
|
|
270
|
+
// onMouseEnter would seem less expensive but it misses some cases
|
|
271
|
+
onMouseMove: () => {
|
|
272
|
+
if (keyBoardNavigation.current) {
|
|
273
|
+
keyBoardNavigation.current = false;
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
onMouseLeave: () => {
|
|
277
|
+
keyBoardNavigation.current = true;
|
|
278
|
+
setIgnoreFocus(false);
|
|
279
|
+
setHighlightedIndex(-1);
|
|
280
|
+
}
|
|
281
|
+
}),
|
|
282
|
+
[
|
|
283
|
+
highlightedIndex,
|
|
284
|
+
setHighlightedIndex,
|
|
285
|
+
navigateChildldItems,
|
|
286
|
+
onActivate,
|
|
287
|
+
onCloseMenu,
|
|
288
|
+
onOpenMenu,
|
|
289
|
+
setHighlightedIdx
|
|
290
|
+
]
|
|
291
|
+
);
|
|
292
|
+
return {
|
|
293
|
+
focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,
|
|
294
|
+
controlledHighlighting,
|
|
295
|
+
highlightedIndex,
|
|
296
|
+
setHighlightedIndex,
|
|
297
|
+
// keyBoardNavigation,
|
|
298
|
+
listProps,
|
|
299
|
+
setIgnoreFocus
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
function nextItemIdx(count, key, idx) {
|
|
303
|
+
if (key === "ArrowUp") {
|
|
304
|
+
if (idx > 0) {
|
|
305
|
+
return idx - 1;
|
|
306
|
+
} else {
|
|
307
|
+
return idx;
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
if (idx === null) {
|
|
311
|
+
return 0;
|
|
312
|
+
} else if (idx === count - 1) {
|
|
313
|
+
return idx;
|
|
314
|
+
} else {
|
|
315
|
+
return idx + 1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/menu/use-items-with-ids.ts
|
|
321
|
+
import React, { useCallback as useCallback3, useMemo as useMemo3 } from "react";
|
|
322
|
+
var isMenuItemGroup = (child) => child.type === MenuItemGroup || !!child.props["data-group"];
|
|
323
|
+
var useItemsWithIds = (childrenProp) => {
|
|
324
|
+
const normalizeChildren = useCallback3(() => {
|
|
325
|
+
const collectChildren = (children, path = "root", menus2 = {}, actions2 = {}) => {
|
|
326
|
+
const list = menus2[path] = [];
|
|
327
|
+
let idx = 0;
|
|
328
|
+
let hasSeparator = false;
|
|
329
|
+
React.Children.forEach(children, (child) => {
|
|
330
|
+
if (child.type === Separator) {
|
|
331
|
+
hasSeparator = true;
|
|
332
|
+
} else {
|
|
333
|
+
const group = isMenuItemGroup(child);
|
|
334
|
+
const childPath = path === "root" ? `${idx}` : `${path}.${idx}`;
|
|
335
|
+
const {
|
|
336
|
+
props: { action, options }
|
|
337
|
+
} = child;
|
|
338
|
+
const { childWithId, grandChildren } = assignId(
|
|
339
|
+
child,
|
|
340
|
+
childPath,
|
|
341
|
+
group,
|
|
342
|
+
hasSeparator
|
|
343
|
+
);
|
|
344
|
+
list.push(childWithId);
|
|
345
|
+
if (grandChildren) {
|
|
346
|
+
collectChildren(grandChildren, childPath, menus2, actions2);
|
|
347
|
+
} else {
|
|
348
|
+
actions2[childPath] = { action, options };
|
|
349
|
+
}
|
|
350
|
+
idx += 1;
|
|
351
|
+
hasSeparator = false;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
return [menus2, actions2];
|
|
355
|
+
};
|
|
356
|
+
const assignId = (child, path, group, hasSeparator = false) => {
|
|
357
|
+
const {
|
|
358
|
+
props: { children }
|
|
359
|
+
} = child;
|
|
360
|
+
return {
|
|
361
|
+
childWithId: React.cloneElement(child, {
|
|
362
|
+
hasSeparator,
|
|
363
|
+
id: `${path}`,
|
|
364
|
+
key: path,
|
|
365
|
+
children: group ? void 0 : children
|
|
366
|
+
}),
|
|
367
|
+
grandChildren: group ? children : void 0
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
return collectChildren(childrenProp);
|
|
371
|
+
}, [childrenProp]);
|
|
372
|
+
const [menus, actions] = useMemo3(
|
|
373
|
+
() => normalizeChildren(),
|
|
374
|
+
[normalizeChildren]
|
|
375
|
+
);
|
|
376
|
+
return [menus, actions];
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// src/menu/MenuList.tsx
|
|
380
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
381
|
+
var classBase2 = "vuuMenuList";
|
|
382
|
+
var Separator = () => /* @__PURE__ */ jsx3("li", { className: "vuuMenuItem-divider" });
|
|
383
|
+
var MenuItemGroup = () => null;
|
|
384
|
+
var MenuItem = ({ children, idx, ...props }) => {
|
|
385
|
+
return /* @__PURE__ */ jsx3("div", { ...props, children });
|
|
386
|
+
};
|
|
387
|
+
var hasIcon = (child) => child.props["data-icon"];
|
|
388
|
+
var MenuList = ({
|
|
389
|
+
activatedByKeyboard,
|
|
390
|
+
childMenuShowing = -1,
|
|
391
|
+
children,
|
|
392
|
+
className,
|
|
393
|
+
highlightedIdx: highlightedIdxProp,
|
|
394
|
+
id: idProp,
|
|
395
|
+
isRoot: isRoot2,
|
|
396
|
+
listItemProps,
|
|
397
|
+
menuId,
|
|
398
|
+
onHighlightMenuItem,
|
|
399
|
+
onActivate,
|
|
400
|
+
onCloseMenu,
|
|
401
|
+
onOpenMenu,
|
|
402
|
+
...props
|
|
403
|
+
}) => {
|
|
404
|
+
const id = useId(idProp);
|
|
405
|
+
const root = useRef3(null);
|
|
406
|
+
const mapIdxToId = useMemo4(() => /* @__PURE__ */ new Map(), []);
|
|
407
|
+
const handleOpenMenu = (idx) => {
|
|
408
|
+
var _a;
|
|
409
|
+
const el = (_a = root.current) == null ? void 0 : _a.querySelector(`:scope > [data-idx='${idx}']`);
|
|
410
|
+
(el == null ? void 0 : el.id) && (onOpenMenu == null ? void 0 : onOpenMenu(el.id));
|
|
411
|
+
};
|
|
412
|
+
const handleActivate = (idx) => {
|
|
413
|
+
var _a;
|
|
414
|
+
const el = (_a = root.current) == null ? void 0 : _a.querySelector(`:scope > [data-idx='${idx}']`);
|
|
415
|
+
(el == null ? void 0 : el.id) && (onActivate == null ? void 0 : onActivate(el.id));
|
|
416
|
+
};
|
|
417
|
+
const { focusVisible, highlightedIndex, listProps } = useKeyboardNavigation({
|
|
418
|
+
count: React2.Children.count(children),
|
|
419
|
+
highlightedIndex: highlightedIdxProp,
|
|
420
|
+
onActivate: handleActivate,
|
|
421
|
+
onHighlight: onHighlightMenuItem,
|
|
422
|
+
onOpenMenu: handleOpenMenu,
|
|
423
|
+
onCloseMenu
|
|
424
|
+
});
|
|
425
|
+
const appliedFocusVisible = childMenuShowing == -1 ? focusVisible : -1;
|
|
426
|
+
useLayoutEffect2(() => {
|
|
427
|
+
var _a;
|
|
428
|
+
if (childMenuShowing === -1 && activatedByKeyboard) {
|
|
429
|
+
(_a = root.current) == null ? void 0 : _a.focus();
|
|
430
|
+
}
|
|
431
|
+
}, [activatedByKeyboard, childMenuShowing]);
|
|
432
|
+
const getActiveDescendant = () => highlightedIndex === void 0 || highlightedIndex === -1 ? void 0 : mapIdxToId.get(highlightedIndex);
|
|
433
|
+
return /* @__PURE__ */ jsx3(
|
|
434
|
+
"div",
|
|
435
|
+
{
|
|
436
|
+
...props,
|
|
437
|
+
...listProps,
|
|
438
|
+
"aria-activedescendant": getActiveDescendant(),
|
|
439
|
+
className: cx2(classBase2, className, {
|
|
440
|
+
[`${classBase2}-childMenuShowing`]: childMenuShowing !== -1
|
|
441
|
+
}),
|
|
442
|
+
"data-root": isRoot2 || void 0,
|
|
443
|
+
id: `${id}-${menuId}`,
|
|
444
|
+
ref: root,
|
|
445
|
+
role: "menu",
|
|
446
|
+
tabIndex: 0,
|
|
447
|
+
children: renderContent()
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
function renderContent() {
|
|
451
|
+
const propsCommonToAllListItems = {
|
|
452
|
+
...listItemProps,
|
|
453
|
+
role: "menuitem"
|
|
454
|
+
};
|
|
455
|
+
const maybeIcon = (childElement, withIcon, iconName) => withIcon ? [
|
|
456
|
+
/* @__PURE__ */ jsx3(
|
|
457
|
+
"span",
|
|
458
|
+
{
|
|
459
|
+
className: "vuuIconContainer",
|
|
460
|
+
"data-icon": iconName
|
|
461
|
+
},
|
|
462
|
+
"icon"
|
|
463
|
+
)
|
|
464
|
+
].concat(childElement) : childElement;
|
|
465
|
+
function addClonedChild(list, child, idx, withIcon) {
|
|
466
|
+
var _a;
|
|
467
|
+
const {
|
|
468
|
+
children: children2,
|
|
469
|
+
className: className2,
|
|
470
|
+
"data-icon": iconName,
|
|
471
|
+
id: itemId,
|
|
472
|
+
hasSeparator,
|
|
473
|
+
label,
|
|
474
|
+
...props2
|
|
475
|
+
} = child.props;
|
|
476
|
+
const hasSubMenu = isMenuItemGroup(child);
|
|
477
|
+
const subMenuShowing = hasSubMenu && childMenuShowing === idx;
|
|
478
|
+
const ariaControls = subMenuShowing ? `${id}-${itemId}` : void 0;
|
|
479
|
+
list.push(
|
|
480
|
+
/* @__PURE__ */ jsx3(
|
|
481
|
+
MenuItem,
|
|
482
|
+
{
|
|
483
|
+
...props2,
|
|
484
|
+
...propsCommonToAllListItems,
|
|
485
|
+
...getMenuItemProps(
|
|
486
|
+
`${id}-${menuId}`,
|
|
487
|
+
itemId,
|
|
488
|
+
idx,
|
|
489
|
+
(_a = child.key) != null ? _a : itemId,
|
|
490
|
+
highlightedIndex,
|
|
491
|
+
appliedFocusVisible,
|
|
492
|
+
className2,
|
|
493
|
+
hasSeparator
|
|
494
|
+
),
|
|
495
|
+
"aria-controls": ariaControls,
|
|
496
|
+
"aria-haspopup": hasSubMenu || void 0,
|
|
497
|
+
"aria-expanded": subMenuShowing || void 0,
|
|
498
|
+
children: hasSubMenu ? maybeIcon(label, withIcon, iconName) : maybeIcon(children2, withIcon, iconName)
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
const listItems = [];
|
|
504
|
+
if (children.length > 0) {
|
|
505
|
+
const withIcon = children.some(hasIcon);
|
|
506
|
+
children.forEach((child, idx) => {
|
|
507
|
+
addClonedChild(listItems, child, idx, withIcon);
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return listItems;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var getMenuItemProps = (baseId, itemId, idx, key, highlightedIdx, focusVisible, className, hasSeparator) => ({
|
|
514
|
+
id: `${baseId}-${itemId}`,
|
|
515
|
+
key: key != null ? key : idx,
|
|
516
|
+
"data-idx": idx,
|
|
517
|
+
"data-highlighted": idx === highlightedIdx || void 0,
|
|
518
|
+
className: cx2("vuuMenuItem", className, {
|
|
519
|
+
"vuuMenuItem-separator": hasSeparator,
|
|
520
|
+
focusVisible: focusVisible === idx
|
|
521
|
+
})
|
|
522
|
+
});
|
|
523
|
+
MenuList.displayName = "MenuList";
|
|
524
|
+
var MenuList_default = MenuList;
|
|
525
|
+
|
|
526
|
+
// src/menu/use-cascade.ts
|
|
527
|
+
import {
|
|
528
|
+
useCallback as useCallback4,
|
|
529
|
+
useMemo as useMemo5,
|
|
530
|
+
useRef as useRef4,
|
|
531
|
+
useState as useState3
|
|
532
|
+
} from "react";
|
|
533
|
+
|
|
534
|
+
// src/menu/list-dom-utils.ts
|
|
535
|
+
function listItemIndex(listItemEl) {
|
|
536
|
+
if (listItemEl) {
|
|
537
|
+
const idx = listItemEl.dataset.idx;
|
|
538
|
+
if (idx) {
|
|
539
|
+
return parseInt(idx, 10);
|
|
540
|
+
} else if (listItemEl.ariaPosInSet) {
|
|
541
|
+
return parseInt(listItemEl.ariaPosInSet, 10) - 1;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
var closestListItem = (el) => el == null ? void 0 : el.closest("[data-idx],[aria-posinset]");
|
|
546
|
+
|
|
547
|
+
// src/menu/use-cascade.ts
|
|
548
|
+
var nudge = (menus, distance, pos) => {
|
|
549
|
+
return menus.map(
|
|
550
|
+
(m, i) => i === menus.length - 1 ? {
|
|
551
|
+
...m,
|
|
552
|
+
[pos]: m[pos] - distance
|
|
553
|
+
} : m
|
|
554
|
+
);
|
|
555
|
+
};
|
|
556
|
+
var nudgeLeft = (menus, distance) => nudge(menus, distance, "left");
|
|
557
|
+
var nudgeUp = (menus, distance) => nudge(menus, distance, "top");
|
|
558
|
+
var flipSides = (id, menus) => {
|
|
559
|
+
const [parentMenu, menu] = menus.slice(-2);
|
|
560
|
+
const el = document.getElementById(`${id}-${menu.id}`);
|
|
561
|
+
if (el === null) {
|
|
562
|
+
throw Error(`useCascade.flipSides element with id ${menu.id} not found`);
|
|
563
|
+
}
|
|
564
|
+
const { width } = el.getBoundingClientRect();
|
|
565
|
+
return menus.map(
|
|
566
|
+
(m) => m === menu ? {
|
|
567
|
+
...m,
|
|
568
|
+
left: parentMenu.left - (width - 2)
|
|
569
|
+
} : m
|
|
570
|
+
);
|
|
571
|
+
};
|
|
572
|
+
var getPosition = (el, openMenus) => {
|
|
573
|
+
const [{ left, top: menuTop }] = openMenus.slice(-1);
|
|
574
|
+
const { offsetWidth: width, offsetTop: top } = el;
|
|
575
|
+
return { left: left + width, top: top + menuTop };
|
|
576
|
+
};
|
|
577
|
+
var getItemId = (id) => {
|
|
578
|
+
const pos = id.lastIndexOf("-");
|
|
579
|
+
return pos === -1 ? id : id.slice(pos + 1);
|
|
580
|
+
};
|
|
581
|
+
var getMenuId = (id) => {
|
|
582
|
+
const itemId = getItemId(id);
|
|
583
|
+
const pos = itemId.lastIndexOf(".");
|
|
584
|
+
return pos > -1 ? itemId.slice(0, pos) : "root";
|
|
585
|
+
};
|
|
586
|
+
var getMenuDepth = (id) => {
|
|
587
|
+
let count = 0, pos = id.indexOf(".", 0);
|
|
588
|
+
while (pos !== -1) {
|
|
589
|
+
count += 1;
|
|
590
|
+
pos = id.indexOf(".", pos + 1);
|
|
591
|
+
}
|
|
592
|
+
return count;
|
|
593
|
+
};
|
|
594
|
+
var identifyItem = (el) => ({
|
|
595
|
+
menuId: getMenuId(el.id),
|
|
596
|
+
itemId: getItemId(el.id),
|
|
597
|
+
isGroup: el.ariaHasPopup === "true",
|
|
598
|
+
isOpen: el.ariaExpanded === "true",
|
|
599
|
+
level: getMenuDepth(el.id)
|
|
600
|
+
});
|
|
601
|
+
var useCascade = ({
|
|
602
|
+
id,
|
|
603
|
+
onActivate,
|
|
604
|
+
onMouseEnterItem,
|
|
605
|
+
position: { x: posX, y: posY }
|
|
606
|
+
}) => {
|
|
607
|
+
const [, forceRefresh] = useState3({});
|
|
608
|
+
const openMenus = useRef4([
|
|
609
|
+
{ id: "root", left: posX, top: posY }
|
|
610
|
+
]);
|
|
611
|
+
const setOpenMenus = useCallback4((menus) => {
|
|
612
|
+
openMenus.current = menus;
|
|
613
|
+
forceRefresh({});
|
|
614
|
+
}, []);
|
|
615
|
+
const menuOpenPendingTimeout = useRef4();
|
|
616
|
+
const menuClosePendingTimeout = useRef4();
|
|
617
|
+
const menuState = useRef4({ root: "no-popup" });
|
|
618
|
+
const prevLevel = useRef4(0);
|
|
619
|
+
const openMenu = useCallback4(
|
|
620
|
+
(menuId = "root", itemId = null, listItemEl = null) => {
|
|
621
|
+
if (menuId === "root" && itemId === null) {
|
|
622
|
+
setOpenMenus([{ id: "root", left: posX, top: posY }]);
|
|
623
|
+
} else {
|
|
624
|
+
menuState.current[menuId] = "popup-open";
|
|
625
|
+
const doc = listItemEl ? listItemEl.ownerDocument : document;
|
|
626
|
+
const el = doc.getElementById(`${id}-${menuId}-${itemId}`);
|
|
627
|
+
const { left, top } = getPosition(el, openMenus.current);
|
|
628
|
+
setOpenMenus(openMenus.current.concat({ id: itemId, left, top }));
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
[id, posX, posY, setOpenMenus]
|
|
632
|
+
);
|
|
633
|
+
const closeMenu = useCallback4(
|
|
634
|
+
(menuId) => {
|
|
635
|
+
if (menuId === "root") {
|
|
636
|
+
setOpenMenus([]);
|
|
637
|
+
} else {
|
|
638
|
+
setOpenMenus(openMenus.current.slice(0, -1));
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
[setOpenMenus]
|
|
642
|
+
);
|
|
643
|
+
const closeMenus = useCallback4(
|
|
644
|
+
(menuId, itemId) => {
|
|
645
|
+
const menus = openMenus.current.slice();
|
|
646
|
+
let { id: lastMenuId } = menus[menus.length - 1];
|
|
647
|
+
while (menus.length > 1 && !itemId.startsWith(lastMenuId)) {
|
|
648
|
+
const parentMenuId = getMenuId(lastMenuId);
|
|
649
|
+
menus.pop();
|
|
650
|
+
menuState.current[lastMenuId] = "no-popup";
|
|
651
|
+
menuState.current[parentMenuId] = "no-popup";
|
|
652
|
+
({ id: lastMenuId } = menus[menus.length - 1]);
|
|
653
|
+
}
|
|
654
|
+
if (menus.length < openMenus.current.length) {
|
|
655
|
+
setOpenMenus(menus);
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
[setOpenMenus]
|
|
659
|
+
);
|
|
660
|
+
const scheduleOpen = useCallback4(
|
|
661
|
+
(menuId, itemId, listItemEl) => {
|
|
662
|
+
if (menuOpenPendingTimeout.current) {
|
|
663
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
664
|
+
}
|
|
665
|
+
menuOpenPendingTimeout.current = window.setTimeout(() => {
|
|
666
|
+
console.log(`scheduleOpen timed out opening ${itemId}`);
|
|
667
|
+
closeMenus(menuId, itemId);
|
|
668
|
+
menuState.current[menuId] = "popup-open";
|
|
669
|
+
menuState.current[itemId] = "no-popup";
|
|
670
|
+
openMenu(menuId, itemId, listItemEl);
|
|
671
|
+
}, 400);
|
|
672
|
+
},
|
|
673
|
+
[closeMenus, openMenu]
|
|
674
|
+
);
|
|
675
|
+
const scheduleClose = useCallback4(
|
|
676
|
+
(openMenuId, menuId, itemId) => {
|
|
677
|
+
console.log(
|
|
678
|
+
`scheduleClose openMenuId ${openMenuId} menuId ${menuId} itemId ${itemId}`
|
|
679
|
+
);
|
|
680
|
+
menuState.current[openMenuId] = "pending-close";
|
|
681
|
+
menuClosePendingTimeout.current = window.setTimeout(() => {
|
|
682
|
+
closeMenus(menuId, itemId);
|
|
683
|
+
}, 400);
|
|
684
|
+
},
|
|
685
|
+
[closeMenus]
|
|
686
|
+
);
|
|
687
|
+
const handleRender = useCallback4(() => {
|
|
688
|
+
const { current: menus } = openMenus;
|
|
689
|
+
const [menu] = menus.slice(-1);
|
|
690
|
+
const el = document.getElementById(`${id}-${menu.id}`);
|
|
691
|
+
if (el) {
|
|
692
|
+
const { right, bottom } = el.getBoundingClientRect();
|
|
693
|
+
const { clientHeight, clientWidth } = document.body;
|
|
694
|
+
if (right > clientWidth) {
|
|
695
|
+
const newMenus = menus.length > 1 ? flipSides(id, menus) : nudgeLeft(menus, right - clientWidth);
|
|
696
|
+
setOpenMenus(newMenus);
|
|
697
|
+
} else if (bottom > clientHeight) {
|
|
698
|
+
const newMenus = nudgeUp(menus, bottom - clientHeight);
|
|
699
|
+
setOpenMenus(newMenus);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}, [id, setOpenMenus]);
|
|
703
|
+
const listItemProps = useMemo5(
|
|
704
|
+
() => ({
|
|
705
|
+
onMouseEnter: (evt) => {
|
|
706
|
+
const listItemEl = closestListItem(evt.target);
|
|
707
|
+
const { menuId, itemId, isGroup, isOpen, level } = identifyItem(listItemEl);
|
|
708
|
+
const sameLevel = prevLevel.current === level;
|
|
709
|
+
const {
|
|
710
|
+
current: { [menuId]: state }
|
|
711
|
+
} = menuState;
|
|
712
|
+
prevLevel.current = level;
|
|
713
|
+
if (state === "no-popup" && isGroup) {
|
|
714
|
+
menuState.current[menuId] = "popup-pending";
|
|
715
|
+
scheduleOpen(menuId, itemId, listItemEl);
|
|
716
|
+
} else if (state === "popup-pending" && !isGroup) {
|
|
717
|
+
menuState.current[menuId] = "no-popup";
|
|
718
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
719
|
+
menuOpenPendingTimeout.current = void 0;
|
|
720
|
+
} else if (state === "popup-pending" && isGroup) {
|
|
721
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
722
|
+
scheduleOpen(menuId, itemId, listItemEl);
|
|
723
|
+
} else if (state === "popup-open") {
|
|
724
|
+
const [{ id: parentMenuId }, { id: openMenuId }] = openMenus.current.slice(-2);
|
|
725
|
+
if (parentMenuId === menuId && menuState.current[openMenuId] !== "pending-close" && sameLevel) {
|
|
726
|
+
scheduleClose(openMenuId, menuId, itemId);
|
|
727
|
+
if (isGroup && !isOpen) {
|
|
728
|
+
scheduleOpen(menuId, itemId, listItemEl);
|
|
729
|
+
}
|
|
730
|
+
} else if (parentMenuId === menuId && isGroup && itemId !== openMenuId && menuState.current[openMenuId] === "pending-close") {
|
|
731
|
+
scheduleOpen(menuId, itemId, listItemEl);
|
|
732
|
+
} else if (isGroup) {
|
|
733
|
+
closeMenus(menuId, itemId);
|
|
734
|
+
scheduleOpen(menuId, itemId, listItemEl);
|
|
735
|
+
} else if (!(menuState.current[openMenuId] === "pending-close" && sameLevel)) {
|
|
736
|
+
closeMenus(menuId, itemId);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (state === "pending-close") {
|
|
740
|
+
if (menuOpenPendingTimeout.current) {
|
|
741
|
+
clearTimeout(menuOpenPendingTimeout.current);
|
|
742
|
+
menuOpenPendingTimeout.current = void 0;
|
|
743
|
+
}
|
|
744
|
+
clearTimeout(menuClosePendingTimeout.current);
|
|
745
|
+
menuClosePendingTimeout.current = void 0;
|
|
746
|
+
menuState.current[menuId] = "popup-open";
|
|
747
|
+
}
|
|
748
|
+
onMouseEnterItem(evt, itemId);
|
|
749
|
+
},
|
|
750
|
+
onClick: (evt) => {
|
|
751
|
+
const targetElement = evt.target;
|
|
752
|
+
const listItemEl = closestListItem(targetElement);
|
|
753
|
+
const idx = listItemIndex(listItemEl);
|
|
754
|
+
console.log(
|
|
755
|
+
`list item click [${idx}] hasPopup ${listItemEl.ariaHasPopup}`
|
|
756
|
+
);
|
|
757
|
+
if (listItemEl.ariaHasPopup === "true") {
|
|
758
|
+
if (listItemEl.ariaExpanded !== "true") {
|
|
759
|
+
openMenu(idx);
|
|
760
|
+
} else {
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
onActivate(getItemId(listItemEl.id));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}),
|
|
767
|
+
[
|
|
768
|
+
closeMenus,
|
|
769
|
+
onActivate,
|
|
770
|
+
onMouseEnterItem,
|
|
771
|
+
openMenu,
|
|
772
|
+
scheduleClose,
|
|
773
|
+
scheduleOpen
|
|
774
|
+
]
|
|
775
|
+
);
|
|
776
|
+
return {
|
|
777
|
+
closeMenu,
|
|
778
|
+
handleRender,
|
|
779
|
+
listItemProps,
|
|
780
|
+
openMenu,
|
|
781
|
+
openMenus: openMenus.current
|
|
782
|
+
};
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// src/menu/use-click-away.ts
|
|
786
|
+
import { useEffect } from "react";
|
|
787
|
+
var useClickAway = ({
|
|
788
|
+
containerClassName,
|
|
789
|
+
isOpen,
|
|
790
|
+
onClose
|
|
791
|
+
}) => {
|
|
792
|
+
useEffect(() => {
|
|
793
|
+
let clickHandler;
|
|
794
|
+
if (isOpen) {
|
|
795
|
+
clickHandler = (evt) => {
|
|
796
|
+
const target = evt.target;
|
|
797
|
+
const container = target.closest(`.${containerClassName}`);
|
|
798
|
+
if (container === null) {
|
|
799
|
+
onClose == null ? void 0 : onClose("root");
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
document.body.addEventListener("click", clickHandler, true);
|
|
803
|
+
}
|
|
804
|
+
return () => {
|
|
805
|
+
if (clickHandler) {
|
|
806
|
+
document.body.removeEventListener("click", clickHandler, true);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}, [containerClassName, isOpen, onClose]);
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// src/menu/ContextMenu.tsx
|
|
813
|
+
import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
|
|
814
|
+
import { createElement } from "react";
|
|
815
|
+
var noop = () => void 0;
|
|
816
|
+
var ContextMenu = ({
|
|
817
|
+
activatedByKeyboard,
|
|
818
|
+
children: childrenProp,
|
|
819
|
+
className,
|
|
820
|
+
id: idProp,
|
|
821
|
+
onClose = () => void 0,
|
|
822
|
+
position = { x: 0, y: 0 },
|
|
823
|
+
style,
|
|
824
|
+
...menuListProps
|
|
825
|
+
}) => {
|
|
826
|
+
const id = useId2(idProp);
|
|
827
|
+
const closeMenuRef = useRef5(noop);
|
|
828
|
+
const [menus, actions] = useItemsWithIds(childrenProp);
|
|
829
|
+
const navigatingWithKeyboard = useRef5(activatedByKeyboard);
|
|
830
|
+
const handleMouseEnterItem = useCallback5(() => {
|
|
831
|
+
navigatingWithKeyboard.current = false;
|
|
832
|
+
}, []);
|
|
833
|
+
const handleActivate = useCallback5(
|
|
834
|
+
(menuId) => {
|
|
835
|
+
const { action, options } = actions[menuId];
|
|
836
|
+
closeMenuRef.current("root");
|
|
837
|
+
onClose(action, options);
|
|
838
|
+
},
|
|
839
|
+
[actions, onClose]
|
|
840
|
+
);
|
|
841
|
+
const { closeMenu, listItemProps, openMenu, openMenus, handleRender } = useCascade({
|
|
842
|
+
id,
|
|
843
|
+
onActivate: handleActivate,
|
|
844
|
+
onMouseEnterItem: handleMouseEnterItem,
|
|
845
|
+
position
|
|
846
|
+
});
|
|
847
|
+
closeMenuRef.current = closeMenu;
|
|
848
|
+
console.log({ openMenus });
|
|
849
|
+
const handleClose = useCallback5(() => {
|
|
850
|
+
closeMenu();
|
|
851
|
+
onClose();
|
|
852
|
+
}, [closeMenu, onClose]);
|
|
853
|
+
useClickAway({
|
|
854
|
+
containerClassName: "vuuMenuList",
|
|
855
|
+
onClose: handleClose,
|
|
856
|
+
isOpen: openMenus.length > 0
|
|
857
|
+
});
|
|
858
|
+
const handleOpenMenu = (id2) => {
|
|
859
|
+
const itemId = getItemId(id2);
|
|
860
|
+
const menuId = getMenuId(itemId);
|
|
861
|
+
navigatingWithKeyboard.current = true;
|
|
862
|
+
openMenu(menuId, itemId);
|
|
863
|
+
};
|
|
864
|
+
const handleCloseMenu = () => {
|
|
865
|
+
navigatingWithKeyboard.current = true;
|
|
866
|
+
closeMenu();
|
|
867
|
+
};
|
|
868
|
+
const handleHighlightMenuItem = () => {
|
|
869
|
+
};
|
|
870
|
+
const lastMenu = openMenus.length - 1;
|
|
871
|
+
const getChildMenuIndex = (i) => {
|
|
872
|
+
if (i >= lastMenu) {
|
|
873
|
+
return -1;
|
|
874
|
+
} else {
|
|
875
|
+
const { id: menuId } = openMenus[i + 1];
|
|
876
|
+
const pos = menuId.lastIndexOf(".");
|
|
877
|
+
const idx = pos === -1 ? parseInt(menuId, 10) : parseInt(menuId.slice(-pos), 10);
|
|
878
|
+
return idx;
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
return /* @__PURE__ */ jsx4(Fragment, { children: openMenus.map(({ id: menuId, left, top }, i) => {
|
|
882
|
+
const childMenuIndex = getChildMenuIndex(i);
|
|
883
|
+
return /* @__PURE__ */ jsx4(Portal, { x: left, y: top, onRender: handleRender, children: /* @__PURE__ */ createElement(
|
|
884
|
+
MenuList_default,
|
|
885
|
+
{
|
|
886
|
+
...menuListProps,
|
|
887
|
+
activatedByKeyboard: navigatingWithKeyboard.current,
|
|
888
|
+
childMenuShowing: childMenuIndex,
|
|
889
|
+
className,
|
|
890
|
+
id,
|
|
891
|
+
menuId,
|
|
892
|
+
isRoot: i === 0,
|
|
893
|
+
key: i,
|
|
894
|
+
listItemProps,
|
|
895
|
+
onActivate: handleActivate,
|
|
896
|
+
onHighlightMenuItem: handleHighlightMenuItem,
|
|
897
|
+
onCloseMenu: handleCloseMenu,
|
|
898
|
+
onOpenMenu: handleOpenMenu,
|
|
899
|
+
style
|
|
900
|
+
},
|
|
901
|
+
menus[menuId]
|
|
902
|
+
) }, i);
|
|
903
|
+
}) });
|
|
904
|
+
};
|
|
905
|
+
ContextMenu.displayName = "ContextMenu";
|
|
906
|
+
|
|
907
|
+
// src/menu/context-menu-provider.tsx
|
|
908
|
+
import { createContext, useCallback as useCallback6, useMemo as useMemo6 } from "react";
|
|
909
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
910
|
+
var ContextMenuContext = createContext(
|
|
911
|
+
null
|
|
912
|
+
);
|
|
913
|
+
var isGroupMenuItemDescriptor = (menuItem) => menuItem !== void 0 && "children" in menuItem;
|
|
914
|
+
var Provider = ({
|
|
915
|
+
children,
|
|
916
|
+
context,
|
|
917
|
+
menuActionHandler,
|
|
918
|
+
menuBuilder
|
|
919
|
+
}) => {
|
|
920
|
+
const menuBuilders = useMemo6(() => {
|
|
921
|
+
if ((context == null ? void 0 : context.menuBuilders) && menuBuilder) {
|
|
922
|
+
return context.menuBuilders.concat(menuBuilder);
|
|
923
|
+
} else if (menuBuilder) {
|
|
924
|
+
return [menuBuilder];
|
|
925
|
+
} else {
|
|
926
|
+
return (context == null ? void 0 : context.menuBuilders) || [];
|
|
927
|
+
}
|
|
928
|
+
}, [context, menuBuilder]);
|
|
929
|
+
const handleMenuAction = useCallback6(
|
|
930
|
+
(type, options) => {
|
|
931
|
+
var _a;
|
|
932
|
+
if (menuActionHandler == null ? void 0 : menuActionHandler(type, options)) {
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
if ((_a = context == null ? void 0 : context.menuActionHandler) == null ? void 0 : _a.call(context, type, options)) {
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
[context, menuActionHandler]
|
|
940
|
+
);
|
|
941
|
+
return /* @__PURE__ */ jsx5(
|
|
942
|
+
ContextMenuContext.Provider,
|
|
943
|
+
{
|
|
944
|
+
value: {
|
|
945
|
+
menuActionHandler: handleMenuAction,
|
|
946
|
+
menuBuilders
|
|
947
|
+
},
|
|
948
|
+
children
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
};
|
|
952
|
+
var ContextMenuProvider = ({
|
|
953
|
+
children,
|
|
954
|
+
label,
|
|
955
|
+
menuActionHandler,
|
|
956
|
+
menuBuilder
|
|
957
|
+
}) => {
|
|
958
|
+
return /* @__PURE__ */ jsx5(ContextMenuContext.Consumer, { children: (parentContext) => /* @__PURE__ */ jsx5(
|
|
959
|
+
Provider,
|
|
960
|
+
{
|
|
961
|
+
context: parentContext,
|
|
962
|
+
label,
|
|
963
|
+
menuActionHandler,
|
|
964
|
+
menuBuilder,
|
|
965
|
+
children
|
|
966
|
+
}
|
|
967
|
+
) });
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
// src/menu/useContextMenu.tsx
|
|
971
|
+
import { useCallback as useCallback7, useContext } from "react";
|
|
972
|
+
|
|
973
|
+
// src/popup/popup-service.ts
|
|
974
|
+
import cx3 from "classnames";
|
|
975
|
+
import React3, {
|
|
976
|
+
createElement as createElement2,
|
|
977
|
+
useEffect as useEffect2,
|
|
978
|
+
useRef as useRef6
|
|
979
|
+
} from "react";
|
|
980
|
+
import ReactDOM3 from "react-dom";
|
|
981
|
+
var _dialogOpen = false;
|
|
982
|
+
var _popups = [];
|
|
983
|
+
function specialKeyHandler(e) {
|
|
984
|
+
if (e.key === "Esc") {
|
|
985
|
+
if (_popups.length) {
|
|
986
|
+
closeAllPopups();
|
|
987
|
+
} else if (_dialogOpen) {
|
|
988
|
+
const dialogRoot = document.body.querySelector(".vuuDialog");
|
|
989
|
+
if (dialogRoot) {
|
|
990
|
+
ReactDOM3.unmountComponentAtNode(dialogRoot);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
function outsideClickHandler(e) {
|
|
996
|
+
if (_popups.length) {
|
|
997
|
+
const popupContainers = document.body.querySelectorAll(".vuuPopup");
|
|
998
|
+
for (let i = 0; i < popupContainers.length; i++) {
|
|
999
|
+
if (popupContainers[i].contains(e.target)) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
closeAllPopups();
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function closeAllPopups() {
|
|
1007
|
+
if (_popups.length) {
|
|
1008
|
+
const popupContainers = document.body.querySelectorAll(".vuuPopup");
|
|
1009
|
+
for (let i = 0; i < popupContainers.length; i++) {
|
|
1010
|
+
ReactDOM3.unmountComponentAtNode(popupContainers[i]);
|
|
1011
|
+
}
|
|
1012
|
+
popupClosed("*");
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function dialogOpened() {
|
|
1016
|
+
if (_dialogOpen === false) {
|
|
1017
|
+
_dialogOpen = true;
|
|
1018
|
+
window.addEventListener("keydown", specialKeyHandler, true);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function dialogClosed() {
|
|
1022
|
+
if (_dialogOpen) {
|
|
1023
|
+
_dialogOpen = false;
|
|
1024
|
+
window.removeEventListener("keydown", specialKeyHandler, true);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function popupOpened(name) {
|
|
1028
|
+
if (_popups.indexOf(name) === -1) {
|
|
1029
|
+
_popups.push(name);
|
|
1030
|
+
if (_dialogOpen === false) {
|
|
1031
|
+
window.addEventListener("keydown", specialKeyHandler, true);
|
|
1032
|
+
window.addEventListener("click", outsideClickHandler, true);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function popupClosed(name) {
|
|
1037
|
+
if (_popups.length) {
|
|
1038
|
+
if (name === "*") {
|
|
1039
|
+
_popups.length = 0;
|
|
1040
|
+
} else {
|
|
1041
|
+
const pos = _popups.indexOf(name);
|
|
1042
|
+
if (pos !== -1) {
|
|
1043
|
+
_popups.splice(pos, 1);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
if (_popups.length === 0 && _dialogOpen === false) {
|
|
1047
|
+
window.removeEventListener("keydown", specialKeyHandler, true);
|
|
1048
|
+
window.removeEventListener("click", outsideClickHandler, true);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
var PopupComponent = ({
|
|
1053
|
+
children,
|
|
1054
|
+
position,
|
|
1055
|
+
style
|
|
1056
|
+
}) => {
|
|
1057
|
+
const className = cx3("hwPopup", "hwPopupContainer", position);
|
|
1058
|
+
return createElement2("div", { className, style }, children);
|
|
1059
|
+
};
|
|
1060
|
+
var incrementingKey = 1;
|
|
1061
|
+
var PopupService = class {
|
|
1062
|
+
static showPopup({
|
|
1063
|
+
name = "anon",
|
|
1064
|
+
group = "all",
|
|
1065
|
+
position = "",
|
|
1066
|
+
left = 0,
|
|
1067
|
+
right = "auto",
|
|
1068
|
+
top = 0,
|
|
1069
|
+
width = "auto",
|
|
1070
|
+
component
|
|
1071
|
+
}) {
|
|
1072
|
+
if (!component) {
|
|
1073
|
+
throw Error(`PopupService showPopup, no component supplied`);
|
|
1074
|
+
}
|
|
1075
|
+
popupOpened(name);
|
|
1076
|
+
let el = document.body.querySelector(".vuuPopup." + group);
|
|
1077
|
+
if (el === null) {
|
|
1078
|
+
el = document.createElement("div");
|
|
1079
|
+
el.className = "vuuPopup " + group;
|
|
1080
|
+
document.body.appendChild(el);
|
|
1081
|
+
}
|
|
1082
|
+
const style = { width };
|
|
1083
|
+
renderPortal(
|
|
1084
|
+
createElement2(
|
|
1085
|
+
PopupComponent,
|
|
1086
|
+
{ key: incrementingKey++, position, style },
|
|
1087
|
+
component
|
|
1088
|
+
),
|
|
1089
|
+
el,
|
|
1090
|
+
left,
|
|
1091
|
+
top,
|
|
1092
|
+
() => {
|
|
1093
|
+
PopupService.keepWithinThePage(el, right);
|
|
1094
|
+
}
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
static hidePopup(name = "anon", group = "all") {
|
|
1098
|
+
if (_popups.indexOf(name) !== -1) {
|
|
1099
|
+
popupClosed(name);
|
|
1100
|
+
const popupRoot = document.body.querySelector(`.vuuPopup.${group}`);
|
|
1101
|
+
if (popupRoot) {
|
|
1102
|
+
ReactDOM3.unmountComponentAtNode(popupRoot);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
static keepWithinThePage(el, right = "auto") {
|
|
1107
|
+
const target = el.querySelector(".vuuPopupContainer > *");
|
|
1108
|
+
if (target) {
|
|
1109
|
+
const {
|
|
1110
|
+
top,
|
|
1111
|
+
left,
|
|
1112
|
+
width,
|
|
1113
|
+
height,
|
|
1114
|
+
right: currentRight
|
|
1115
|
+
} = target.getBoundingClientRect();
|
|
1116
|
+
const w = window.innerWidth;
|
|
1117
|
+
const h = window.innerHeight;
|
|
1118
|
+
const overflowH = h - (top + height);
|
|
1119
|
+
if (overflowH < 0) {
|
|
1120
|
+
target.style.top = Math.round(top) + overflowH + "px";
|
|
1121
|
+
}
|
|
1122
|
+
const overflowW = w - (left + width);
|
|
1123
|
+
if (overflowW < 0) {
|
|
1124
|
+
target.style.left = Math.round(left) + overflowW + "px";
|
|
1125
|
+
}
|
|
1126
|
+
if (typeof right === "number" && right !== currentRight) {
|
|
1127
|
+
const adjustment = right - currentRight;
|
|
1128
|
+
target.style.left = left + adjustment + "px";
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
var DialogService = class {
|
|
1134
|
+
static showDialog(dialog) {
|
|
1135
|
+
const containerEl = ".vuuDialog";
|
|
1136
|
+
const onClose = dialog.props.onClose;
|
|
1137
|
+
dialogOpened();
|
|
1138
|
+
ReactDOM3.render(
|
|
1139
|
+
React3.cloneElement(dialog, {
|
|
1140
|
+
container: containerEl,
|
|
1141
|
+
onClose: () => {
|
|
1142
|
+
DialogService.closeDialog();
|
|
1143
|
+
if (onClose) {
|
|
1144
|
+
onClose();
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}),
|
|
1148
|
+
document.body.querySelector(containerEl)
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
static closeDialog() {
|
|
1152
|
+
dialogClosed();
|
|
1153
|
+
const dialogRoot = document.body.querySelector(".vuuDialog");
|
|
1154
|
+
if (dialogRoot) {
|
|
1155
|
+
ReactDOM3.unmountComponentAtNode(dialogRoot);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
var Popup = (props) => {
|
|
1160
|
+
const pendingTask = useRef6();
|
|
1161
|
+
const ref = useRef6(null);
|
|
1162
|
+
const show = (props2, boundingClientRect) => {
|
|
1163
|
+
const { name, group, depth, width } = props2;
|
|
1164
|
+
let left;
|
|
1165
|
+
let top;
|
|
1166
|
+
if (pendingTask.current) {
|
|
1167
|
+
window.clearTimeout(pendingTask.current);
|
|
1168
|
+
pendingTask.current = void 0;
|
|
1169
|
+
}
|
|
1170
|
+
if (props2.close === true) {
|
|
1171
|
+
PopupService.hidePopup(name, group);
|
|
1172
|
+
} else {
|
|
1173
|
+
const { position, children: component } = props2;
|
|
1174
|
+
const {
|
|
1175
|
+
left: targetLeft,
|
|
1176
|
+
top: targetTop,
|
|
1177
|
+
width: clientWidth,
|
|
1178
|
+
bottom: targetBottom
|
|
1179
|
+
} = boundingClientRect;
|
|
1180
|
+
if (position === "below") {
|
|
1181
|
+
left = targetLeft;
|
|
1182
|
+
top = targetBottom;
|
|
1183
|
+
} else if (position === "above") {
|
|
1184
|
+
left = targetLeft;
|
|
1185
|
+
top = targetTop;
|
|
1186
|
+
}
|
|
1187
|
+
pendingTask.current = window.setTimeout(() => {
|
|
1188
|
+
PopupService.showPopup({
|
|
1189
|
+
name,
|
|
1190
|
+
group,
|
|
1191
|
+
depth,
|
|
1192
|
+
position,
|
|
1193
|
+
left,
|
|
1194
|
+
top,
|
|
1195
|
+
width: width || clientWidth,
|
|
1196
|
+
component
|
|
1197
|
+
});
|
|
1198
|
+
}, 10);
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
useEffect2(() => {
|
|
1202
|
+
if (ref.current) {
|
|
1203
|
+
const el = ref.current.parentElement;
|
|
1204
|
+
const boundingClientRect = el == null ? void 0 : el.getBoundingClientRect();
|
|
1205
|
+
if (boundingClientRect) {
|
|
1206
|
+
show(props, boundingClientRect);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return () => {
|
|
1210
|
+
PopupService.hidePopup(props.name, props.group);
|
|
1211
|
+
};
|
|
1212
|
+
}, [props]);
|
|
1213
|
+
return React3.createElement("div", { className: "popup-proxy", ref });
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
// src/menu/useContextMenu.tsx
|
|
1217
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1218
|
+
var useContextMenu = () => {
|
|
1219
|
+
const ctx = useContext(ContextMenuContext);
|
|
1220
|
+
const buildMenuOptions = useCallback7(
|
|
1221
|
+
(menuBuilders, location, options) => {
|
|
1222
|
+
let results = [];
|
|
1223
|
+
for (const menuBuilder of menuBuilders) {
|
|
1224
|
+
results = results.concat(menuBuilder(location, options));
|
|
1225
|
+
}
|
|
1226
|
+
return results;
|
|
1227
|
+
},
|
|
1228
|
+
[]
|
|
1229
|
+
);
|
|
1230
|
+
const handleShowContextMenu = useCallback7(
|
|
1231
|
+
(e, location, options) => {
|
|
1232
|
+
var _a;
|
|
1233
|
+
e.stopPropagation();
|
|
1234
|
+
e.preventDefault();
|
|
1235
|
+
const menuBuilders = (_a = ctx == null ? void 0 : ctx.menuBuilders) != null ? _a : [];
|
|
1236
|
+
const menuItemDescriptors = buildMenuOptions(
|
|
1237
|
+
menuBuilders,
|
|
1238
|
+
location,
|
|
1239
|
+
options
|
|
1240
|
+
);
|
|
1241
|
+
console.log({
|
|
1242
|
+
menuItemDescriptors
|
|
1243
|
+
});
|
|
1244
|
+
if (menuItemDescriptors.length && (ctx == null ? void 0 : ctx.menuActionHandler)) {
|
|
1245
|
+
console.log(`showContextMenu ${location}`, {
|
|
1246
|
+
options
|
|
1247
|
+
});
|
|
1248
|
+
showContextMenu(e, menuItemDescriptors, ctx.menuActionHandler);
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
[buildMenuOptions, ctx]
|
|
1252
|
+
);
|
|
1253
|
+
return handleShowContextMenu;
|
|
1254
|
+
};
|
|
1255
|
+
var showContextMenu = (e, menuDescriptors, handleContextMenuAction) => {
|
|
1256
|
+
const { clientX: left, clientY: top } = e;
|
|
1257
|
+
const menuItems = (menuDescriptors2) => {
|
|
1258
|
+
const fromDescriptor = (menuItem, i) => isGroupMenuItemDescriptor(menuItem) ? /* @__PURE__ */ jsx6(MenuItemGroup, { label: menuItem.label, children: menuItem.children.map(fromDescriptor) }, i) : /* @__PURE__ */ jsx6(
|
|
1259
|
+
MenuItem,
|
|
1260
|
+
{
|
|
1261
|
+
action: menuItem.action,
|
|
1262
|
+
"data-icon": menuItem.icon,
|
|
1263
|
+
options: menuItem.options,
|
|
1264
|
+
children: menuItem.label
|
|
1265
|
+
},
|
|
1266
|
+
i
|
|
1267
|
+
);
|
|
1268
|
+
return menuDescriptors2.map(fromDescriptor);
|
|
1269
|
+
};
|
|
1270
|
+
const handleClose = (menuId, options) => {
|
|
1271
|
+
if (menuId) {
|
|
1272
|
+
handleContextMenuAction(menuId, options);
|
|
1273
|
+
PopupService.hidePopup();
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
const component = /* @__PURE__ */ jsx6(ContextMenu, { onClose: handleClose, position: { x: left, y: top }, children: menuItems(menuDescriptors) });
|
|
1277
|
+
PopupService.showPopup({ left: 0, top: 0, component });
|
|
1278
|
+
};
|
|
1279
|
+
export {
|
|
1280
|
+
ContextMenu,
|
|
1281
|
+
ContextMenuContext,
|
|
1282
|
+
ContextMenuProvider,
|
|
1283
|
+
Dialog,
|
|
1284
|
+
DialogService,
|
|
1285
|
+
MenuItem,
|
|
1286
|
+
MenuItemGroup,
|
|
1287
|
+
Popup,
|
|
1288
|
+
PopupService,
|
|
1289
|
+
Portal,
|
|
1290
|
+
Separator,
|
|
1291
|
+
createContainer,
|
|
1292
|
+
installTheme,
|
|
1293
|
+
isGroupMenuItemDescriptor,
|
|
1294
|
+
renderPortal,
|
|
1295
|
+
useContextMenu
|
|
1296
|
+
};
|
|
2
1297
|
//# sourceMappingURL=index.js.map
|