@@ -31,8 +31,7 @@ export interface CustomScrollAction {
31
31
export type ScrollMode = 'always' | 'if-needed'
32
32
33
33
// 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
36
35
37
36
export interface Options {
38
37
block ?: ScrollLogicalPosition
@@ -46,7 +45,7 @@ export interface Options {
46
45
let viewport : HTMLElement
47
46
48
47
// return the current viewport depending on wether quirks mode is active or not
49
- function getViewport ( ) {
48
+ function getViewport ( ) {
50
49
const doc = document
51
50
52
51
if ( ! viewport ) {
@@ -242,15 +241,6 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
242
241
boundary,
243
242
skipOverflowHiddenElements,
244
243
} = 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
- }
254
244
255
245
const targetRect = target . getBoundingClientRect ( )
256
246
@@ -260,7 +250,10 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
260
250
// @TODO have a better shadowdom test here
261
251
while (
262
252
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
264
257
) {
265
258
if ( isScrollable ( parent , skipOverflowHiddenElements ) ) {
266
259
frames . push ( parent )
@@ -282,34 +275,6 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
282
275
const viewportX = window . scrollX || window . pageXOffset
283
276
const viewportY = window . scrollY || window . pageYOffset
284
277
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
-
313
278
// @TODO remove duplicate results
314
279
// These values mutate as we loop through and generate scroll coordinates
315
280
let targetBlock : number =
@@ -327,139 +292,149 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
327
292
? targetRect . left + targetRect . width / 2
328
293
: inline === 'end'
329
294
? targetRect . right
330
- : targetRect . left // inline === 'nearest
295
+ : targetRect . left // inline === 'nearest && inline === 'start
331
296
332
297
// 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 [ ]
461
311
}
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
+ } , [ ] )
463
438
464
439
return computations
465
440
}
0 commit comments