scroll.mjs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { isShadowRoot } from "./aria.mjs";
  2. import { isClient } from "../browser.mjs";
  3. import { easeInOutCubic } from "../easings.mjs";
  4. import { isFunction, isWindow } from "../types.mjs";
  5. import { cAF, rAF } from "../raf.mjs";
  6. import { getStyle } from "./style.mjs";
  7. //#region ../../packages/utils/dom/scroll.ts
  8. const isScroll = (el, isVertical) => {
  9. if (!isClient) return false;
  10. const key = {
  11. undefined: "overflow",
  12. true: "overflow-y",
  13. false: "overflow-x"
  14. }[String(isVertical)];
  15. const overflow = getStyle(el, key);
  16. return [
  17. "scroll",
  18. "auto",
  19. "overlay"
  20. ].some((s) => overflow.includes(s));
  21. };
  22. const getScrollContainer = (el, isVertical) => {
  23. if (!isClient) return;
  24. let parent = el;
  25. while (parent) {
  26. if ([
  27. window,
  28. document,
  29. document.documentElement
  30. ].includes(parent)) return window;
  31. if (isScroll(parent, isVertical)) return parent;
  32. if (isShadowRoot(parent)) parent = parent.host;
  33. else parent = parent.parentNode;
  34. }
  35. return parent;
  36. };
  37. let scrollBarWidth;
  38. const getScrollBarWidth = (namespace) => {
  39. if (!isClient) return 0;
  40. if (scrollBarWidth !== void 0) return scrollBarWidth;
  41. const outer = document.createElement("div");
  42. outer.className = `${namespace}-scrollbar__wrap`;
  43. outer.style.visibility = "hidden";
  44. outer.style.width = "100px";
  45. outer.style.position = "absolute";
  46. outer.style.top = "-9999px";
  47. document.body.appendChild(outer);
  48. const widthNoScroll = outer.offsetWidth;
  49. outer.style.overflow = "scroll";
  50. const inner = document.createElement("div");
  51. inner.style.width = "100%";
  52. outer.appendChild(inner);
  53. const widthWithScroll = inner.offsetWidth;
  54. outer.parentNode?.removeChild(outer);
  55. scrollBarWidth = widthNoScroll - widthWithScroll;
  56. return scrollBarWidth;
  57. };
  58. /**
  59. * Scroll with in the container element, positioning the **selected** element at the top
  60. * of the container
  61. */
  62. function scrollIntoView(container, selected) {
  63. if (!isClient) return;
  64. if (!selected) {
  65. container.scrollTop = 0;
  66. return;
  67. }
  68. const offsetParents = [];
  69. let pointer = selected.offsetParent;
  70. while (pointer !== null && container !== pointer && container.contains(pointer)) {
  71. offsetParents.push(pointer);
  72. pointer = pointer.offsetParent;
  73. }
  74. const top = selected.offsetTop + offsetParents.reduce((prev, curr) => prev + curr.offsetTop, 0);
  75. const bottom = top + selected.offsetHeight;
  76. const viewRectTop = container.scrollTop;
  77. const viewRectBottom = viewRectTop + container.clientHeight;
  78. if (top < viewRectTop) container.scrollTop = top;
  79. else if (bottom > viewRectBottom) container.scrollTop = bottom - container.clientHeight;
  80. }
  81. function animateScrollTo(container, from, to, duration, callback) {
  82. const startTime = Date.now();
  83. let handle;
  84. const scroll = () => {
  85. const time = Date.now() - startTime;
  86. const nextScrollTop = easeInOutCubic(time > duration ? duration : time, from, to, duration);
  87. if (isWindow(container)) container.scrollTo(window.pageXOffset, nextScrollTop);
  88. else container.scrollTop = nextScrollTop;
  89. if (time < duration) handle = rAF(scroll);
  90. else if (isFunction(callback)) callback();
  91. };
  92. scroll();
  93. return () => {
  94. handle && cAF(handle);
  95. };
  96. }
  97. const getScrollElement = (target, container) => {
  98. if (isWindow(container)) return target.ownerDocument.documentElement;
  99. return container;
  100. };
  101. const getScrollTop = (container) => {
  102. if (isWindow(container)) return window.scrollY;
  103. return container.scrollTop;
  104. };
  105. //#endregion
  106. export { animateScrollTo, getScrollBarWidth, getScrollContainer, getScrollElement, getScrollTop, isScroll, scrollIntoView };
  107. //# sourceMappingURL=scroll.mjs.map