@data-slot/popover 0.2.40 → 0.2.42

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 CHANGED
@@ -53,6 +53,7 @@ const popover = createPopover(element, {
53
53
  alignOffset: 0,
54
54
  avoidCollisions: true,
55
55
  collisionPadding: 8,
56
+ portal: true,
56
57
  closeOnClickOutside: true,
57
58
  closeOnEscape: true,
58
59
  onOpenChange: (open) => console.log(open),
@@ -70,6 +71,7 @@ const popover = createPopover(element, {
70
71
  | `alignOffset` | `number` | `0` | Offset from alignment edge in pixels |
71
72
  | `avoidCollisions` | `boolean` | `true` | Flip/shift to stay in viewport |
72
73
  | `collisionPadding` | `number` | `8` | Viewport edge padding in pixels |
74
+ | `portal` | `boolean` | `true` | Portal content to `document.body` while open |
73
75
  | `position` | `"top" \| "bottom" \| "left" \| "right"` | - | Deprecated alias for `side` |
74
76
  | `closeOnClickOutside` | `boolean` | `true` | Close when clicking outside |
75
77
  | `closeOnEscape` | `boolean` | `true` | Close when pressing Escape |
@@ -119,6 +121,7 @@ Options can also be set via data attributes on the root element. JS options take
119
121
  | `data-align-offset` | number | `0` | Offset from alignment edge (px) |
120
122
  | `data-avoid-collisions` | boolean | `true` | Flip/shift to stay in viewport |
121
123
  | `data-collision-padding` | number | `8` | Viewport edge padding (px) |
124
+ | `data-portal` | boolean | `true` | Portal content to `document.body` while open |
122
125
  | `data-close-on-click-outside` | boolean | `true` | Close when clicking outside |
123
126
  | `data-close-on-escape` | boolean | `true` | Close when pressing Escape |
124
127
 
@@ -141,7 +144,8 @@ Placement can be set on root or content (content takes precedence):
141
144
 
142
145
  ## Styling
143
146
 
144
- Popover position is computed in JavaScript and applied as `position: fixed` + inline `top/left`.
147
+ Popover position is computed in JavaScript and applied as `position: absolute` + inline `top/left`.
148
+ By default, content is portaled to `document.body` while open, so absolute positioning is based on document coordinates.
145
149
  Use `data-state`, `data-side`, and `data-align` for styling/animation:
146
150
 
147
151
  ```css
@@ -151,7 +155,7 @@ Use `data-state`, `data-side`, and `data-align` for styling/animation:
151
155
  }
152
156
 
153
157
  [data-slot="popover-content"] {
154
- position: fixed;
158
+ position: absolute;
155
159
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
156
160
  }
157
161
 
@@ -178,7 +182,7 @@ With Tailwind:
178
182
  data-slot="popover-content"
179
183
  data-side="bottom"
180
184
  data-align="start"
181
- class="fixed bg-white shadow-lg rounded-lg p-4 transition data-[state=closed]:opacity-0"
185
+ class="absolute bg-white shadow-lg rounded-lg p-4 transition data-[state=closed]:opacity-0"
182
186
  >
183
187
  Content
184
188
  </div>
@@ -28,6 +28,8 @@ interface PopoverOptions {
28
28
  collisionPadding?: number;
29
29
  /** Callback when open state changes */
30
30
  onOpenChange?: (open: boolean) => void;
31
+ /** Portal content to body while open. @default true */
32
+ portal?: boolean;
31
33
  /** Close when clicking outside */
32
34
  closeOnClickOutside?: boolean;
33
35
  /** Close when pressing Escape */
@@ -28,6 +28,8 @@ interface PopoverOptions {
28
28
  collisionPadding?: number;
29
29
  /** Callback when open state changes */
30
30
  onOpenChange?: (open: boolean) => void;
31
+ /** Portal content to body while open. @default true */
32
+ portal?: boolean;
31
33
  /** Close when clicking outside */
32
34
  closeOnClickOutside?: boolean;
33
35
  /** Close when pressing Escape */
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`);const t=[`top`,`right`,`bottom`,`left`],n=[`start`,`center`,`end`];function r(r,i={}){let a=(0,e.getPart)(r,`popover-trigger`),o=(0,e.getPart)(r,`popover-content`),s=(0,e.getPart)(r,`popover-close`);if(!a||!o)throw Error(`Popover requires trigger and content slots`);let c=i.defaultOpen??(0,e.getDataBool)(r,`defaultOpen`)??!1,l=i.onOpenChange,u=i.closeOnClickOutside??(0,e.getDataBool)(r,`closeOnClickOutside`)??!0,d=i.closeOnEscape??(0,e.getDataBool)(r,`closeOnEscape`)??!0,f=i.position??(0,e.getDataEnum)(o,`position`,t)??(0,e.getDataEnum)(r,`position`,t),p=i.side??(0,e.getDataEnum)(o,`side`,t)??(0,e.getDataEnum)(r,`side`,t)??f??`bottom`,m=i.align??(0,e.getDataEnum)(o,`align`,n)??(0,e.getDataEnum)(r,`align`,n)??`center`,h=i.sideOffset??(0,e.getDataNumber)(o,`sideOffset`)??(0,e.getDataNumber)(r,`sideOffset`)??4,g=i.alignOffset??(0,e.getDataNumber)(o,`alignOffset`)??(0,e.getDataNumber)(r,`alignOffset`)??0,_=i.avoidCollisions??(0,e.getDataBool)(o,`avoidCollisions`)??(0,e.getDataBool)(r,`avoidCollisions`)??!0,v=i.collisionPadding??(0,e.getDataNumber)(o,`collisionPadding`)??(0,e.getDataNumber)(r,`collisionPadding`)??8,y=c,b=[],x=null,S=!1,C=()=>{S&&=(o.removeAttribute(`tabindex`),!1)},w=()=>{let e=o.querySelector(`[autofocus]`);if(e)return e.focus();let t=o.querySelector(`a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])`);if(t)return t.focus();o.getAttribute(`tabindex`)||(o.setAttribute(`tabindex`,`-1`),S=!0),o.focus()},T=(0,e.ensureId)(o,`popover-content`);a.setAttribute(`aria-haspopup`,`dialog`),a.setAttribute(`aria-controls`,T),o.setAttribute(`data-side`,p),o.setAttribute(`data-align`,m),o.setAttribute(`data-position`,p);let E=()=>{let t=r.ownerDocument.defaultView??window,n=(0,e.computeFloatingPosition)({anchorRect:a.getBoundingClientRect(),contentRect:o.getBoundingClientRect(),side:p,align:m,sideOffset:h,alignOffset:g,avoidCollisions:_,collisionPadding:v});o.style.position=`absolute`,o.style.top=`${n.y+t.scrollY}px`,o.style.left=`${n.x+t.scrollX}px`,o.style.margin=`0`,o.setAttribute(`data-side`,n.side),o.setAttribute(`data-align`,n.align),o.setAttribute(`data-position`,n.side)},D=(0,e.createPositionSync)({observedElements:[a,o],isActive:()=>y,ancestorScroll:!1,onUpdate:E}),O=t=>{y!==t&&(t&&(x=document.activeElement),y=t,(0,e.setAria)(a,`expanded`,y),(0,e.emit)(r,`popover:change`,{open:y}),l?.(y),t?(o.hidden=!1,r.setAttribute(`data-state`,`open`),o.setAttribute(`data-state`,`open`),E(),D.start(),D.update(),requestAnimationFrame(w)):(D.stop(),o.hidden=!0,r.setAttribute(`data-state`,`closed`),o.setAttribute(`data-state`,`closed`),C(),requestAnimationFrame(()=>{x&&x.isConnected?x.focus():a.focus(),x=null})))};return(0,e.setAria)(a,`expanded`,y),o.hidden=!y,r.setAttribute(`data-state`,y?`open`:`closed`),o.setAttribute(`data-state`,y?`open`:`closed`),c&&(E(),D.start(),D.update(),requestAnimationFrame(w)),b.push((0,e.on)(a,`click`,()=>O(!y))),s&&b.push((0,e.on)(s,`click`,()=>O(!1))),b.push((0,e.createDismissLayer)({root:r,isOpen:()=>y,onDismiss:()=>O(!1),closeOnClickOutside:u,closeOnEscape:d})),b.push((0,e.on)(r,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&O(n)})),{open:()=>O(!0),close:()=>O(!1),toggle:()=>O(!y),get isOpen(){return y},destroy:()=>{D.stop(),b.forEach(e=>e()),b.length=0,C()}}}const i=new WeakSet;function a(t=document){let n=[];for(let a of(0,e.getRoots)(t,`popover`))i.has(a)||(i.add(a),n.push(r(a)));return n}exports.create=a,exports.createPopover=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`);const t=[`top`,`right`,`bottom`,`left`],n=[`start`,`center`,`end`];function r(r,i={}){let a=(0,e.getPart)(r,`popover-trigger`),o=(0,e.getPart)(r,`popover-content`),s=(0,e.getPart)(r,`popover-close`);if(!a||!o)throw Error(`Popover requires trigger and content slots`);let c=i.defaultOpen??(0,e.getDataBool)(r,`defaultOpen`)??!1,l=i.onOpenChange,u=i.closeOnClickOutside??(0,e.getDataBool)(r,`closeOnClickOutside`)??!0,d=i.closeOnEscape??(0,e.getDataBool)(r,`closeOnEscape`)??!0,f=i.portal??(0,e.getDataBool)(o,`portal`)??(0,e.getDataBool)(r,`portal`)??!0,p=i.position??(0,e.getDataEnum)(o,`position`,t)??(0,e.getDataEnum)(r,`position`,t),m=i.side??(0,e.getDataEnum)(o,`side`,t)??(0,e.getDataEnum)(r,`side`,t)??p??`bottom`,h=i.align??(0,e.getDataEnum)(o,`align`,n)??(0,e.getDataEnum)(r,`align`,n)??`center`,g=i.sideOffset??(0,e.getDataNumber)(o,`sideOffset`)??(0,e.getDataNumber)(r,`sideOffset`)??4,_=i.alignOffset??(0,e.getDataNumber)(o,`alignOffset`)??(0,e.getDataNumber)(r,`alignOffset`)??0,v=i.avoidCollisions??(0,e.getDataBool)(o,`avoidCollisions`)??(0,e.getDataBool)(r,`avoidCollisions`)??!0,y=i.collisionPadding??(0,e.getDataNumber)(o,`collisionPadding`)??(0,e.getDataNumber)(r,`collisionPadding`)??8,b=c,x=[],S=(0,e.createPortalLifecycle)({content:o,root:r,enabled:f}),C=null,w=!1,T=()=>{w&&=(o.removeAttribute(`tabindex`),!1)},E=()=>{let e=o.querySelector(`[autofocus]`);if(e)return e.focus();let t=o.querySelector(`a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])`);if(t)return t.focus();o.getAttribute(`tabindex`)||(o.setAttribute(`tabindex`,`-1`),w=!0),o.focus()},D=(0,e.ensureId)(o,`popover-content`);a.setAttribute(`aria-haspopup`,`dialog`),a.setAttribute(`aria-controls`,D),o.setAttribute(`data-side`,m),o.setAttribute(`data-align`,h),o.setAttribute(`data-position`,m);let O=()=>{let t=r.ownerDocument.defaultView??window,n=(0,e.computeFloatingPosition)({anchorRect:a.getBoundingClientRect(),contentRect:o.getBoundingClientRect(),side:m,align:h,sideOffset:g,alignOffset:_,avoidCollisions:v,collisionPadding:y});o.style.position=`absolute`,o.style.top=`${n.y+t.scrollY}px`,o.style.left=`${n.x+t.scrollX}px`,o.style.margin=`0`,o.setAttribute(`data-side`,n.side),o.setAttribute(`data-align`,n.align),o.setAttribute(`data-position`,n.side)},k=(0,e.createPositionSync)({observedElements:[a,o],isActive:()=>b,ancestorScroll:!1,onUpdate:O}),A=t=>{b!==t&&(t&&(C=document.activeElement),b=t,(0,e.setAria)(a,`expanded`,b),(0,e.emit)(r,`popover:change`,{open:b}),l?.(b),t?(S.mount(),o.hidden=!1,r.setAttribute(`data-state`,`open`),o.setAttribute(`data-state`,`open`),O(),k.start(),k.update(),requestAnimationFrame(E)):(k.stop(),S.restore(),o.hidden=!0,r.setAttribute(`data-state`,`closed`),o.setAttribute(`data-state`,`closed`),T(),requestAnimationFrame(()=>{C&&C.isConnected?C.focus():a.focus(),C=null})))};return(0,e.setAria)(a,`expanded`,b),o.hidden=!b,r.setAttribute(`data-state`,b?`open`:`closed`),o.setAttribute(`data-state`,b?`open`:`closed`),c&&(S.mount(),O(),k.start(),k.update(),requestAnimationFrame(E)),x.push((0,e.on)(a,`click`,()=>A(!b))),s&&x.push((0,e.on)(s,`click`,()=>A(!1))),x.push((0,e.createDismissLayer)({root:r,isOpen:()=>b,onDismiss:()=>A(!1),closeOnClickOutside:u,closeOnEscape:d})),x.push((0,e.on)(r,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&A(n)})),{open:()=>A(!0),close:()=>A(!1),toggle:()=>A(!b),get isOpen(){return b},destroy:()=>{k.stop(),S.cleanup(),x.forEach(e=>e()),x.length=0,T()}}}const i=new WeakSet;function a(t=document){let n=[];for(let a of(0,e.getRoots)(t,`popover`))i.has(a)||(i.add(a),n.push(r(a)));return n}exports.create=a,exports.createPopover=r;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{computeFloatingPosition as e,createDismissLayer as t,createPositionSync as n,emit as r,ensureId as i,getDataBool as a,getDataEnum as o,getDataNumber as s,getPart as c,getRoots as l,on as u,setAria as d}from"@data-slot/core";const f=[`top`,`right`,`bottom`,`left`],p=[`start`,`center`,`end`];function m(l,m={}){let h=c(l,`popover-trigger`),g=c(l,`popover-content`),_=c(l,`popover-close`);if(!h||!g)throw Error(`Popover requires trigger and content slots`);let v=m.defaultOpen??a(l,`defaultOpen`)??!1,y=m.onOpenChange,b=m.closeOnClickOutside??a(l,`closeOnClickOutside`)??!0,x=m.closeOnEscape??a(l,`closeOnEscape`)??!0,S=m.position??o(g,`position`,f)??o(l,`position`,f),C=m.side??o(g,`side`,f)??o(l,`side`,f)??S??`bottom`,w=m.align??o(g,`align`,p)??o(l,`align`,p)??`center`,T=m.sideOffset??s(g,`sideOffset`)??s(l,`sideOffset`)??4,E=m.alignOffset??s(g,`alignOffset`)??s(l,`alignOffset`)??0,D=m.avoidCollisions??a(g,`avoidCollisions`)??a(l,`avoidCollisions`)??!0,O=m.collisionPadding??s(g,`collisionPadding`)??s(l,`collisionPadding`)??8,k=v,A=[],j=null,M=!1,N=()=>{M&&=(g.removeAttribute(`tabindex`),!1)},P=()=>{let e=g.querySelector(`[autofocus]`);if(e)return e.focus();let t=g.querySelector(`a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])`);if(t)return t.focus();g.getAttribute(`tabindex`)||(g.setAttribute(`tabindex`,`-1`),M=!0),g.focus()},F=i(g,`popover-content`);h.setAttribute(`aria-haspopup`,`dialog`),h.setAttribute(`aria-controls`,F),g.setAttribute(`data-side`,C),g.setAttribute(`data-align`,w),g.setAttribute(`data-position`,C);let I=()=>{let t=l.ownerDocument.defaultView??window,n=e({anchorRect:h.getBoundingClientRect(),contentRect:g.getBoundingClientRect(),side:C,align:w,sideOffset:T,alignOffset:E,avoidCollisions:D,collisionPadding:O});g.style.position=`absolute`,g.style.top=`${n.y+t.scrollY}px`,g.style.left=`${n.x+t.scrollX}px`,g.style.margin=`0`,g.setAttribute(`data-side`,n.side),g.setAttribute(`data-align`,n.align),g.setAttribute(`data-position`,n.side)},L=n({observedElements:[h,g],isActive:()=>k,ancestorScroll:!1,onUpdate:I}),R=e=>{k!==e&&(e&&(j=document.activeElement),k=e,d(h,`expanded`,k),r(l,`popover:change`,{open:k}),y?.(k),e?(g.hidden=!1,l.setAttribute(`data-state`,`open`),g.setAttribute(`data-state`,`open`),I(),L.start(),L.update(),requestAnimationFrame(P)):(L.stop(),g.hidden=!0,l.setAttribute(`data-state`,`closed`),g.setAttribute(`data-state`,`closed`),N(),requestAnimationFrame(()=>{j&&j.isConnected?j.focus():h.focus(),j=null})))};return d(h,`expanded`,k),g.hidden=!k,l.setAttribute(`data-state`,k?`open`:`closed`),g.setAttribute(`data-state`,k?`open`:`closed`),v&&(I(),L.start(),L.update(),requestAnimationFrame(P)),A.push(u(h,`click`,()=>R(!k))),_&&A.push(u(_,`click`,()=>R(!1))),A.push(t({root:l,isOpen:()=>k,onDismiss:()=>R(!1),closeOnClickOutside:b,closeOnEscape:x})),A.push(u(l,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&R(n)})),{open:()=>R(!0),close:()=>R(!1),toggle:()=>R(!k),get isOpen(){return k},destroy:()=>{L.stop(),A.forEach(e=>e()),A.length=0,N()}}}const h=new WeakSet;function g(e=document){let t=[];for(let n of l(e,`popover`))h.has(n)||(h.add(n),t.push(m(n)));return t}export{g as create,m as createPopover};
1
+ import{computeFloatingPosition as e,createDismissLayer as t,createPortalLifecycle as n,createPositionSync as r,emit as i,ensureId as a,getDataBool as o,getDataEnum as s,getDataNumber as c,getPart as l,getRoots as u,on as d,setAria as f}from"@data-slot/core";const p=[`top`,`right`,`bottom`,`left`],m=[`start`,`center`,`end`];function h(u,h={}){let g=l(u,`popover-trigger`),_=l(u,`popover-content`),v=l(u,`popover-close`);if(!g||!_)throw Error(`Popover requires trigger and content slots`);let y=h.defaultOpen??o(u,`defaultOpen`)??!1,b=h.onOpenChange,x=h.closeOnClickOutside??o(u,`closeOnClickOutside`)??!0,S=h.closeOnEscape??o(u,`closeOnEscape`)??!0,C=h.portal??o(_,`portal`)??o(u,`portal`)??!0,w=h.position??s(_,`position`,p)??s(u,`position`,p),T=h.side??s(_,`side`,p)??s(u,`side`,p)??w??`bottom`,E=h.align??s(_,`align`,m)??s(u,`align`,m)??`center`,D=h.sideOffset??c(_,`sideOffset`)??c(u,`sideOffset`)??4,O=h.alignOffset??c(_,`alignOffset`)??c(u,`alignOffset`)??0,k=h.avoidCollisions??o(_,`avoidCollisions`)??o(u,`avoidCollisions`)??!0,A=h.collisionPadding??c(_,`collisionPadding`)??c(u,`collisionPadding`)??8,j=y,M=[],N=n({content:_,root:u,enabled:C}),P=null,F=!1,I=()=>{F&&=(_.removeAttribute(`tabindex`),!1)},L=()=>{let e=_.querySelector(`[autofocus]`);if(e)return e.focus();let t=_.querySelector(`a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])`);if(t)return t.focus();_.getAttribute(`tabindex`)||(_.setAttribute(`tabindex`,`-1`),F=!0),_.focus()},R=a(_,`popover-content`);g.setAttribute(`aria-haspopup`,`dialog`),g.setAttribute(`aria-controls`,R),_.setAttribute(`data-side`,T),_.setAttribute(`data-align`,E),_.setAttribute(`data-position`,T);let z=()=>{let t=u.ownerDocument.defaultView??window,n=e({anchorRect:g.getBoundingClientRect(),contentRect:_.getBoundingClientRect(),side:T,align:E,sideOffset:D,alignOffset:O,avoidCollisions:k,collisionPadding:A});_.style.position=`absolute`,_.style.top=`${n.y+t.scrollY}px`,_.style.left=`${n.x+t.scrollX}px`,_.style.margin=`0`,_.setAttribute(`data-side`,n.side),_.setAttribute(`data-align`,n.align),_.setAttribute(`data-position`,n.side)},B=r({observedElements:[g,_],isActive:()=>j,ancestorScroll:!1,onUpdate:z}),V=e=>{j!==e&&(e&&(P=document.activeElement),j=e,f(g,`expanded`,j),i(u,`popover:change`,{open:j}),b?.(j),e?(N.mount(),_.hidden=!1,u.setAttribute(`data-state`,`open`),_.setAttribute(`data-state`,`open`),z(),B.start(),B.update(),requestAnimationFrame(L)):(B.stop(),N.restore(),_.hidden=!0,u.setAttribute(`data-state`,`closed`),_.setAttribute(`data-state`,`closed`),I(),requestAnimationFrame(()=>{P&&P.isConnected?P.focus():g.focus(),P=null})))};return f(g,`expanded`,j),_.hidden=!j,u.setAttribute(`data-state`,j?`open`:`closed`),_.setAttribute(`data-state`,j?`open`:`closed`),y&&(N.mount(),z(),B.start(),B.update(),requestAnimationFrame(L)),M.push(d(g,`click`,()=>V(!j))),v&&M.push(d(v,`click`,()=>V(!1))),M.push(t({root:u,isOpen:()=>j,onDismiss:()=>V(!1),closeOnClickOutside:x,closeOnEscape:S})),M.push(d(u,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&V(n)})),{open:()=>V(!0),close:()=>V(!1),toggle:()=>V(!j),get isOpen(){return j},destroy:()=>{B.stop(),N.cleanup(),M.forEach(e=>e()),M.length=0,I()}}}const g=new WeakSet;function _(e=document){let t=[];for(let n of u(e,`popover`))g.has(n)||(g.add(n),t.push(h(n)));return t}export{_ as create,h as createPopover};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-slot/popover",
3
- "version": "0.2.40",
3
+ "version": "0.2.42",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.cjs",
@@ -38,6 +38,6 @@
38
38
  ],
39
39
  "license": "MIT",
40
40
  "dependencies": {
41
- "@data-slot/core": "0.2.40"
41
+ "@data-slot/core": "0.2.42"
42
42
  }
43
43
  }