UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

187 lines (162 loc) 5.75 kB
import { getScrollbarWidth } from './scroll.js' export function validatePosition (pos) { let parts = pos.split(' ') if (parts.length !== 2) { return false } if (!['top', 'center', 'bottom'].includes(parts[0])) { console.error('Anchor/Self position must start with one of top/center/bottom') return false } if (!['left', 'middle', 'right'].includes(parts[1])) { console.error('Anchor/Self position must end with one of left/middle/right') return false } return true } export function validateOffset (val) { if (!val) { return true } if (val.length !== 2) { return false } if (typeof val[0] !== 'number' || typeof val[1] !== 'number') { return false } return true } export function parsePosition (pos) { let parts = pos.split(' ') return { vertical: parts[0], horizontal: parts[1] } } export function validateCover (val) { if (val === true || val === false) { return true } return validatePosition(val) } export function getAnchorProps (el, offset) { let { top, left, right, bottom, width, height } = el.getBoundingClientRect() if (offset !== void 0) { top -= offset[1] left -= offset[0] bottom += offset[1] right += offset[0] width += offset[0] height += offset[1] } return { top, left, right, bottom, width, height, middle: left + (right - left) / 2, center: top + (bottom - top) / 2 } } export function getTargetProps (el) { return { top: 0, center: el.offsetHeight / 2, bottom: el.offsetHeight, left: 0, middle: el.offsetWidth / 2, right: el.offsetWidth } } // cfg: { el, anchorEl, anchorOrigin, selfOrigin, offset, absoluteOffset, cover, fit, maxHeight, maxWidth } export function setPosition (cfg) { let anchorProps // scroll position might change // if max-height changes, so we // need to restore it after we calculate // the new positioning const scrollTop = cfg.el.scrollTop cfg.el.style.maxHeight = cfg.maxHeight cfg.el.style.maxWidth = cfg.maxWidth if (cfg.absoluteOffset === void 0) { anchorProps = getAnchorProps(cfg.anchorEl, cfg.cover === true ? [0, 0] : cfg.offset) } else { const { top: anchorTop, left: anchorLeft } = cfg.anchorEl.getBoundingClientRect(), top = anchorTop + cfg.absoluteOffset.top, left = anchorLeft + cfg.absoluteOffset.left anchorProps = { top, left, width: 1, height: 1, right: left + 1, center: top, middle: left, bottom: top + 1 } } if (cfg.fit === true || cfg.cover === true) { cfg.el.style.minWidth = anchorProps.width + 'px' if (cfg.cover === true) { cfg.el.style.minHeight = anchorProps.height + 'px' } } const targetProps = getTargetProps(cfg.el), props = { top: anchorProps[cfg.anchorOrigin.vertical] - targetProps[cfg.selfOrigin.vertical], left: anchorProps[cfg.anchorOrigin.horizontal] - targetProps[cfg.selfOrigin.horizontal] } applyBoundaries(props, anchorProps, targetProps, cfg.anchorOrigin, cfg.selfOrigin) cfg.el.style.top = Math.max(0, Math.floor(props.top)) + 'px' cfg.el.style.left = Math.max(0, Math.floor(props.left)) + 'px' if (props.maxHeight !== void 0) { cfg.el.style.maxHeight = Math.floor(props.maxHeight) + 'px' } if (props.maxWidth !== void 0) { cfg.el.style.maxWidth = Math.floor(props.maxWidth) + 'px' } // restore scroll position if (cfg.el.scrollTop !== scrollTop) { cfg.el.scrollTop = scrollTop } } function applyBoundaries (props, anchorProps, targetProps, anchorOrigin, selfOrigin) { const margin = getScrollbarWidth() let { innerHeight, innerWidth } = window // don't go bellow scrollbars innerHeight -= margin innerWidth -= margin if (props.top < 0 || props.top + targetProps.bottom > innerHeight) { if (selfOrigin.vertical === 'center') { props.top = anchorProps[selfOrigin.vertical] > innerHeight / 2 ? innerHeight - targetProps.bottom : 0 props.maxHeight = Math.min(targetProps.bottom, innerHeight) } else if (anchorProps[selfOrigin.vertical] > innerHeight / 2) { const anchorY = Math.min( innerHeight, anchorOrigin.vertical === 'center' ? anchorProps.center : (anchorOrigin.vertical === selfOrigin.vertical ? anchorProps.bottom : anchorProps.top) ) props.maxHeight = Math.min(targetProps.bottom, anchorY) props.top = Math.max(0, anchorY - props.maxHeight) } else { props.top = anchorOrigin.vertical === 'center' ? anchorProps.center : (anchorOrigin.vertical === selfOrigin.vertical ? anchorProps.top : anchorProps.bottom) props.maxHeight = Math.min(targetProps.bottom, innerHeight - props.top) } } if (props.left < 0 || props.left + targetProps.right > innerWidth) { props.maxWidth = Math.min(targetProps.right, innerWidth) if (selfOrigin.horizontal === 'middle') { props.left = anchorProps[selfOrigin.horizontal] > innerWidth / 2 ? innerWidth - targetProps.right : 0 } else if (anchorProps[selfOrigin.horizontal] > innerWidth / 2) { const anchorX = Math.min( innerWidth, anchorOrigin.horizontal === 'middle' ? anchorProps.center : (anchorOrigin.horizontal === selfOrigin.horizontal ? anchorProps.right : anchorProps.left) ) props.maxWidth = Math.min(targetProps.right, anchorX) props.left = Math.max(0, anchorX - props.maxWidth) } else { props.left = anchorOrigin.horizontal === 'middle' ? anchorProps.center : (anchorOrigin.horizontal === selfOrigin.horizontal ? anchorProps.left : anchorProps.right) props.maxWidth = Math.min(targetProps.right, innerWidth - props.left) } } }