foundation.util.box.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. 'use strict';
  2. import { rtl as Rtl } from "./foundation.core.utils";
  3. var Box = {
  4. ImNotTouchingYou: ImNotTouchingYou,
  5. OverlapArea: OverlapArea,
  6. GetDimensions: GetDimensions,
  7. GetOffsets: GetOffsets,
  8. GetExplicitOffsets: GetExplicitOffsets
  9. }
  10. /**
  11. * Compares the dimensions of an element to a container and determines collision events with container.
  12. * @function
  13. * @param {jQuery} element - jQuery object to test for collisions.
  14. * @param {jQuery} parent - jQuery object to use as bounding container.
  15. * @param {Boolean} lrOnly - set to true to check left and right values only.
  16. * @param {Boolean} tbOnly - set to true to check top and bottom values only.
  17. * @default if no parent object passed, detects collisions with `window`.
  18. * @returns {Boolean} - true if collision free, false if a collision in any direction.
  19. */
  20. function ImNotTouchingYou(element, parent, lrOnly, tbOnly, ignoreBottom) {
  21. return OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) === 0;
  22. };
  23. function OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) {
  24. var eleDims = GetDimensions(element),
  25. topOver, bottomOver, leftOver, rightOver;
  26. if (parent) {
  27. var parDims = GetDimensions(parent);
  28. bottomOver = (parDims.height + parDims.offset.top) - (eleDims.offset.top + eleDims.height);
  29. topOver = eleDims.offset.top - parDims.offset.top;
  30. leftOver = eleDims.offset.left - parDims.offset.left;
  31. rightOver = (parDims.width + parDims.offset.left) - (eleDims.offset.left + eleDims.width);
  32. }
  33. else {
  34. bottomOver = (eleDims.windowDims.height + eleDims.windowDims.offset.top) - (eleDims.offset.top + eleDims.height);
  35. topOver = eleDims.offset.top - eleDims.windowDims.offset.top;
  36. leftOver = eleDims.offset.left - eleDims.windowDims.offset.left;
  37. rightOver = eleDims.windowDims.width - (eleDims.offset.left + eleDims.width);
  38. }
  39. bottomOver = ignoreBottom ? 0 : Math.min(bottomOver, 0);
  40. topOver = Math.min(topOver, 0);
  41. leftOver = Math.min(leftOver, 0);
  42. rightOver = Math.min(rightOver, 0);
  43. if (lrOnly) {
  44. return leftOver + rightOver;
  45. }
  46. if (tbOnly) {
  47. return topOver + bottomOver;
  48. }
  49. // use sum of squares b/c we care about overlap area.
  50. return Math.sqrt((topOver * topOver) + (bottomOver * bottomOver) + (leftOver * leftOver) + (rightOver * rightOver));
  51. }
  52. /**
  53. * Uses native methods to return an object of dimension values.
  54. * @function
  55. * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions. Can be any element other that document or window.
  56. * @returns {Object} - nested object of integer pixel values
  57. * TODO - if element is window, return only those values.
  58. */
  59. function GetDimensions(elem){
  60. elem = elem.length ? elem[0] : elem;
  61. if (elem === window || elem === document) {
  62. throw new Error("I'm sorry, Dave. I'm afraid I can't do that.");
  63. }
  64. var rect = elem.getBoundingClientRect(),
  65. parRect = elem.parentNode.getBoundingClientRect(),
  66. winRect = document.body.getBoundingClientRect(),
  67. winY = window.pageYOffset,
  68. winX = window.pageXOffset;
  69. return {
  70. width: rect.width,
  71. height: rect.height,
  72. offset: {
  73. top: rect.top + winY,
  74. left: rect.left + winX
  75. },
  76. parentDims: {
  77. width: parRect.width,
  78. height: parRect.height,
  79. offset: {
  80. top: parRect.top + winY,
  81. left: parRect.left + winX
  82. }
  83. },
  84. windowDims: {
  85. width: winRect.width,
  86. height: winRect.height,
  87. offset: {
  88. top: winY,
  89. left: winX
  90. }
  91. }
  92. }
  93. }
  94. /**
  95. * Returns an object of top and left integer pixel values for dynamically rendered elements,
  96. * such as: Tooltip, Reveal, and Dropdown. Maintained for backwards compatibility, and where
  97. * you don't know alignment, but generally from
  98. * 6.4 forward you should use GetExplicitOffsets, as GetOffsets conflates position and alignment.
  99. * @function
  100. * @param {jQuery} element - jQuery object for the element being positioned.
  101. * @param {jQuery} anchor - jQuery object for the element's anchor point.
  102. * @param {String} position - a string relating to the desired position of the element, relative to it's anchor
  103. * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element.
  104. * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element.
  105. * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset.
  106. * TODO alter/rewrite to work with `em` values as well/instead of pixels
  107. */
  108. function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
  109. console.log("NOTE: GetOffsets is deprecated in favor of GetExplicitOffsets and will be removed in 6.5");
  110. switch (position) {
  111. case 'top':
  112. return Rtl() ?
  113. GetExplicitOffsets(element, anchor, 'top', 'left', vOffset, hOffset, isOverflow) :
  114. GetExplicitOffsets(element, anchor, 'top', 'right', vOffset, hOffset, isOverflow);
  115. case 'bottom':
  116. return Rtl() ?
  117. GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow) :
  118. GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow);
  119. case 'center top':
  120. return GetExplicitOffsets(element, anchor, 'top', 'center', vOffset, hOffset, isOverflow);
  121. case 'center bottom':
  122. return GetExplicitOffsets(element, anchor, 'bottom', 'center', vOffset, hOffset, isOverflow);
  123. case 'center left':
  124. return GetExplicitOffsets(element, anchor, 'left', 'center', vOffset, hOffset, isOverflow);
  125. case 'center right':
  126. return GetExplicitOffsets(element, anchor, 'right', 'center', vOffset, hOffset, isOverflow);
  127. case 'left bottom':
  128. return GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow);
  129. case 'right bottom':
  130. return GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow);
  131. // Backwards compatibility... this along with the reveal and reveal full
  132. // classes are the only ones that didn't reference anchor
  133. case 'center':
  134. return {
  135. left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset,
  136. top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset)
  137. }
  138. case 'reveal':
  139. return {
  140. left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset,
  141. top: $eleDims.windowDims.offset.top + vOffset
  142. }
  143. case 'reveal full':
  144. return {
  145. left: $eleDims.windowDims.offset.left,
  146. top: $eleDims.windowDims.offset.top
  147. }
  148. break;
  149. default:
  150. return {
  151. left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset),
  152. top: $anchorDims.offset.top + $anchorDims.height + vOffset
  153. }
  154. }
  155. }
  156. function GetExplicitOffsets(element, anchor, position, alignment, vOffset, hOffset, isOverflow) {
  157. var $eleDims = GetDimensions(element),
  158. $anchorDims = anchor ? GetDimensions(anchor) : null;
  159. var topVal, leftVal;
  160. // set position related attribute
  161. switch (position) {
  162. case 'top':
  163. topVal = $anchorDims.offset.top - ($eleDims.height + vOffset);
  164. break;
  165. case 'bottom':
  166. topVal = $anchorDims.offset.top + $anchorDims.height + vOffset;
  167. break;
  168. case 'left':
  169. leftVal = $anchorDims.offset.left - ($eleDims.width + hOffset);
  170. break;
  171. case 'right':
  172. leftVal = $anchorDims.offset.left + $anchorDims.width + hOffset;
  173. break;
  174. }
  175. // set alignment related attribute
  176. switch (position) {
  177. case 'top':
  178. case 'bottom':
  179. switch (alignment) {
  180. case 'left':
  181. leftVal = $anchorDims.offset.left + hOffset;
  182. break;
  183. case 'right':
  184. leftVal = $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset;
  185. break;
  186. case 'center':
  187. leftVal = isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset;
  188. break;
  189. }
  190. break;
  191. case 'right':
  192. case 'left':
  193. switch (alignment) {
  194. case 'bottom':
  195. topVal = $anchorDims.offset.top - vOffset + $anchorDims.height - $eleDims.height;
  196. break;
  197. case 'top':
  198. topVal = $anchorDims.offset.top + vOffset
  199. break;
  200. case 'center':
  201. topVal = ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2)
  202. break;
  203. }
  204. break;
  205. }
  206. return {top: topVal, left: leftVal};
  207. }
  208. export {Box};