compute-scroll-into-view 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- function t(t){return"object"==typeof t&&null!=t&&1===t.nodeType}function e(t,e){return(!e||"hidden"!==t)&&"visible"!==t&&"clip"!==t}function n(t,n){if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){var i=getComputedStyle(t,null);return e(i.overflowY,n)||e(i.overflowX,n)||function(t){var e=function(t){if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}}(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)}(t)}return!1}function i(t,e,n,i,r,o,l,d){return o<t&&l>e||o>t&&l<e?0:o<=t&&d<=n||l>=e&&d>=n?o-t-i:l>e&&d<n||o<t&&d>n?l-e+r:0}module.exports=function(e,r){var o=window,l=r.scrollMode,d=r.block,f=r.inline,h=r.boundary,u=r.skipOverflowHiddenElements,s="function"==typeof h?h:function(t){return t!==h};if(!t(e))throw new TypeError("Invalid target");for(var c=document.scrollingElement||document.documentElement,a=[],g=e;t(g)&&s(g);){if((g=g.parentElement)===c){a.push(g);break}null!=g&&g===document.body&&n(g)&&!n(document.documentElement)||null!=g&&n(g,u)&&a.push(g)}for(var m=o.visualViewport?o.visualViewport.width:innerWidth,p=o.visualViewport?o.visualViewport.height:innerHeight,w=window.scrollX||pageXOffset,v=window.scrollY||pageYOffset,W=e.getBoundingClientRect(),H=W.height,b=W.width,y=W.top,E=W.right,M=W.bottom,V=W.left,x="start"===d||"nearest"===d?y:"end"===d?M:y+H/2,I="center"===f?V+b/2:"end"===f?E:V,C=[],T=0;T<a.length;T++){var k=a[T],B=k.getBoundingClientRect(),D=B.height,O=B.width,R=B.top,X=B.right,Y=B.bottom,L=B.left;if("if-needed"===l&&y>=0&&V>=0&&M<=p&&E<=m&&y>=R&&M<=Y&&V>=L&&E<=X)return C;var S=getComputedStyle(k),j=parseInt(S.borderLeftWidth,10),q=parseInt(S.borderTopWidth,10),z=parseInt(S.borderRightWidth,10),A=parseInt(S.borderBottomWidth,10),F=0,G=0,J="offsetWidth"in k?k.offsetWidth-k.clientWidth-j-z:0,K="offsetHeight"in k?k.offsetHeight-k.clientHeight-q-A:0,N="offsetWidth"in k?0===k.offsetWidth?0:O/k.offsetWidth:0,P="offsetHeight"in k?0===k.offsetHeight?0:D/k.offsetHeight:0;if(c===k)F="start"===d?x:"end"===d?x-p:"nearest"===d?i(v,v+p,p,q,A,v+x,v+x+H,H):x-p/2,G="start"===f?I:"center"===f?I-m/2:"end"===f?I-m:i(w,w+m,m,j,z,w+I,w+I+b,b),F=Math.max(0,F+v),G=Math.max(0,G+w);else{F="start"===d?x-R-q:"end"===d?x-Y+A+K:"nearest"===d?i(R,Y,D,q,A+K,x,x+H,H):x-(R+D/2)+K/2,G="start"===f?I-L-j:"center"===f?I-(L+O/2)+J/2:"end"===f?I-X+z+J:i(L,X,O,j,z+J,I,I+b,b);var Q=k.scrollLeft,U=k.scrollTop;x+=U-(F=Math.max(0,Math.min(U+F/P,k.scrollHeight-D/P+K))),I+=Q-(G=Math.max(0,Math.min(Q+G/N,k.scrollWidth-O/N+J)))}C.push({el:k,top:F,left:G})}return C};
1
+ function t(t){return"object"==typeof t&&null!=t&&1===t.nodeType}function e(t,e){return(!e||"hidden"!==t)&&"visible"!==t&&"clip"!==t}function n(t,n){if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){var i=getComputedStyle(t,null);return e(i.overflowY,n)||e(i.overflowX,n)||function(t){var e=function(t){if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}}(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)}(t)}return!1}function i(t,e,n,i,r,o,l,d){return o<t&&l>e||o>t&&l<e?0:o<=t&&d<=n||l>=e&&d>=n?o-t-i:l>e&&d<n||o<t&&d>n?l-e+r:0}module.exports=function(e,r){var o=window,l=r.scrollMode,d=r.block,f=r.inline,h=r.boundary,u=r.skipOverflowHiddenElements,s="function"==typeof h?h:function(t){return t!==h};if(!t(e))throw new TypeError("Invalid target");for(var c,a,g=document.scrollingElement||document.documentElement,m=[],p=e;t(p)&&s(p);){if((p=null==(a=(c=p).parentElement)?c.getRootNode().host||null:a)===g){m.push(p);break}null!=p&&p===document.body&&n(p)&&!n(document.documentElement)||null!=p&&n(p,u)&&m.push(p)}for(var w=o.visualViewport?o.visualViewport.width:innerWidth,v=o.visualViewport?o.visualViewport.height:innerHeight,W=window.scrollX||pageXOffset,H=window.scrollY||pageYOffset,b=e.getBoundingClientRect(),y=b.height,E=b.width,M=b.top,V=b.right,x=b.bottom,I=b.left,C="start"===d||"nearest"===d?M:"end"===d?x:M+y/2,R="center"===f?I+E/2:"end"===f?V:I,T=[],k=0;k<m.length;k++){var B=m[k],D=B.getBoundingClientRect(),O=D.height,X=D.width,Y=D.top,L=D.right,S=D.bottom,j=D.left;if("if-needed"===l&&M>=0&&I>=0&&x<=v&&V<=w&&M>=Y&&x<=S&&I>=j&&V<=L)return T;var N=getComputedStyle(B),q=parseInt(N.borderLeftWidth,10),z=parseInt(N.borderTopWidth,10),A=parseInt(N.borderRightWidth,10),F=parseInt(N.borderBottomWidth,10),G=0,J=0,K="offsetWidth"in B?B.offsetWidth-B.clientWidth-q-A:0,P="offsetHeight"in B?B.offsetHeight-B.clientHeight-z-F:0,Q="offsetWidth"in B?0===B.offsetWidth?0:X/B.offsetWidth:0,U="offsetHeight"in B?0===B.offsetHeight?0:O/B.offsetHeight:0;if(g===B)G="start"===d?C:"end"===d?C-v:"nearest"===d?i(H,H+v,v,z,F,H+C,H+C+y,y):C-v/2,J="start"===f?R:"center"===f?R-w/2:"end"===f?R-w:i(W,W+w,w,q,A,W+R,W+R+E,E),G=Math.max(0,G+H),J=Math.max(0,J+W);else{G="start"===d?C-Y-z:"end"===d?C-S+F+P:"nearest"===d?i(Y,S,O,z,F+P,C,C+y,y):C-(Y+O/2)+P/2,J="start"===f?R-j-q:"center"===f?R-(j+X/2)+K/2:"end"===f?R-L+A+K:i(j,L,X,q,A+K,R,R+E,E);var Z=B.scrollLeft,$=B.scrollTop;C+=$-(G=Math.max(0,Math.min($+G/U,B.scrollHeight-O/U+P))),R+=Z-(J=Math.max(0,Math.min(Z+J/Q,B.scrollWidth-X/Q+K)))}T.push({el:B,top:G,left:J})}return T};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = cursor.parentElement\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","TypeError","scrollingElement","document","documentElement","frames","cursor","parentElement","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","viewportX","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","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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"}
1
+ {"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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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":["el","nodeType","canOverflow","overflow","skipOverflowHiddenElements","isScrollable","clientHeight","scrollHeight","clientWidth","scrollWidth","style","getComputedStyle","overflowY","overflowX","frame","ownerDocument","defaultView","frameElement","e","getFrameElement","isHiddenByFrame","scrollingEdgeStart","scrollingEdgeEnd","scrollingSize","scrollingBorderStart","scrollingBorderEnd","elementEdgeStart","elementEdgeEnd","elementSize","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","isElement","element","parent","scrollingElement","document","documentElement","frames","cursor","parentElement","getRootNode","host","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","_target$getBoundingCl","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","targetBlock","targetInline","computations","index","length","_frame$getBoundingCli","frameStyle","borderLeft","parseInt","borderLeftWidth","borderTop","borderTopWidth","borderRightWidth","borderBottom","borderBottomWidth","blockScroll","inlineScroll","scrollbarWidth","offsetWidth","borderRight","scrollbarHeight","offsetHeight","scaleX","scaleY","alignNearest","viewportX","Math","max","scrollLeft","scrollTop","min"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- function t(t){return"object"==typeof t&&null!=t&&1===t.nodeType}function e(t,e){return(!e||"hidden"!==t)&&"visible"!==t&&"clip"!==t}function n(t,n){if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){var r=getComputedStyle(t,null);return e(r.overflowY,n)||e(r.overflowX,n)||function(t){var e=function(t){if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}}(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)}(t)}return!1}function r(t,e,n,r,i,o,l,d){return o<t&&l>e||o>t&&l<e?0:o<=t&&d<=n||l>=e&&d>=n?o-t-r:l>e&&d<n||o<t&&d>n?l-e+i:0}var i=function(e,i){var o=window,l=i.scrollMode,d=i.block,f=i.inline,h=i.boundary,u=i.skipOverflowHiddenElements,a="function"==typeof h?h:function(t){return t!==h};if(!t(e))throw new TypeError("Invalid target");for(var s=document.scrollingElement||document.documentElement,c=[],g=e;t(g)&&a(g);){if((g=g.parentElement)===s){c.push(g);break}null!=g&&g===document.body&&n(g)&&!n(document.documentElement)||null!=g&&n(g,u)&&c.push(g)}for(var p=o.visualViewport?o.visualViewport.width:innerWidth,m=o.visualViewport?o.visualViewport.height:innerHeight,w=window.scrollX||pageXOffset,v=window.scrollY||pageYOffset,W=e.getBoundingClientRect(),H=W.height,b=W.width,y=W.top,E=W.right,M=W.bottom,V=W.left,x="start"===d||"nearest"===d?y:"end"===d?M:y+H/2,I="center"===f?V+b/2:"end"===f?E:V,C=[],T=0;T<c.length;T++){var k=c[T],B=k.getBoundingClientRect(),D=B.height,O=B.width,R=B.top,X=B.right,Y=B.bottom,L=B.left;if("if-needed"===l&&y>=0&&V>=0&&M<=m&&E<=p&&y>=R&&M<=Y&&V>=L&&E<=X)return C;var S=getComputedStyle(k),j=parseInt(S.borderLeftWidth,10),q=parseInt(S.borderTopWidth,10),z=parseInt(S.borderRightWidth,10),A=parseInt(S.borderBottomWidth,10),F=0,G=0,J="offsetWidth"in k?k.offsetWidth-k.clientWidth-j-z:0,K="offsetHeight"in k?k.offsetHeight-k.clientHeight-q-A:0,N="offsetWidth"in k?0===k.offsetWidth?0:O/k.offsetWidth:0,P="offsetHeight"in k?0===k.offsetHeight?0:D/k.offsetHeight:0;if(s===k)F="start"===d?x:"end"===d?x-m:"nearest"===d?r(v,v+m,m,q,A,v+x,v+x+H,H):x-m/2,G="start"===f?I:"center"===f?I-p/2:"end"===f?I-p:r(w,w+p,p,j,z,w+I,w+I+b,b),F=Math.max(0,F+v),G=Math.max(0,G+w);else{F="start"===d?x-R-q:"end"===d?x-Y+A+K:"nearest"===d?r(R,Y,D,q,A+K,x,x+H,H):x-(R+D/2)+K/2,G="start"===f?I-L-j:"center"===f?I-(L+O/2)+J/2:"end"===f?I-X+z+J:r(L,X,O,j,z+J,I,I+b,b);var Q=k.scrollLeft,U=k.scrollTop;x+=U-(F=Math.max(0,Math.min(U+F/P,k.scrollHeight-D/P+K))),I+=Q-(G=Math.max(0,Math.min(Q+G/N,k.scrollWidth-O/N+J)))}C.push({el:k,top:F,left:G})}return C};export{i as default};
1
+ function t(t){return"object"==typeof t&&null!=t&&1===t.nodeType}function e(t,e){return(!e||"hidden"!==t)&&"visible"!==t&&"clip"!==t}function n(t,n){if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){var r=getComputedStyle(t,null);return e(r.overflowY,n)||e(r.overflowX,n)||function(t){var e=function(t){if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}}(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)}(t)}return!1}function r(t,e,n,r,i,o,l,d){return o<t&&l>e||o>t&&l<e?0:o<=t&&d<=n||l>=e&&d>=n?o-t-r:l>e&&d<n||o<t&&d>n?l-e+i:0}var i=function(e,i){var o=window,l=i.scrollMode,d=i.block,f=i.inline,h=i.boundary,u=i.skipOverflowHiddenElements,s="function"==typeof h?h:function(t){return t!==h};if(!t(e))throw new TypeError("Invalid target");for(var a,c,g=document.scrollingElement||document.documentElement,p=[],m=e;t(m)&&s(m);){if((m=null==(c=(a=m).parentElement)?a.getRootNode().host||null:c)===g){p.push(m);break}null!=m&&m===document.body&&n(m)&&!n(document.documentElement)||null!=m&&n(m,u)&&p.push(m)}for(var w=o.visualViewport?o.visualViewport.width:innerWidth,v=o.visualViewport?o.visualViewport.height:innerHeight,W=window.scrollX||pageXOffset,H=window.scrollY||pageYOffset,b=e.getBoundingClientRect(),y=b.height,E=b.width,M=b.top,V=b.right,x=b.bottom,I=b.left,C="start"===d||"nearest"===d?M:"end"===d?x:M+y/2,R="center"===f?I+E/2:"end"===f?V:I,T=[],k=0;k<p.length;k++){var B=p[k],D=B.getBoundingClientRect(),O=D.height,X=D.width,Y=D.top,L=D.right,S=D.bottom,j=D.left;if("if-needed"===l&&M>=0&&I>=0&&x<=v&&V<=w&&M>=Y&&x<=S&&I>=j&&V<=L)return T;var N=getComputedStyle(B),q=parseInt(N.borderLeftWidth,10),z=parseInt(N.borderTopWidth,10),A=parseInt(N.borderRightWidth,10),F=parseInt(N.borderBottomWidth,10),G=0,J=0,K="offsetWidth"in B?B.offsetWidth-B.clientWidth-q-A:0,P="offsetHeight"in B?B.offsetHeight-B.clientHeight-z-F:0,Q="offsetWidth"in B?0===B.offsetWidth?0:X/B.offsetWidth:0,U="offsetHeight"in B?0===B.offsetHeight?0:O/B.offsetHeight:0;if(g===B)G="start"===d?C:"end"===d?C-v:"nearest"===d?r(H,H+v,v,z,F,H+C,H+C+y,y):C-v/2,J="start"===f?R:"center"===f?R-w/2:"end"===f?R-w:r(W,W+w,w,q,A,W+R,W+R+E,E),G=Math.max(0,G+H),J=Math.max(0,J+W);else{G="start"===d?C-Y-z:"end"===d?C-S+F+P:"nearest"===d?r(Y,S,O,z,F+P,C,C+y,y):C-(Y+O/2)+P/2,J="start"===f?R-j-q:"center"===f?R-(j+X/2)+K/2:"end"===f?R-L+A+K:r(j,L,X,q,A+K,R,R+E,E);var Z=B.scrollLeft,$=B.scrollTop;C+=$-(G=Math.max(0,Math.min($+G/U,B.scrollHeight-O/U+P))),R+=Z-(J=Math.max(0,Math.min(Z+J/Q,B.scrollWidth-X/Q+K)))}T.push({el:B,top:G,left:J})}return T};export{i as default};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = cursor.parentElement\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","TypeError","scrollingElement","document","documentElement","frames","cursor","parentElement","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","viewportX","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","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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"}
1
+ {"version":3,"file":"index.mjs","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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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":["el","nodeType","canOverflow","overflow","skipOverflowHiddenElements","isScrollable","clientHeight","scrollHeight","clientWidth","scrollWidth","style","getComputedStyle","overflowY","overflowX","frame","ownerDocument","defaultView","frameElement","e","getFrameElement","isHiddenByFrame","scrollingEdgeStart","scrollingEdgeEnd","scrollingSize","scrollingBorderStart","scrollingBorderEnd","elementEdgeStart","elementEdgeEnd","elementSize","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","isElement","element","parent","scrollingElement","document","documentElement","frames","cursor","parentElement","getRootNode","host","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","_target$getBoundingCl","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","targetBlock","targetInline","computations","index","length","_frame$getBoundingCli","frameStyle","borderLeft","parseInt","borderLeftWidth","borderTop","borderTopWidth","borderRightWidth","borderBottom","borderBottomWidth","blockScroll","inlineScroll","scrollbarWidth","offsetWidth","borderRight","scrollbarHeight","offsetHeight","scaleX","scaleY","alignNearest","viewportX","Math","max","scrollLeft","scrollTop","min"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compute-scroll-into-view",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "The engine that powers scroll-into-view-if-needed",
5
5
  "keywords": [
6
6
  "if-needed",
@@ -65,22 +65,21 @@
65
65
  },
66
66
  "prettier": {
67
67
  "semi": false,
68
- "singleQuote": true,
69
- "trailingComma": "es5"
68
+ "singleQuote": true
70
69
  },
71
70
  "devDependencies": {
72
71
  "@sanity/semantic-release-preset": "^2.0.2",
73
- "husky": "^8.0.1",
74
- "jest": "^26.6.3",
75
- "jest-junit": "^12.2.0",
76
- "jest-puppeteer": "^4.4.0",
77
- "lint-staged": "^13.0.3",
72
+ "husky": "^8.0.2",
73
+ "jest": "^29.3.1",
74
+ "jest-junit": "^15.0.0",
75
+ "jest-puppeteer": "^6.1.1",
76
+ "lint-staged": "^13.0.4",
78
77
  "microbundle": "^0.15.1",
79
78
  "prettier": "^2.8.0",
80
79
  "prettier-plugin-packagejson": "^2.3.0",
81
- "puppeteer": "^5.5.0",
80
+ "puppeteer": "^19.3.0",
82
81
  "rimraf": "^3.0.2",
83
- "serve": "^11.3.2",
82
+ "serve": "^14.1.2",
84
83
  "typescript": "^4.9.3"
85
84
  },
86
85
  "bundlesize": [
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).computeScrollIntoView=t()}(this,function(){function e(e){return"object"==typeof e&&null!=e&&1===e.nodeType}function t(e,t){return(!t||"hidden"!==e)&&"visible"!==e&&"clip"!==e}function n(e,n){if(e.clientHeight<e.scrollHeight||e.clientWidth<e.scrollWidth){var i=getComputedStyle(e,null);return t(i.overflowY,n)||t(i.overflowX,n)||function(e){var t=function(e){if(!e.ownerDocument||!e.ownerDocument.defaultView)return null;try{return e.ownerDocument.defaultView.frameElement}catch(e){return null}}(e);return!!t&&(t.clientHeight<e.scrollHeight||t.clientWidth<e.scrollWidth)}(e)}return!1}function i(e,t,n,i,o,r,l,f){return r<e&&l>t||r>e&&l<t?0:r<=e&&f<=n||l>=t&&f>=n?r-e-i:l>t&&f<n||r<e&&f>n?l-t+o:0}return function(t,o){var r=window,l=o.scrollMode,f=o.block,d=o.inline,u=o.boundary,h=o.skipOverflowHiddenElements,s="function"==typeof u?u:function(e){return e!==u};if(!e(t))throw new TypeError("Invalid target");for(var c=document.scrollingElement||document.documentElement,a=[],p=t;e(p)&&s(p);){if((p=p.parentElement)===c){a.push(p);break}null!=p&&p===document.body&&n(p)&&!n(document.documentElement)||null!=p&&n(p,h)&&a.push(p)}for(var g=r.visualViewport?r.visualViewport.width:innerWidth,m=r.visualViewport?r.visualViewport.height:innerHeight,w=window.scrollX||pageXOffset,v=window.scrollY||pageYOffset,W=t.getBoundingClientRect(),b=W.height,y=W.width,H=W.top,E=W.right,M=W.bottom,V=W.left,x="start"===f||"nearest"===f?H:"end"===f?M:H+b/2,I="center"===d?V+y/2:"end"===d?E:V,T=[],C=0;C<a.length;C++){var k=a[C],B=k.getBoundingClientRect(),D=B.height,O=B.width,R=B.top,S=B.right,X=B.bottom,Y=B.left;if("if-needed"===l&&H>=0&&V>=0&&M<=m&&E<=g&&H>=R&&M<=X&&V>=Y&&E<=S)return T;var j=getComputedStyle(k),L=parseInt(j.borderLeftWidth,10),q=parseInt(j.borderTopWidth,10),z=parseInt(j.borderRightWidth,10),A=parseInt(j.borderBottomWidth,10),F=0,G=0,J="offsetWidth"in k?k.offsetWidth-k.clientWidth-L-z:0,K="offsetHeight"in k?k.offsetHeight-k.clientHeight-q-A:0,N="offsetWidth"in k?0===k.offsetWidth?0:O/k.offsetWidth:0,P="offsetHeight"in k?0===k.offsetHeight?0:D/k.offsetHeight:0;if(c===k)F="start"===f?x:"end"===f?x-m:"nearest"===f?i(v,v+m,m,q,A,v+x,v+x+b,b):x-m/2,G="start"===d?I:"center"===d?I-g/2:"end"===d?I-g:i(w,w+g,g,L,z,w+I,w+I+y,y),F=Math.max(0,F+v),G=Math.max(0,G+w);else{F="start"===f?x-R-q:"end"===f?x-X+A+K:"nearest"===f?i(R,X,D,q,A+K,x,x+b,b):x-(R+D/2)+K/2,G="start"===d?I-Y-L:"center"===d?I-(Y+O/2)+J/2:"end"===d?I-S+z+J:i(Y,S,O,L,z+J,I,I+y,y);var Q=k.scrollLeft,U=k.scrollTop;x+=U-(F=Math.max(0,Math.min(U+F/P,k.scrollHeight-D/P+K))),I+=Q-(G=Math.max(0,Math.min(Q+G/N,k.scrollWidth-O/N+J)))}T.push({el:k,top:F,left:G})}return T}});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t||self).computeScrollIntoView=e()}(this,function(){function t(t){return"object"==typeof t&&null!=t&&1===t.nodeType}function e(t,e){return(!e||"hidden"!==t)&&"visible"!==t&&"clip"!==t}function n(t,n){if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){var o=getComputedStyle(t,null);return e(o.overflowY,n)||e(o.overflowX,n)||function(t){var e=function(t){if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}}(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)}(t)}return!1}function o(t,e,n,o,i,r,l,f){return r<t&&l>e||r>t&&l<e?0:r<=t&&f<=n||l>=e&&f>=n?r-t-o:l>e&&f<n||r<t&&f>n?l-e+i:0}return function(e,i){var r=window,l=i.scrollMode,f=i.block,d=i.inline,u=i.boundary,h=i.skipOverflowHiddenElements,s="function"==typeof u?u:function(t){return t!==u};if(!t(e))throw new TypeError("Invalid target");for(var c,a,p=document.scrollingElement||document.documentElement,g=[],m=e;t(m)&&s(m);){if((m=null==(a=(c=m).parentElement)?c.getRootNode().host||null:a)===p){g.push(m);break}null!=m&&m===document.body&&n(m)&&!n(document.documentElement)||null!=m&&n(m,h)&&g.push(m)}for(var w=r.visualViewport?r.visualViewport.width:innerWidth,v=r.visualViewport?r.visualViewport.height:innerHeight,W=window.scrollX||pageXOffset,b=window.scrollY||pageYOffset,y=e.getBoundingClientRect(),H=y.height,E=y.width,M=y.top,V=y.right,x=y.bottom,I=y.left,T="start"===f||"nearest"===f?M:"end"===f?x:M+H/2,C="center"===d?I+E/2:"end"===d?V:I,R=[],k=0;k<g.length;k++){var B=g[k],D=B.getBoundingClientRect(),O=D.height,S=D.width,X=D.top,Y=D.right,j=D.bottom,L=D.left;if("if-needed"===l&&M>=0&&I>=0&&x<=v&&V<=w&&M>=X&&x<=j&&I>=L&&V<=Y)return R;var N=getComputedStyle(B),q=parseInt(N.borderLeftWidth,10),z=parseInt(N.borderTopWidth,10),A=parseInt(N.borderRightWidth,10),F=parseInt(N.borderBottomWidth,10),G=0,J=0,K="offsetWidth"in B?B.offsetWidth-B.clientWidth-q-A:0,P="offsetHeight"in B?B.offsetHeight-B.clientHeight-z-F:0,Q="offsetWidth"in B?0===B.offsetWidth?0:S/B.offsetWidth:0,U="offsetHeight"in B?0===B.offsetHeight?0:O/B.offsetHeight:0;if(p===B)G="start"===f?T:"end"===f?T-v:"nearest"===f?o(b,b+v,v,z,F,b+T,b+T+H,H):T-v/2,J="start"===d?C:"center"===d?C-w/2:"end"===d?C-w:o(W,W+w,w,q,A,W+C,W+C+E,E),G=Math.max(0,G+b),J=Math.max(0,J+W);else{G="start"===f?T-X-z:"end"===f?T-j+F+P:"nearest"===f?o(X,j,O,z,F+P,T,T+H,H):T-(X+O/2)+P/2,J="start"===d?C-L-q:"center"===d?C-(L+S/2)+K/2:"end"===d?C-Y+A+K:o(L,Y,S,q,A+K,C,C+E,E);var Z=B.scrollLeft,$=B.scrollTop;T+=$-(G=Math.max(0,Math.min($+G/U,B.scrollHeight-O/U+P))),C+=Z-(J=Math.max(0,Math.min(Z+J/Q,B.scrollWidth-S/Q+K)))}R.push({el:B,top:G,left:J})}return R}});
2
2
  //# sourceMappingURL=compute-scroll-into-view.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compute-scroll-into-view.min.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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = cursor.parentElement\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","TypeError","scrollingElement","document","documentElement","frames","cursor","parentElement","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","viewportX","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","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":"wOAuCA,SAASA,EAAUC,GACjB,MAAqB,iBAAPA,GAAyB,MAANA,GAA8B,IAAhBA,EAAGC,SAGpD,SAASC,EACPC,EACAC,GAEA,QAAIA,GAA2C,WAAbD,IAId,YAAbA,GAAuC,SAAbA,EA0BnC,SAASE,EAAaL,EAAaI,GACjC,GAAIJ,EAAGM,aAAeN,EAAGO,cAAgBP,EAAGQ,YAAcR,EAAGS,YAAa,CACxE,IAAMC,EAAQC,iBAAiBX,EAAI,MACnC,OACEE,EAAYQ,EAAME,UAAWR,IAC7BF,EAAYQ,EAAMG,UAAWT,IAhBnC,SAAyBJ,GACvB,IAAMc,EAbR,SAAyBd,GACvB,IAAKA,EAAGe,gBAAkBf,EAAGe,cAAcC,YACzC,YAGF,IACE,OAAOhB,EAAGe,cAAcC,YAAYC,aACpC,MAAOC,GACP,aAKYC,CAAgBnB,GAC9B,QAAKc,IAKHA,EAAMR,aAAeN,EAAGO,cAAgBO,EAAMN,YAAcR,EAAGS,aAU7DW,CAAgBpB,GAIpB,SAWF,SAASqB,EACPC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,GAqBA,OACGF,EAAmBL,GAClBM,EAAiBL,GAClBI,EAAmBL,GAAsBM,EAAiBL,IA6C1DI,GAAoBL,GAAsBO,GAAeL,GACzDI,GAAkBL,GAAoBM,GAAeL,EAE/CG,EAAmBL,EAAqBG,EA4C9CG,EAAiBL,GAAoBM,EAAcL,GACnDG,EAAmBL,GAAsBO,EAAcL,EAEjDI,EAAiBL,EAAmBG,oBAM/BI,EAAiBC,GAE/B,IAAMC,EAAqBC,OAInBC,EACNH,EADMG,WAAYC,EAClBJ,EADkBI,MAAOC,EACzBL,EADyBK,OAAQC,EACjCN,EADiCM,SAAUjC,EAC3C2B,EAD2C3B,2BAKvCkC,EACgB,mBAAbD,EAA0BA,EAAW,SAACE,UAAcA,IAASF,GAEtE,IAAKtC,EAAU+B,GACb,UAAUU,UAAU,kBAStB,IALA,IAAMC,EAAmBC,SAASD,kBAAoBC,SAASC,gBAGzDC,EAAoB,GACtBC,EAAyBf,EACtB/B,EAAU8C,IAAWP,EAAcO,IAAS,CAKjD,IAHAA,EAASA,EAAOC,iBAGDL,EAAkB,CAC/BG,EAAOG,KAAKF,GACZ,MAKU,MAAVA,GACAA,IAAWH,SAASM,MACpB3C,EAAawC,KACZxC,EAAaqC,SAASC,kBAMX,MAAVE,GAAkBxC,EAAawC,EAAQzC,IACzCwC,EAAOG,KAAKF,GA8ChB,IArCA,IAAMI,EAAgBjB,EAAmBkB,eACrClB,EAAmBkB,eAAeC,MAClCC,WACEC,EAAiBrB,EAAmBkB,eACtClB,EAAmBkB,eAAeI,OAClCC,YAGEC,EAAYvB,OAAOwB,SAAWC,YAC9BC,EAAY1B,OAAO2B,SAAWC,cAShC/B,EAAOgC,wBANDC,IAART,OACOU,IAAPb,MACKc,IAALC,IACOC,IAAPC,MACQC,IAARC,OACMC,IAANC,KAIEC,EACQ,UAAVtC,GAA+B,YAAVA,EACjB8B,EACU,QAAV9B,EACAkC,EACAJ,EAAYF,EAAe,EAC7BW,EACS,WAAXtC,EACImC,EAAaP,EAAc,EAChB,QAAX5B,EACA+B,EACAI,EAGAI,EAAqC,GAElCC,EAAQ,EAAGA,EAAQhC,EAAOiC,OAAQD,IAAS,CAClD,IAAM9D,EAAQ8B,EAAOgC,KAKnB9D,EAAMgD,wBADAR,IAAAA,OAAQH,IAAAA,MAAOe,IAAAA,IAAKE,IAAAA,MAAOE,IAAAA,OAAQE,IAAAA,KAK3C,GACiB,cAAftC,GACA+B,GAAa,GACbM,GAAc,GACdF,GAAgBhB,GAChBc,GAAelB,GACfgB,GAAaC,GACbG,GAAgBC,GAChBC,GAAcC,GACdL,GAAeC,EAGf,OAAOO,EAGT,IAAMG,EAAanE,iBAAiBG,GAC9BiE,EAAaC,SAASF,EAAWG,gBAA2B,IAC5DC,EAAYF,SAASF,EAAWK,eAA0B,IAC1DC,EAAcJ,SAASF,EAAWO,iBAA4B,IAC9DC,EAAeN,SAASF,EAAWS,kBAA6B,IAElEC,EAAsB,EACtBC,EAAuB,EAIrBC,EACJ,gBAAiB5E,EACZA,EAAsB6E,YACtB7E,EAAsBN,YACvBuE,EACAK,EACA,EACAQ,EACJ,iBAAkB9E,EACbA,EAAsB+E,aACtB/E,EAAsBR,aACvB4E,EACAI,EACA,EAEAQ,EACJ,gBAAiBhF,EAC0B,IAAtCA,EAAsB6E,YACrB,EACAxC,EAASrC,EAAsB6E,YACjC,EACAI,EACJ,iBAAkBjF,EAC0B,IAAvCA,EAAsB+E,aACrB,EACAvC,EAAUxC,EAAsB+E,aAClC,EAEN,GAAIpD,IAAqB3B,EAIrB0E,EADY,UAAVrD,EACYsC,EACK,QAAVtC,EACKsC,EAAcpB,EACT,YAAVlB,EACKd,EACZsC,EACAA,EAAYN,EACZA,EACA6B,EACAI,EACA3B,EAAYc,EACZd,EAAYc,EAAcV,EAC1BA,GAIYU,EAAcpB,EAAiB,EAI7CoC,EADa,UAAXrD,EACasC,EACK,WAAXtC,EACMsC,EAAezB,EAAgB,EAC1B,QAAXb,EACMsC,EAAezB,EAGf5B,EACbmC,EACAA,EAAYP,EACZA,EACA8B,EACAK,EACA5B,EAAYkB,EACZlB,EAAYkB,EAAeV,EAC3BA,GAMJwB,EAAcQ,KAAKC,IAAI,EAAGT,EAAc7B,GACxC8B,EAAeO,KAAKC,IAAI,EAAGR,EAAejC,OACrC,CAIHgC,EADY,UAAVrD,EACYsC,EAAcP,EAAMgB,EACf,QAAV/C,EACKsC,EAAcH,EAASgB,EAAeM,EACjC,YAAVzD,EACKd,EACZ6C,EACAI,EACAhB,EACA4B,EACAI,EAAeM,EACfnB,EACAA,EAAcV,EACdA,GAIYU,GAAeP,EAAMZ,EAAS,GAAKsC,EAAkB,EAInEH,EADa,UAAXrD,EACasC,EAAeF,EAAOO,EACjB,WAAX3C,EACMsC,GAAgBF,EAAOrB,EAAQ,GAAKuC,EAAiB,EAChD,QAAXtD,EACMsC,EAAeN,EAAQgB,EAAcM,EAGrCrE,EACbmD,EACAJ,EACAjB,EACA4B,EACAK,EAAcM,EACdhB,EACAA,EAAeV,EACfA,GAIJ,IAAQkC,EAA0BpF,EAA1BoF,WAAYC,EAAcrF,EAAdqF,UAkBpB1B,GAAe0B,GAhBfX,EAAcQ,KAAKC,IACjB,EACAD,KAAKI,IACHD,EAAYX,EAAcO,EAC1BjF,EAAMP,aAAe+C,EAASyC,EAASH,KAa3ClB,GAAgBwB,GAVhBT,EAAeO,KAAKC,IAClB,EACAD,KAAKI,IACHF,EAAaT,EAAeK,EAC5BhF,EAAML,YAAc0C,EAAQ2C,EAASJ,KAS3Cf,EAAa5B,KAAK,CAAE/C,GAAIc,EAAOoD,IAAKsB,EAAahB,KAAMiB,IAGzD,OAAOd"}
1
+ {"version":3,"file":"compute-scroll-into-view.min.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// add support for visualViewport object currently implemented in chrome\ninterface visualViewport {\n height: number\n width: number\n}\n\ntype ScrollLogicalPosition = 'start' | 'center' | 'end' | 'nearest'\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\ntype ScrollMode = 'always' | 'if-needed'\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\ntype SkipOverflowHiddenElements = boolean\n\ninterface Options {\n block?: ScrollLogicalPosition\n inline?: ScrollLogicalPosition\n scrollMode?: ScrollMode\n boundary?: CustomScrollBoundary\n skipOverflowHiddenElements?: SkipOverflowHiddenElements\n}\n\n// Custom behavior, not in any spec\ntype CustomScrollBoundaryCallback = (parent: Element) => boolean\ntype CustomScrollBoundary = Element | CustomScrollBoundaryCallback | null\ninterface CustomScrollAction {\n el: Element\n top: number\n left: number\n}\n\n// @TODO better shadowdom test, 11 = document fragment\nfunction isElement(el: any): el is Element {\n return typeof el === 'object' && el != null && el.nodeType === 1\n}\n\nfunction 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\nfunction 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\nfunction 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\nfunction 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 */\nfunction 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\nfunction 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\nexport default (target: Element, options: Options): CustomScrollAction[] => {\n //TODO: remove this hack when microbundle will support typescript >= 4.0\n const windowWithViewport = window as unknown as Window & {\n visualViewport: visualViewport\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 = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.width\n : innerWidth\n const viewportHeight = windowWithViewport.visualViewport\n ? windowWithViewport.visualViewport.height\n : innerHeight\n\n // Newer browsers supports scroll[X|Y], page[X|Y]Offset is\n const viewportX = window.scrollX || pageXOffset\n const viewportY = window.scrollY || pageYOffset\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\n // These values mutate as we loop through and generate scroll coordinates\n let targetBlock: number =\n block === 'start' || block === 'nearest'\n ? targetTop\n : block === 'end'\n ? targetBottom\n : targetTop + targetHeight / 2 // block === 'center\n let targetInline: number =\n inline === 'center'\n ? targetLeft + targetWidth / 2\n : inline === 'end'\n ? targetRight\n : targetLeft // inline === 'start || inline === 'nearest\n\n // Collect new scroll positions\n const computations: CustomScrollAction[] = []\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 viewportY,\n viewportY + viewportHeight,\n viewportHeight,\n borderTop,\n borderBottom,\n viewportY + targetBlock,\n viewportY + 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 viewportX,\n viewportX + viewportWidth,\n viewportWidth,\n borderLeft,\n borderRight,\n viewportX + targetInline,\n viewportX + 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 + viewportY)\n inlineScroll = Math.max(0, inlineScroll + viewportX)\n } else {\n // Handle each scrolling frame that might exist between the target and the viewport\n\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 = Math.max(\n 0,\n Math.min(\n scrollTop + blockScroll / scaleY,\n frame.scrollHeight - height / scaleY + scrollbarHeight\n )\n )\n inlineScroll = 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":["el","nodeType","canOverflow","overflow","skipOverflowHiddenElements","isScrollable","clientHeight","scrollHeight","clientWidth","scrollWidth","style","getComputedStyle","overflowY","overflowX","frame","ownerDocument","defaultView","frameElement","e","getFrameElement","isHiddenByFrame","scrollingEdgeStart","scrollingEdgeEnd","scrollingSize","scrollingBorderStart","scrollingBorderEnd","elementEdgeStart","elementEdgeEnd","elementSize","target","options","windowWithViewport","window","scrollMode","block","inline","boundary","checkBoundary","node","isElement","element","parent","scrollingElement","document","documentElement","frames","cursor","parentElement","getRootNode","host","push","body","viewportWidth","visualViewport","width","innerWidth","viewportHeight","height","innerHeight","scrollX","pageXOffset","viewportY","scrollY","pageYOffset","_target$getBoundingCl","getBoundingClientRect","targetHeight","targetWidth","targetTop","top","targetRight","right","targetBottom","bottom","targetLeft","left","targetBlock","targetInline","computations","index","length","_frame$getBoundingCli","frameStyle","borderLeft","parseInt","borderLeftWidth","borderTop","borderTopWidth","borderRightWidth","borderBottom","borderBottomWidth","blockScroll","inlineScroll","scrollbarWidth","offsetWidth","borderRight","scrollbarHeight","offsetHeight","scaleX","scaleY","alignNearest","viewportX","Math","max","scrollLeft","scrollTop","min"],"mappings":"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"}