SmartGap.js (4368B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import { 6 svg, 7 polygon, 8 } from "devtools/client/shared/vendor/react-dom-factories"; 9 import PropTypes from "devtools/client/shared/vendor/react-prop-types"; 10 11 function shorten(coordinates) { 12 // In cases where the token is wider than the preview, the smartGap 13 // gets distorted. This shortens the coordinate array so that the smartGap 14 // is only touching 2 corners of the token (instead of all 4 corners) 15 coordinates.splice(0, 2); 16 coordinates.splice(4, 2); 17 return coordinates; 18 } 19 20 function getSmartGapCoordinates( 21 preview, 22 token, 23 offset, 24 orientation, 25 gapHeight, 26 coords 27 ) { 28 if (orientation === "up") { 29 const coordinates = [ 30 token.left - coords.left + offset, 31 token.top + token.height - (coords.top + preview.height) + gapHeight, 32 0, 33 0, 34 preview.width + offset, 35 0, 36 token.left + token.width - coords.left + offset, 37 token.top + token.height - (coords.top + preview.height) + gapHeight, 38 token.left + token.width - coords.left + offset, 39 token.top - (coords.top + preview.height) + gapHeight, 40 token.left - coords.left + offset, 41 token.top - (coords.top + preview.height) + gapHeight, 42 ]; 43 return preview.width > token.width ? coordinates : shorten(coordinates); 44 } 45 if (orientation === "down") { 46 const coordinates = [ 47 token.left + token.width - (coords.left + preview.top) + offset, 48 0, 49 preview.width + offset, 50 coords.top - token.top + gapHeight, 51 0, 52 coords.top - token.top + gapHeight, 53 token.left - (coords.left + preview.top) + offset, 54 0, 55 token.left - (coords.left + preview.top) + offset, 56 token.height, 57 token.left + token.width - (coords.left + preview.top) + offset, 58 token.height, 59 ]; 60 return preview.width > token.width ? coordinates : shorten(coordinates); 61 } 62 return [ 63 0, 64 token.top - coords.top, 65 gapHeight + token.width, 66 0, 67 gapHeight + token.width, 68 preview.height - gapHeight, 69 0, 70 token.top + token.height - coords.top, 71 token.width, 72 token.top + token.height - coords.top, 73 token.width, 74 token.top - coords.top, 75 ]; 76 } 77 78 function getSmartGapDimensions( 79 previewRect, 80 tokenRect, 81 offset, 82 orientation, 83 gapHeight, 84 coords 85 ) { 86 if (orientation === "up") { 87 return { 88 height: 89 tokenRect.top + 90 tokenRect.height - 91 coords.top - 92 previewRect.height + 93 gapHeight, 94 width: Math.max(previewRect.width, tokenRect.width) + offset, 95 }; 96 } 97 if (orientation === "down") { 98 return { 99 height: coords.top - tokenRect.top + gapHeight, 100 width: Math.max(previewRect.width, tokenRect.width) + offset, 101 }; 102 } 103 return { 104 height: previewRect.height - gapHeight, 105 width: coords.left - tokenRect.left + gapHeight, 106 }; 107 } 108 109 export default function SmartGap({ 110 token, 111 preview, 112 gapHeight, 113 coords, 114 offset, 115 }) { 116 const tokenRect = token.getBoundingClientRect(); 117 const previewRect = preview.getBoundingClientRect(); 118 const { orientation } = coords; 119 let optionalMarginLeft, optionalMarginTop; 120 121 if (orientation === "down") { 122 optionalMarginTop = -tokenRect.height; 123 } else if (orientation === "right") { 124 optionalMarginLeft = -tokenRect.width; 125 } 126 127 const { height, width } = getSmartGapDimensions( 128 previewRect, 129 tokenRect, 130 -offset, 131 orientation, 132 gapHeight, 133 coords 134 ); 135 const coordinates = getSmartGapCoordinates( 136 previewRect, 137 tokenRect, 138 -offset, 139 orientation, 140 gapHeight, 141 coords 142 ); 143 return svg( 144 { 145 version: "1.1", 146 xmlns: "http://www.w3.org/2000/svg", 147 style: { 148 height, 149 width, 150 position: "absolute", 151 marginLeft: optionalMarginLeft, 152 marginTop: optionalMarginTop, 153 }, 154 }, 155 polygon({ 156 points: coordinates, 157 fill: "transparent", 158 }) 159 ); 160 } 161 162 SmartGap.propTypes = { 163 coords: PropTypes.object.isRequired, 164 gapHeight: PropTypes.number.isRequired, 165 offset: PropTypes.number.isRequired, 166 preview: PropTypes.object.isRequired, 167 token: PropTypes.object.isRequired, 168 type: PropTypes.oneOf(["popover", "tooltip"]).isRequired, 169 };