Skip to content

Commit b0763b8

Browse files
committed
experimenting with ways to reduce the size
1 parent 60c5fdd commit b0763b8

File tree

1 file changed

+146
-171
lines changed

1 file changed

+146
-171
lines changed

src/index.ts

Lines changed: 146 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ export interface CustomScrollAction {
3131
export type ScrollMode = 'always' | 'if-needed'
3232

3333
// Refactor to just the callback variant
34-
export type CustomScrollBoundaryCallback = (parent: Element) => boolean
35-
export type CustomScrollBoundary = Element | CustomScrollBoundaryCallback
34+
export type CustomScrollBoundary = (parent: Element) => boolean
3635

3736
export interface Options {
3837
block?: ScrollLogicalPosition
@@ -46,7 +45,7 @@ export interface Options {
4645
let viewport: HTMLElement
4746

4847
// return the current viewport depending on wether quirks mode is active or not
49-
function getViewport () {
48+
function getViewport() {
5049
const doc = document
5150

5251
if (!viewport) {
@@ -242,15 +241,6 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
242241
boundary,
243242
skipOverflowHiddenElements,
244243
} = options
245-
// Allow using a callback to check the boundary
246-
// The default behavior is to check if the current target matches the boundary element or not
247-
// If undefined it'll check that target is never undefined (can happen as we recurse up the tree)
248-
const checkBoundary =
249-
typeof boundary === 'function' ? boundary : (node: any) => node !== boundary
250-
251-
if (!isElement(target)) {
252-
throw new Error('Element is required in scrollIntoView')
253-
}
254244

255245
const targetRect = target.getBoundingClientRect()
256246

@@ -260,7 +250,10 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
260250
// @TODO have a better shadowdom test here
261251
while (
262252
isElement((parent = target.parentNode || target.host)) &&
263-
checkBoundary(target)
253+
// Allow using a callback to check the boundary
254+
boundary
255+
? boundary(target)
256+
: true
264257
) {
265258
if (isScrollable(parent, skipOverflowHiddenElements)) {
266259
frames.push(parent)
@@ -282,34 +275,6 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
282275
const viewportX = window.scrollX || window.pageXOffset
283276
const viewportY = window.scrollY || window.pageYOffset
284277

285-
// If the element is already visible we can end it here
286-
if (scrollMode === 'if-needed') {
287-
// @TODO optimize, as getBoundingClientRect is also called from computations loop
288-
const isVisible = frames.every(frame => {
289-
const frameRect = frame.getBoundingClientRect()
290-
291-
if (targetRect.top < frameRect.top) {
292-
return false
293-
}
294-
if (targetRect.bottom > frameRect.bottom) {
295-
return false
296-
}
297-
298-
if (frame === viewport) {
299-
if (targetRect.bottom > viewportHeight || targetRect.top < 0) {
300-
return false
301-
}
302-
if (targetRect.left > viewportWidth || targetRect.right < 0) {
303-
return false
304-
}
305-
}
306-
return true
307-
})
308-
if (isVisible) {
309-
return []
310-
}
311-
}
312-
313278
// @TODO remove duplicate results
314279
// These values mutate as we loop through and generate scroll coordinates
315280
let targetBlock: number =
@@ -327,139 +292,149 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
327292
? targetRect.left + targetRect.width / 2
328293
: inline === 'end'
329294
? targetRect.right
330-
: targetRect.left // inline === 'nearest
295+
: targetRect.left // inline === 'nearest && inline === 'start
331296

332297
// Collect new scroll positions
333-
const computations = frames.map(
334-
(frame): CustomScrollAction => {
335-
const frameRect = frame.getBoundingClientRect()
336-
337-
const frameStyle = getComputedStyle(frame)
338-
const borderLeft = parseInt(frameStyle.borderLeftWidth as string, 10)
339-
const borderTop = parseInt(frameStyle.borderTopWidth as string, 10)
340-
const borderRight = parseInt(frameStyle.borderRightWidth as string, 10)
341-
const borderBottom = parseInt(frameStyle.borderBottomWidth as string, 10)
342-
// The property existance checks for offfset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here
343-
// @TODO find out if the "as HTMLElement" overrides can be dropped
344-
const scrollbarWidth =
345-
'offsetWidth' in frame
346-
? (frame as HTMLElement).offsetWidth -
347-
(frame as HTMLElement).clientWidth -
348-
borderLeft -
349-
borderRight
350-
: 0
351-
const scrollbarHeight =
352-
'offsetHeight' in frame
353-
? (frame as HTMLElement).offsetHeight -
354-
(frame as HTMLElement).clientHeight -
355-
borderTop -
356-
borderBottom
357-
: 0
358-
359-
let blockScroll: number = 0
360-
let inlineScroll: number = 0
361-
362-
if (block === 'start') {
363-
blockScroll =
364-
viewport === frame
365-
? viewportY + targetBlock
366-
: targetBlock - frameRect.top - borderTop
367-
} else if (block === 'end') {
368-
blockScroll =
369-
viewport === frame
370-
? viewportY + (targetBlock - viewportHeight)
371-
: frame.scrollTop -
372-
(frameRect.bottom - targetBlock) +
373-
borderBottom +
374-
scrollbarHeight
375-
} else if (block === 'nearest') {
376-
blockScroll =
377-
viewport === frame
378-
? viewportY +
379-
alignNearest(
380-
viewportY,
381-
viewportY + viewportHeight,
382-
viewportHeight,
383-
borderTop,
384-
borderBottom,
385-
viewportY + targetBlock,
386-
viewportY + targetBlock + targetRect.height,
387-
targetRect.height
388-
)
389-
: frame.scrollTop +
390-
alignNearest(
391-
frameRect.top,
392-
frameRect.bottom,
393-
frameRect.height,
394-
borderTop,
395-
borderBottom + scrollbarHeight,
396-
targetBlock,
397-
targetBlock + targetRect.height,
398-
targetRect.height
399-
)
400-
} else {
401-
// block === 'center' is the default
402-
blockScroll =
403-
viewport === frame
404-
? viewportY + targetBlock - viewportHeight / 2
405-
: frame.scrollTop -
406-
(frameRect.top + frameRect.height / 2 - targetBlock)
407-
}
408-
409-
if (inline === 'start') {
410-
inlineScroll =
411-
viewport === frame
412-
? viewportX + targetInline
413-
: frame.scrollLeft + (targetInline - frameRect.left) - borderLeft
414-
} else if (inline === 'center') {
415-
inlineScroll =
416-
viewport === frame
417-
? viewportX + targetInline - viewportWidth / 2
418-
: frame.scrollLeft -
419-
(frameRect.left + frameRect.width / 2 - targetInline)
420-
} else if (inline === 'end') {
421-
inlineScroll =
422-
viewport === frame
423-
? viewportX + (targetInline - viewportWidth)
424-
: frame.scrollLeft -
425-
(frameRect.right - targetInline) +
426-
borderRight +
427-
scrollbarWidth
428-
} else {
429-
// inline === 'nearest' is the default
430-
inlineScroll =
431-
viewport === frame
432-
? viewportX +
433-
alignNearest(
434-
viewportX,
435-
viewportX + viewportWidth,
436-
viewportWidth,
437-
borderLeft,
438-
borderRight,
439-
viewportX + targetInline,
440-
viewportX + targetInline + targetRect.width,
441-
targetRect.width
442-
)
443-
: frame.scrollLeft +
444-
alignNearest(
445-
frameRect.left,
446-
frameRect.right,
447-
frameRect.width,
448-
borderLeft,
449-
borderRight + scrollbarWidth,
450-
targetInline,
451-
targetInline + targetRect.width,
452-
targetRect.width
453-
)
454-
}
455-
456-
// Cache the offset so that parent frames can scroll this into view correctly
457-
targetBlock += frame.scrollTop - blockScroll
458-
targetInline += frame.scrollLeft - inlineScroll
459-
460-
return { el: frame, top: blockScroll, left: inlineScroll }
298+
const computations = frames.reduce<CustomScrollAction[]>((results, frame) => {
299+
const frameRect = frame.getBoundingClientRect()
300+
301+
// Handle scrollMode: 'if-needed'
302+
// If the element is already visible we can end it here
303+
if (
304+
scrollMode === 'if-needed' && frame === viewport
305+
? targetRect.bottom > viewportHeight ||
306+
targetRect.top < 0 ||
307+
(targetRect.left > viewportWidth || targetRect.right < 0)
308+
: targetRect.top < frameRect.top || targetRect.bottom > frameRect.bottom
309+
) {
310+
return []
461311
}
462-
)
312+
313+
const frameStyle = getComputedStyle(frame)
314+
const borderLeft = parseInt(frameStyle.borderLeftWidth as string, 10)
315+
const borderTop = parseInt(frameStyle.borderTopWidth as string, 10)
316+
const borderRight = parseInt(frameStyle.borderRightWidth as string, 10)
317+
const borderBottom = parseInt(frameStyle.borderBottomWidth as string, 10)
318+
// The property existance checks for offfset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here
319+
// @TODO find out if the "as HTMLElement" overrides can be dropped
320+
const scrollbarWidth =
321+
'offsetWidth' in frame
322+
? (frame as HTMLElement).offsetWidth -
323+
(frame as HTMLElement).clientWidth -
324+
borderLeft -
325+
borderRight
326+
: 0
327+
const scrollbarHeight =
328+
'offsetHeight' in frame
329+
? (frame as HTMLElement).offsetHeight -
330+
(frame as HTMLElement).clientHeight -
331+
borderTop -
332+
borderBottom
333+
: 0
334+
335+
let blockScroll: number = 0
336+
let inlineScroll: number = 0
337+
338+
if (block === 'start') {
339+
blockScroll =
340+
viewport === frame
341+
? viewportY + targetBlock
342+
: targetBlock - frameRect.top - borderTop
343+
} else if (block === 'end') {
344+
blockScroll =
345+
viewport === frame
346+
? viewportY + (targetBlock - viewportHeight)
347+
: frame.scrollTop -
348+
(frameRect.bottom - targetBlock) +
349+
borderBottom +
350+
scrollbarHeight
351+
} else if (block === 'nearest') {
352+
blockScroll =
353+
viewport === frame
354+
? viewportY +
355+
alignNearest(
356+
viewportY,
357+
viewportY + viewportHeight,
358+
viewportHeight,
359+
borderTop,
360+
borderBottom,
361+
viewportY + targetBlock,
362+
viewportY + targetBlock + targetRect.height,
363+
targetRect.height
364+
)
365+
: frame.scrollTop +
366+
alignNearest(
367+
frameRect.top,
368+
frameRect.bottom,
369+
frameRect.height,
370+
borderTop,
371+
borderBottom + scrollbarHeight,
372+
targetBlock,
373+
targetBlock + targetRect.height,
374+
targetRect.height
375+
)
376+
} else {
377+
// block === 'center' is the default
378+
blockScroll =
379+
viewport === frame
380+
? viewportY + targetBlock - viewportHeight / 2
381+
: frame.scrollTop -
382+
(frameRect.top + frameRect.height / 2 - targetBlock)
383+
}
384+
385+
if (inline === 'start') {
386+
inlineScroll =
387+
viewport === frame
388+
? viewportX + targetInline
389+
: frame.scrollLeft + (targetInline - frameRect.left) - borderLeft
390+
} else if (inline === 'center') {
391+
inlineScroll =
392+
viewport === frame
393+
? viewportX + targetInline - viewportWidth / 2
394+
: frame.scrollLeft -
395+
(frameRect.left + frameRect.width / 2 - targetInline)
396+
} else if (inline === 'end') {
397+
inlineScroll =
398+
viewport === frame
399+
? viewportX + (targetInline - viewportWidth)
400+
: frame.scrollLeft -
401+
(frameRect.right - targetInline) +
402+
borderRight +
403+
scrollbarWidth
404+
} else {
405+
// inline === 'nearest' is the default
406+
inlineScroll =
407+
viewport === frame
408+
? viewportX +
409+
alignNearest(
410+
viewportX,
411+
viewportX + viewportWidth,
412+
viewportWidth,
413+
borderLeft,
414+
borderRight,
415+
viewportX + targetInline,
416+
viewportX + targetInline + targetRect.width,
417+
targetRect.width
418+
)
419+
: frame.scrollLeft +
420+
alignNearest(
421+
frameRect.left,
422+
frameRect.right,
423+
frameRect.width,
424+
borderLeft,
425+
borderRight + scrollbarWidth,
426+
targetInline,
427+
targetInline + targetRect.width,
428+
targetRect.width
429+
)
430+
}
431+
432+
// Cache the offset so that parent frames can scroll this into view correctly
433+
targetBlock += frame.scrollTop - blockScroll
434+
targetInline += frame.scrollLeft - inlineScroll
435+
436+
return [...results, { el: frame, top: blockScroll, left: inlineScroll }]
437+
}, [])
463438

464439
return computations
465440
}

0 commit comments

Comments
 (0)