Skip to content

Commit 0246e86

Browse files
committed
fix: improve typings, reduce bundlesize
1 parent d31fec0 commit 0246e86

File tree

1 file changed

+76
-37
lines changed

1 file changed

+76
-37
lines changed

src/index.ts

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,54 @@ export type ScrollMode = 'always' | 'if-needed'
1414

1515
/** @public */
1616
export 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

Comments
 (0)