sb-floating-panel-vue 5.0.3 → 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.
- package/README.md +133 -80
- package/dist/components/SbContainer.vue.d.ts +7 -7
- package/dist/components/SbFloating.vue.d.ts +3 -2
- package/dist/composables/useSbFloating.d.ts +6 -0
- package/dist/composables/useSbFloatingContext.d.ts +20 -2
- package/dist/index.d.ts +2 -1
- package/dist/sb-floating-panel.es.js +308 -249
- package/dist/sb-floating-panel.umd.js +1 -1
- package/dist/types.d.ts +48 -3
- package/dist/utils/dom.d.ts +2 -0
- package/package.json +1 -1
|
@@ -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=(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}},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},O=e=>{S.value=m(e)},k=(0,t.computed)(()=>({...l.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),A=()=>({"aria-labelledby":r.role&&Object.hasOwn(x,r.role)&&c[`aria-label`]==null?p.value:void 0,...c}),j=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&l.trigger.value!==`hover`&&l.trigger.value!==`focus`&&!T(l.reference.value))),M=(0,t.computed)(()=>u.value&&r.teleport!==!1&&r.role!==`tooltip`),N={[d]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},P=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),F=e=>e.setAttribute(`data-sbf-state`,`closed`),I=()=>a(`after-enter`),L=()=>{u.value||(b.value=!1),a(`after-leave`)},R=e=>e instanceof Node&&!!S.value?.contains(e),z=e=>{let t=S.value;if(!t)return;let n=_(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},B=e=>{R(e.relatedTarget)?l.reference.value?.focus():z(`first`)},V=e=>{if(!R(e.relatedTarget)){z(`last`);return}let t=l.reference.value;((t&&v(t,e=>l.contains(e)))??t)?.focus()},H=()=>{let e=l.navigation.value;return S.value&&e?w(S.value,e):[]},U={menu:`role="menuitem", "menuitemcheckbox" or "menuitemradio"`,listbox:`role="option"`},W=!1,G=e=>{let t=l.navigation.value;if(W||!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 ${U[t]} and must not be disabled.`;n&&(W=!0,i(`<SbFloating role="${t}"> ${n}`))},K=e=>{let t=S.value;if(!t)return;let n=H();e&&G(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})},q=()=>{let e=l.focusRequest.value;return l.focusRequest.value=null,e},J=``,Y;(0,t.onScopeDispose)(()=>clearTimeout(Y));let X=(e,t,n)=>{J+=n.toLowerCase(),clearTimeout(Y),Y=setTimeout(()=>J=``,500);let r=[...J].every(e=>e===J[0])?J[0]:J,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))},Z=e=>{let t=l,n=[t];for(;t.parent?.navigation.value===`menu`;)t=t.parent,n.push(t);let r=t.reference.value,i=e?r:(r&&v(r,e=>t.contains(e)))??r;for(let e of n)e.skipFocusReturn=!0,e.close();i?.focus()},Q=e=>{let t=l.navigation.value;if(!t||e.defaultPrevented||e.isComposing||T(e.target))return;let n=H(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,o;if([`ArrowDown`,`ArrowUp`,`Home`,`End`].includes(e.key)&&G(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(),Z(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!==` `||J))e.preventDefault(),o=X(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=q();(n||j.value)&&(await(0,t.nextTick)(),K(n))}),(0,t.watch)(l.focusRequest,e=>{e&&u.value&&l.isPositioned.value&&K(q())}),(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)(k.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)},[M.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},N,{onFocus:B}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(P.value,{appear:``,onBeforeLeave:F,onAfterEnter:I,onAfterLeave:L}),{default:(0,t.withCtx)(()=>[(0,t.unref)(u)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},A(),{id:(0,t.unref)(f),ref:O,role:r.role??void 0,tabindex:j.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(y),onKeydown:Q}),{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),M.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},N,{onFocus:V}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});e.SbContainer=b,e.SbFloating=O,e.SbReference=D});
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, TransitionProps } from 'vue';
|
|
1
|
+
import { Component, Ref, TransitionProps } from 'vue';
|
|
2
2
|
/** Side of the reference the panel is placed on. */
|
|
3
3
|
export type SbSide = 'top' | 'right' | 'bottom' | 'left';
|
|
4
4
|
/** Alignment of the panel along the side. */
|
|
@@ -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
|
|
20
|
-
* panel, so the pointer can move onto the panel,
|
|
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
|
*/
|
|
@@ -246,4 +269,26 @@ export interface SbFloatingProps {
|
|
|
246
269
|
* @default true
|
|
247
270
|
*/
|
|
248
271
|
keyboardNavigation?: boolean;
|
|
272
|
+
/**
|
|
273
|
+
* With `role="menu"` or `role="listbox"`: activating an item (click, Enter or Space) closes
|
|
274
|
+
* the panel and gives focus back to the reference, after the `click` handler of the item.
|
|
275
|
+
* - Menu: `menuitem` items, not `menuitemcheckbox` / `menuitemradio` nor submenu triggers.
|
|
276
|
+
* The menus it opened from close too.
|
|
277
|
+
* - Listbox: `option` items, unless the listbox is `aria-multiselectable="true"`.
|
|
278
|
+
*
|
|
279
|
+
* Disabled items are left out. Call `event.preventDefault()` in the `click` handler of an
|
|
280
|
+
* item to keep the panel open.
|
|
281
|
+
* @default false
|
|
282
|
+
*/
|
|
283
|
+
closeOnSelect?: boolean;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returned by `useSbFloating()`: the open state of the closest `SbContainer` and its actions.
|
|
287
|
+
*/
|
|
288
|
+
export interface UseSbFloatingReturn {
|
|
289
|
+
/** Whether the panel is open. */
|
|
290
|
+
isOpen: Readonly<Ref<boolean>>;
|
|
291
|
+
open: () => void;
|
|
292
|
+
close: () => void;
|
|
293
|
+
toggle: () => void;
|
|
249
294
|
}
|
package/dist/utils/dom.d.ts
CHANGED
|
@@ -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.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",
|