@scidian/osui
Version:
Lightweight JavaScript UI library.
120 lines (90 loc) • 4.48 kB
JavaScript
// NOTES:
// When seeking window width / height:
// document.documentElement.clientHeight // Returns height of <html> element
// document.body.clientHeight // Returns height of <body> element
// window.innerHeight // Returns height of the window rather than height of the content
//
// Different browsers could give different values for the sizes of the <html> and <body> elements.
// The <html> element can either represent the window, or the entire page. The <body> element can either
// be the same size as the html element, or the size of the content in the page. Best to use 'window'.
import { Css } from './Css.js';
const EDGE_SPACE = 4;
export const ALIGN = {
LEFT: 'left',
CENTER: 'center',
MIDDLE: 'center',
RIGHT: 'right',
}
export const OVERFLOW = {
LEFT: 'left',
RIGHT: 'right',
}
export const POSITION = {
OVER: 'over',
UNDER: 'under',
}
/** Determines proper layout within browser window to place pop up div */
class Popper {
/******************** SIZE ********************/
static getLeft(dom) { return dom.getBoundingClientRect().left; }
static getTop(dom) { return dom.getBoundingClientRect().top; }
// static getWidth(dom) { return dom.getBoundingClientRect().width; }
// static getHeight(dom) { return dom.getBoundingClientRect().height; }
static getWidth(dom) { return dom.offsetWidth; }
static getHeight(dom) { return dom.offsetHeight; }
/******************** POP ********************/
static popOver(dom, parent, align = ALIGN.LEFT, offsetX = 0, offsetY = 0, overflow = OVERFLOW.RIGHT) {
return Popper.popOverUnder(dom, parent, POSITION.OVER, align, offsetX, offsetY, overflow);
}
static popUnder(dom, parent, align = ALIGN.LEFT, offsetX = 0, offsetY = 0, overflow = OVERFLOW.RIGHT) {
return Popper.popOverUnder(dom, parent, POSITION.UNDER, align, offsetX, offsetY, overflow);
}
/** Pops over or under, returns actual location after popping */
static popOverUnder(dom, parent, overUnder = POSITION.UNDER, align = ALIGN.LEFT,
offsetX = 0, offsetY = 0, overflow = OVERFLOW.RIGHT) {
let maxRight = window.innerWidth - EDGE_SPACE;
let maxBottom = window.innerHeight - EDGE_SPACE;
offsetX = (offsetX) ? parseInt(Css.toPx(offsetX), 10) : 0;
offsetY = (offsetY) ? parseInt(Css.toPx(offsetY), 10) : 0;
// Left, originally parent left (align === ALIGN.LEFT)
let desiredLeft = Popper.getLeft(parent) + offsetX;
// Check for other alignment options
if (align === ALIGN.CENTER) {
let offset = Popper.getLeft(parent) + ((Popper.getWidth(parent) - Popper.getWidth(dom)) / 2.0);
desiredLeft = offset + offsetX;
} else if (align === ALIGN.RIGHT) {
let offset = Popper.getLeft(parent) + (Popper.getWidth(parent) - Popper.getWidth(dom));
desiredLeft = offset + offsetX;
}
if (overflow === OVERFLOW.LEFT) {
maxRight = Popper.getLeft(parent) + Popper.getWidth(parent);
}
// Rightside adjust
let rightSide = desiredLeft + Popper.getWidth(dom);
if (rightSide > maxRight) desiredLeft -= (rightSide - maxRight);
// Leftside adjust
if (desiredLeft < EDGE_SPACE) desiredLeft = EDGE_SPACE;
// Top
let underTop = Popper.getTop(parent) + Popper.getHeight(parent) + offsetY;
let overTop = Popper.getTop(parent) - Popper.getHeight(dom) - offsetY;
// Check if outside of page bounds
let bottomSide = underTop + Popper.getHeight(dom);
if (bottomSide > maxBottom) overUnder = POSITION.OVER;
if (overTop < EDGE_SPACE) overUnder = POSITION.UNDER;
// Update position after checks
let desiredTop = (overUnder === POSITION.UNDER) ? underTop : overTop;
// Check if still hidden, adjust upward
if (overUnder === POSITION.UNDER) {
bottomSide = desiredTop + Popper.getHeight(dom);
if (bottomSide > maxBottom) {
desiredTop = maxBottom - Popper.getHeight(dom);
if (desiredTop < EDGE_SPACE) desiredTop = EDGE_SPACE;
}
}
// Set Location
dom.style.left = Css.toPx(desiredLeft);
dom.style.top = Css.toPx(desiredTop);
return overUnder;
}
}
export { Popper };