Socket
Socket
Sign inDemoInstall

placement.js

Package Overview
Dependencies
0
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.0-beta.3 to 1.0.0-beta.4

5

CHANGELOG.md

@@ -8,3 +8,6 @@ # Changelog

## [Unreleased]
## [1.0.0-beta.4] - 2021-05-09
### Changed
- Reverted to `default` export.
- Refactored code to be more reliable and concise.

@@ -11,0 +14,0 @@ ## [1.0.0-beta.3] - 2021-03-02

6

dist/index.d.ts

@@ -1,6 +0,8 @@

declare type Options = {
export declare type Options = {
placement?: Placement;
flip?: boolean;
cap?: boolean;
};
declare type Placement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' | 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end';
export declare function place(anchor: HTMLElement, overlay: HTMLElement, options: Options): void;
export default function (anchor: HTMLElement, overlay: HTMLElement, options: Options): void;
export {};

@@ -1,1 +0,1 @@

const t={x:{start:"left",Start:"Left",end:"right",End:"Right",size:"width",Size:"Width"},y:{start:"top",Start:"Top",end:"bottom",End:"Bottom",size:"height",Size:"Height"}};function e(e,n,o){var i;const s=o.placement||"bottom";let[a,d]=s.split("-");const r=["top","bottom"].includes(a)?"y":"x",l="y"===r?"x":"y",c=t[r],f=t[l],m=n.style;m.position="absolute",m.maxWidth=m.maxHeight="";const u=e.getBoundingClientRect(),p=(null===(i=function(t){for(;(t=t.parentNode)&&t instanceof Element;){const e=getComputedStyle(t).overflow;if(["auto","scroll"].includes(e))return t}}(n))||void 0===i?void 0:i.getBoundingClientRect())||(h=0,g=0,x=window.innerWidth,S=window.innerHeight,{top:h,left:g,right:x,bottom:S,width:x,height:S});var h,g,x,S;m["max"+f.Size]=p[f.size]+"px";const z={[c.start]:u[c.start]-p[c.start],[c.end]:p[c.end]-u[c.end]};let y;n["offset"+c.Size]>z[a]&&(a=z[c.start]>z[c.end]?c.start:c.end),m["max"+c.Size]=z[a]+"px";const b=n.offsetParent;if(b&&b!==document.body){const e=b.getBoundingClientRect(),n=getComputedStyle(b);y=o=>e[t[o].start]+parseInt(n["border"+t[o].Start+"Width"])}const w=(e,o)=>Math.max(p[t[o].start],Math.min(e,p[t[o].end]-n["offset"+t[o].Size]))-(y?y(o):0),v=document.documentElement;if(a===c.start?(m[c.start]="auto",m[c.end]=w(v["client"+c.Size]-u[c.start],r)+"px"):(m[c.start]=w(u[c.end],r)+"px",m[c.end]="auto"),"end"===d)m[f.start]="auto",m[f.end]=w(v["client"+f.Size]-u[f.end],l)+"px";else{let t=0;if("start"!==d){t=u[f.size]/2-n["offset"+f.Size]/2}m[f.start]=w(u[f.start]+t,l)+"px",m[f.end]="auto"}n.dataset.placement=a+(d?"-"+d:"")}export{e as place};
const t={x:{start:"left",Start:"Left",end:"right",End:"Right",size:"width",Size:"Width"},y:{start:"top",Start:"Top",end:"bottom",End:"Bottom",size:"height",Size:"Height"}};function e(e,n,i){var o;const a=n.style;Object.assign(a,{position:"absolute",maxWidth:"",maxHeight:""});let[s="bottom",d="center"]=i.placement.split("-");const r=["top","bottom"].includes(s)?"y":"x";let c=s===t[r].start?t[r].end:t[r].start;const l="x"===r?"y":"x",p=e.getBoundingClientRect(),g=(null===(o=function(t){for(;(t=t.parentNode)&&t instanceof Element;){const e=getComputedStyle(t).overflow;if(["auto","scroll"].includes(e))return t}}(n))||void 0===o?void 0:o.getBoundingClientRect())||new DOMRect(0,0,window.innerWidth,window.innerHeight),f=n.offsetParent||document.body,m=f===document.body?new DOMRect(0,-pageYOffset,window.innerWidth,window.innerHeight):f.getBoundingClientRect(),h=getComputedStyle(f),u=getComputedStyle(n);if(i.flip||void 0===i.flip){const e=t=>Math.abs(p[t]-g[t]),i=e(s);n["offset"+t[r].Size]>i&&e(c)>i&&([s,c]=[c,s])}if(n.dataset.placement=`${s}-${d}`,i.cap||void 0===i.cap){const e=(e,i)=>{const o=u["max"+t[e].Size];i-=parseInt(u["margin"+t[e].Start])+parseInt(u["margin"+t[e].End]),("none"===o||i<parseInt(o))&&(n.style["max"+t[e].Size]=i+"px")};e(r,Math.abs(g[s]-p[s])),e(l,g[t[l].size])}Object.assign(a,{[s]:"auto",[c]:(s===t[r].start?m[t[r].end]-p[t[r].start]:p[t[r].end]-m[t[r].start])-parseInt(h["border"+t[r].Start+"Width"])+"px"});const S="end"===d?"end":"start",b="end"===d?"start":"end",x=p[l]-m[l],w=p[t[l].size],z=n["offset"+t[l].Size],y="end"===d?-1:1;Object.assign(a,{[t[l][b]]:"auto",[t[l][S]]:Math.max(y*(g[t[l][S]]-m[t[l][S]]),Math.min("end"===d?m[t[l].size]-x-w:x+("start"!==d?w/2-z/2:0),y*(g[t[l][b]]-m[t[l][S]])-z))-parseInt(h["border"+t[l].Start+"Width"])+"px"})}export default e;

@@ -1,1 +0,1 @@

var placement=function(t){"use strict";const e={x:{start:"left",Start:"Left",end:"right",End:"Right",size:"width",Size:"Width"},y:{start:"top",Start:"Top",end:"bottom",End:"Bottom",size:"height",Size:"Height"}};return t.place=function(t,n,o){var i;const s=o.placement||"bottom";let[a,d]=s.split("-");const r=["top","bottom"].includes(a)?"y":"x",l="y"===r?"x":"y",c=e[r],u=e[l],f=n.style;f.position="absolute",f.maxWidth=f.maxHeight="";const m=t.getBoundingClientRect(),p=(null===(i=function(t){for(;(t=t.parentNode)&&t instanceof Element;){const e=getComputedStyle(t).overflow;if(["auto","scroll"].includes(e))return t}}(n))||void 0===i?void 0:i.getBoundingClientRect())||(h=0,g=0,x=window.innerWidth,S=window.innerHeight,{top:h,left:g,right:x,bottom:S,width:x,height:S});var h,g,x,S;f["max"+u.Size]=p[u.size]+"px";const z={[c.start]:m[c.start]-p[c.start],[c.end]:p[c.end]-m[c.end]};let y;n["offset"+c.Size]>z[a]&&(a=z[c.start]>z[c.end]?c.start:c.end),f["max"+c.Size]=z[a]+"px";const b=n.offsetParent;if(b&&b!==document.body){const t=b.getBoundingClientRect(),n=getComputedStyle(b);y=o=>t[e[o].start]+parseInt(n["border"+e[o].Start+"Width"])}const v=(t,o)=>Math.max(p[e[o].start],Math.min(t,p[e[o].end]-n["offset"+e[o].Size]))-(y?y(o):0),w=document.documentElement;if(a===c.start?(f[c.start]="auto",f[c.end]=v(w["client"+c.Size]-m[c.start],r)+"px"):(f[c.start]=v(m[c.end],r)+"px",f[c.end]="auto"),"end"===d)f[u.start]="auto",f[u.end]=v(w["client"+u.Size]-m[u.end],l)+"px";else{let t=0;if("start"!==d){t=m[u.size]/2-n["offset"+u.Size]/2}f[u.start]=v(m[u.start]+t,l)+"px",f[u.end]="auto"}n.dataset.placement=a+(d?"-"+d:"")},Object.defineProperty(t,"__esModule",{value:!0}),t}({});
var placement=function(){"use strict";const t={x:{start:"left",Start:"Left",end:"right",End:"Right",size:"width",Size:"Width"},y:{start:"top",Start:"Top",end:"bottom",End:"Bottom",size:"height",Size:"Height"}};return function(e,n,i){var o;const a=n.style;Object.assign(a,{position:"absolute",maxWidth:"",maxHeight:""});let[s="bottom",d="center"]=i.placement.split("-");const r=["top","bottom"].includes(s)?"y":"x";let c=s===t[r].start?t[r].end:t[r].start;const l="x"===r?"y":"x",p=e.getBoundingClientRect(),g=(null===(o=function(t){for(;(t=t.parentNode)&&t instanceof Element;){const e=getComputedStyle(t).overflow;if(["auto","scroll"].includes(e))return t}}(n))||void 0===o?void 0:o.getBoundingClientRect())||new DOMRect(0,0,window.innerWidth,window.innerHeight),f=n.offsetParent||document.body,m=f===document.body?new DOMRect(0,-pageYOffset,window.innerWidth,window.innerHeight):f.getBoundingClientRect(),u=getComputedStyle(f),h=getComputedStyle(n);if(i.flip||void 0===i.flip){const e=t=>Math.abs(p[t]-g[t]),i=e(s);n["offset"+t[r].Size]>i&&e(c)>i&&([s,c]=[c,s])}if(n.dataset.placement=`${s}-${d}`,i.cap||void 0===i.cap){const e=(e,i)=>{const o=h["max"+t[e].Size];i-=parseInt(h["margin"+t[e].Start])+parseInt(h["margin"+t[e].End]),("none"===o||i<parseInt(o))&&(n.style["max"+t[e].Size]=i+"px")};e(r,Math.abs(g[s]-p[s])),e(l,g[t[l].size])}Object.assign(a,{[s]:"auto",[c]:(s===t[r].start?m[t[r].end]-p[t[r].start]:p[t[r].end]-m[t[r].start])-parseInt(u["border"+t[r].Start+"Width"])+"px"});const S="end"===d?"end":"start",b="end"===d?"start":"end",w=p[l]-m[l],x=p[t[l].size],z=n["offset"+t[l].Size],y="end"===d?-1:1;Object.assign(a,{[t[l][b]]:"auto",[t[l][S]]:Math.max(y*(g[t[l][S]]-m[t[l][S]]),Math.min("end"===d?m[t[l].size]-w-x:w+("start"!==d?x/2-z/2:0),y*(g[t[l][b]]-m[t[l][S]])-z))-parseInt(u["border"+t[l].Start+"Width"])+"px"})}}();
{
"name": "placement.js",
"version": "1.0.0-beta.3",
"version": "1.0.0-beta.4",
"description": "A tiny library for positioning overlays. Useful for tooltips, popovers etc.",
"main": "./dist/index.cjs.js",
"module": "./dist/index.es.js",

@@ -12,3 +11,4 @@ "unpkg": "./dist/index.js",

"build": "rollup -c",
"build:watch": "rollup -cw"
"build:watch": "rollup -cw",
"release": "release-it"
},

@@ -31,4 +31,7 @@ "repository": {

],
"sideEffects": false,
"devDependencies": {
"@release-it/keep-a-changelog": "^2.2.2",
"@rollup/plugin-typescript": "^8.1.0",
"release-it": "^14.6.1",
"rollup": "^2.36.2",

@@ -39,3 +42,16 @@ "rollup-plugin-filesize": "^9.1.1",

"typescript": "^4.1.3"
},
"release-it": {
"github": {
"release": true
},
"plugins": {
"@release-it/keep-a-changelog": {
"filename": "CHANGELOG.md"
}
},
"hooks": {
"after:bump": "npm run build"
}
}
}
# Placement.js
> A tiny library for positioning overlays. Useful for tooltips, popovers etc.
**A tiny library for positioning overlays. Useful for tooltips, popovers etc.**

@@ -27,5 +27,5 @@ ![Size](https://img.shields.io/bundlephobia/minzip/placement.js)

```ts
import { place } from 'placement.js';
import placement from 'placement.js';
place(
placement(
anchor: HTMLElement,

@@ -37,22 +37,28 @@ overlay: HTMLElement,

type Options = {
placement?: Placement // defaults to 'bottom'
// The overlay placement relative to the anchor.
// Defaults to 'bottom'.
placement?:
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'right'
| 'right-start'
| 'right-end'
| 'left'
| 'left-start'
| 'left-end',
// Whether or not the overlay can flip to the other side if there's more
// room available. Defaults to true.
flip?: boolean,
// Whether or not the overlay size should be capped to the available space.
// Defaults to true.
cap?: boolean,
};
type Placement =
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'right'
| 'right-start'
| 'right-end'
| 'left'
| 'left-start'
| 'left-end';
```
Check out the [demo](https://tobyzerner.github.io/placement.js/demo.html) to see it in action.
## Contributing

@@ -59,0 +65,0 @@

@@ -1,5 +0,12 @@

type Options = {
placement?: Placement
export type Options = {
placement?: Placement,
flip?: boolean,
cap?: boolean,
};
const PROPS = {
x: { start: 'left', Start: 'Left', end: 'right', End: 'Right', size: 'width', Size: 'Width' },
y: { start: 'top', Start: 'Top', end: 'bottom', End: 'Bottom', size: 'height', Size: 'Height' }
};
type Placement =

@@ -19,8 +26,5 @@ | 'top'

const PROPS = {
x: { start: 'left', Start: 'Left', end: 'right', End: 'Right', size: 'width', Size: 'Width' },
y: { start: 'top', Start: 'Top', end: 'bottom', End: 'Bottom', size: 'height', Size: 'Height' }
};
type Axis = 'x' | 'y';
export function place(
export default function(
anchor: HTMLElement,

@@ -30,98 +34,87 @@ overlay: HTMLElement,

) {
const placement = options.placement || 'bottom';
let [side, align] = placement.split('-');
const mainAxis = ['top', 'bottom'].includes(side) ? 'y' : 'x';
const altAxis = mainAxis === 'y' ? 'x' : 'y';
const mainProps = PROPS[mainAxis];
const altProps = PROPS[altAxis];
// Reset the position and uncap the maximum size of the popup so that we
// can reliably determine if the popup is "too big" below.
// Reset
const overlayStyle = overlay.style;
overlayStyle.position = 'absolute';
overlayStyle.maxWidth = overlayStyle.maxHeight = '';
Object.assign(overlayStyle, {
position: 'absolute',
maxWidth: '',
maxHeight: ''
});
// Get the rectangles defining the anchor element and the overflow boundary.
// To ensure a reliable calculation, this comes after resetting the overlay
// position to absolute.
let [side = 'bottom', align = 'center'] = options.placement.split('-');
const axisSide = ['top', 'bottom'].includes(side) ? 'y' : 'x';
let oppositeSide = side === PROPS[axisSide].start ? PROPS[axisSide].end : PROPS[axisSide].start;
const axisAlign = axisSide === 'x' ? 'y' : 'x';
const anchorRect = anchor.getBoundingClientRect();
const boundRect = scrollParent(overlay)?.getBoundingClientRect() || createRect(0, 0, window.innerWidth, window.innerHeight);
const boundRect = scrollParent(overlay)?.getBoundingClientRect() || new DOMRect(0, 0, window.innerWidth, window.innerHeight);
const offsetParent = overlay.offsetParent || document.body;
const offsetParentRect = offsetParent === document.body ? new DOMRect(0, -pageYOffset, window.innerWidth, window.innerHeight) : offsetParent.getBoundingClientRect();
const offsetParentComputed = getComputedStyle(offsetParent);
const overlayComputed = getComputedStyle(overlay);
// Constrain the maximum size of the popup along the alignment axis.
overlayStyle['max' + altProps.Size] = boundRect[altProps.size] + 'px';
// Flip
if (options.flip || typeof options.flip === 'undefined') {
// Calculate the available room on either side of the anchor element. If
// the size of the popup is more than is available on the given side, then
// we will flip it to the side with more room.
const room = side => Math.abs(anchorRect[side] - boundRect[side]);
const roomThisSide = room(side);
const overlaySize = overlay['offset' + PROPS[axisSide].Size];
// Calculate the available room on either side of the anchor element. If
// the size of the popup is more than is available on the given side, then
// we will flip it to the side with more room.
const room = {
[mainProps.start]: anchorRect[mainProps.start] - boundRect[mainProps.start],
[mainProps.end]: boundRect[mainProps.end] - anchorRect[mainProps.end]
};
if (overlay['offset' + mainProps.Size] > room[side]) {
side = room[mainProps.start] > room[mainProps.end] ? mainProps.start : mainProps.end;
if (overlaySize > roomThisSide && room(oppositeSide) > roomThisSide) {
[side, oppositeSide] = [oppositeSide, side];
}
}
// Constrain the maximum size of the popup along the main axis.
overlayStyle['max' + mainProps.Size] = room[side] + 'px';
// Data attribute
overlay.dataset.placement = `${side}-${align}`;
let offset;
const offsetParent = overlay.offsetParent;
if (offsetParent && offsetParent !== document.body) {
const parentRect = offsetParent.getBoundingClientRect();
const parentStyle = getComputedStyle(offsetParent);
offset = axis => parentRect[PROPS[axis].start] + parseInt(parentStyle['border' + PROPS[axis].Start + 'Width']);
}
// Cap
if (options.cap || typeof options.cap === 'undefined') {
const cap = (axis: Axis, room: number) => {
const intrinsicMaxSize = overlayComputed['max' + PROPS[axis].Size];
room -= parseInt(overlayComputed['margin' + PROPS[axis].Start]) + parseInt(overlayComputed['margin' + PROPS[axis].End]);
if (intrinsicMaxSize === 'none' || room < parseInt(intrinsicMaxSize)) {
overlay.style['max' + PROPS[axis].Size] = room + 'px';
}
};
const pos = (pos, axis) => {
return Math.max(
boundRect[PROPS[axis].start],
Math.min(
pos,
boundRect[PROPS[axis].end] - overlay['offset' + PROPS[axis].Size]
)
) - (offset ? offset(axis) : 0);
};
const dde = document.documentElement;
// Set the position of the popup along the main axis.
if (side === mainProps.start) { // top or left
overlayStyle[mainProps.start] = 'auto';
overlayStyle[mainProps.end] = pos(dde['client' + mainProps.Size] - anchorRect[mainProps.start], mainAxis) + 'px';
} else { // bottom or right
overlayStyle[mainProps.start] = pos(anchorRect[mainProps.end], mainAxis) + 'px';
overlayStyle[mainProps.end] = 'auto';
cap(axisSide, Math.abs(boundRect[side] - anchorRect[side]));
cap(axisAlign, boundRect[PROPS[axisAlign].size]);
}
// Set the position of the popup along the secondary axis.
if (align === 'end') {
overlayStyle[altProps.start] = 'auto';
overlayStyle[altProps.end] = pos(dde['client' + altProps.Size] - anchorRect[altProps.end], altAxis) + 'px';
} else {
let offset = 0;
if (align !== 'start') {
const anchorSize = anchorRect[altProps.size];
offset = anchorSize / 2 - overlay['offset' + altProps.Size] / 2;
}
// Side
Object.assign(overlayStyle, {
[side]: 'auto',
[oppositeSide]: (
(side === PROPS[axisSide].start
? offsetParentRect[PROPS[axisSide].end] - anchorRect[PROPS[axisSide].start]
: anchorRect[PROPS[axisSide].end] - offsetParentRect[PROPS[axisSide].start])
- parseInt(offsetParentComputed['border' + PROPS[axisSide].Start + 'Width'])
) + 'px'
});
overlayStyle[altProps.start] = pos(anchorRect[altProps.start] + offset, altAxis) + 'px';
overlayStyle[altProps.end] = 'auto';
}
overlay.dataset.placement = side + (align ? '-' + align : '');
// Align
const fromAlign = align === 'end' ? 'end' : 'start';
const oppositeAlign = align === 'end' ? 'start' : 'end';
const anchorAlign = anchorRect[axisAlign] - offsetParentRect[axisAlign];
const anchorSize = anchorRect[PROPS[axisAlign].size];
const overlaySize = overlay['offset' + PROPS[axisAlign].Size];
const factor = align === 'end' ? -1 : 1;
Object.assign(overlayStyle, {
[PROPS[axisAlign][oppositeAlign]]: 'auto',
[PROPS[axisAlign][fromAlign]]: (
Math.max(
factor * (boundRect[PROPS[axisAlign][fromAlign]] - offsetParentRect[PROPS[axisAlign][fromAlign]]),
Math.min(
align === 'end'
? offsetParentRect[PROPS[axisAlign].size] - anchorAlign - anchorSize
: anchorAlign + (align !== 'start' ? anchorSize / 2 - overlaySize / 2 : 0),
factor * (boundRect[PROPS[axisAlign][oppositeAlign]] - offsetParentRect[PROPS[axisAlign][fromAlign]]) - overlaySize
)
)
- parseInt(offsetParentComputed['border' + PROPS[axisAlign].Start + 'Width'])
) + 'px'
});
}
function createRect(top, left, width, height): ClientRect {
return {
top,
left,
right: width,
bottom: height,
width,
height
};
}
function scrollParent(node) {

@@ -128,0 +121,0 @@ while ((node = node.parentNode) && node instanceof Element) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc