zarm-web
Version:
基于 React 的桌面端UI库
611 lines (491 loc) • 17.6 kB
JavaScript
"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;