import { getArrowOffset } from '../style/placementArrow'; export function getOverflowOptions(placement, arrowOffset, arrowWidth, autoAdjustOverflow) { if (autoAdjustOverflow === false) { return { adjustX: false, adjustY: false }; } const overflow = autoAdjustOverflow && typeof autoAdjustOverflow === 'object' ? autoAdjustOverflow : {}; const baseOverflow = {}; switch (placement) { case 'top': case 'bottom': baseOverflow.shiftX = arrowOffset.dropdownArrowOffset * 2 + arrowWidth; baseOverflow.shiftY = true; baseOverflow.adjustY = true; break; case 'left': case 'right': baseOverflow.shiftY = arrowOffset.dropdownArrowOffsetVertical * 2 + arrowWidth; baseOverflow.shiftX = true; baseOverflow.adjustX = true; break; } const mergedOverflow = Object.assign(Object.assign({}, baseOverflow), overflow); // Support auto shift if (!mergedOverflow.shiftX) { mergedOverflow.adjustX = true; } if (!mergedOverflow.shiftY) { mergedOverflow.adjustY = true; } return mergedOverflow; } const PlacementAlignMap = { left: { points: ['cr', 'cl'] }, right: { points: ['cl', 'cr'] }, top: { points: ['bc', 'tc'] }, bottom: { points: ['tc', 'bc'] }, topLeft: { points: ['bl', 'tl'] }, leftTop: { points: ['tr', 'tl'] }, topRight: { points: ['br', 'tr'] }, rightTop: { points: ['tl', 'tr'] }, bottomRight: { points: ['tr', 'br'] }, rightBottom: { points: ['bl', 'br'] }, bottomLeft: { points: ['tl', 'bl'] }, leftBottom: { points: ['br', 'bl'] } }; const ArrowCenterPlacementAlignMap = { topLeft: { points: ['bl', 'tc'] }, leftTop: { points: ['tr', 'cl'] }, topRight: { points: ['br', 'tc'] }, rightTop: { points: ['tl', 'cr'] }, bottomRight: { points: ['tr', 'bc'] }, rightBottom: { points: ['bl', 'cr'] }, bottomLeft: { points: ['tl', 'bc'] }, leftBottom: { points: ['br', 'cl'] } }; const DisableAutoArrowList = new Set(['topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom']); export default function getPlacements(config) { const { arrowWidth, autoAdjustOverflow, arrowPointAtCenter, offset, borderRadius, visibleFirst } = config; const halfArrowWidth = arrowWidth / 2; const placementMap = {}; Object.keys(PlacementAlignMap).forEach(key => { const template = arrowPointAtCenter && ArrowCenterPlacementAlignMap[key] || PlacementAlignMap[key]; const placementInfo = Object.assign(Object.assign({}, template), { offset: [0, 0], dynamicInset: true }); placementMap[key] = placementInfo; // Disable autoArrow since design is fixed position if (DisableAutoArrowList.has(key)) { placementInfo.autoArrow = false; } // Static offset switch (key) { case 'top': case 'topLeft': case 'topRight': placementInfo.offset[1] = -halfArrowWidth - offset; break; case 'bottom': case 'bottomLeft': case 'bottomRight': placementInfo.offset[1] = halfArrowWidth + offset; break; case 'left': case 'leftTop': case 'leftBottom': placementInfo.offset[0] = -halfArrowWidth - offset; break; case 'right': case 'rightTop': case 'rightBottom': placementInfo.offset[0] = halfArrowWidth + offset; break; } // Dynamic offset const arrowOffset = getArrowOffset({ contentRadius: borderRadius, limitVerticalRadius: true }); if (arrowPointAtCenter) { switch (key) { case 'topLeft': case 'bottomLeft': placementInfo.offset[0] = -arrowOffset.dropdownArrowOffset - halfArrowWidth; break; case 'topRight': case 'bottomRight': placementInfo.offset[0] = arrowOffset.dropdownArrowOffset + halfArrowWidth; break; case 'leftTop': case 'rightTop': placementInfo.offset[1] = -arrowOffset.dropdownArrowOffset - halfArrowWidth; break; case 'leftBottom': case 'rightBottom': placementInfo.offset[1] = arrowOffset.dropdownArrowOffset + halfArrowWidth; break; } } // Overflow placementInfo.overflow = getOverflowOptions(key, arrowOffset, arrowWidth, autoAdjustOverflow); // VisibleFirst if (visibleFirst) { placementInfo.htmlRegion = 'visibleFirst'; } }); return placementMap; }