sb-floating-panel-vue 5.1.0 → 5.2.0

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.
@@ -1 +1 @@
1
- (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue"),require("@floating-ui/vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`,`@floating-ui/vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SbFloatingPanelVue={},e.Vue,e.FloatingUIVue))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=(()=>{try{return process.env.NODE_ENV!==`production`}catch{return!1}})();function i(e){r&&console.warn(`[sb-floating-panel-vue] ${e}`)}var a=Symbol(`SbFloatingContext`);function o(e,t,n){return++e.parts[t]>1&&i(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`),()=>e.parts[t]--}function s(e){let n=(0,t.inject)(a,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var c=e=>`${Math.max(0,e)}px`,l=(e,t)=>{let[n,r]=e.split(`-`),i=t&&(n===`top`||n===`bottom`),a=r===`start`?i?`100%`:`0%`:r===`end`?i?`0%`:`100%`:`50%`;return n===`top`?`${a} 100%`:n===`bottom`?`${a} 0%`:n===`left`?`100% ${a}`:`0% ${a}`};function u(e){let r=(0,t.ref)(null),i=(0,t.ref)(null),a=(0,t.computed)(()=>{let r=(0,t.toValue)(e.padding),i=(0,t.toValue)(e.matchWidth),a=(0,t.toValue)(e.placement).includes(`-`),o=(0,n.flip)({padding:r,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),s=(0,n.shift)({padding:r,limiter:(0,n.limitShift)()}),u=[(0,n.offset)((0,t.toValue)(e.offset)),...a?[o,s]:[s,o],(0,n.size)({padding:r,apply({rects:e,elements:t,availableWidth:n,availableHeight:r,placement:a}){let{style:o}=t.floating,s=c(e.reference.width);o.setProperty(`--sbf-reference-width`,s);let u=getComputedStyle(t.floating).direction===`rtl`;o.setProperty(`--sbf-transform-origin`,l(a,u)),o.setProperty(`--sbf-available-width`,c(n)),o.setProperty(`--sbf-available-height`,c(r)),o.width=i===!0?s:``,o.minWidth=i===`min`?s:``}})];return(0,t.toValue)(e.hideWhenDetached)&&u.push((0,n.hide)({padding:r})),u}),{floatingStyles:o,middlewareData:s,placement:u,isPositioned:d,update:f}=(0,n.useFloating)(r,i,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:a,whileElementsMounted:n.autoUpdate}),p=(0,t.computed)(()=>!!s.value.hide?.referenceHidden);return{reference:r,floating:i,floatingStyles:(0,t.computed)(()=>({...o.value,...p.value?{visibility:`hidden`}:{}})),placement:u,side:(0,t.computed)(()=>u.value.split(`-`)[0]),align:(0,t.computed)(()=>u.value.split(`-`)[1]),isPositioned:d,referenceHidden:p,update:f}}var d=`data-sbf-focus-guard`,f=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`select:not([disabled])`,`textarea:not([disabled])`,`summary`,`audio[controls]`,`video[controls]`,`[tabindex]`,`[contenteditable="true"]`].join(`,`),p=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function m(e){let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;return t instanceof HTMLElement?t:null}function h(e){let t=e instanceof Element?e.closest(f):null;return!!t&&!t.closest(`[inert]`)}var g=e=>e.matches(f)&&!e.closest(`[inert]`);function _(e){return[...e.querySelectorAll(f)].filter(p)}function v(e,t){return _(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var y=[],b=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbContainer`,props:(0,t.mergeModels)({placement:{default:`bottom-start`},strategy:{default:`absolute`},offset:{default:8},padding:{default:8},matchWidth:{type:[Boolean,String],default:!1},hideWhenDetached:{type:[Boolean,String],default:!1},defaultOpen:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},trigger:{default:`click`},delay:{default:()=>({open:300,close:150})},closeOnEscape:{type:Boolean,default:!0},closeOnClickOutside:{type:Boolean,default:!0},closeOnFocusOut:{type:Boolean,default:!0}},{open:{type:Boolean,default:void 0},openModifiers:{}}),emits:(0,t.mergeModels)([`open`,`close`],[`update:open`]),setup(e,{expose:n,emit:r}){let i=e,o=(0,t.useModel)(e,`open`),s=r,c=i.defaultOpen,l=(0,t.computed)(()=>!i.disabled&&(o.value??c)),d;(0,t.onScopeDispose)(()=>clearTimeout(d));let f=e=>{clearTimeout(d),!(e&&i.disabled)&&e!==l.value&&(o.value=e)},p=()=>f(!0),m=()=>f(!1),g=()=>f(!l.value),_=e=>{if(e.pointerType===`touch`||(x?.hover(e),i.trigger!==`hover`))return;let t=e.type===`pointerenter`||S.contains(e.relatedTarget);if(clearTimeout(d),t===l.value)return;let{delay:n}=i,r=typeof n==`number`?{open:n,close:n}:n,a=(t?r.open:r.close)??(t?300:150);d=setTimeout(()=>f(t),a)},v=u({isOpen:l,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),b=new Set,x=(0,t.inject)(a,null),S={...v,isOpen:l,disabled:(0,t.toRef)(i,`disabled`),trigger:(0,t.toRef)(i,`trigger`),floatingId:`${(0,t.useId)()}-floating`,referenceId:(0,t.ref)(),floatingRole:(0,t.ref)(`dialog`),navigation:(0,t.ref)(null),focusRequest:(0,t.ref)(null),parent:x,skipFocusReturn:!1,parts:{reference:0,floating:0},isUnmounting:!1,open:p,close:m,toggle:g,hover:_,contains:e=>e instanceof Node?v.reference.value?.contains(e)||v.floating.value?.contains(e)?!0:[...b].some(t=>t.contains(e)):!1,registerChild:e=>(b.add(e),()=>b.delete(e))};x&&(0,t.onScopeDispose)(x.registerChild(S)),(0,t.provide)(a,S),(0,t.onBeforeUnmount)(()=>S.isUnmounting=!0);let C=e=>{let t=e.composedPath()[0]??e.target;return y.findLast(e=>e.contains(t))??y[y.length-1]},w=e=>{e.key===`Escape`&&C(e)===S&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),m()))},T=e=>{S.contains(e)||(S.skipFocusReturn=h(e),m())},E,D=e=>{if(E?.(),!i.closeOnClickOutside)return;let t=e.composedPath()[0]??e.target;if(e.pointerType!==`touch`){T(t);return}if(S.contains(t))return;let n=e=>{E?.(),T(e.composedPath()[0]??e.target)};document.addEventListener(`click`,n,!0),E=()=>{document.removeEventListener(`click`,n,!0),E=void 0}},O=e=>{i.closeOnFocusOut&&(S.contains(e.composedPath()[0]??e.target)||m())};return(0,t.watch)(l,(e,t,n)=>{e&&typeof document<`u`&&(y.push(S),document.addEventListener(`keydown`,w),document.addEventListener(`pointerdown`,D,!0),document.addEventListener(`focusin`,O),n(()=>{document.removeEventListener(`keydown`,w),document.removeEventListener(`pointerdown`,D,!0),document.removeEventListener(`focusin`,O),E?.(),y.splice(y.indexOf(S),1)}))},{immediate:!0}),(0,t.watch)(l,e=>s(e?`open`:`close`)),(0,t.watch)(v.referenceHidden,e=>{e&&i.hideWhenDetached===`close`&&m()}),(0,t.watch)(()=>i.disabled,e=>{e&&(o.value??c)&&(o.value=!1)}),n({get isOpen(){return l.value},open:p,close:m,toggle:g,update:v.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:l.value,open:p,close:m,toggle:g,placement:(0,t.unref)(v).placement.value,side:(0,t.unref)(v).side.value,align:(0,t.unref)(v).align.value,floatingId:S.floatingId,referenceId:S.referenceId.value})}}),x={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},S={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},C=e=>!!e&&Object.hasOwn(S,e);function w(e,t){return[...e.querySelectorAll(S[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var T=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),E=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`},D=(0,t.defineComponent)({__name:`SbReference`,props:{as:{default:`button`},id:{default:void 0},ariaHaspopup:{default:void 0}},setup(e){let n=e,r=(0,t.useAttrs)(),a=s(`SbReference`),{isOpen:c,disabled:l,floatingId:u,floatingRole:d}=a,f=o(a,`reference`,`SbReference`),p=[`button`,`input`,`textarea`,`select`],h=()=>typeof n.as!=`string`||p.includes(n.as)||n.as===`a`&&r.href!=null,g=`${(0,t.useId)()}-reference`,v=(0,t.computed)(()=>n.id??g);(0,t.watchEffect)(()=>{a.referenceId.value=v.value}),(0,t.onMounted)(()=>{let e=a.reference.value;e?T(e)&&a.navigation.value&&i(`<SbReference> is a text field with a role="${a.navigation.value}" panel: the arrow keys move focus into the panel, so typing stops there. The combobox pattern (focus kept in the field with aria-activedescendant) is not supported.`):i("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.")}),(0,t.onBeforeUnmount)(()=>{a.referenceId.value===v.value&&(a.referenceId.value=void 0),f(),!a.parts.reference&&!a.isUnmounting&&a.close()});let y=(0,t.computed)(()=>n.ariaHaspopup??(d.value&&Object.hasOwn(x,d.value)?x[d.value]:void 0)),b=e=>{a.reference.value=m(e)},S=()=>{a.trigger.value===`click`&&(T(a.reference.value)?a.open():a.toggle())},C=(0,t.computed)(()=>d.value===`tooltip`),w=()=>a.trigger.value===`hover`||a.trigger.value===`focus`,D=()=>a.parent?.navigation.value===`menu`,O=e=>{w()&&!D()&&!a.contains(e.relatedTarget)&&a.open()},k=e=>{w()&&!a.contains(e.relatedTarget)&&a.close()},A=e=>{let t=a.floating.value,n=t&&_(t)[0];n&&(e.preventDefault(),n.focus())},j=e=>{if(a.navigation.value&&!l.value&&a.trigger.value!==`manual`){if(D()){let t=e.currentTarget;return e.key===E(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},M=e=>{if(e.key===`Tab`&&!e.shiftKey&&c.value&&!D()){A(e);return}let t=j(e);t?(e.preventDefault(),a.focusRequest.value=t,a.open()):h()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),S())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:v.value,ref:b,type:n.as===`button`?`button`:void 0,role:h()?void 0:`button`,tabindex:h()?void 0:(0,t.unref)(l)?-1:0,disabled:n.as===`button`&&(0,t.unref)(l)?!0:void 0,"aria-disabled":n.as!==`button`&&(0,t.unref)(l)?`true`:void 0,"aria-haspopup":y.value,"aria-expanded":C.value?void 0:(0,t.unref)(c)?`true`:`false`,"aria-controls":(0,t.unref)(c)&&!C.value?(0,t.unref)(u):void 0,"aria-describedby":(0,t.unref)(c)&&C.value?(0,t.unref)(u):void 0,"data-sbf-state":(0,t.unref)(c)?`open`:`closed`,onClick:S,onKeydown:M,onFocusin:O,onFocusout:k,onPointerenter:(0,t.unref)(a).hover,onPointerleave:(0,t.unref)(a).hover},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},40,[`id`,`type`,`role`,`tabindex`,`disabled`,`aria-disabled`,`aria-haspopup`,`aria-expanded`,`aria-controls`,`aria-describedby`,`data-sbf-state`,`onPointerenter`,`onPointerleave`]))}}),O=`:disabled, [aria-disabled="true"], [aria-haspopup]:not([aria-haspopup="false"])`,k=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbFloating`,props:{as:{default:`div`},role:{default:`dialog`},transition:{default:void 0},teleport:{type:[String,Boolean],default:`body`},zIndex:{default:void 0},autoFocus:{type:Boolean,default:void 0},keyboardNavigation:{type:Boolean,default:!0},closeOnSelect:{type:Boolean,default:!1}},emits:[`after-enter`,`after-leave`],setup(e,{emit:n}){let r=e,a=n,c=(0,t.useAttrs)(),l=s(`SbFloating`);(0,t.onBeforeUnmount)(o(l,`floating`,`SbFloating`));let{isOpen:u,floatingId:f,referenceId:p,side:h,align:y}=l;(0,t.watchEffect)(()=>{l.floatingRole.value=r.role,l.navigation.value=r.keyboardNavigation&&C(r.role)?r.role:null}),(0,t.onMounted)(()=>{r.role===`tooltip`&&l.trigger.value===`click`&&i(`<SbFloating role="tooltip"> opens on click: a tooltip shows on hover and focus. Set trigger="hover" or "focus" on SbContainer, or use another role for a panel opened by click.`)});let b=(0,t.ref)(u.value),S=(0,t.ref)(null),D=e=>{l.floating.value=e instanceof HTMLElement?e:null},k=e=>{S.value=m(e)},A=(0,t.computed)(()=>({...l.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),j=()=>({"aria-labelledby":r.role&&Object.hasOwn(x,r.role)&&c[`aria-label`]==null?p.value:void 0,...c}),M=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&l.trigger.value!==`hover`&&l.trigger.value!==`focus`&&!T(l.reference.value))),N=(0,t.computed)(()=>u.value&&r.teleport!==!1&&r.role!==`tooltip`),P={[d]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},F=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),I=e=>e.setAttribute(`data-sbf-state`,`closed`),L=()=>a(`after-enter`),R=()=>{u.value||(b.value=!1),a(`after-leave`)},z=e=>e instanceof Node&&!!S.value?.contains(e),B=e=>{let t=S.value;if(!t)return;let n=_(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},V=e=>{z(e.relatedTarget)?l.reference.value?.focus():B(`first`)},H=e=>{if(!z(e.relatedTarget)){B(`last`);return}let t=l.reference.value;((t&&v(t,e=>l.contains(e)))??t)?.focus()},U=()=>{let e=l.navigation.value;return S.value&&e?w(S.value,e):[]},W={menu:`[role="menuitem"]`,listbox:`[role="option"]`},G={menu:`role="menuitem", "menuitemcheckbox" or "menuitemradio"`,listbox:`role="option"`},K=!1,q=e=>{let t=l.navigation.value;if(K||!t)return;let n=e.length?e.every(g)?null:`has items that cannot take focus, so the arrow keys cannot move to them: make each one a button, or give it tabindex="-1".`:`has no items the arrow keys can move to: items need ${G[t]} and must not be disabled.`;n&&(K=!0,i(`<SbFloating role="${t}"> ${n}`))},J=e=>{let t=S.value;if(!t)return;let n=U();e&&q(n),(n.find(e=>e.getAttribute(`aria-selected`)===`true`)??(e===`last`?n[n.length-1]:e?n[0]:void 0)??t.querySelector(`[autofocus]`)??n[0]??_(t)[0]??t).focus({preventScroll:!0})},Y=()=>{let e=l.focusRequest.value;return l.focusRequest.value=null,e},X=``,Z;(0,t.onScopeDispose)(()=>clearTimeout(Z));let ee=(e,t,n)=>{X+=n.toLowerCase(),clearTimeout(Z),Z=setTimeout(()=>X=``,500);let r=[...X].every(e=>e===X[0])?X[0]:X,i=Math.max(t,0),a=[...e.slice(i),...e.slice(0,i)];return r.length===1&&t>=0&&a.push(a.shift()),a.find(e=>e.textContent?.trim().toLowerCase().startsWith(r))},Q=()=>{let e=[l];for(;e[e.length-1].parent?.navigation.value===`menu`;)e.push(e[e.length-1].parent);return e},$=(e,t)=>{for(let t of e)t.skipFocusReturn=!0,t.close();t?.focus({preventScroll:!0})},te=e=>{let t=Q(),n=t[t.length-1],r=n.reference.value,i=e?r:(r&&v(r,e=>n.contains(e)))??r;$(t,i)},ne=e=>{let{role:t}=r,n=S.value;if(!r.closeOnSelect||e.defaultPrevented||!n||!C(t))return;let i=e.target.closest(W[t]);if(!i||i.closest(`[role="${t}"]`)!==n||i.matches(O)||t===`listbox`&&n.getAttribute(`aria-multiselectable`)===`true`)return;let a=t===`menu`?Q():[l];$(a,a[a.length-1].reference.value)},re=e=>{let t=l.navigation.value;if(!t||e.defaultPrevented||e.isComposing||T(e.target))return;let n=U(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,o;if([`ArrowDown`,`ArrowUp`,`Home`,`End`].includes(e.key)&&q(n),e.key===`ArrowDown`)o=n[r<i?r+1:a?0:i];else if(e.key===`ArrowUp`)o=n[r>0?r-1:a||r<0?i:0];else if(e.key===`Home`)o=n[0];else if(e.key===`End`)o=n[i];else if(e.key===`Tab`&&t===`menu`){e.preventDefault(),te(e.shiftKey);return}else if(t===`menu`&&l.parent?.navigation.value===`menu`&&S.value&&e.key===E(S.value).close){e.preventDefault(),l.close();return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||X))e.preventDefault(),o=ee(n,r,e.key);else if((e.key===`Enter`||e.key===` `)&&e.target===n[r]){let t=n[r];if(t.matches(`button, a[href], input, summary`))return;e.preventDefault(),t.getAttribute(`aria-disabled`)!==`true`&&t.click();return}o&&(e.preventDefault(),o.focus())};return(0,t.watch)(u,e=>{let t=l.skipFocusReturn;if(l.skipFocusReturn=!1,e){b.value=!0;return}let n=document.activeElement;!t&&n&&l.floating.value?.contains(n)&&l.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>u.value&&l.isPositioned.value,async e=>{if(!e)return;let n=Y();(n||M.value)&&(await(0,t.nextTick)(),J(n))}),(0,t.watch)(l.focusRequest,e=>{e&&u.value&&l.isPositioned.value&&J(Y())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:r.teleport||`body`,disabled:r.teleport===!1},[b.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:D,style:(0,t.normalizeStyle)(A.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(l).hover&&(0,t.unref)(l).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(l).hover&&(0,t.unref)(l).hover(...e)},[N.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},P,{onFocus:V}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(F.value,{appear:``,onBeforeLeave:I,onAfterEnter:L,onAfterLeave:R}),{default:(0,t.withCtx)(()=>[(0,t.unref)(u)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},j(),{id:(0,t.unref)(f),ref:k,role:r.role??void 0,tabindex:M.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(y),onKeydown:re,onClick:ne}),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},16,[`id`,`role`,`tabindex`,`data-sbf-side`,`data-sbf-align`])):(0,t.createCommentVNode)(``,!0)]),_:3},16),N.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},P,{onFocus:H}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});function A(){let e=(0,t.inject)(a,null);if(!e)throw Error(`[sb-floating-panel-vue] useSbFloating() must be called in a component inside <SbContainer>.`);let{isOpen:n,open:r,close:i,toggle:o}=e;return{isOpen:(0,t.readonly)(n),open:r,close:i,toggle:o}}e.SbContainer=b,e.SbFloating=k,e.SbReference=D,e.useSbFloating=A});
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue"),require("@floating-ui/vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`,`@floating-ui/vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SbFloatingPanelVue={},e.Vue,e.FloatingUIVue))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=`data-sbf-focus-guard`,i=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`select:not([disabled])`,`textarea:not([disabled])`,`summary`,`audio[controls]`,`video[controls]`,`[tabindex]`,`[contenteditable="true"]`].join(`,`),a=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function o(e){let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;return t instanceof HTMLElement?t:null}function s(e){let t=e instanceof Element?e.closest(i):null;return!!t&&!t.closest(`[inert]`)}function c(e){try{return e.matches(`:focus-visible`)}catch{return!0}}var l=e=>e.matches(i)&&!e.closest(`[inert]`);function u(e){return[...e.querySelectorAll(i)].filter(a)}function d(e,t){return u(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var f=(()=>{try{return process.env.NODE_ENV!==`production`}catch{return!1}})();function p(e){f&&console.warn(`[sb-floating-panel-vue] ${e}`)}var m=e=>e.parent?.navigation.value===`menu`;function h(e,t){let n=document.activeElement,r=e.reference.value;return!n||!r||r.contains(n)||!e.contains(n)?!1:((t?r:d(r,t=>e.contains(t)))?.focus({preventScroll:!0}),!0)}var g=Symbol(`SbFloatingContext`);function _(e,t,n){return++e.parts[t]>1&&p(`<SbContainer> has more than one <${n}>: they share one open state and position, so only the last one works. Use one SbContainer for each panel.`),()=>e.parts[t]--}function v(e){let n=(0,t.inject)(g,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var y=e=>`${Math.max(0,e)}px`,b=(e,t)=>{let[n,r]=e.split(`-`),i=t&&(n===`top`||n===`bottom`),a=r===`start`?i?`100%`:`0%`:r===`end`?i?`0%`:`100%`:`50%`;return n===`top`?`${a} 100%`:n===`bottom`?`${a} 0%`:n===`left`?`100% ${a}`:`0% ${a}`};function x(e){let r=(0,t.ref)(null),i=(0,t.ref)(null),a=(0,t.computed)(()=>{let r=(0,t.toValue)(e.padding),i=(0,t.toValue)(e.matchWidth),a=(0,t.toValue)(e.placement).includes(`-`),o=(0,n.flip)({padding:r,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),s=(0,n.shift)({padding:r,limiter:(0,n.limitShift)()}),c=[(0,n.offset)((0,t.toValue)(e.offset)),...a?[o,s]:[s,o],(0,n.size)({padding:r,apply({rects:e,elements:t,availableWidth:n,availableHeight:r,placement:a}){let{style:o}=t.floating,s=y(e.reference.width);o.setProperty(`--sbf-reference-width`,s);let c=getComputedStyle(t.floating).direction===`rtl`;o.setProperty(`--sbf-transform-origin`,b(a,c)),o.setProperty(`--sbf-available-width`,y(n)),o.setProperty(`--sbf-available-height`,y(r)),o.width=i===!0?s:``,o.minWidth=i===`min`?s:``}})];return(0,t.toValue)(e.hideWhenDetached)&&c.push((0,n.hide)({padding:r})),c}),{floatingStyles:o,middlewareData:s,placement:c,isPositioned:l,update:u}=(0,n.useFloating)(r,i,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:a,whileElementsMounted:n.autoUpdate}),d=(0,t.computed)(()=>!!s.value.hide?.referenceHidden);return{reference:r,floating:i,floatingStyles:(0,t.computed)(()=>({...o.value,...d.value?{visibility:`hidden`}:{}})),placement:c,side:(0,t.computed)(()=>c.value.split(`-`)[0]),align:(0,t.computed)(()=>c.value.split(`-`)[1]),isPositioned:l,referenceHidden:d,update:u}}var S=[],C={open:300,close:150},w=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbContainer`,props:(0,t.mergeModels)({placement:{default:`bottom-start`},strategy:{default:`absolute`},offset:{default:8},padding:{default:8},matchWidth:{type:[Boolean,String],default:!1},hideWhenDetached:{type:[Boolean,String],default:!1},defaultOpen:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},trigger:{default:`click`},delay:{default:()=>({...C})},closeOnEscape:{type:Boolean,default:!0},closeOnClickOutside:{type:Boolean,default:!0},closeOnFocusOut:{type:Boolean,default:!0}},{open:{type:Boolean,default:void 0},openModifiers:{}}),emits:(0,t.mergeModels)([`open`,`close`],[`update:open`]),setup(e,{expose:n,emit:r}){let i=e,a=(0,t.useModel)(e,`open`),o=r,c=i.defaultOpen,l=(0,t.computed)(()=>!i.disabled&&(a.value??c)),u;(0,t.onScopeDispose)(()=>clearTimeout(u));let d,f=((e,n)=>{clearTimeout(u),!(e&&i.disabled)&&e!==l.value&&(d=n,a.value=e,(0,t.nextTick)(()=>d=void 0))}),p=()=>f(!0,`api`),m=()=>f(!1,`api`),_=()=>f(!l.value,`api`),v=e=>{if(e.pointerType===`touch`||(w?.hover(e),i.trigger!==`hover`))return;let t=e.type===`pointerenter`||T.contains(e.relatedTarget);if(clearTimeout(u),t===l.value)return;let{delay:n}=i,r=t?`open`:`close`,a=typeof n==`number`?n:n[r]??C[r];u=setTimeout(()=>f(t,`hover`),a)},y=x({isOpen:l,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),b=new Set,w=(0,t.inject)(g,null),T={...y,isOpen:l,disabled:(0,t.toRef)(i,`disabled`),trigger:(0,t.toRef)(i,`trigger`),floatingId:`${(0,t.useId)()}-floating`,referenceId:(0,t.ref)(),floatingRole:(0,t.ref)(`dialog`),navigation:(0,t.ref)(null),focusRequest:(0,t.ref)(null),parent:w,skipFocusReturn:!1,parts:{reference:0,floating:0},isUnmounting:!1,open:p,close:m,toggle:_,setOpen:f,hover:v,contains:e=>e instanceof Node?y.reference.value?.contains(e)||y.floating.value?.contains(e)?!0:[...b].some(t=>t.contains(e)):!1,registerChild:e=>(b.add(e),()=>b.delete(e))};w&&(0,t.onScopeDispose)(w.registerChild(T)),(0,t.provide)(g,T),(0,t.onBeforeUnmount)(()=>{T.isUnmounting=!0,h(T,!1)});let E=e=>{let t=e.composedPath()[0]??e.target;return S.findLast(e=>e.contains(t))??S[S.length-1]},D=e=>{e.key===`Escape`&&E(e)===T&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),f(!1,`escape`)))},O=e=>{T.contains(e)||(T.skipFocusReturn=s(e),f(!1,`outside`))},k,A=e=>{if(k?.(),!i.closeOnClickOutside)return;let t=e.composedPath()[0]??e.target;if(e.pointerType!==`touch`){O(t);return}if(T.contains(t))return;let n=e=>{k?.(),O(e.composedPath()[0]??e.target)};document.addEventListener(`click`,n,!0),k=()=>{document.removeEventListener(`click`,n,!0),k=void 0}},j=e=>{i.closeOnFocusOut&&(T.contains(e.composedPath()[0]??e.target)||f(!1,`focus-out`))};return(0,t.watch)(l,(e,t,n)=>{e&&typeof document<`u`&&(S.push(T),document.addEventListener(`keydown`,D),document.addEventListener(`pointerdown`,A,!0),document.addEventListener(`focusin`,j),n(()=>{document.removeEventListener(`keydown`,D),document.removeEventListener(`pointerdown`,A,!0),document.removeEventListener(`focusin`,j),k?.(),S.splice(S.indexOf(T),1)}))},{immediate:!0}),(0,t.watch)(l,e=>{let t=d??(!e&&i.disabled?`disabled`:`api`);d=void 0,o(e?`open`:`close`,t)}),(0,t.watch)(y.referenceHidden,e=>{e&&i.hideWhenDetached===`close`&&f(!1,`detached`)}),(0,t.watch)(()=>i.disabled,e=>{e&&(a.value??c)&&(a.value=!1)}),n({get isOpen(){return l.value},open:p,close:m,toggle:_,update:y.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:l.value,open:p,close:m,toggle:_,placement:(0,t.unref)(y).placement.value,side:(0,t.unref)(y).side.value,align:(0,t.unref)(y).align.value,floatingId:T.floatingId,referenceId:T.referenceId.value})}}),T={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},E={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},D=e=>!!e&&Object.hasOwn(E,e);function O(e,t){return[...e.querySelectorAll(E[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var k=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),A=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`},j=(0,t.defineComponent)({__name:`SbReference`,props:{as:{default:`button`},id:{default:void 0},ariaHaspopup:{default:void 0}},setup(e){let n=e,r=(0,t.useAttrs)(),i=v(`SbReference`),{isOpen:a,disabled:s,floatingId:l,floatingRole:d}=i,f=_(i,`reference`,`SbReference`),g=[`button`,`input`,`textarea`,`select`],y=()=>typeof n.as!=`string`||g.includes(n.as)||n.as===`a`&&r.href!=null,b=`${(0,t.useId)()}-reference`,x=(0,t.computed)(()=>n.id??b);(0,t.watchEffect)(()=>{i.referenceId.value=x.value}),(0,t.onMounted)(()=>{let e=i.reference.value;e?k(e)&&i.navigation.value&&p(`<SbReference> is a text field with a role="${i.navigation.value}" panel: the arrow keys move focus into the panel, so typing stops there. The combobox pattern (focus kept in the field with aria-activedescendant) is not supported.`):p("<SbReference> rendered no element, so the panel cannot be positioned: a component in `as` must render a single root element.")}),(0,t.onBeforeUnmount)(()=>{i.referenceId.value===x.value&&(i.referenceId.value=void 0),f(),!i.parts.reference&&!i.isUnmounting&&(i.skipFocusReturn=h(i,!1),i.setOpen(!1,`api`))});let S=(0,t.computed)(()=>n.ariaHaspopup??(d.value&&Object.hasOwn(T,d.value)?T[d.value]:void 0)),C=e=>{i.reference.value=o(e)},w=()=>{i.trigger.value===`click`&&(k(i.reference.value)?i.setOpen(!0,`trigger`):i.setOpen(!a.value,`trigger`))},E=(0,t.computed)(()=>d.value===`tooltip`),D=()=>i.trigger.value===`hover`||i.trigger.value===`focus`,O=e=>{if(!D()||m(i)||i.contains(e.relatedTarget))return;let t=e.target;(i.trigger.value===`focus`||c(t))&&i.setOpen(!0,`focus`)},j=e=>{D()&&!i.contains(e.relatedTarget)&&i.setOpen(!1,`focus-out`)},M=e=>{let t=i.floating.value,n=t&&u(t)[0];n&&(e.preventDefault(),n.focus())},N=e=>{if(i.navigation.value&&!s.value&&i.trigger.value!==`manual`){if(m(i)){let t=e.currentTarget;return e.key===A(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},P=e=>{if(e.key===`Tab`&&!e.shiftKey&&a.value&&!m(i)){M(e);return}let t=N(e);t?(e.preventDefault(),i.focusRequest.value=t,i.setOpen(!0,`trigger`)):y()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),w())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:x.value,ref:C,type:n.as===`button`?`button`:void 0,role:y()?void 0:`button`,tabindex:y()?void 0:(0,t.unref)(s)?-1:0,disabled:n.as===`button`&&(0,t.unref)(s)?!0:void 0,"aria-disabled":n.as!==`button`&&(0,t.unref)(s)?`true`:void 0,"aria-haspopup":S.value,"aria-expanded":E.value?void 0:(0,t.unref)(a)?`true`:`false`,"aria-controls":(0,t.unref)(a)&&!E.value?(0,t.unref)(l):void 0,"aria-describedby":(0,t.unref)(a)&&E.value?(0,t.unref)(l):void 0,"data-sbf-state":(0,t.unref)(a)?`open`:`closed`,onClick:w,onKeydown:P,onFocusin:O,onFocusout:j,onPointerenter:(0,t.unref)(i).hover,onPointerleave:(0,t.unref)(i).hover},{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},40,[`id`,`type`,`role`,`tabindex`,`disabled`,`aria-disabled`,`aria-haspopup`,`aria-expanded`,`aria-controls`,`aria-describedby`,`data-sbf-state`,`onPointerenter`,`onPointerleave`]))}}),M=`:disabled, [aria-disabled="true"], [aria-haspopup]:not([aria-haspopup="false"])`,N=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbFloating`,props:{as:{default:`div`},role:{default:`dialog`},transition:{default:void 0},teleport:{type:[String,Boolean],default:`body`},zIndex:{default:void 0},autoFocus:{type:Boolean,default:void 0},keyboardNavigation:{type:Boolean,default:!0},closeOnSelect:{type:Boolean,default:!1}},emits:[`after-enter`,`after-leave`],setup(e,{emit:n}){let i=e,a=n,s=(0,t.useAttrs)(),c=v(`SbFloating`);(0,t.onBeforeUnmount)(_(c,`floating`,`SbFloating`)),(0,t.onBeforeUnmount)(()=>{c.isUnmounting||h(c,!0)});let{isOpen:f,floatingId:g,referenceId:y,side:b,align:x}=c;(0,t.watchEffect)(()=>{c.floatingRole.value=i.role,c.navigation.value=i.keyboardNavigation&&D(i.role)?i.role:null}),(0,t.onMounted)(()=>{i.role===`tooltip`&&c.trigger.value===`click`&&p(`<SbFloating role="tooltip"> opens on click: a tooltip shows on hover and focus. Set trigger="hover" or "focus" on SbContainer, or use another role for a panel opened by click.`)});let S=(0,t.ref)(f.value),C=(0,t.ref)(null),w=e=>{c.floating.value=e instanceof HTMLElement?e:null},E=e=>{C.value=o(e)},j=(0,t.computed)(()=>({...c.floatingStyles.value,...i.zIndex===void 0?{}:{zIndex:i.zIndex}})),N=()=>({"aria-labelledby":i.role&&Object.hasOwn(T,i.role)&&s[`aria-label`]==null?y.value:void 0,...s}),P=(0,t.computed)(()=>i.autoFocus??(i.role!==`tooltip`&&c.trigger.value!==`hover`&&c.trigger.value!==`focus`&&!k(c.reference.value))),F=(0,t.computed)(()=>f.value&&i.teleport!==!1&&i.role!==`tooltip`),I={[r]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},L=(0,t.computed)(()=>{let{transition:e}=i;return e?typeof e==`string`?{name:e}:e:{css:!1}}),R=e=>e.setAttribute(`data-sbf-state`,`closed`),z=()=>a(`after-enter`),B=()=>{f.value||(S.value=!1),a(`after-leave`)},V=e=>e instanceof Node&&!!C.value?.contains(e),H=e=>{let t=C.value;if(!t)return;let n=u(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},U=e=>{V(e.relatedTarget)?c.reference.value?.focus():H(`first`)},ee=e=>{if(!V(e.relatedTarget)){H(`last`);return}let t=c.reference.value;((t&&d(t,e=>c.contains(e)))??t)?.focus()},W=e=>{if(e.key!==`Tab`||!e.shiftKey||e.defaultPrevented)return;let t=c.reference.value;t&&C.value&&u(C.value).length&&(e.composedPath()[0]??e.target)===d(t,e=>c.contains(e))&&(e.preventDefault(),H(`last`))};(0,t.watch)(F,(e,t,n)=>{e&&typeof document<`u`&&(document.addEventListener(`keydown`,W),n(()=>document.removeEventListener(`keydown`,W)))},{immediate:!0});let G=()=>{let e=c.navigation.value;return C.value&&e?O(C.value,e):[]},te={menu:`[role="menuitem"]`,listbox:`[role="option"]`},ne={menu:`role="menuitem", "menuitemcheckbox" or "menuitemradio"`,listbox:`role="option"`},K=!1,q=e=>{let t=c.navigation.value;if(K||!t)return;let n=e.length?e.every(l)?null:`has items that cannot take focus, so the arrow keys cannot move to them: make each one a button, or give it tabindex="-1".`:`has no items the arrow keys can move to: items need ${ne[t]} and must not be disabled.`;n&&(K=!0,p(`<SbFloating role="${t}"> ${n}`))},J=e=>{let t=C.value;if(!t)return;let n=G();e&&q(n),(n.find(e=>e.getAttribute(`aria-selected`)===`true`)??(e===`last`?n[n.length-1]:e?n[0]:void 0)??t.querySelector(`[autofocus]`)??n[0]??u(t)[0]??t).focus({preventScroll:!0})},Y=()=>{let e=c.focusRequest.value;return c.focusRequest.value=null,e},X=``,Z;(0,t.onScopeDispose)(()=>clearTimeout(Z));let re=(e,t,n)=>{X+=n.toLowerCase(),clearTimeout(Z),Z=setTimeout(()=>X=``,500);let r=[...X].every(e=>e===X[0])?X[0]:X,i=Math.max(t,0),a=[...e.slice(i),...e.slice(0,i)];return r.length===1&&t>=0&&a.push(a.shift()),a.find(e=>e.textContent?.trim().toLowerCase().startsWith(r))},Q=()=>{let e=[c];for(;m(e[e.length-1]);)e.push(e[e.length-1].parent);return e},$=(e,t,n)=>{for(let t of e)t.skipFocusReturn=!0,t.setOpen(!1,n);t?.focus({preventScroll:!0})},ie=e=>{let t=Q(),n=t[t.length-1],r=n.reference.value,i=e?r:(r&&d(r,e=>n.contains(e)))??r;$(t,i,`focus-out`)},ae=e=>{let{role:t}=i,n=C.value;if(!i.closeOnSelect||e.defaultPrevented||!n||!D(t))return;let r=e.target.closest(te[t]);if(!r||r.closest(`[role="${t}"]`)!==n||r.matches(M)||t===`listbox`&&n.getAttribute(`aria-multiselectable`)===`true`)return;let a=t===`menu`?Q():[c];$(a,a[a.length-1].reference.value,`select`)},oe=e=>{let t=c.navigation.value;if(!t||e.defaultPrevented||e.isComposing||k(e.target))return;let n=G(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,o;if([`ArrowDown`,`ArrowUp`,`Home`,`End`].includes(e.key)&&q(n),e.key===`ArrowDown`)o=n[r<i?r+1:a?0:i];else if(e.key===`ArrowUp`)o=n[r>0?r-1:a||r<0?i:0];else if(e.key===`Home`)o=n[0];else if(e.key===`End`)o=n[i];else if(e.key===`Tab`&&t===`menu`){e.preventDefault(),ie(e.shiftKey);return}else if(t===`menu`&&m(c)&&C.value&&e.key===A(C.value).close){e.preventDefault(),c.setOpen(!1,`escape`);return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||X))e.preventDefault(),o=re(n,r,e.key);else if((e.key===`Enter`||e.key===` `)&&e.target===n[r]){let t=n[r];if(t.matches(`button, a[href], input, summary`))return;e.preventDefault(),t.getAttribute(`aria-disabled`)!==`true`&&t.click();return}o&&(e.preventDefault(),o.focus())};return(0,t.watch)(f,e=>{let t=c.skipFocusReturn;if(c.skipFocusReturn=!1,e){S.value=!0;return}let n=document.activeElement;!t&&n&&c.floating.value?.contains(n)&&c.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>f.value&&c.isPositioned.value,async e=>{if(!e)return;let n=Y();(n||P.value)&&(await(0,t.nextTick)(),J(n))}),(0,t.watch)(c.focusRequest,e=>{e&&f.value&&c.isPositioned.value&&J(Y())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:i.teleport||`body`,disabled:i.teleport===!1},[S.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:w,style:(0,t.normalizeStyle)(j.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(c).hover&&(0,t.unref)(c).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(c).hover&&(0,t.unref)(c).hover(...e)},[F.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},I,{onFocus:U}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(L.value,{appear:``,onBeforeLeave:R,onAfterEnter:z,onAfterLeave:B}),{default:(0,t.withCtx)(()=>[(0,t.unref)(f)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(i.as),(0,t.mergeProps)({key:0},N(),{id:(0,t.unref)(g),ref:E,role:i.role??void 0,tabindex:P.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(b),"data-sbf-align":(0,t.unref)(x),onKeydown:oe,onClick:ae}),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,`default`)]),_:3},16,[`id`,`role`,`tabindex`,`data-sbf-side`,`data-sbf-align`])):(0,t.createCommentVNode)(``,!0)]),_:3},16),F.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},I,{onFocus:ee}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});function P(){let e=(0,t.inject)(g,null);if(!e)throw Error(`[sb-floating-panel-vue] useSbFloating() must be called in a component inside <SbContainer>.`);let{isOpen:n,open:r,close:i,toggle:a}=e;return{isOpen:(0,t.readonly)(n),open:r,close:i,toggle:a}}e.SbContainer=w,e.SbFloating=N,e.SbReference=j,e.useSbFloating=P});
package/dist/types.d.ts CHANGED
@@ -16,12 +16,35 @@ export type SbFloatingRole = 'dialog' | 'alertdialog' | 'menu' | 'listbox' | 'tr
16
16
  * How the reference opens the floating panel.
17
17
  * - `click`: clicking (or pressing Enter/Space on) `SbReference` toggles the panel.
18
18
  * - `hover`: the pointer over `SbReference` opens the panel after `delay.open`, keyboard focus
19
- * opens it at once. It closes `delay.close` after the pointer leaves both the reference and the
20
- * panel, so the pointer can move onto the panel, and when focus leaves them. Touch has no hover.
19
+ * (`:focus-visible`, not the focus of a click) opens it at once. It closes `delay.close` after
20
+ * the pointer leaves both the reference and the panel, so the pointer can move onto the panel,
21
+ * and when focus leaves them. Touch has no hover.
21
22
  * - `focus`: focus on `SbReference` opens the panel, focus moving elsewhere closes it.
22
23
  * - `manual`: the panel opens only through `v-model:open`, the slot props or the exposed methods.
23
24
  */
24
25
  export type SbTrigger = 'click' | 'hover' | 'focus' | 'manual';
26
+ /**
27
+ * Why the panel opened, passed to the `open` event of `SbContainer`.
28
+ * - `trigger`: a click on the reference, or Enter, Space or an arrow key on it.
29
+ * - `hover`: the pointer over the reference (`hover` trigger).
30
+ * - `focus`: focus on the reference (`hover` and `focus` triggers).
31
+ * - `api`: `v-model:open`, the slot props, the exposed methods or `useSbFloating()`.
32
+ */
33
+ export type SbOpenReason = 'trigger' | 'hover' | 'focus' | 'api';
34
+ /**
35
+ * Why the panel closed, passed to the `close` event of `SbContainer`.
36
+ * - `trigger`: a click on the reference, or Enter or Space on it.
37
+ * - `hover`: the pointer left the reference and the panel (`hover` trigger).
38
+ * - `focus-out`: focus moved outside the reference and the panel, e.g. Tab out of a menu.
39
+ * - `escape`: the Escape key, or the arrow key that closes a submenu.
40
+ * - `outside`: a press outside the reference and the panel.
41
+ * - `select`: an item was activated with `closeOnSelect`.
42
+ * - `detached`: the reference was scrolled out of view, with `hideWhenDetached: 'close'`.
43
+ * - `disabled`: the container was disabled.
44
+ * - `api`: `v-model:open`, the slot props, the exposed methods, `useSbFloating()`, or the
45
+ * reference unmounting.
46
+ */
47
+ export type SbCloseReason = 'trigger' | 'hover' | 'focus-out' | 'escape' | 'outside' | 'select' | 'detached' | 'disabled' | 'api';
25
48
  /**
26
49
  * Props for the `SbContainer` component: positioning, open state and interaction.
27
50
  */
@@ -4,6 +4,8 @@ export declare const FOCUS_GUARD_ATTRIBUTE = "data-sbf-focus-guard";
4
4
  export declare function toElement(el: unknown): HTMLElement | null;
5
5
  /** Whether pressing `target` moves focus to it (or to a focusable ancestor). */
6
6
  export declare function isFocusable(target: EventTarget | null): boolean;
7
+ /** Whether focus on `el` came from the keyboard (`:focus-visible`); true where it is unsupported. */
8
+ export declare function isFocusVisible(el: Element): boolean;
7
9
  /** Whether `el` itself can take focus, with Tab or from script. */
8
10
  export declare const canFocus: (el: Element) => boolean;
9
11
  /** Elements reachable with Tab inside `root`, in document order (focus guards excluded). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sb-floating-panel-vue",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "description": "Headless Vue 3 floating panel components (dropdowns, popovers, menus) powered by @floating-ui/vue: positioning, accessibility and open state handled, zero CSS shipped.",
5
5
  "keywords": [
6
6
  "vue",