sb-floating-panel-vue 4.0.0 → 5.0.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 +175 -108
- package/dist/components/SbContainer.vue.d.ts +7 -3
- package/dist/components/SbFloating.vue.d.ts +6 -6
- package/dist/components/SbReference.vue.d.ts +2 -2
- package/dist/composables/useSbFloatingPanel.d.ts +3 -1
- package/dist/context.d.ts +20 -0
- package/dist/dev.d.ts +2 -0
- package/dist/focus.d.ts +16 -0
- package/dist/index.d.ts +1 -1
- package/dist/sb-floating-panel.es.js +322 -171
- package/dist/sb-floating-panel.umd.js +1 -1
- package/dist/types.d.ts +59 -15
- 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=e=>`${Math.max(0,e)}px`,i=e=>{let[t,n]=e.split(`-`),r=n===`start`?`0%`:n===`end`?`100%`:`50%`;return t===`top`?`${r} 100%`:t===`bottom`?`${r} 0%`:t===`left`?`100% ${r}`:`0% ${r}`};function a(e){let a=(0,t.ref)(null),o=(0,t.ref)(null),s=(0,t.computed)(()=>{let a=(0,t.toValue)(e.padding),o=(0,t.toValue)(e.matchWidth),s=(0,t.toValue)(e.placement).includes(`-`),c=(0,n.flip)({padding:a,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),l=(0,n.shift)({padding:a,limiter:(0,n.limitShift)()}),u=[(0,n.offset)((0,t.toValue)(e.offset)),...s?[c,l]:[l,c],(0,n.size)({padding:a,apply({rects:e,elements:t,availableWidth:n,availableHeight:a,placement:s}){let{style:c}=t.floating,l=r(e.reference.width);c.setProperty(`--sbf-reference-width`,l),c.setProperty(`--sbf-transform-origin`,i(s)),c.setProperty(`--sbf-available-width`,r(n)),c.setProperty(`--sbf-available-height`,r(a)),c.width=o===!0?l:``,c.minWidth=o===`min`?l:``}})];return(0,t.toValue)(e.hideWhenDetached)&&u.push((0,n.hide)({padding:a})),u}),{floatingStyles:c,middlewareData:l,placement:u,isPositioned:d,update:f}=(0,n.useFloating)(a,o,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:s,whileElementsMounted:n.autoUpdate});return{reference:a,floating:o,floatingStyles:(0,t.computed)(()=>({...c.value,...l.value.hide?.referenceHidden?{visibility:`hidden`}:{}})),placement:u,side:(0,t.computed)(()=>u.value.split(`-`)[0]),align:(0,t.computed)(()=>u.value.split(`-`)[1]),isPositioned:d,update:f}}var o={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},s=Symbol(`SbFloatingContext`);function c(e){let n=(0,t.inject)(s,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var l=`data-sbf-focus-guard`,u=[`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(`,`),d=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function f(e){let t=e instanceof Element?e.closest(u):null;return!!t&&!t.closest(`[inert]`)}function p(e){return[...e.querySelectorAll(u)].filter(d)}function m(e,t){return p(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var h=[],g=(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,default:!1},defaultOpen:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},trigger:{default:`click`},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`),c=r,l=i.defaultOpen,u=(0,t.computed)(()=>!i.disabled&&(o.value??l)),d=e=>{e&&i.disabled||e!==u.value&&(o.value=e)},p=()=>d(!0),m=()=>d(!1),g=()=>d(!u.value),_=a({isOpen:u,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),v=new Set,y={..._,isOpen:u,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`),skipFocusReturn:!1,open:p,close:m,toggle:g,contains:e=>e instanceof Node?_.reference.value?.contains(e)||_.floating.value?.contains(e)?!0:[...v].some(t=>t.contains(e)):!1,registerChild:e=>(v.add(e),()=>v.delete(e))},b=(0,t.inject)(s,null);b&&(0,t.onScopeDispose)(b.registerChild(y)),(0,t.provide)(s,y);let x=e=>{e.key===`Escape`&&h[h.length-1]===y&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),m()))},S=e=>{y.contains(e)||(y.skipFocusReturn=f(e),m())},C,w=e=>{if(C?.(),!i.closeOnClickOutside)return;let t=e.composedPath()[0]??e.target;if(e.pointerType!==`touch`){S(t);return}if(y.contains(t))return;let n=e=>{C?.(),S(e.composedPath()[0]??e.target)};document.addEventListener(`click`,n,!0),C=()=>{document.removeEventListener(`click`,n,!0),C=void 0}},T=e=>{i.closeOnFocusOut&&(y.contains(e.composedPath()[0]??e.target)||m())};return(0,t.watch)(u,(e,t,n)=>{e&&typeof document<`u`&&(h.push(y),document.addEventListener(`keydown`,x),document.addEventListener(`pointerdown`,w,!0),document.addEventListener(`focusin`,T),n(()=>{document.removeEventListener(`keydown`,x),document.removeEventListener(`pointerdown`,w,!0),document.removeEventListener(`focusin`,T),C?.(),h.splice(h.indexOf(y),1)}))},{immediate:!0}),(0,t.watch)(u,e=>c(e?`open`:`close`)),(0,t.watch)(()=>i.disabled,e=>{e&&(o.value??l)&&(o.value=!1)}),n({open:p,close:m,toggle:g,update:_.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:u.value,open:p,close:m,toggle:g,placement:(0,t.unref)(_).placement.value,side:(0,t.unref)(_).side.value,align:(0,t.unref)(_).align.value})}}),_=(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=c(`SbReference`),{isOpen:a,disabled:s,floatingId:l,floatingRole:u}=i,d=()=>typeof n.as!=`string`||n.as===`button`||n.as===`a`&&r.href!=null,f=`${(0,t.useId)()}-reference`,m=(0,t.computed)(()=>n.id??f);(0,t.watchEffect)(()=>{i.referenceId.value=m.value}),(0,t.onBeforeUnmount)(()=>{i.referenceId.value===m.value&&(i.referenceId.value=void 0)});let h=(0,t.computed)(()=>n.ariaHaspopup??(u.value&&Object.hasOwn(o,u.value)?o[u.value]:void 0)),g=e=>{let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;i.reference.value=t instanceof HTMLElement?t:null},_=()=>{i.trigger.value===`click`&&i.toggle()},v=e=>{let t=i.floating.value,n=t&&p(t)[0];n&&(e.preventDefault(),n.focus())},y=e=>{e.key===`Tab`&&!e.shiftKey&&a.value?v(e):d()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),_())};return(e,r)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(n.as),{id:m.value,ref:g,type:n.as===`button`?`button`:void 0,role:d()?void 0:`button`,tabindex:d()?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":h.value,"aria-expanded":(0,t.unref)(a)?`true`:`false`,"aria-controls":(0,t.unref)(a)?(0,t.unref)(l):void 0,"data-sbf-state":(0,t.unref)(a)?`open`:`closed`,onClick:_,onKeydown:y},{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`,`data-sbf-state`]))}}),v=(0,t.defineComponent)({inheritAttrs:!1,__name:`SbFloating`,props:{as:{default:`div`},role:{default:`dialog`},transition:{default:void 0},teleportTo:{default:`body`},teleportDisabled:{type:Boolean,default:!1},zIndex:{default:void 0},autoFocus:{type:Boolean,default:!0}},emits:[`after-enter`,`after-leave`],setup(e,{emit:n}){let r=e,i=n,a=(0,t.useAttrs)(),s=c(`SbFloating`),{isOpen:u,floatingId:d,referenceId:f,side:h,align:g}=s;(0,t.watchEffect)(()=>{s.floatingRole.value=r.role});let _=(0,t.ref)(u.value),v=(0,t.ref)(null),y=e=>{s.floating.value=e instanceof HTMLElement?e:null},b=e=>{v.value=e instanceof HTMLElement?e:null},x=(0,t.computed)(()=>({...s.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),S=()=>({"aria-labelledby":r.role&&Object.hasOwn(o,r.role)&&a[`aria-label`]==null?f.value:void 0,...a}),C=(0,t.computed)(()=>u.value&&!r.teleportDisabled),w={[l]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},T=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),E=e=>e.setAttribute(`data-sbf-state`,`closed`),D=()=>i(`after-enter`),O=()=>{u.value||(_.value=!1),i(`after-leave`)},k=e=>e instanceof Node&&!!v.value?.contains(e),A=e=>{let t=v.value;if(!t)return;let n=p(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},j=e=>{k(e.relatedTarget)?s.reference.value?.focus():A(`first`)},M=e=>{if(!k(e.relatedTarget)){A(`last`);return}let t=s.reference.value;((t&&m(t,e=>s.contains(e)))??t)?.focus()},N=()=>{let e=v.value;e&&(e.querySelector(`[autofocus]`)??p(e)[0]??e).focus({preventScroll:!0})};return(0,t.watch)(u,e=>{let t=s.skipFocusReturn;if(s.skipFocusReturn=!1,e){_.value=!0;return}let n=document.activeElement;!t&&n&&s.floating.value?.contains(n)&&s.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>u.value&&s.isPositioned.value,async e=>{e&&r.autoFocus&&(await(0,t.nextTick)(),N())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:r.teleportTo,disabled:r.teleportDisabled},[_.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:y,style:(0,t.normalizeStyle)(x.value)},[C.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},w,{onFocus:j}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(T.value,{appear:``,onBeforeLeave:E,onAfterEnter:D,onAfterLeave:O}),{default:(0,t.withCtx)(()=>[(0,t.unref)(u)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},S(),{id:(0,t.unref)(d),ref:b,role:r.role??void 0,tabindex:r.autoFocus?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(g)}),{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),C.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},w,{onFocus:M}),null,16)):(0,t.createCommentVNode)(``,!0)],4)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});e.SbContainer=g,e.SbFloating=v,e.SbReference=_});
|
|
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=e=>`${Math.max(0,e)}px`,i=(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 a(e){let a=(0,t.ref)(null),o=(0,t.ref)(null),s=(0,t.computed)(()=>{let a=(0,t.toValue)(e.padding),o=(0,t.toValue)(e.matchWidth),s=(0,t.toValue)(e.placement).includes(`-`),c=(0,n.flip)({padding:a,crossAxis:`alignment`,fallbackAxisSideDirection:`end`}),l=(0,n.shift)({padding:a,limiter:(0,n.limitShift)()}),u=[(0,n.offset)((0,t.toValue)(e.offset)),...s?[c,l]:[l,c],(0,n.size)({padding:a,apply({rects:e,elements:t,availableWidth:n,availableHeight:a,placement:s}){let{style:c}=t.floating,l=r(e.reference.width);c.setProperty(`--sbf-reference-width`,l);let u=getComputedStyle(t.floating).direction===`rtl`;c.setProperty(`--sbf-transform-origin`,i(s,u)),c.setProperty(`--sbf-available-width`,r(n)),c.setProperty(`--sbf-available-height`,r(a)),c.width=o===!0?l:``,c.minWidth=o===`min`?l:``}})];return(0,t.toValue)(e.hideWhenDetached)&&u.push((0,n.hide)({padding:a})),u}),{floatingStyles:c,middlewareData:l,placement:u,isPositioned:d,update:f}=(0,n.useFloating)(a,o,{open:()=>(0,t.toValue)(e.isOpen),placement:()=>(0,t.toValue)(e.placement),strategy:()=>(0,t.toValue)(e.strategy),middleware:s,whileElementsMounted:n.autoUpdate}),p=(0,t.computed)(()=>!!l.value.hide?.referenceHidden);return{reference:a,floating:o,floatingStyles:(0,t.computed)(()=>({...c.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 o=(()=>{try{return process.env.NODE_ENV!==`production`}catch{return!1}})();function s(e){o&&console.warn(`[sb-floating-panel-vue] ${e}`)}var c={dialog:`dialog`,alertdialog:`dialog`,menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`},l=Symbol(`SbFloatingContext`);function u(e){let t=e&&typeof e==`object`&&`$el`in e?e.$el:e;return t instanceof HTMLElement?t:null}function d(e,t,n){return++e.parts[t]>1&&s(`<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 f(e){let n=(0,t.inject)(l,null);if(!n)throw Error(`[sb-floating-panel-vue] <${e}> must be used inside <SbContainer>.`);return n}var p=`data-sbf-focus-guard`,m=[`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(`,`),h=e=>e.tabIndex>=0&&!e.hasAttribute(`data-sbf-focus-guard`)&&!e.closest(`[inert]`)&&(e.checkVisibility?.()??!0);function g(e){let t=e instanceof Element?e.closest(m):null;return!!t&&!t.closest(`[inert]`)}function _(e){return[...e.querySelectorAll(m)].filter(h)}var v={menu:`[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]`,listbox:`[role="option"]`},y=e=>!!e&&Object.hasOwn(v,e);function b(e,t){return[...e.querySelectorAll(v[t])].filter(n=>n.closest(`[role="${t}"]`)===e&&!n.matches(`:disabled`)&&(n.checkVisibility?.()??!0))}var x=e=>e instanceof Element&&!!e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`),S=e=>getComputedStyle(e).direction===`rtl`?{open:`ArrowLeft`,close:`ArrowRight`}:{open:`ArrowRight`,close:`ArrowLeft`};function C(e,t){return _(e.ownerDocument).find(n=>!!(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)&&!e.contains(n)&&!t(n))}var w=[],T=(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,u=(0,t.computed)(()=>!i.disabled&&(o.value??c)),d;(0,t.onScopeDispose)(()=>clearTimeout(d));let f=e=>{clearTimeout(d),!(e&&i.disabled)&&e!==u.value&&(o.value=e)},p=()=>f(!0),m=()=>f(!1),h=()=>f(!u.value),_=e=>{if(i.trigger!==`hover`||e.pointerType===`touch`)return;let t=e.type===`pointerenter`;if(clearTimeout(d),t===u.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=a({isOpen:u,placement:()=>i.placement,strategy:()=>i.strategy,offset:()=>i.offset,padding:()=>i.padding,matchWidth:()=>i.matchWidth,hideWhenDetached:()=>i.hideWhenDetached}),y=new Set,b=(0,t.inject)(l,null),x={...v,isOpen:u,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:b,skipFocusReturn:!1,parts:{reference:0,floating:0},isUnmounting:!1,open:p,close:m,toggle:h,hover:_,contains:e=>e instanceof Node?v.reference.value?.contains(e)||v.floating.value?.contains(e)?!0:[...y].some(t=>t.contains(e)):!1,registerChild:e=>(y.add(e),()=>y.delete(e))};b&&(0,t.onScopeDispose)(b.registerChild(x)),(0,t.provide)(l,x),(0,t.onBeforeUnmount)(()=>x.isUnmounting=!0);let S=e=>{let t=e.composedPath()[0]??e.target;return w.findLast(e=>e.contains(t))??w[w.length-1]},C=e=>{e.key===`Escape`&&S(e)===x&&(!i.closeOnEscape||e.defaultPrevented||e.isComposing||(e.preventDefault(),m()))},T=e=>{x.contains(e)||(x.skipFocusReturn=g(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(x.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&&(x.contains(e.composedPath()[0]??e.target)||m())};return(0,t.watch)(u,(e,t,n)=>{e&&typeof document<`u`&&(w.push(x),document.addEventListener(`keydown`,C),document.addEventListener(`pointerdown`,D,!0),document.addEventListener(`focusin`,O),n(()=>{document.removeEventListener(`keydown`,C),document.removeEventListener(`pointerdown`,D,!0),document.removeEventListener(`focusin`,O),E?.(),w.splice(w.indexOf(x),1)}))},{immediate:!0}),(0,t.watch)(u,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 u.value},open:p,close:m,toggle:h,update:v.update}),(e,n)=>(0,t.renderSlot)(e.$slots,`default`,{isOpen:u.value,open:p,close:m,toggle:h,placement:(0,t.unref)(v).placement.value,side:(0,t.unref)(v).side.value,align:(0,t.unref)(v).align.value,floatingId:x.floatingId,referenceId:x.referenceId.value})}}),E=(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=f(`SbReference`),{isOpen:a,disabled:o,floatingId:l,floatingRole:p}=i,m=d(i,`reference`,`SbReference`),h=()=>typeof n.as!=`string`||n.as===`button`||n.as===`a`&&r.href!=null,g=`${(0,t.useId)()}-reference`,v=(0,t.computed)(()=>n.id??g);(0,t.watchEffect)(()=>{i.referenceId.value=v.value}),(0,t.onMounted)(()=>{i.reference.value||s("<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===v.value&&(i.referenceId.value=void 0),m(),!i.parts.reference&&!i.isUnmounting&&i.close()});let y=(0,t.computed)(()=>n.ariaHaspopup??(p.value&&Object.hasOwn(c,p.value)?c[p.value]:void 0)),b=e=>{i.reference.value=u(e)},x=()=>{i.trigger.value===`click`&&i.toggle()},C=(0,t.computed)(()=>p.value===`tooltip`),w=()=>i.trigger.value===`hover`||i.trigger.value===`focus`,T=e=>{w()&&!i.contains(e.relatedTarget)&&i.open()},E=e=>{w()&&!i.contains(e.relatedTarget)&&i.close()},D=e=>{let t=i.floating.value,n=t&&_(t)[0];n&&(e.preventDefault(),n.focus())},O=e=>{if(i.navigation.value&&!o.value&&i.trigger.value!==`manual`){if(i.parent?.navigation.value===`menu`){let t=e.currentTarget;return e.key===S(t).open?`first`:void 0}return e.key===`ArrowDown`?`first`:e.key===`ArrowUp`?`last`:void 0}},k=e=>{let t=i.parent?.navigation.value===`menu`;if(e.key===`Tab`&&!e.shiftKey&&a.value&&!t){D(e);return}let n=O(e);n?(e.preventDefault(),i.focusRequest.value=n,i.open()):h()||e.key!==`Enter`&&e.key!==` `||(e.preventDefault(),x())};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)(o)?-1:0,disabled:n.as===`button`&&(0,t.unref)(o)?!0:void 0,"aria-disabled":n.as!==`button`&&(0,t.unref)(o)?`true`:void 0,"aria-haspopup":y.value,"aria-expanded":C.value?void 0:(0,t.unref)(a)?`true`:`false`,"aria-controls":(0,t.unref)(a)&&!C.value?(0,t.unref)(l):void 0,"aria-describedby":(0,t.unref)(a)&&C.value?(0,t.unref)(l):void 0,"data-sbf-state":(0,t.unref)(a)?`open`:`closed`,onClick:x,onKeydown:k,onFocusin:T,onFocusout:E,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`]))}}),D=(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,i=n,a=(0,t.useAttrs)(),o=f(`SbFloating`);(0,t.onBeforeUnmount)(d(o,`floating`,`SbFloating`));let{isOpen:s,floatingId:l,referenceId:m,side:h,align:g}=o;(0,t.watchEffect)(()=>{o.floatingRole.value=r.role,o.navigation.value=r.keyboardNavigation&&y(r.role)?r.role:null});let v=(0,t.ref)(s.value),w=(0,t.ref)(null),T=e=>{o.floating.value=e instanceof HTMLElement?e:null},E=e=>{w.value=u(e)},D=(0,t.computed)(()=>({...o.floatingStyles.value,...r.zIndex===void 0?{}:{zIndex:r.zIndex}})),O=()=>({"aria-labelledby":r.role&&Object.hasOwn(c,r.role)&&a[`aria-label`]==null?m.value:void 0,...a}),k=(0,t.computed)(()=>r.autoFocus??(r.role!==`tooltip`&&o.trigger.value!==`hover`&&o.trigger.value!==`focus`)),A=(0,t.computed)(()=>s.value&&r.teleport!==!1&&r.role!==`tooltip`),j={[p]:``,tabindex:0,style:{position:`fixed`,opacity:0,outline:`none`,pointerEvents:`none`}},M=(0,t.computed)(()=>{let{transition:e}=r;return e?typeof e==`string`?{name:e}:e:{css:!1}}),N=e=>e.setAttribute(`data-sbf-state`,`closed`),P=()=>i(`after-enter`),F=()=>{s.value||(v.value=!1),i(`after-leave`)},I=e=>e instanceof Node&&!!w.value?.contains(e),L=e=>{let t=w.value;if(!t)return;let n=_(t);((e===`first`?n[0]:n[n.length-1])??t).focus({preventScroll:!0})},R=e=>{I(e.relatedTarget)?o.reference.value?.focus():L(`first`)},z=e=>{if(!I(e.relatedTarget)){L(`last`);return}let t=o.reference.value;((t&&C(t,e=>o.contains(e)))??t)?.focus()},B=()=>{let e=o.navigation.value;return w.value&&e?b(w.value,e):[]},V=e=>{let t=w.value;if(!t)return;let n=B();(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})},H=()=>{let e=o.focusRequest.value;return o.focusRequest.value=null,e},U=``,W;(0,t.onScopeDispose)(()=>clearTimeout(W));let G=(e,t,n)=>{U+=n.toLowerCase(),clearTimeout(W),W=setTimeout(()=>U=``,500);let r=[...U].every(e=>e===U[0])?U[0]:U,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))},K=e=>{let t=o,n=[t];for(;t.parent?.navigation.value===`menu`;)t=t.parent,n.push(t);let r=t.reference.value,i=e?r:(r&&C(r,e=>t.contains(e)))??r;for(let e of n)e.skipFocusReturn=!0,e.close();i?.focus()},q=e=>{let t=o.navigation.value;if(!t||e.defaultPrevented||e.isComposing||x(e.target))return;let n=B(),r=n.findIndex(t=>t.contains(e.target)),i=n.length-1,a=t===`menu`,s;if(e.key===`ArrowDown`)s=n[r<i?r+1:a?0:i];else if(e.key===`ArrowUp`)s=n[r>0?r-1:a||r<0?i:0];else if(e.key===`Home`)s=n[0];else if(e.key===`End`)s=n[i];else if(e.key===`Tab`&&t===`menu`){e.preventDefault(),K(e.shiftKey);return}else if(t===`menu`&&o.parent?.navigation.value===`menu`&&w.value&&e.key===S(w.value).close){e.preventDefault(),o.close();return}else if(e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.key!==` `||U))e.preventDefault(),s=G(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}s&&(e.preventDefault(),s.focus())};return(0,t.watch)(s,e=>{let t=o.skipFocusReturn;if(o.skipFocusReturn=!1,e){v.value=!0;return}let n=document.activeElement;!t&&n&&o.floating.value?.contains(n)&&o.reference.value?.focus({preventScroll:!0})}),(0,t.watch)(()=>s.value&&o.isPositioned.value,async e=>{if(!e)return;let n=H();(n||k.value)&&(await(0,t.nextTick)(),V(n))}),(0,t.watch)(o.focusRequest,e=>{e&&s.value&&o.isPositioned.value&&V(H())}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:r.teleport||`body`,disabled:r.teleport===!1},[v.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,ref:T,style:(0,t.normalizeStyle)(D.value),onPointerenter:n[0]||=(...e)=>(0,t.unref)(o).hover&&(0,t.unref)(o).hover(...e),onPointerleave:n[1]||=(...e)=>(0,t.unref)(o).hover&&(0,t.unref)(o).hover(...e)},[A.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:0},j,{onFocus:R}),null,16)):(0,t.createCommentVNode)(``,!0),(0,t.createVNode)(t.Transition,(0,t.mergeProps)(M.value,{appear:``,onBeforeLeave:N,onAfterEnter:P,onAfterLeave:F}),{default:(0,t.withCtx)(()=>[(0,t.unref)(s)?((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(r.as),(0,t.mergeProps)({key:0},O(),{id:(0,t.unref)(l),ref:E,role:r.role??void 0,tabindex:k.value?-1:void 0,"data-sbf-state":`open`,"data-sbf-side":(0,t.unref)(h),"data-sbf-align":(0,t.unref)(g),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),A.value?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,(0,t.mergeProps)({key:1},j,{onFocus:z}),null,16)):(0,t.createCommentVNode)(``,!0)],36)):(0,t.createCommentVNode)(``,!0)],8,[`to`,`disabled`]))}});e.SbContainer=T,e.SbFloating=D,e.SbReference=E});
|
package/dist/types.d.ts
CHANGED
|
@@ -7,12 +7,21 @@ export type SbAlignment = 'start' | 'end';
|
|
|
7
7
|
export type SbPlacement = SbSide | `${SbSide}-${SbAlignment}`;
|
|
8
8
|
/** CSS position strategy of the panel. */
|
|
9
9
|
export type SbStrategy = 'absolute' | 'fixed';
|
|
10
|
+
/**
|
|
11
|
+
* ARIA role of the panel. The roles the components act on are suggested by your editor;
|
|
12
|
+
* any other role is accepted as a string.
|
|
13
|
+
*/
|
|
14
|
+
export type SbFloatingRole = 'dialog' | 'alertdialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'tooltip' | (string & {});
|
|
10
15
|
/**
|
|
11
16
|
* How the reference opens the floating panel.
|
|
12
17
|
* - `click`: clicking (or pressing Enter/Space on) `SbReference` toggles the panel.
|
|
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.
|
|
21
|
+
* - `focus`: focus on `SbReference` opens the panel, focus moving elsewhere closes it.
|
|
13
22
|
* - `manual`: the panel opens only through `v-model:open`, the slot props or the exposed methods.
|
|
14
23
|
*/
|
|
15
|
-
export type SbTrigger = 'click' | 'manual';
|
|
24
|
+
export type SbTrigger = 'click' | 'hover' | 'focus' | 'manual';
|
|
16
25
|
/**
|
|
17
26
|
* Props for the `SbContainer` component: positioning, open state and interaction.
|
|
18
27
|
*/
|
|
@@ -48,10 +57,12 @@ export interface SbContainerProps {
|
|
|
48
57
|
*/
|
|
49
58
|
matchWidth?: boolean | 'min';
|
|
50
59
|
/**
|
|
51
|
-
*
|
|
60
|
+
* When the reference is scrolled out of view:
|
|
61
|
+
* - `true`: hides the floating panel (`visibility: hidden`) until the reference is back.
|
|
62
|
+
* - `'close'`: closes the panel, so focus never stays inside a hidden panel.
|
|
52
63
|
* @default false
|
|
53
64
|
*/
|
|
54
|
-
hideWhenDetached?: boolean;
|
|
65
|
+
hideWhenDetached?: boolean | 'close';
|
|
55
66
|
/**
|
|
56
67
|
* Open state, bound with `v-model:open`. Leave it unset to let the container manage it.
|
|
57
68
|
*/
|
|
@@ -71,6 +82,15 @@ export interface SbContainerProps {
|
|
|
71
82
|
* @default 'click'
|
|
72
83
|
*/
|
|
73
84
|
trigger?: SbTrigger;
|
|
85
|
+
/**
|
|
86
|
+
* Delay in milliseconds before the `hover` trigger opens and closes the panel: a number for
|
|
87
|
+
* both, or an object (missing keys keep their default). Focus opens the panel without delay.
|
|
88
|
+
* @default { open: 300, close: 150 }
|
|
89
|
+
*/
|
|
90
|
+
delay?: number | {
|
|
91
|
+
open?: number;
|
|
92
|
+
close?: number;
|
|
93
|
+
};
|
|
74
94
|
/**
|
|
75
95
|
* Closes the panel when the Escape key is pressed.
|
|
76
96
|
* @default true
|
|
@@ -106,11 +126,17 @@ export interface SbContainerSlotProps {
|
|
|
106
126
|
side: SbSide;
|
|
107
127
|
/** Alignment of the resolved placement, `undefined` when centered. */
|
|
108
128
|
align: SbAlignment | undefined;
|
|
129
|
+
/** `id` of the panel, e.g. for an `aria-describedby` of your own. */
|
|
130
|
+
floatingId: string;
|
|
131
|
+
/** `id` of the reference, `undefined` until `SbReference` is set up. */
|
|
132
|
+
referenceId: string | undefined;
|
|
109
133
|
}
|
|
110
134
|
/**
|
|
111
135
|
* Methods exposed by `SbContainer` through a template ref.
|
|
112
136
|
*/
|
|
113
137
|
export interface SbContainerExposed {
|
|
138
|
+
/** Whether the panel is open. */
|
|
139
|
+
readonly isOpen: boolean;
|
|
114
140
|
/** Opens the panel. */
|
|
115
141
|
open: () => void;
|
|
116
142
|
/** Closes the panel. */
|
|
@@ -153,18 +179,21 @@ export interface SbReferenceProps {
|
|
|
153
179
|
*/
|
|
154
180
|
export interface SbFloatingProps {
|
|
155
181
|
/**
|
|
156
|
-
* HTML tag of the panel element (the one that receives your `class`, `style`
|
|
182
|
+
* HTML tag or component of the panel element (the one that receives your `class`, `style`
|
|
183
|
+
* and attributes). A component must render a single root element.
|
|
157
184
|
* @default 'div'
|
|
158
185
|
*/
|
|
159
|
-
as?: string;
|
|
186
|
+
as?: string | Component;
|
|
160
187
|
/**
|
|
161
188
|
* ARIA role of the panel. With a popup role (`dialog`, `alertdialog`, `menu`, `listbox`,
|
|
162
189
|
* `tree`, `grid`), the panel is named after the reference with `aria-labelledby`, unless
|
|
163
190
|
* you pass `aria-label` or `aria-labelledby` yourself.
|
|
191
|
+
* With `tooltip`, the open panel describes the reference with `aria-describedby` (unless you
|
|
192
|
+
* pass it on `SbReference`) instead of `aria-expanded` and `aria-controls`.
|
|
164
193
|
* `null` renders no role (disclosure pattern).
|
|
165
194
|
* @default 'dialog'
|
|
166
195
|
*/
|
|
167
|
-
role?:
|
|
196
|
+
role?: SbFloatingRole | null;
|
|
168
197
|
/**
|
|
169
198
|
* Animation of the panel, through a Vue `<Transition>` on the panel element.
|
|
170
199
|
* - A name: the classes of your CSS (`<name>-enter-active`, …).
|
|
@@ -177,15 +206,11 @@ export interface SbFloatingProps {
|
|
|
177
206
|
*/
|
|
178
207
|
transition?: string | TransitionProps;
|
|
179
208
|
/**
|
|
180
|
-
*
|
|
209
|
+
* Where the panel is teleported: a CSS selector or an element. `false` renders it in place,
|
|
210
|
+
* next to the reference.
|
|
181
211
|
* @default 'body'
|
|
182
212
|
*/
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Renders the panel in place instead of teleporting it.
|
|
186
|
-
* @default false
|
|
187
|
-
*/
|
|
188
|
-
teleportDisabled?: boolean;
|
|
213
|
+
teleport?: string | HTMLElement | false;
|
|
189
214
|
/**
|
|
190
215
|
* `z-index` of the positioned wrapper.
|
|
191
216
|
*/
|
|
@@ -193,8 +218,27 @@ export interface SbFloatingProps {
|
|
|
193
218
|
/**
|
|
194
219
|
* Moves focus inside the panel once it is positioned: to the first element with
|
|
195
220
|
* `autofocus`, otherwise to the first focusable element, otherwise to the panel itself.
|
|
196
|
-
*
|
|
197
|
-
* @default true
|
|
221
|
+
* With `role="menu"` or `listbox` the first item gets focus (a listbox: its selected option).
|
|
222
|
+
* @default true, false with `role="tooltip"` and with the `hover` and `focus` triggers
|
|
198
223
|
*/
|
|
199
224
|
autoFocus?: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Handles the keyboard model of `role="menu"` and `role="listbox"` panels
|
|
227
|
+
* (WAI-ARIA menu button and listbox patterns). Items are the elements with role `menuitem`,
|
|
228
|
+
* `menuitemcheckbox`, `menuitemradio` (menu) or `option` (listbox); they must be focusable,
|
|
229
|
+
* e.g. buttons or elements with `tabindex="-1"`.
|
|
230
|
+
* - ArrowDown / ArrowUp on the reference open the panel on the first / last item
|
|
231
|
+
* (not with the `manual` trigger).
|
|
232
|
+
* - ArrowDown / ArrowUp move between items (a menu wraps around), Home / End go to the ends,
|
|
233
|
+
* typing letters focuses the next item whose text starts with them.
|
|
234
|
+
* - Enter and Space activate items that are not buttons or links, with a click.
|
|
235
|
+
* - Menu only: Tab and Shift+Tab close it and move focus on, as from its reference.
|
|
236
|
+
* - Submenu (a menu inside a menu): ArrowRight on its reference opens it, ArrowLeft closes it
|
|
237
|
+
* (mirrored in right-to-left text).
|
|
238
|
+
*
|
|
239
|
+
* Keys whose `keydown` you already handled with `preventDefault()` are left to you.
|
|
240
|
+
* No effect with other roles.
|
|
241
|
+
* @default true
|
|
242
|
+
*/
|
|
243
|
+
keyboardNavigation?: boolean;
|
|
200
244
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sb-floating-panel-vue",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.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",
|