{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["// Compute what scrolling needs to be done on required scrolling boxes for target to be in view\n\n// The type names here are named after the spec to make it easier to find more information around what they mean:\n// To reduce churn and reduce things that need be maintained things from the official TS DOM library is used here\n// https://drafts.csswg.org/cssom-view/\n\n// For a definition on what is \"block flow direction\" exactly, check this: https://drafts.csswg.org/css-writing-modes-4/#block-flow-direction\n\n/**\n * This new option is tracked in this PR, which is the most likely candidate at the time: https://github.com/w3c/csswg-drafts/pull/1805\n * @public\n */\nexport type ScrollMode = 'always' | 'if-needed'\n\n/** @public */\nexport interface Options {\n /**\n * Control the logical scroll position on the y-axis. The spec states that the `block` direction is related to the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), but this is not implemented yet in this library.\n * This means that `block: 'start'` aligns to the top edge and `block: 'end'` to the bottom.\n * @defaultValue 'center'\n */\n block?: ScrollLogicalPosition\n /**\n * Like `block` this is affected by the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode). In left-to-right pages `inline: 'start'` will align to the left edge. In right-to-left it should be flipped. This will be supported in a future release.\n * @defaultValue 'nearest'\n */\n inline?: ScrollLogicalPosition\n /**\n * This is a proposed addition to the spec that you can track here: https://github.com/w3c/csswg-drafts/pull/5677\n *\n * This library will be updated to reflect any changes to the spec and will provide a migration path.\n * To be backwards compatible with `Element.scrollIntoViewIfNeeded` if something is not 100% visible it will count as \"needs scrolling\". If you need a different visibility ratio your best option would be to implement an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n * @defaultValue 'always'\n */\n scrollMode?: ScrollMode\n /**\n * By default there is no boundary. All the parent elements of your target is checked until it reaches the viewport ([`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)) when calculating layout and what to scroll.\n * By passing a boundary you can short-circuit this loop depending on your needs:\n * \n * - Prevent the browser window from scrolling.\n * - Scroll elements into view in a list, without scrolling container elements.\n * \n * You can also pass a function to do more dynamic checks to override the scroll scoping:\n * \n * ```js\n * let actions = compute(target, {\n * boundary: (parent) => {\n * // By default `overflow: hidden` elements are allowed, only `overflow: visible | clip` is skipped as\n * // this is required by the CSSOM spec\n * if (getComputedStyle(parent)['overflow'] === 'hidden') {\n * return false\n * }\n\n * return true\n * },\n * })\n * ```\n * @defaultValue null\n */\n boundary?: Element | ((parent: Element) => boolean) | null\n /**\n * New option that skips auto-scrolling all nodes with overflow: hidden set\n * See FF implementation: https://hg.mozilla.org/integration/fx-team/rev/c48c3ec05012#l7.18\n * @defaultValue false\n * @public\n */\n skipOverflowHiddenElements?: boolean\n}\n\n/** @public */\nexport interface ScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nconst isElement = (el: any): el is Element =>\n typeof el === 'object' && el != null && el.nodeType === 1\n\nconst canOverflow = (\n overflow: string | null,\n skipOverflowHiddenElements?: boolean\n) => {\n if (skipOverflowHiddenElements && overflow === 'hidden') {\n return false\n }\n\n return overflow !== 'visible' && overflow !== 'clip'\n}\n\nconst getFrameElement = (el: Element) => {\n if (!el.ownerDocument || !el.ownerDocument.defaultView) {\n return null\n }\n\n try {\n return el.ownerDocument.defaultView.frameElement\n } catch (e) {\n return null\n }\n}\n\nconst isHiddenByFrame = (el: Element): boolean => {\n const frame = getFrameElement(el)\n if (!frame) {\n return false\n }\n\n return (\n frame.clientHeight < el.scrollHeight || frame.clientWidth < el.scrollWidth\n )\n}\n\nconst isScrollable = (el: Element, skipOverflowHiddenElements?: boolean) => {\n if (el.clientHeight < el.scrollHeight || el.clientWidth < el.scrollWidth) {\n const style = getComputedStyle(el, null)\n return (\n canOverflow(style.overflowY, skipOverflowHiddenElements) ||\n canOverflow(style.overflowX, skipOverflowHiddenElements) ||\n isHiddenByFrame(el)\n )\n }\n\n return false\n}\n/**\n * Find out which edge to align against when logical scroll position is \"nearest\"\n * Interesting fact: \"nearest\" works similarily to \"if-needed\", if the element is fully visible it will not scroll it\n *\n * Legends:\n * ┌────────┐ ┏ ━ ━ ━ ┓\n * │ target │ frame\n * └────────┘ ┗ ━ ━ ━ ┛\n */\nconst alignNearest = (\n scrollingEdgeStart: number,\n scrollingEdgeEnd: number,\n scrollingSize: number,\n scrollingBorderStart: number,\n scrollingBorderEnd: number,\n elementEdgeStart: number,\n elementEdgeEnd: number,\n elementSize: number\n) => {\n /**\n * If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B\n *\n * ┌──┐\n * ┏━│━━│━┓\n * │ │\n * ┃ │ │ ┃ do nothing\n * │ │\n * ┗━│━━│━┛\n * └──┘\n *\n * If element edge C and element edge D are both outside scrolling box edge C and scrolling box edge D\n *\n * ┏ ━ ━ ━ ━ ┓\n * ┌───────────┐\n * │┃ ┃│ do nothing\n * └───────────┘\n * ┗ ━ ━ ━ ━ ┛\n */\n if (\n (elementEdgeStart < scrollingEdgeStart &&\n elementEdgeEnd > scrollingEdgeEnd) ||\n (elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd)\n ) {\n return 0\n }\n\n /**\n * If element edge A is outside scrolling box edge A and element height is less than scrolling box height\n *\n * ┌──┐\n * ┏━│━━│━┓ ┏━┌━━┐━┓\n * └──┘ │ │\n * from ┃ ┃ to ┃ └──┘ ┃\n *\n * ┗━ ━━ ━┛ ┗━ ━━ ━┛\n *\n * If element edge B is outside scrolling box edge B and element height is greater than scrolling box height\n *\n * ┏━ ━━ ━┓ ┏━┌━━┐━┓\n * │ │\n * from ┃ ┌──┐ ┃ to ┃ │ │ ┃\n * │ │ │ │\n * ┗━│━━│━┛ ┗━│━━│━┛\n * │ │ └──┘\n * │ │\n * └──┘\n *\n * If element edge C is outside scrolling box edge C and element width is less than scrolling box width\n *\n * from to\n * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓\n * ┌───┐ ┌───┐\n * │ ┃ │ ┃ ┃ │ ┃\n * └───┘ └───┘\n * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛\n *\n * If element edge D is outside scrolling box edge D and element width is greater than scrolling box width\n *\n * from to\n * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓\n * ┌───────────┐ ┌───────────┐\n * ┃ │ ┃ │ ┃ ┃ │\n * └───────────┘ └───────────┘\n * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛\n */\n if (\n (elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize) ||\n (elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize)\n ) {\n return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart\n }\n\n /**\n * If element edge B is outside scrolling box edge B and element height is less than scrolling box height\n *\n * ┏━ ━━ ━┓ ┏━ ━━ ━┓\n *\n * from ┃ ┃ to ┃ ┌──┐ ┃\n * ┌──┐ │ │\n * ┗━│━━│━┛ ┗━└━━┘━┛\n * └──┘\n *\n * If element edge A is outside scrolling box edge A and element height is greater than scrolling box height\n *\n * ┌──┐\n * │ │\n * │ │ ┌──┐\n * ┏━│━━│━┓ ┏━│━━│━┓\n * │ │ │ │\n * from ┃ └──┘ ┃ to ┃ │ │ ┃\n * │ │\n * ┗━ ━━ ━┛ ┗━└━━┘━┛\n *\n * If element edge C is outside scrolling box edge C and element width is greater than scrolling box width\n *\n * from to\n * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓\n * ┌───────────┐ ┌───────────┐\n * │ ┃ │ ┃ │ ┃ ┃\n * └───────────┘ └───────────┘\n * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛\n *\n * If element edge D is outside scrolling box edge D and element width is less than scrolling box width\n *\n * from to\n * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓\n * ┌───┐ ┌───┐\n * ┃ │ ┃ │ ┃ │ ┃\n * └───┘ └───┘\n * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛\n *\n */\n if (\n (elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize) ||\n (elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize)\n ) {\n return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd\n }\n\n return 0\n}\n\nconst getParentElement = (element: Node): Element | null => {\n const parent = element.parentElement\n if (parent == null) {\n return (element.getRootNode() as ShadowRoot).host || null\n }\n return parent\n}\n\nconst getScrollMargins = (target: Element) => {\n const computedStyle = window.getComputedStyle(target)\n return {\n top: parseFloat(computedStyle.scrollMarginTop) || 0,\n right: parseFloat(computedStyle.scrollMarginRight) || 0,\n bottom: parseFloat(computedStyle.scrollMarginBottom) || 0,\n left: parseFloat(computedStyle.scrollMarginLeft) || 0,\n }\n}\n\n/** @public */\nexport const compute = (target: Element, options: Options): ScrollAction[] => {\n if (typeof document === 'undefined') {\n // If there's no DOM we assume it's not in a browser environment\n return []\n }\n\n const { scrollMode, block, inline, boundary, skipOverflowHiddenElements } =\n options\n // Allow using a callback to check the boundary\n // The default behavior is to check if the current target matches the boundary element or not\n // If undefined it'll check that target is never undefined (can happen as we recurse up the tree)\n const checkBoundary =\n typeof boundary === 'function' ? boundary : (node: any) => node !== boundary\n\n if (!isElement(target)) {\n throw new TypeError('Invalid target')\n }\n\n // Used to handle the top most element that can be scrolled\n const scrollingElement = document.scrollingElement || document.documentElement\n\n // Collect all the scrolling boxes, as defined in the spec: https://drafts.csswg.org/cssom-view/#scrolling-box\n const frames: Element[] = []\n let cursor: Element | null = target\n while (isElement(cursor) && checkBoundary(cursor)) {\n // Move cursor to parent\n cursor = getParentElement(cursor)\n\n // Stop when we reach the viewport\n if (cursor === scrollingElement) {\n frames.push(cursor)\n break\n }\n\n // Skip document.body if it's not the scrollingElement and documentElement isn't independently scrollable\n if (\n cursor != null &&\n cursor === document.body &&\n isScrollable(cursor) &&\n !isScrollable(document.documentElement)\n ) {\n continue\n }\n\n // Now we check if the element is scrollable, this code only runs if the loop haven't already hit the viewport or a custom boundary\n if (cursor != null && isScrollable(cursor, skipOverflowHiddenElements)) {\n frames.push(cursor)\n }\n }\n\n // Support pinch-zooming properly, making sure elements scroll into the visual viewport\n // Browsers that don't support visualViewport will report the layout viewport dimensions on document.documentElement.clientWidth/Height\n // and viewport dimensions on window.innerWidth/Height\n // https://www.quirksmode.org/mobile/viewports2.html\n // https://bokand.github.io/viewport/index.html\n const viewportWidth = window.visualViewport?.width ?? innerWidth\n const viewportHeight = window.visualViewport?.height ?? innerHeight\n const { scrollX, scrollY } = window\n\n const {\n height: targetHeight,\n width: targetWidth,\n top: targetTop,\n right: targetRight,\n bottom: targetBottom,\n left: targetLeft,\n } = target.getBoundingClientRect()\n const {\n top: marginTop,\n right: marginRight,\n bottom: marginBottom,\n left: marginLeft,\n } = getScrollMargins(target)\n\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop - marginTop\n : block === 'end'\n ? targetBottom + marginBottom\n : targetTop + targetHeight / 2 - marginTop + marginBottom // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2 - marginLeft + marginRight\n : inline === 'end'\n ? targetRight + marginRight\n : targetLeft - marginLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: ScrollAction[] = []\n // In chrome there's no longer a difference between caching the `frames.length` to a var or not, so we don't in this case (size > speed anyways)\n for (let index = 0; index < frames.length; index++) {\n const frame = frames[index]\n\n // @TODO add a shouldScroll hook here that allows userland code to take control\n\n const { height, width, top, right, bottom, left } =\n frame.getBoundingClientRect()\n\n // If the element is already visible we can end it here\n // @TODO targetBlock and targetInline should be taken into account to be compliant with https://github.com/w3c/csswg-drafts/pull/1805/files#diff-3c17f0e43c20f8ecf89419d49e7ef5e0R1333\n if (\n scrollMode === 'if-needed' &&\n targetTop >= 0 &&\n targetLeft >= 0 &&\n targetBottom <= viewportHeight &&\n targetRight <= viewportWidth &&\n targetTop >= top &&\n targetBottom <= bottom &&\n targetLeft >= left &&\n targetRight <= right\n ) {\n // Break the loop and return the computations for things that are not fully visible\n return computations\n }\n\n const frameStyle = getComputedStyle(frame)\n const borderLeft = parseInt(frameStyle.borderLeftWidth as string, 10)\n const borderTop = parseInt(frameStyle.borderTopWidth as string, 10)\n const borderRight = parseInt(frameStyle.borderRightWidth as string, 10)\n const borderBottom = parseInt(frameStyle.borderBottomWidth as string, 10)\n\n let blockScroll: number = 0\n let inlineScroll: number = 0\n\n // The property existance checks for offfset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here\n // @TODO find out if the \"as HTMLElement\" overrides can be dropped\n const scrollbarWidth =\n 'offsetWidth' in frame\n ? (frame as HTMLElement).offsetWidth -\n (frame as HTMLElement).clientWidth -\n borderLeft -\n borderRight\n : 0\n const scrollbarHeight =\n 'offsetHeight' in frame\n ? (frame as HTMLElement).offsetHeight -\n (frame as HTMLElement).clientHeight -\n borderTop -\n borderBottom\n : 0\n\n const scaleX =\n 'offsetWidth' in frame\n ? (frame as HTMLElement).offsetWidth === 0\n ? 0\n : width / (frame as HTMLElement).offsetWidth\n : 0\n const scaleY =\n 'offsetHeight' in frame\n ? (frame as HTMLElement).offsetHeight === 0\n ? 0\n : height / (frame as HTMLElement).offsetHeight\n : 0\n\n if (scrollingElement === frame) {\n // Handle viewport logic (document.documentElement or document.body)\n\n if (block === 'start') {\n blockScroll = targetBlock\n } else if (block === 'end') {\n blockScroll = targetBlock - viewportHeight\n } else if (block === 'nearest') {\n blockScroll = alignNearest(\n scrollY,\n scrollY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n scrollY + targetBlock,\n scrollY + targetBlock + targetHeight,\n targetHeight\n )\n } else {\n // block === 'center' is the default\n blockScroll = targetBlock - viewportHeight / 2\n }\n\n if (inline === 'start') {\n inlineScroll = targetInline\n } else if (inline === 'center') {\n inlineScroll = targetInline - viewportWidth / 2\n } else if (inline === 'end') {\n inlineScroll = targetInline - viewportWidth\n } else {\n // inline === 'nearest' is the default\n inlineScroll = alignNearest(\n scrollX,\n scrollX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n scrollX + targetInline,\n scrollX + targetInline + targetWidth,\n targetWidth\n )\n }\n\n // Apply scroll position offsets and ensure they are within bounds\n // @TODO add more test cases to cover this 100%\n blockScroll = Math.max(0, blockScroll + scrollY)\n inlineScroll = Math.max(0, inlineScroll + scrollX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n if (block === 'start') {\n blockScroll = targetBlock - top - borderTop\n } else if (block === 'end') {\n blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight\n } else if (block === 'nearest') {\n blockScroll = alignNearest(\n top,\n bottom,\n height,\n borderTop,\n borderBottom + scrollbarHeight,\n targetBlock,\n targetBlock + targetHeight,\n targetHeight\n )\n } else {\n // block === 'center' is the default\n blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2\n }\n\n if (inline === 'start') {\n inlineScroll = targetInline - left - borderLeft\n } else if (inline === 'center') {\n inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2\n } else if (inline === 'end') {\n inlineScroll = targetInline - right + borderRight + scrollbarWidth\n } else {\n // inline === 'nearest' is the default\n inlineScroll = alignNearest(\n left,\n right,\n width,\n borderLeft,\n borderRight + scrollbarWidth,\n targetInline,\n targetInline + targetWidth,\n targetWidth\n )\n }\n\n const { scrollLeft, scrollTop } = frame\n // Ensure scroll coordinates are not out of bounds while applying scroll offsets\n blockScroll =\n scaleY === 0\n ? 0\n : Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll =\n scaleX === 0\n ? 0\n : Math.max(\n 0,\n Math.min(\n scrollLeft + inlineScroll / scaleX,\n frame.scrollWidth - width / scaleX + scrollbarWidth\n )\n )\n\n // Cache the offset so that parent frames can scroll this into view correctly\n targetBlock += scrollTop - blockScroll\n targetInline += scrollLeft - inlineScroll\n }\n\n computations.push({ el: frame, top: blockScroll, left: inlineScroll })\n }\n\n return computations\n}\n"],"names":["isElement","el","nodeType","canOverflow","overflow","skipOverflowHiddenElements","isScrollable","clientHeight","scrollHeight","clientWidth","scrollWidth","style","getComputedStyle","overflowY","overflowX","frame","ownerDocument","defaultView","frameElement","e","getFrameElement","isHiddenByFrame","alignNearest","scrollingEdgeStart","scrollingEdgeEnd","scrollingSize","scrollingBorderStart","scrollingBorderEnd","elementEdgeStart","elementEdgeEnd","elementSize","getParentElement","element","parent","parentElement","getRootNode","host","compute","target","options","_a","_b","_c","_d","document","scrollMode","block","inline","boundary","checkBoundary","node","TypeError","scrollingElement","documentElement","frames","cursor","push","body","viewportWidth","window","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","scrollX","scrollY","targetHeight","targetWidth","top","targetTop","right","targetRight","bottom","targetBottom","left","targetLeft","getBoundingClientRect","marginTop","marginRight","marginBottom","marginLeft","computedStyle","parseFloat","scrollMarginTop","scrollMarginRight","scrollMarginBottom","scrollMarginLeft","getScrollMargins","targetBlock","targetInline","computations","index","length","frameStyle","borderLeft","parseInt","borderLeftWidth","borderTop","borderTopWidth","borderRight","borderRightWidth","borderBottom","borderBottomWidth","blockScroll","inlineScroll","scrollbarWidth","offsetWidth","scrollbarHeight","offsetHeight","scaleX","scaleY","Math","max","scrollLeft","scrollTop","min"],"mappings":"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"}