123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- "use strict";
- var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = useAlign;
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
- var _findDOMNode = require("rc-util/lib/Dom/findDOMNode");
- var _isVisible = _interopRequireDefault(require("rc-util/lib/Dom/isVisible"));
- var _useEvent = _interopRequireDefault(require("rc-util/lib/hooks/useEvent"));
- var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
- var React = _interopRequireWildcard(require("react"));
- var _util = require("../util");
- function getUnitOffset(size) {
- var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
- var offsetStr = "".concat(offset);
- var cells = offsetStr.match(/^(.*)\%$/);
- if (cells) {
- return size * (parseFloat(cells[1]) / 100);
- }
- return parseFloat(offsetStr);
- }
- function getNumberOffset(rect, offset) {
- var _ref = offset || [],
- _ref2 = (0, _slicedToArray2.default)(_ref, 2),
- offsetX = _ref2[0],
- offsetY = _ref2[1];
- return [getUnitOffset(rect.width, offsetX), getUnitOffset(rect.height, offsetY)];
- }
- function splitPoints() {
- var points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
- return [points[0], points[1]];
- }
- function getAlignPoint(rect, points) {
- var topBottom = points[0];
- var leftRight = points[1];
- var x;
- var y;
- // Top & Bottom
- if (topBottom === 't') {
- y = rect.y;
- } else if (topBottom === 'b') {
- y = rect.y + rect.height;
- } else {
- y = rect.y + rect.height / 2;
- }
- // Left & Right
- if (leftRight === 'l') {
- x = rect.x;
- } else if (leftRight === 'r') {
- x = rect.x + rect.width;
- } else {
- x = rect.x + rect.width / 2;
- }
- return {
- x: x,
- y: y
- };
- }
- function reversePoints(points, index) {
- var reverseMap = {
- t: 'b',
- b: 't',
- l: 'r',
- r: 'l'
- };
- return points.map(function (point, i) {
- if (i === index) {
- return reverseMap[point] || 'c';
- }
- return point;
- }).join('');
- }
- function useAlign(open, popupEle, target, placement, builtinPlacements, popupAlign, onPopupAlign) {
- var _React$useState = React.useState({
- ready: false,
- offsetX: 0,
- offsetY: 0,
- offsetR: 0,
- offsetB: 0,
- arrowX: 0,
- arrowY: 0,
- scaleX: 1,
- scaleY: 1,
- align: builtinPlacements[placement] || {}
- }),
- _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
- offsetInfo = _React$useState2[0],
- setOffsetInfo = _React$useState2[1];
- var alignCountRef = React.useRef(0);
- var scrollerList = React.useMemo(function () {
- if (!popupEle) {
- return [];
- }
- return (0, _util.collectScroller)(popupEle);
- }, [popupEle]);
- // ========================= Flip ==========================
- // We will memo flip info.
- // If size change to make flip, it will memo the flip info and use it in next align.
- var prevFlipRef = React.useRef({});
- var resetFlipCache = function resetFlipCache() {
- prevFlipRef.current = {};
- };
- if (!open) {
- resetFlipCache();
- }
- // ========================= Align =========================
- var onAlign = (0, _useEvent.default)(function () {
- if (popupEle && target && open) {
- var _popupElement$parentE, _popupRect$x, _popupRect$y, _popupElement$parentE2;
- var popupElement = popupEle;
- var doc = popupElement.ownerDocument;
- var win = (0, _util.getWin)(popupElement);
- var _win$getComputedStyle = win.getComputedStyle(popupElement),
- popupPosition = _win$getComputedStyle.position;
- var originLeft = popupElement.style.left;
- var originTop = popupElement.style.top;
- var originRight = popupElement.style.right;
- var originBottom = popupElement.style.bottom;
- var originOverflow = popupElement.style.overflow;
- // Placement
- var placementInfo = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, builtinPlacements[placement]), popupAlign);
- // placeholder element
- var placeholderElement = doc.createElement('div');
- (_popupElement$parentE = popupElement.parentElement) === null || _popupElement$parentE === void 0 || _popupElement$parentE.appendChild(placeholderElement);
- placeholderElement.style.left = "".concat(popupElement.offsetLeft, "px");
- placeholderElement.style.top = "".concat(popupElement.offsetTop, "px");
- placeholderElement.style.position = popupPosition;
- placeholderElement.style.height = "".concat(popupElement.offsetHeight, "px");
- placeholderElement.style.width = "".concat(popupElement.offsetWidth, "px");
- // Reset first
- popupElement.style.left = '0';
- popupElement.style.top = '0';
- popupElement.style.right = 'auto';
- popupElement.style.bottom = 'auto';
- popupElement.style.overflow = 'hidden';
- // Calculate align style, we should consider `transform` case
- var targetRect;
- if (Array.isArray(target)) {
- targetRect = {
- x: target[0],
- y: target[1],
- width: 0,
- height: 0
- };
- } else {
- var _rect$x, _rect$y;
- var rect = target.getBoundingClientRect();
- rect.x = (_rect$x = rect.x) !== null && _rect$x !== void 0 ? _rect$x : rect.left;
- rect.y = (_rect$y = rect.y) !== null && _rect$y !== void 0 ? _rect$y : rect.top;
- targetRect = {
- x: rect.x,
- y: rect.y,
- width: rect.width,
- height: rect.height
- };
- }
- var popupRect = popupElement.getBoundingClientRect();
- var _win$getComputedStyle2 = win.getComputedStyle(popupElement),
- height = _win$getComputedStyle2.height,
- width = _win$getComputedStyle2.width;
- popupRect.x = (_popupRect$x = popupRect.x) !== null && _popupRect$x !== void 0 ? _popupRect$x : popupRect.left;
- popupRect.y = (_popupRect$y = popupRect.y) !== null && _popupRect$y !== void 0 ? _popupRect$y : popupRect.top;
- var _doc$documentElement = doc.documentElement,
- clientWidth = _doc$documentElement.clientWidth,
- clientHeight = _doc$documentElement.clientHeight,
- scrollWidth = _doc$documentElement.scrollWidth,
- scrollHeight = _doc$documentElement.scrollHeight,
- scrollTop = _doc$documentElement.scrollTop,
- scrollLeft = _doc$documentElement.scrollLeft;
- var popupHeight = popupRect.height;
- var popupWidth = popupRect.width;
- var targetHeight = targetRect.height;
- var targetWidth = targetRect.width;
- // Get bounding of visible area
- var visibleRegion = {
- left: 0,
- top: 0,
- right: clientWidth,
- bottom: clientHeight
- };
- var scrollRegion = {
- left: -scrollLeft,
- top: -scrollTop,
- right: scrollWidth - scrollLeft,
- bottom: scrollHeight - scrollTop
- };
- var htmlRegion = placementInfo.htmlRegion;
- var VISIBLE = 'visible';
- var VISIBLE_FIRST = 'visibleFirst';
- if (htmlRegion !== 'scroll' && htmlRegion !== VISIBLE_FIRST) {
- htmlRegion = VISIBLE;
- }
- var isVisibleFirst = htmlRegion === VISIBLE_FIRST;
- var scrollRegionArea = (0, _util.getVisibleArea)(scrollRegion, scrollerList);
- var visibleRegionArea = (0, _util.getVisibleArea)(visibleRegion, scrollerList);
- var visibleArea = htmlRegion === VISIBLE ? visibleRegionArea : scrollRegionArea;
- // When set to `visibleFirst`,
- // the check `adjust` logic will use `visibleRegion` for check first.
- var adjustCheckVisibleArea = isVisibleFirst ? visibleRegionArea : visibleArea;
- // Record right & bottom align data
- popupElement.style.left = 'auto';
- popupElement.style.top = 'auto';
- popupElement.style.right = '0';
- popupElement.style.bottom = '0';
- var popupMirrorRect = popupElement.getBoundingClientRect();
- // Reset back
- popupElement.style.left = originLeft;
- popupElement.style.top = originTop;
- popupElement.style.right = originRight;
- popupElement.style.bottom = originBottom;
- popupElement.style.overflow = originOverflow;
- (_popupElement$parentE2 = popupElement.parentElement) === null || _popupElement$parentE2 === void 0 || _popupElement$parentE2.removeChild(placeholderElement);
- // Calculate scale
- var _scaleX = (0, _util.toNum)(Math.round(popupWidth / parseFloat(width) * 1000) / 1000);
- var _scaleY = (0, _util.toNum)(Math.round(popupHeight / parseFloat(height) * 1000) / 1000);
- // No need to align since it's not visible in view
- if (_scaleX === 0 || _scaleY === 0 || (0, _findDOMNode.isDOM)(target) && !(0, _isVisible.default)(target)) {
- return;
- }
- // Offset
- var offset = placementInfo.offset,
- targetOffset = placementInfo.targetOffset;
- var _getNumberOffset = getNumberOffset(popupRect, offset),
- _getNumberOffset2 = (0, _slicedToArray2.default)(_getNumberOffset, 2),
- popupOffsetX = _getNumberOffset2[0],
- popupOffsetY = _getNumberOffset2[1];
- var _getNumberOffset3 = getNumberOffset(targetRect, targetOffset),
- _getNumberOffset4 = (0, _slicedToArray2.default)(_getNumberOffset3, 2),
- targetOffsetX = _getNumberOffset4[0],
- targetOffsetY = _getNumberOffset4[1];
- targetRect.x -= targetOffsetX;
- targetRect.y -= targetOffsetY;
- // Points
- var _ref3 = placementInfo.points || [],
- _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
- popupPoint = _ref4[0],
- targetPoint = _ref4[1];
- var targetPoints = splitPoints(targetPoint);
- var popupPoints = splitPoints(popupPoint);
- var targetAlignPoint = getAlignPoint(targetRect, targetPoints);
- var popupAlignPoint = getAlignPoint(popupRect, popupPoints);
- // Real align info may not same as origin one
- var nextAlignInfo = (0, _objectSpread2.default)({}, placementInfo);
- // Next Offset
- var nextOffsetX = targetAlignPoint.x - popupAlignPoint.x + popupOffsetX;
- var nextOffsetY = targetAlignPoint.y - popupAlignPoint.y + popupOffsetY;
- // ============== Intersection ===============
- // Get area by position. Used for check if flip area is better
- function getIntersectionVisibleArea(offsetX, offsetY) {
- var area = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : visibleArea;
- var l = popupRect.x + offsetX;
- var t = popupRect.y + offsetY;
- var r = l + popupWidth;
- var b = t + popupHeight;
- var visibleL = Math.max(l, area.left);
- var visibleT = Math.max(t, area.top);
- var visibleR = Math.min(r, area.right);
- var visibleB = Math.min(b, area.bottom);
- return Math.max(0, (visibleR - visibleL) * (visibleB - visibleT));
- }
- var originIntersectionVisibleArea = getIntersectionVisibleArea(nextOffsetX, nextOffsetY);
- // As `visibleFirst`, we prepare this for check
- var originIntersectionRecommendArea = getIntersectionVisibleArea(nextOffsetX, nextOffsetY, visibleRegionArea);
- // ========================== Overflow ===========================
- var targetAlignPointTL = getAlignPoint(targetRect, ['t', 'l']);
- var popupAlignPointTL = getAlignPoint(popupRect, ['t', 'l']);
- var targetAlignPointBR = getAlignPoint(targetRect, ['b', 'r']);
- var popupAlignPointBR = getAlignPoint(popupRect, ['b', 'r']);
- var overflow = placementInfo.overflow || {};
- var adjustX = overflow.adjustX,
- adjustY = overflow.adjustY,
- shiftX = overflow.shiftX,
- shiftY = overflow.shiftY;
- var supportAdjust = function supportAdjust(val) {
- if (typeof val === 'boolean') {
- return val;
- }
- return val >= 0;
- };
- // Prepare position
- var nextPopupY;
- var nextPopupBottom;
- var nextPopupX;
- var nextPopupRight;
- function syncNextPopupPosition() {
- nextPopupY = popupRect.y + nextOffsetY;
- nextPopupBottom = nextPopupY + popupHeight;
- nextPopupX = popupRect.x + nextOffsetX;
- nextPopupRight = nextPopupX + popupWidth;
- }
- syncNextPopupPosition();
- // >>>>>>>>>> Top & Bottom
- var needAdjustY = supportAdjust(adjustY);
- var sameTB = popupPoints[0] === targetPoints[0];
- // Bottom to Top
- if (needAdjustY && popupPoints[0] === 't' && (nextPopupBottom > adjustCheckVisibleArea.bottom || prevFlipRef.current.bt)) {
- var tmpNextOffsetY = nextOffsetY;
- if (sameTB) {
- tmpNextOffsetY -= popupHeight - targetHeight;
- } else {
- tmpNextOffsetY = targetAlignPointTL.y - popupAlignPointBR.y - popupOffsetY;
- }
- var newVisibleArea = getIntersectionVisibleArea(nextOffsetX, tmpNextOffsetY);
- var newVisibleRecommendArea = getIntersectionVisibleArea(nextOffsetX, tmpNextOffsetY, visibleRegionArea);
- if (
- // Of course use larger one
- newVisibleArea > originIntersectionVisibleArea || newVisibleArea === originIntersectionVisibleArea && (!isVisibleFirst ||
- // Choose recommend one
- newVisibleRecommendArea >= originIntersectionRecommendArea)) {
- prevFlipRef.current.bt = true;
- nextOffsetY = tmpNextOffsetY;
- popupOffsetY = -popupOffsetY;
- nextAlignInfo.points = [reversePoints(popupPoints, 0), reversePoints(targetPoints, 0)];
- } else {
- prevFlipRef.current.bt = false;
- }
- }
- // Top to Bottom
- if (needAdjustY && popupPoints[0] === 'b' && (nextPopupY < adjustCheckVisibleArea.top || prevFlipRef.current.tb)) {
- var _tmpNextOffsetY = nextOffsetY;
- if (sameTB) {
- _tmpNextOffsetY += popupHeight - targetHeight;
- } else {
- _tmpNextOffsetY = targetAlignPointBR.y - popupAlignPointTL.y - popupOffsetY;
- }
- var _newVisibleArea = getIntersectionVisibleArea(nextOffsetX, _tmpNextOffsetY);
- var _newVisibleRecommendArea = getIntersectionVisibleArea(nextOffsetX, _tmpNextOffsetY, visibleRegionArea);
- if (
- // Of course use larger one
- _newVisibleArea > originIntersectionVisibleArea || _newVisibleArea === originIntersectionVisibleArea && (!isVisibleFirst ||
- // Choose recommend one
- _newVisibleRecommendArea >= originIntersectionRecommendArea)) {
- prevFlipRef.current.tb = true;
- nextOffsetY = _tmpNextOffsetY;
- popupOffsetY = -popupOffsetY;
- nextAlignInfo.points = [reversePoints(popupPoints, 0), reversePoints(targetPoints, 0)];
- } else {
- prevFlipRef.current.tb = false;
- }
- }
- // >>>>>>>>>> Left & Right
- var needAdjustX = supportAdjust(adjustX);
- // >>>>> Flip
- var sameLR = popupPoints[1] === targetPoints[1];
- // Right to Left
- if (needAdjustX && popupPoints[1] === 'l' && (nextPopupRight > adjustCheckVisibleArea.right || prevFlipRef.current.rl)) {
- var tmpNextOffsetX = nextOffsetX;
- if (sameLR) {
- tmpNextOffsetX -= popupWidth - targetWidth;
- } else {
- tmpNextOffsetX = targetAlignPointTL.x - popupAlignPointBR.x - popupOffsetX;
- }
- var _newVisibleArea2 = getIntersectionVisibleArea(tmpNextOffsetX, nextOffsetY);
- var _newVisibleRecommendArea2 = getIntersectionVisibleArea(tmpNextOffsetX, nextOffsetY, visibleRegionArea);
- if (
- // Of course use larger one
- _newVisibleArea2 > originIntersectionVisibleArea || _newVisibleArea2 === originIntersectionVisibleArea && (!isVisibleFirst ||
- // Choose recommend one
- _newVisibleRecommendArea2 >= originIntersectionRecommendArea)) {
- prevFlipRef.current.rl = true;
- nextOffsetX = tmpNextOffsetX;
- popupOffsetX = -popupOffsetX;
- nextAlignInfo.points = [reversePoints(popupPoints, 1), reversePoints(targetPoints, 1)];
- } else {
- prevFlipRef.current.rl = false;
- }
- }
- // Left to Right
- if (needAdjustX && popupPoints[1] === 'r' && (nextPopupX < adjustCheckVisibleArea.left || prevFlipRef.current.lr)) {
- var _tmpNextOffsetX = nextOffsetX;
- if (sameLR) {
- _tmpNextOffsetX += popupWidth - targetWidth;
- } else {
- _tmpNextOffsetX = targetAlignPointBR.x - popupAlignPointTL.x - popupOffsetX;
- }
- var _newVisibleArea3 = getIntersectionVisibleArea(_tmpNextOffsetX, nextOffsetY);
- var _newVisibleRecommendArea3 = getIntersectionVisibleArea(_tmpNextOffsetX, nextOffsetY, visibleRegionArea);
- if (
- // Of course use larger one
- _newVisibleArea3 > originIntersectionVisibleArea || _newVisibleArea3 === originIntersectionVisibleArea && (!isVisibleFirst ||
- // Choose recommend one
- _newVisibleRecommendArea3 >= originIntersectionRecommendArea)) {
- prevFlipRef.current.lr = true;
- nextOffsetX = _tmpNextOffsetX;
- popupOffsetX = -popupOffsetX;
- nextAlignInfo.points = [reversePoints(popupPoints, 1), reversePoints(targetPoints, 1)];
- } else {
- prevFlipRef.current.lr = false;
- }
- }
- // ============================ Shift ============================
- syncNextPopupPosition();
- var numShiftX = shiftX === true ? 0 : shiftX;
- if (typeof numShiftX === 'number') {
- // Left
- if (nextPopupX < visibleRegionArea.left) {
- nextOffsetX -= nextPopupX - visibleRegionArea.left - popupOffsetX;
- if (targetRect.x + targetWidth < visibleRegionArea.left + numShiftX) {
- nextOffsetX += targetRect.x - visibleRegionArea.left + targetWidth - numShiftX;
- }
- }
- // Right
- if (nextPopupRight > visibleRegionArea.right) {
- nextOffsetX -= nextPopupRight - visibleRegionArea.right - popupOffsetX;
- if (targetRect.x > visibleRegionArea.right - numShiftX) {
- nextOffsetX += targetRect.x - visibleRegionArea.right + numShiftX;
- }
- }
- }
- var numShiftY = shiftY === true ? 0 : shiftY;
- if (typeof numShiftY === 'number') {
- // Top
- if (nextPopupY < visibleRegionArea.top) {
- nextOffsetY -= nextPopupY - visibleRegionArea.top - popupOffsetY;
- // When target if far away from visible area
- // Stop shift
- if (targetRect.y + targetHeight < visibleRegionArea.top + numShiftY) {
- nextOffsetY += targetRect.y - visibleRegionArea.top + targetHeight - numShiftY;
- }
- }
- // Bottom
- if (nextPopupBottom > visibleRegionArea.bottom) {
- nextOffsetY -= nextPopupBottom - visibleRegionArea.bottom - popupOffsetY;
- if (targetRect.y > visibleRegionArea.bottom - numShiftY) {
- nextOffsetY += targetRect.y - visibleRegionArea.bottom + numShiftY;
- }
- }
- }
- // ============================ Arrow ============================
- // Arrow center align
- var popupLeft = popupRect.x + nextOffsetX;
- var popupRight = popupLeft + popupWidth;
- var popupTop = popupRect.y + nextOffsetY;
- var popupBottom = popupTop + popupHeight;
- var targetLeft = targetRect.x;
- var targetRight = targetLeft + targetWidth;
- var targetTop = targetRect.y;
- var targetBottom = targetTop + targetHeight;
- var maxLeft = Math.max(popupLeft, targetLeft);
- var minRight = Math.min(popupRight, targetRight);
- var xCenter = (maxLeft + minRight) / 2;
- var nextArrowX = xCenter - popupLeft;
- var maxTop = Math.max(popupTop, targetTop);
- var minBottom = Math.min(popupBottom, targetBottom);
- var yCenter = (maxTop + minBottom) / 2;
- var nextArrowY = yCenter - popupTop;
- onPopupAlign === null || onPopupAlign === void 0 || onPopupAlign(popupEle, nextAlignInfo);
- // Additional calculate right & bottom position
- var offsetX4Right = popupMirrorRect.right - popupRect.x - (nextOffsetX + popupRect.width);
- var offsetY4Bottom = popupMirrorRect.bottom - popupRect.y - (nextOffsetY + popupRect.height);
- if (_scaleX === 1) {
- nextOffsetX = Math.round(nextOffsetX);
- offsetX4Right = Math.round(offsetX4Right);
- }
- if (_scaleY === 1) {
- nextOffsetY = Math.round(nextOffsetY);
- offsetY4Bottom = Math.round(offsetY4Bottom);
- }
- var nextOffsetInfo = {
- ready: true,
- offsetX: nextOffsetX / _scaleX,
- offsetY: nextOffsetY / _scaleY,
- offsetR: offsetX4Right / _scaleX,
- offsetB: offsetY4Bottom / _scaleY,
- arrowX: nextArrowX / _scaleX,
- arrowY: nextArrowY / _scaleY,
- scaleX: _scaleX,
- scaleY: _scaleY,
- align: nextAlignInfo
- };
- setOffsetInfo(nextOffsetInfo);
- }
- });
- var triggerAlign = function triggerAlign() {
- alignCountRef.current += 1;
- var id = alignCountRef.current;
- // Merge all align requirement into one frame
- Promise.resolve().then(function () {
- if (alignCountRef.current === id) {
- onAlign();
- }
- });
- };
- // Reset ready status when placement & open changed
- var resetReady = function resetReady() {
- setOffsetInfo(function (ori) {
- return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, ori), {}, {
- ready: false
- });
- });
- };
- (0, _useLayoutEffect.default)(resetReady, [placement]);
- (0, _useLayoutEffect.default)(function () {
- if (!open) {
- resetReady();
- }
- }, [open]);
- return [offsetInfo.ready, offsetInfo.offsetX, offsetInfo.offsetY, offsetInfo.offsetR, offsetInfo.offsetB, offsetInfo.arrowX, offsetInfo.arrowY, offsetInfo.scaleX, offsetInfo.scaleY, offsetInfo.align, triggerAlign];
- }
|