UNPKG

zarm-web

Version:
611 lines (491 loc) 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _dom = _interopRequireDefault(require("../utils/dom")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var getBoundingClientRect = _dom.default.getBoundingClientRect, getSupportedPropertyName = _dom.default.getSupportedPropertyName, getOffsetParent = _dom.default.getOffsetParent, getOuterSizes = _dom.default.getOuterSizes, getScrollParent = _dom.default.getScrollParent, isFixed = _dom.default.isFixed, getScrollTopValue = _dom.default.getScrollTopValue, getScrollLeftValue = _dom.default.getScrollLeftValue, setStyle = _dom.default.setStyle; var root = {}; if (typeof window !== 'undefined') { root = window; } var DEFAULTS = { placement: 'bottom', offset: 0, boundariesPadding: 5, preventOverflowOrder: ['left', 'right', 'top', 'bottom'], arrowElement: '[x-arrow]', modifiers: ['shift', 'offset', 'preventOverflow', 'keepTogether', 'arrow', 'flip', 'applyStyle'], removeOnDestroy: false }; /** * 获取一个元素相对任意父元素的偏移值 */ function getOffsetRectRelativeToCustomParent(element, parent, fixed) { var elementRect = getBoundingClientRect(element); var parentRect = getBoundingClientRect(parent); if (fixed) { var scrollParent = getScrollParent(parent); parentRect.top += scrollParent.scrollTop; parentRect.bottom += scrollParent.scrollTop; parentRect.left += scrollParent.scrollLeft; parentRect.right += scrollParent.scrollLeft; } var rect = { top: elementRect.top - parentRect.top, left: elementRect.left - parentRect.left, bottom: elementRect.top - parentRect.top + elementRect.height, right: elementRect.left - parentRect.left + elementRect.width, width: elementRect.width, height: elementRect.height }; return rect; } /** * Helpers */ /** * 获取相反的方向 */ function getOppositePlacement(placement) { var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; return placement.replace(/left|right|bottom|top/g, function (matched) { return hash[matched]; }); } /** * 生成气泡框的ClientRect */ function getPopperClientRect(popperOffsets) { var offsets = _objectSpread({}, popperOffsets); offsets.right = offsets.left + offsets.width; offsets.bottom = offsets.top + offsets.height; return offsets; } /** * 获取数组某个值的index */ function getArrayKeyIndex(arr, keyToFind) { var i = 0; var key; for (key in arr) { if (arr[key] === keyToFind) { return i; } i++; } return null; } /** * 检查是否是函数 */ function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } /** * 获取一个元素的各个offset值 */ function getOffsetRect(element) { var elementRect = { width: element.offsetWidth, height: element.offsetHeight, left: element.offsetLeft, top: element.offsetTop }; elementRect.right = elementRect.left + elementRect.width; elementRect.bottom = elementRect.top + elementRect.height; return elementRect; } /** * @constructor 气泡框构造函数 * @param {HTMLElement} reference * @param {HTMLElement} popper * @param {Object} options */ function Popper(reference, popper, options) { var _this = this; this._reference = reference; this._popper = popper; this.state = {}; this._options = _objectSpread({}, DEFAULTS, {}, options); this._options.modifiers = this._options.modifiers.map(function (modifier) { if (modifier === 'applyStyle') { _this._popper.setAttribute('x-placement', _this._options.placement); } return _this.modifiers[modifier] || modifier; }); this.state.position = this._getPosition(this._popper, this._reference); setStyle(this._popper, { position: this.state.position, top: 0 }); this.update(); if (root.requestAnimationFrame) { requestAnimationFrame(this.update.bind(this)); } else { setTimeout(this.update.bind(this)); } this._setupEventListeners(); // return this; } /** * 销毁 */ Popper.prototype.destroy = function () { this._popper.removeAttribute('x-placement'); this._popper.style.left = ''; this._popper.style.position = ''; this._popper.style.top = ''; this._popper.style[getSupportedPropertyName('transform')] = ''; this._removeEventListeners(); if (this._options.removeOnDestroy) { this._popper.remove(); } return this; }; /** * 更新方位,重新计算偏移量 */ Popper.prototype.update = function () { var data = {}; data.placement = this._options.placement; data._originalPlacement = this._options.placement; // 根据方位计算气泡框和reference相对气泡框定位父元素的left,top等值 data.offsets = this._getOffsets(this._popper, this._reference, data.placement); // 获取边界,用于优化在边界情况下正常显示气泡框 data.boundaries = this._getBoundaries(data, this._options.boundariesPadding); // 运行各个modifier函数细调和修正偏移量,并最终应用样式 data = this.runModifiers(data, this._options.modifiers); }; /** * 判断气泡框应该使用什么定位 */ Popper.prototype._getPosition = function (_, reference) { // const container = getOffsetParent(reference); var isParentFixed = isFixed(reference); return isParentFixed ? 'fixed' : 'absolute'; return 'absolute'; }; /** * 根据方位计算气泡框和reference相对气泡框定位父元素的left,top等值 */ Popper.prototype._getOffsets = function (popper, reference, placement) { placement = placement.split('-')[0]; var popperOffsets = {}; popperOffsets.position = this.state.position; var isParentFixed = popperOffsets.position === 'fixed'; // 根据方位计算reference相对气泡框定位父元素left, top等值 var referenceOffsets = getOffsetRectRelativeToCustomParent(reference, getOffsetParent(popper), isParentFixed); var popperRect = getOuterSizes(popper); if (['right', 'left'].indexOf(placement) !== -1) { popperOffsets.top = referenceOffsets.top + referenceOffsets.height / 2 - popperRect.height / 2; if (placement === 'left') { popperOffsets.left = referenceOffsets.left - popperRect.width; } else { popperOffsets.left = referenceOffsets.right; } } else { popperOffsets.left = referenceOffsets.left + referenceOffsets.width / 2 - popperRect.width / 2; if (placement === 'top') { popperOffsets.top = referenceOffsets.top - popperRect.height; } else { popperOffsets.top = referenceOffsets.bottom; } } popperOffsets.width = popperRect.width; popperOffsets.height = popperRect.height; return { popper: popperOffsets, reference: referenceOffsets }; }; /** * 设置resize和scroll事件,更新气泡框位置 */ Popper.prototype._setupEventListeners = function () { this.state.updateBound = this.update.bind(this); root.addEventListener('resize', this.state.updateBound); var target = getScrollParent(this._reference); if (target === root.document.body || target === root.document.documentElement) { target = root; } target.addEventListener('scroll', this.state.updateBound); }; /** * 移除事件 */ Popper.prototype._removeEventListeners = function () { root.removeEventListener('resize', this.state.updateBound); var target = getScrollParent(this._reference); if (target === root.document.body || target === root.document.documentElement) { target = root; } target.removeEventListener('scroll', this.state.updateBound); this.state.updateBound = null; }; /** * 获取边界,用于优化在边界情况下正常显示气泡框 */ Popper.prototype._getBoundaries = function (data, padding) { var boundaries = {}; var offsetParent = getOffsetParent(this._popper); var scrollParent = getScrollParent(this._popper); var offsetParentRect = getOffsetRect(offsetParent); var scrollTop = data.offsets.popper.position === 'fixed' ? 0 : getScrollTopValue(scrollParent); var scrollLeft = data.offsets.popper.position === 'fixed' ? 0 : getScrollLeftValue(scrollParent); boundaries = { top: 0 - (offsetParentRect.top - scrollTop), right: root.document.documentElement.clientWidth - (offsetParentRect.left - scrollLeft), bottom: root.document.documentElement.clientHeight - (offsetParentRect.top - scrollTop), left: 0 - (offsetParentRect.left - scrollLeft) }; boundaries.left += padding; boundaries.right -= padding; boundaries.top += padding; boundaries.bottom -= padding; return boundaries; }; /** * 运行各个modifier函数细调和修正偏移量,并最终应用样式 */ Popper.prototype.runModifiers = function (data, modifiers, ends) { var _this2 = this; var modifiersToRun = modifiers.slice(); if (ends !== undefined) { modifiersToRun = this._options.modifiers.slice(0, getArrayKeyIndex(this._options.modifiers, ends)); } modifiersToRun.forEach(function (modifier) { if (isFunction(modifier)) { data = modifier.call(_this2, data); } }); return data; }; /** * Modifiers list */ Popper.prototype.modifiers = {}; /** * 应用最后计算出的样式到气泡框 */ Popper.prototype.modifiers.applyStyle = function (data) { var styles = { position: data.offsets.popper.position }; var left = Math.round(data.offsets.popper.left); var top = Math.round(data.offsets.popper.top); var prefixedProperty; if (getSupportedPropertyName('transform')) { prefixedProperty = getSupportedPropertyName('transform'); styles[prefixedProperty] = "translate3d(".concat(left, "px, ").concat(top, "px, 0)"); styles.top = 0; styles.left = 0; } else { styles.left = left; styles.top = top; } setStyle(this._popper, styles); // 给气泡框添加属性选择器,用于定位arrow this._popper.setAttribute('x-placement', data.placement); // 设置arrow定位样式 if (data.offsets.arrow) { setStyle(data.arrowElement, data.offsets.arrow); } return data; }; /** * 计算气泡框在不同方位的top,left值 */ Popper.prototype.modifiers.shift = function (data) { var placement = data.placement; var basePlacement = placement.split('-')[0]; var shiftVariation = placement.split('-')[1]; if (shiftVariation) { var reference = data.offsets.reference; var popper = getPopperClientRect(data.offsets.popper); var shiftOffsets = { y: { start: { top: reference.top }, end: { top: reference.top + reference.height - popper.height } }, x: { start: { left: reference.left }, end: { left: reference.left + reference.width - popper.width } } }; var axis = ['bottom', 'top'].indexOf(basePlacement) !== -1 ? 'x' : 'y'; data.offsets.popper = popper = _objectSpread({}, popper, {}, shiftOffsets[axis][shiftVariation]); } return data; }; /** * 阻止并修正在边界情况下气泡框溢出问题 */ Popper.prototype.modifiers.preventOverflow = function (data) { var order = this._options.preventOverflowOrder; var popper = getPopperClientRect(data.offsets.popper); var check = { left: function left() { var left = popper.left; if (popper.left < data.boundaries.left) { left = Math.max(popper.left, data.boundaries.left); } return { left: left }; }, right: function right() { var left = popper.left; if (popper.right > data.boundaries.right) { left = Math.min(popper.left, data.boundaries.right - popper.width); } return { left: left }; }, top: function top() { var top = popper.top; if (popper.top < data.boundaries.top) { top = Math.max(popper.top, data.boundaries.top); } return { top: top }; }, bottom: function bottom() { var top = popper.top; if (popper.bottom > data.boundaries.bottom) { top = Math.min(popper.top, data.boundaries.bottom - popper.height); } return { top: top }; } }; order.forEach(function (direction) { data.offsets.popper = popper = _objectSpread({}, popper, {}, check[direction]()); }); return data; }; /** * 保持气泡框和reference始终在一起 */ Popper.prototype.modifiers.keepTogether = function (data) { var popper = getPopperClientRect(data.offsets.popper); var reference = data.offsets.reference; var f = Math.floor; if (popper.right < f(reference.left)) { data.offsets.popper.left = f(reference.left) - popper.width; } if (popper.left > f(reference.right)) { data.offsets.popper.left = f(reference.right); } if (popper.bottom < f(reference.top)) { data.offsets.popper.top = f(reference.top) - popper.height; } if (popper.top > f(reference.bottom)) { data.offsets.popper.top = f(reference.bottom); } return data; }; /** * 如果气泡框被边界挤得和reference重叠了,进行方向翻转 */ Popper.prototype.modifiers.flip = function (data) { var _this3 = this; if (data.flipped && data.placement === data._originalPlacement) { // 两边都没空间,导致循环翻转 return data; } var placement = data.placement.split('-')[0]; var placementOpposite = getOppositePlacement(placement); var variation = data.placement.split('-')[1] || ''; var flipOrder = [placement, placementOpposite]; flipOrder.forEach(function (step, index) { if (placement !== step || flipOrder.length === index + 1) { return; } placement = data.placement.split('-')[0]; placementOpposite = getOppositePlacement(placement); var popperOffsets = getPopperClientRect(data.offsets.popper); var a = ['right', 'bottom'].indexOf(placement) !== -1; if (a && Math.floor(data.offsets.reference[placement]) > Math.floor(popperOffsets[placementOpposite]) || !a && Math.floor(data.offsets.reference[placement]) < Math.floor(popperOffsets[placementOpposite])) { data.flipped = true; data.placement = flipOrder[index + 1]; if (variation) { data.placement += "-".concat(variation); } // 计算翻转后的偏移值 data.offsets.popper = _this3._getOffsets(_this3._popper, _this3._reference, data.placement).popper; // 因为翻转了反向,重新跑一遍flip之前的modifiers data = _this3.runModifiers(data, _this3._options.modifiers, _this3._flip); } }); return data; }; /** * 根据传入的offset对气泡框做偏移 */ Popper.prototype.modifiers.offset = function (data) { var offset = this._options.offset; var popper = data.offsets.popper; if (data.placement.indexOf('left') !== -1) { popper.top -= offset; } else if (data.placement.indexOf('right') !== -1) { popper.top += offset; } else if (data.placement.indexOf('top') !== -1) { popper.left -= offset; } else if (data.placement.indexOf('bottom') !== -1) { popper.left += offset; } return data; }; /** * 保证箭头永远在气泡框和reference之间 */ Popper.prototype.modifiers.arrow = function (data) { var arrow = this._popper.querySelector(this._options.arrowElement); var arrowStyle = {}; var placement = data.placement.split('-')[0]; var popper = getPopperClientRect(data.offsets.popper); var reference = data.offsets.reference; var isVertical = ['left', 'right'].indexOf(placement) !== -1; var len = isVertical ? 'height' : 'width'; var side = isVertical ? 'top' : 'left'; var altSide = isVertical ? 'left' : 'top'; var opSide = isVertical ? 'bottom' : 'right'; var arrowSize = getOuterSizes(arrow)[len]; if (reference[opSide] - arrowSize < popper[side]) { data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowSize); } if (reference[side] + arrowSize > popper[opSide]) { data.offsets.popper[side] += reference[side] + arrowSize - popper[opSide]; } var center = reference[side] + reference[len] / 2 - arrowSize / 2; var sideValue = center - popper[side]; sideValue = Math.max(Math.min(popper[len] - arrowSize - 6, sideValue), 6); arrowStyle[side] = sideValue; arrowStyle[altSide] = ''; data.offsets.arrow = arrowStyle; data.arrowElement = arrow; return data; }; var _default = Popper; exports.default = _default;