placements.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import { getArrowOffsetToken } from '../style/placementArrow';
  2. export function getOverflowOptions(placement, arrowOffset, arrowWidth, autoAdjustOverflow) {
  3. if (autoAdjustOverflow === false) {
  4. return {
  5. adjustX: false,
  6. adjustY: false
  7. };
  8. }
  9. const overflow = autoAdjustOverflow && typeof autoAdjustOverflow === 'object' ? autoAdjustOverflow : {};
  10. const baseOverflow = {};
  11. switch (placement) {
  12. case 'top':
  13. case 'bottom':
  14. baseOverflow.shiftX = arrowOffset.arrowOffsetHorizontal * 2 + arrowWidth;
  15. baseOverflow.shiftY = true;
  16. baseOverflow.adjustY = true;
  17. break;
  18. case 'left':
  19. case 'right':
  20. baseOverflow.shiftY = arrowOffset.arrowOffsetVertical * 2 + arrowWidth;
  21. baseOverflow.shiftX = true;
  22. baseOverflow.adjustX = true;
  23. break;
  24. }
  25. const mergedOverflow = Object.assign(Object.assign({}, baseOverflow), overflow);
  26. // Support auto shift
  27. if (!mergedOverflow.shiftX) {
  28. mergedOverflow.adjustX = true;
  29. }
  30. if (!mergedOverflow.shiftY) {
  31. mergedOverflow.adjustY = true;
  32. }
  33. return mergedOverflow;
  34. }
  35. const PlacementAlignMap = {
  36. left: {
  37. points: ['cr', 'cl']
  38. },
  39. right: {
  40. points: ['cl', 'cr']
  41. },
  42. top: {
  43. points: ['bc', 'tc']
  44. },
  45. bottom: {
  46. points: ['tc', 'bc']
  47. },
  48. topLeft: {
  49. points: ['bl', 'tl']
  50. },
  51. leftTop: {
  52. points: ['tr', 'tl']
  53. },
  54. topRight: {
  55. points: ['br', 'tr']
  56. },
  57. rightTop: {
  58. points: ['tl', 'tr']
  59. },
  60. bottomRight: {
  61. points: ['tr', 'br']
  62. },
  63. rightBottom: {
  64. points: ['bl', 'br']
  65. },
  66. bottomLeft: {
  67. points: ['tl', 'bl']
  68. },
  69. leftBottom: {
  70. points: ['br', 'bl']
  71. }
  72. };
  73. const ArrowCenterPlacementAlignMap = {
  74. topLeft: {
  75. points: ['bl', 'tc']
  76. },
  77. leftTop: {
  78. points: ['tr', 'cl']
  79. },
  80. topRight: {
  81. points: ['br', 'tc']
  82. },
  83. rightTop: {
  84. points: ['tl', 'cr']
  85. },
  86. bottomRight: {
  87. points: ['tr', 'bc']
  88. },
  89. rightBottom: {
  90. points: ['bl', 'cr']
  91. },
  92. bottomLeft: {
  93. points: ['tl', 'bc']
  94. },
  95. leftBottom: {
  96. points: ['br', 'cl']
  97. }
  98. };
  99. const DisableAutoArrowList = new Set(['topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom']);
  100. export default function getPlacements(config) {
  101. const {
  102. arrowWidth,
  103. autoAdjustOverflow,
  104. arrowPointAtCenter,
  105. offset,
  106. borderRadius,
  107. visibleFirst
  108. } = config;
  109. const halfArrowWidth = arrowWidth / 2;
  110. const placementMap = {};
  111. // Dynamic offset
  112. const arrowOffset = getArrowOffsetToken({
  113. contentRadius: borderRadius,
  114. limitVerticalRadius: true
  115. });
  116. Object.keys(PlacementAlignMap).forEach(key => {
  117. const template = arrowPointAtCenter && ArrowCenterPlacementAlignMap[key] || PlacementAlignMap[key];
  118. const placementInfo = Object.assign(Object.assign({}, template), {
  119. offset: [0, 0],
  120. dynamicInset: true
  121. });
  122. placementMap[key] = placementInfo;
  123. // Disable autoArrow since design is fixed position
  124. if (DisableAutoArrowList.has(key)) {
  125. placementInfo.autoArrow = false;
  126. }
  127. // Static offset
  128. switch (key) {
  129. case 'top':
  130. case 'topLeft':
  131. case 'topRight':
  132. placementInfo.offset[1] = -halfArrowWidth - offset;
  133. break;
  134. case 'bottom':
  135. case 'bottomLeft':
  136. case 'bottomRight':
  137. placementInfo.offset[1] = halfArrowWidth + offset;
  138. break;
  139. case 'left':
  140. case 'leftTop':
  141. case 'leftBottom':
  142. placementInfo.offset[0] = -halfArrowWidth - offset;
  143. break;
  144. case 'right':
  145. case 'rightTop':
  146. case 'rightBottom':
  147. placementInfo.offset[0] = halfArrowWidth + offset;
  148. break;
  149. }
  150. if (arrowPointAtCenter) {
  151. switch (key) {
  152. case 'topLeft':
  153. case 'bottomLeft':
  154. placementInfo.offset[0] = -arrowOffset.arrowOffsetHorizontal - halfArrowWidth;
  155. break;
  156. case 'topRight':
  157. case 'bottomRight':
  158. placementInfo.offset[0] = arrowOffset.arrowOffsetHorizontal + halfArrowWidth;
  159. break;
  160. case 'leftTop':
  161. case 'rightTop':
  162. placementInfo.offset[1] = -arrowOffset.arrowOffsetHorizontal * 2 + halfArrowWidth;
  163. break;
  164. case 'leftBottom':
  165. case 'rightBottom':
  166. placementInfo.offset[1] = arrowOffset.arrowOffsetHorizontal * 2 - halfArrowWidth;
  167. break;
  168. }
  169. }
  170. // Overflow
  171. placementInfo.overflow = getOverflowOptions(key, arrowOffset, arrowWidth, autoAdjustOverflow);
  172. // VisibleFirst
  173. if (visibleFirst) {
  174. placementInfo.htmlRegion = 'visibleFirst';
  175. }
  176. });
  177. return placementMap;
  178. }