7d81ff49347581df1cd17cf6413dbd5d0b69cbba1d42fefe20334f96d716f1b4b7291afe9798101c4547b051a9c980cd1336a7713382a5fbc762fd1e54d341 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. const getParentElement = element => element.parentElement || element.getRootNode().host || null;
  2. const getElementRect = element => {
  3. const rect = element.getBoundingClientRect();
  4. const scaleX = 'offsetWidth' in element && Math.abs(rect.width) / element.offsetWidth || 1;
  5. const scaleY = 'offsetHeight' in element && Math.abs(rect.height) / element.offsetHeight || 1;
  6. return {
  7. top: rect.top,
  8. right: rect.left + element.clientWidth * scaleX,
  9. bottom: rect.top + element.clientHeight * scaleY,
  10. left: rect.left
  11. };
  12. };
  13. const paddingValueToInt = value => {
  14. const number = parseInt(value, 10);
  15. return Number.isNaN(number) ? 0 : number;
  16. };
  17. // Follow the steps described in https://www.w3.org/TR/cssom-view-1/#element-scrolling-members,
  18. // assuming that the scroll option is set to 'nearest'.
  19. const getScrollDistance = (targetStart, targetEnd, scrollStart, scrollEnd, scrollPaddingStart, scrollPaddingEnd) => {
  20. if (targetStart < scrollStart && targetEnd > scrollEnd) {
  21. return 0;
  22. }
  23. if (targetStart < scrollStart) {
  24. return -(scrollStart - targetStart + scrollPaddingStart);
  25. }
  26. if (targetEnd > scrollEnd) {
  27. return targetEnd - targetStart > scrollEnd - scrollStart ? targetStart + scrollPaddingStart - scrollStart : targetEnd - scrollEnd + scrollPaddingEnd;
  28. }
  29. return 0;
  30. };
  31. const scrollRectIntoView = (root, targetRect) => {
  32. const document = root.ownerDocument;
  33. let rect = targetRect;
  34. let current = root;
  35. while (current) {
  36. const isDocumentBody = current === document.body;
  37. const bounding = isDocumentBody ? {
  38. top: 0,
  39. right: window.visualViewport?.width ?? document.documentElement.clientWidth,
  40. bottom: window.visualViewport?.height ?? document.documentElement.clientHeight,
  41. left: 0
  42. } : getElementRect(current);
  43. const style = getComputedStyle(current);
  44. const scrollDistanceX = getScrollDistance(rect.left, rect.right, bounding.left, bounding.right, paddingValueToInt(style.scrollPaddingLeft), paddingValueToInt(style.scrollPaddingRight));
  45. const scrollDistanceY = getScrollDistance(rect.top, rect.bottom, bounding.top, bounding.bottom, paddingValueToInt(style.scrollPaddingTop), paddingValueToInt(style.scrollPaddingBottom));
  46. if (scrollDistanceX || scrollDistanceY) {
  47. if (isDocumentBody) {
  48. document.defaultView?.scrollBy(scrollDistanceX, scrollDistanceY);
  49. } else {
  50. const {
  51. scrollLeft,
  52. scrollTop
  53. } = current;
  54. if (scrollDistanceY) {
  55. current.scrollTop += scrollDistanceY;
  56. }
  57. if (scrollDistanceX) {
  58. current.scrollLeft += scrollDistanceX;
  59. }
  60. const scrolledLeft = current.scrollLeft - scrollLeft;
  61. const scrolledTop = current.scrollTop - scrollTop;
  62. rect = {
  63. left: rect.left - scrolledLeft,
  64. top: rect.top - scrolledTop,
  65. right: rect.right - scrolledLeft,
  66. bottom: rect.bottom - scrolledTop
  67. };
  68. }
  69. }
  70. current = isDocumentBody || style.position === 'fixed' ? null : getParentElement(current);
  71. }
  72. };
  73. export default scrollRectIntoView;
  74. //# sourceMappingURL=scrollRectIntoView.js.map