@@ -14,14 +14,54 @@ export type ScrollMode = 'always' | 'if-needed'
1414
1515/** @public */
1616export interface Options {
17+ /**
18+ * Control the logical scroll position on the y-axis. The spec states that the `block` direction is related to the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), but this is not implemented yet in this library.
19+ * This means that `block: 'start'` aligns to the top edge and `block: 'end'` to the bottom.
20+ * @defaultValue 'center'
21+ */
1722 block ?: ScrollLogicalPosition
23+ /**
24+ * Like `block` this is affected by the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode). In left-to-right pages `inline: 'start'` will align to the left edge. In right-to-left it should be flipped. This will be supported in a future release.
25+ * @defaultValue 'nearest'
26+ */
1827 inline ?: ScrollLogicalPosition
28+ /**
29+ * This is a proposed addition to the spec that you can track here: https://github.com/w3c/csswg-drafts/pull/5677
30+ *
31+ * This library will be updated to reflect any changes to the spec and will provide a migration path.
32+ * To be backwards compatible with `Element.scrollIntoViewIfNeeded` if something is not 100% visible it will count as "needs scrolling". If you need a different visibility ratio your best option would be to implement an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
33+ * @defaultValue 'always'
34+ */
1935 scrollMode ?: ScrollMode
20- // Custom behavior, not in any spec
36+ /**
37+ * By default there is no boundary. All the parent elements of your target is checked until it reaches the viewport ([`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)) when calculating layout and what to scroll.
38+ * By passing a boundary you can short-circuit this loop depending on your needs:
39+ *
40+ * - Prevent the browser window from scrolling.
41+ * - Scroll elements into view in a list, without scrolling container elements.
42+ *
43+ * You can also pass a function to do more dynamic checks to override the scroll scoping:
44+ *
45+ * ```js
46+ * let actions = compute(target, {
47+ * boundary: (parent) => {
48+ * // By default `overflow: hidden` elements are allowed, only `overflow: visible | clip` is skipped as
49+ * // this is required by the CSSOM spec
50+ * if (getComputedStyle(parent)['overflow'] === 'hidden') {
51+ * return false
52+ * }
53+
54+ * return true
55+ * },
56+ * })
57+ * ```
58+ * @defaultValue null
59+ */
2160 boundary ?: Element | ( ( parent : Element ) => boolean ) | null
2261 /**
2362 * New option that skips auto-scrolling all nodes with overflow: hidden set
2463 * See FF implementation: https://hg.mozilla.org/integration/fx-team/rev/c48c3ec05012#l7.18
64+ * @defaultValue false
2565 * @public
2666 */
2767 skipOverflowHiddenElements ?: boolean
@@ -35,22 +75,21 @@ export interface ScrollAction {
3575}
3676
3777// @TODO better shadowdom test, 11 = document fragment
38- function isElement ( el : any ) : el is Element {
39- return typeof el === 'object' && el != null && el . nodeType === 1
40- }
78+ let isElement = ( el : any ) : el is Element =>
79+ typeof el === 'object' && el != null && el . nodeType === 1
4180
42- function canOverflow (
81+ let canOverflow = (
4382 overflow : string | null ,
4483 skipOverflowHiddenElements ?: boolean
45- ) {
84+ ) => {
4685 if ( skipOverflowHiddenElements && overflow === 'hidden' ) {
4786 return false
4887 }
4988
5089 return overflow !== 'visible' && overflow !== 'clip'
5190}
5291
53- function getFrameElement ( el : Element ) {
92+ let getFrameElement = ( el : Element ) => {
5493 if ( ! el . ownerDocument || ! el . ownerDocument . defaultView ) {
5594 return null
5695 }
@@ -62,8 +101,8 @@ function getFrameElement(el: Element) {
62101 }
63102}
64103
65- function isHiddenByFrame ( el : Element ) : boolean {
66- const frame = getFrameElement ( el )
104+ let isHiddenByFrame = ( el : Element ) : boolean => {
105+ let frame = getFrameElement ( el )
67106 if ( ! frame ) {
68107 return false
69108 }
@@ -73,9 +112,9 @@ function isHiddenByFrame(el: Element): boolean {
73112 )
74113}
75114
76- function isScrollable ( el : Element , skipOverflowHiddenElements ?: boolean ) {
115+ let isScrollable = ( el : Element , skipOverflowHiddenElements ?: boolean ) => {
77116 if ( el . clientHeight < el . scrollHeight || el . clientWidth < el . scrollWidth ) {
78- const style = getComputedStyle ( el , null )
117+ let style = getComputedStyle ( el , null )
79118 return (
80119 canOverflow ( style . overflowY , skipOverflowHiddenElements ) ||
81120 canOverflow ( style . overflowX , skipOverflowHiddenElements ) ||
@@ -94,7 +133,7 @@ function isScrollable(el: Element, skipOverflowHiddenElements?: boolean) {
94133 * │ target │ frame
95134 * └────────┘ ┗ ━ ━ ━ ┛
96135 */
97- function alignNearest (
136+ let alignNearest = (
98137 scrollingEdgeStart : number ,
99138 scrollingEdgeEnd : number ,
100139 scrollingSize : number ,
@@ -103,7 +142,7 @@ function alignNearest(
103142 elementEdgeStart : number ,
104143 elementEdgeEnd : number ,
105144 elementSize : number
106- ) {
145+ ) => {
107146 /**
108147 * If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B
109148 *
@@ -227,8 +266,8 @@ function alignNearest(
227266 return 0
228267}
229268
230- function getParentElement ( element : Node ) : Element | null {
231- const parent = element . parentElement
269+ let getParentElement = ( element : Node ) : Element | null => {
270+ let parent = element . parentElement
232271 if ( parent == null ) {
233272 return ( element . getRootNode ( ) as ShadowRoot ) . host || null
234273 }
@@ -242,23 +281,23 @@ export default (target: Element, options: Options): ScrollAction[] => {
242281 return [ ]
243282 }
244283
245- const { scrollMode, block, inline, boundary, skipOverflowHiddenElements } =
284+ let { scrollMode, block, inline, boundary, skipOverflowHiddenElements } =
246285 options
247286 // Allow using a callback to check the boundary
248287 // The default behavior is to check if the current target matches the boundary element or not
249288 // If undefined it'll check that target is never undefined (can happen as we recurse up the tree)
250- const checkBoundary =
289+ let checkBoundary =
251290 typeof boundary === 'function' ? boundary : ( node : any ) => node !== boundary
252291
253292 if ( ! isElement ( target ) ) {
254293 throw new TypeError ( 'Invalid target' )
255294 }
256295
257296 // Used to handle the top most element that can be scrolled
258- const scrollingElement = document . scrollingElement || document . documentElement
297+ let scrollingElement = document . scrollingElement || document . documentElement
259298
260299 // Collect all the scrolling boxes, as defined in the spec: https://drafts.csswg.org/cssom-view/#scrolling-box
261- const frames : Element [ ] = [ ]
300+ let frames : Element [ ] = [ ]
262301 let cursor : Element | null = target
263302 while ( isElement ( cursor ) && checkBoundary ( cursor ) ) {
264303 // Move cursor to parent
@@ -291,14 +330,14 @@ export default (target: Element, options: Options): ScrollAction[] => {
291330 // and viewport dimensions on window.innerWidth/Height
292331 // https://www.quirksmode.org/mobile/viewports2.html
293332 // https://bokand.github.io/viewport/index.html
294- const viewportWidth = window . visualViewport ?. width ?? innerWidth
295- const viewportHeight = window . visualViewport ?. height ?? innerHeight
333+ let viewportWidth = window . visualViewport ?. width ?? innerWidth
334+ let viewportHeight = window . visualViewport ?. height ?? innerHeight
296335
297336 // Newer browsers supports scroll[X|Y], page[X|Y]Offset is
298- const viewportX = window . scrollX ?? pageXOffset
299- const viewportY = window . scrollY ?? pageYOffset
337+ let viewportX = window . scrollX ?? pageXOffset
338+ let viewportY = window . scrollY ?? pageYOffset
300339
301- const {
340+ let {
302341 height : targetHeight ,
303342 width : targetWidth ,
304343 top : targetTop ,
@@ -322,14 +361,14 @@ export default (target: Element, options: Options): ScrollAction[] => {
322361 : targetLeft // inline === 'start || inline === 'nearest
323362
324363 // Collect new scroll positions
325- const computations : ScrollAction [ ] = [ ]
364+ let computations : ScrollAction [ ] = [ ]
326365 // 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)
327366 for ( let index = 0 ; index < frames . length ; index ++ ) {
328- const frame = frames [ index ]
367+ let frame = frames [ index ]
329368
330369 // @TODO add a shouldScroll hook here that allows userland code to take control
331370
332- const { height, width, top, right, bottom, left } =
371+ let { height, width, top, right, bottom, left } =
333372 frame . getBoundingClientRect ( )
334373
335374 // If the element is already visible we can end it here
@@ -349,39 +388,39 @@ export default (target: Element, options: Options): ScrollAction[] => {
349388 return computations
350389 }
351390
352- const frameStyle = getComputedStyle ( frame )
353- const borderLeft = parseInt ( frameStyle . borderLeftWidth as string , 10 )
354- const borderTop = parseInt ( frameStyle . borderTopWidth as string , 10 )
355- const borderRight = parseInt ( frameStyle . borderRightWidth as string , 10 )
356- const borderBottom = parseInt ( frameStyle . borderBottomWidth as string , 10 )
391+ let frameStyle = getComputedStyle ( frame )
392+ let borderLeft = parseInt ( frameStyle . borderLeftWidth as string , 10 )
393+ let borderTop = parseInt ( frameStyle . borderTopWidth as string , 10 )
394+ let borderRight = parseInt ( frameStyle . borderRightWidth as string , 10 )
395+ let borderBottom = parseInt ( frameStyle . borderBottomWidth as string , 10 )
357396
358397 let blockScroll : number = 0
359398 let inlineScroll : number = 0
360399
361400 // The property existance checks for offfset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here
362401 // @TODO find out if the "as HTMLElement" overrides can be dropped
363- const scrollbarWidth =
402+ let scrollbarWidth =
364403 'offsetWidth' in frame
365404 ? ( frame as HTMLElement ) . offsetWidth -
366405 ( frame as HTMLElement ) . clientWidth -
367406 borderLeft -
368407 borderRight
369408 : 0
370- const scrollbarHeight =
409+ let scrollbarHeight =
371410 'offsetHeight' in frame
372411 ? ( frame as HTMLElement ) . offsetHeight -
373412 ( frame as HTMLElement ) . clientHeight -
374413 borderTop -
375414 borderBottom
376415 : 0
377416
378- const scaleX =
417+ let scaleX =
379418 'offsetWidth' in frame
380419 ? ( frame as HTMLElement ) . offsetWidth === 0
381420 ? 0
382421 : width / ( frame as HTMLElement ) . offsetWidth
383422 : 0
384- const scaleY =
423+ let scaleY =
385424 'offsetHeight' in frame
386425 ? ( frame as HTMLElement ) . offsetHeight === 0
387426 ? 0
@@ -478,7 +517,7 @@ export default (target: Element, options: Options): ScrollAction[] => {
478517 )
479518 }
480519
481- const { scrollLeft, scrollTop } = frame
520+ let { scrollLeft, scrollTop } = frame
482521 // Ensure scroll coordinates are not out of bounds while applying scroll offsets
483522 blockScroll = Math . max (
484523 0 ,
0 commit comments