Skip to content

Commit

Permalink
feat(scroll): allow passing behavior option
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `scrollBehavior` doesn't accept an object with `x` and `y`
coordinates anymore. Instead it accepts an object like
[`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions)
with `left` and `top` properties. You can now also pass the
[`behavior`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior)
property to enable smooth scrolling in most browsers.
  • Loading branch information
posva committed May 27, 2020
1 parent f42ce4b commit 12e9209
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 32 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Check the [playground](https://github.com/vuejs/vue-router-next/tree/master/play
// resolve the request
})
```
- The object returned in `scrollBehavior` is now similar to [`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions): `x` is renamed to `left` and `y` is renamed to `top`.

### Typings

Expand Down
4 changes: 2 additions & 2 deletions e2e/scroll-behavior/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const scrollBehavior: ScrollBehavior = async function (

// specify offset of the element
if (to.hash === '#anchor2') {
position.offset = { y: 100 }
position.offset = { top: 100 }
}

if (document.querySelector(position.selector)) {
Expand All @@ -56,7 +56,7 @@ const scrollBehavior: ScrollBehavior = async function (
if (to.matched.some(m => m.meta.scrollToTop)) {
// coords will be used if no selector is provided,
// or if the selector didn't match any element.
return { x: 0, y: 0 }
return { left: 0, top: 0 }
}

return false
Expand Down
2 changes: 1 addition & 1 deletion playground/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const router = createRouter({
} else {
// TODO: check if parent in common that works with alias
if (to.matched.every((record, i) => from.matched[i] !== record))
return { x: 0, y: 0 }
return { left: 0, top: 0 }
}
// leave scroll as it is by not returning anything
// https://github.com/Microsoft/TypeScript/issues/18319
Expand Down
4 changes: 2 additions & 2 deletions src/history/html5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from './common'
import {
computeScrollPosition,
ScrollPositionCoordinates,
_ScrollPositionNormalized,
} from '../scrollBehavior'
import { warn } from '../warning'
import { stripBase } from '../location'
Expand All @@ -27,7 +27,7 @@ interface StateEntry extends HistoryState {
forward: HistoryLocationNormalized | null
position: number
replaced: boolean
scroll: Required<ScrollPositionCoordinates> | null | false
scroll: _ScrollPositionNormalized | null | false
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import {
} from './types'
import { RouterHistory, HistoryState } from './history/common'
import {
ScrollPositionCoordinates,
ScrollPosition,
getSavedScrollPosition,
getScrollKey,
saveScrollPosition,
computeScrollPosition,
scrollToPosition,
_ScrollPositionNormalized,
} from './scrollBehavior'
import { createRouterMatcher, PathParserOptions } from './matcher'
import {
Expand Down Expand Up @@ -59,7 +59,7 @@ export interface ScrollBehavior {
(
to: RouteLocationNormalized,
from: RouteLocationNormalizedLoaded,
savedPosition: Required<ScrollPositionCoordinates> | null
savedPosition: _ScrollPositionNormalized | null
): Awaitable<ScrollPosition | false | void>
}

Expand Down Expand Up @@ -773,7 +773,7 @@ export function createRouter(options: RouterOptions): Router {
): Promise<any> {
if (!isBrowser || !scrollBehavior) return Promise.resolve()

let scrollPosition: Required<ScrollPositionCoordinates> | null =
let scrollPosition: _ScrollPositionNormalized | null =
(!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
((isFirstNavigation || !isPush) &&
(history.state as HistoryState) &&
Expand Down
66 changes: 42 additions & 24 deletions src/scrollBehavior.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
import { RouteLocationNormalized, RouteLocationNormalizedLoaded } from './types'
import {
RouteLocationNormalized,
RouteLocationNormalizedLoaded,
_RouteLocationBase,
} from './types'
import { warn } from './warning'

// we use types instead of interfaces to make it work with HistoryStateValue type

/**
* Scroll position similar to
* {@link https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions | `ScrollToOptions`}.
* Note that not all browsers support `behavior`.
*/
export type ScrollPositionCoordinates = {
/**
* x position. 0 if not provided
*/
x?: number
/**
* y position. 0 if not provided
*/
y?: number
behavior?: ScrollOptions['behavior']
left?: number
top?: number
}

/**
* Internal normalized version of {@link ScrollPositionCoordinates} that always
* has `left` and `top` coordinates.
*
* @internal
*/
export type _ScrollPositionNormalized = {
behavior?: ScrollOptions['behavior']
left: number
top: number
}

export interface ScrollPositionElement {
Expand Down Expand Up @@ -47,24 +65,25 @@ export interface ScrollBehaviorHandler<T> {
function getElementPosition(
el: Element,
offset: ScrollPositionCoordinates
): Required<ScrollPositionCoordinates> {
): _ScrollPositionNormalized {
const docRect = document.documentElement.getBoundingClientRect()
const elRect = el.getBoundingClientRect()

return {
x: elRect.left - docRect.left - (offset.x || 0),
y: elRect.top - docRect.top - (offset.y || 0),
behavior: offset.behavior,
left: elRect.left - docRect.left - (offset.left || 0),
top: elRect.top - docRect.top - (offset.top || 0),
}
}

export const computeScrollPosition = () =>
({
x: window.pageXOffset,
y: window.pageYOffset,
} as Required<ScrollPositionCoordinates>)
left: window.pageXOffset,
top: window.pageYOffset,
} as _ScrollPositionNormalized)

export function scrollToPosition(position: ScrollPosition): void {
let normalizedPosition: ScrollPositionCoordinates
let scrollToOptions: ScrollPositionCoordinates

if ('selector' in position) {
/**
Expand Down Expand Up @@ -105,27 +124,26 @@ export function scrollToPosition(position: ScrollPosition): void {
warn(`Couldn't find element with selector "${position.selector}"`)
return
}
normalizedPosition = getElementPosition(el, position.offset || {})
scrollToOptions = getElementPosition(el, position.offset || {})
} else {
normalizedPosition = position
scrollToOptions = position
}

window.scrollTo(normalizedPosition.x || 0, normalizedPosition.y || 0)
if ('scrollBehavior' in document.documentElement.style)
window.scrollTo(scrollToOptions)
else window.scrollTo(scrollToOptions.left || 0, scrollToOptions.top || 0)
}

export function getScrollKey(path: string, delta: number): string {
const position: number = history.state ? history.state.position - delta : -1
return position + path
}

export const scrollPositions = new Map<
string,
Required<ScrollPositionCoordinates>
>()
export const scrollPositions = new Map<string, _ScrollPositionNormalized>()

export function saveScrollPosition(
key: string,
scrollPosition: Required<ScrollPositionCoordinates>
scrollPosition: _ScrollPositionNormalized
) {
scrollPositions.set(key, scrollPosition)
}
Expand Down

0 comments on commit 12e9209

Please sign in to comment.