mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-06 00:52:42 +00:00
This commit is contained in:
parent
66b52d7286
commit
92b806dc0e
|
@ -5,6 +5,12 @@ export type ShouldUpdateScrollFn = (
|
||||||
locationContext: MastodonLocation,
|
locationContext: MastodonLocation,
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScrollBehavior will automatically scroll to the top on navigations
|
||||||
|
* or restore saved scroll positions, but on some location changes we
|
||||||
|
* need to prevent this.
|
||||||
|
*/
|
||||||
|
|
||||||
export const defaultShouldUpdateScroll: ShouldUpdateScrollFn = (
|
export const defaultShouldUpdateScroll: ShouldUpdateScrollFn = (
|
||||||
prevLocation,
|
prevLocation,
|
||||||
location,
|
location,
|
||||||
|
|
|
@ -5,15 +5,18 @@ import type { ShouldUpdateScrollFn } from './default_should_update_scroll';
|
||||||
import { ScrollBehaviorContext } from './scroll_context';
|
import { ScrollBehaviorContext } from './scroll_context';
|
||||||
|
|
||||||
interface ScrollContainerProps {
|
interface ScrollContainerProps {
|
||||||
|
/**
|
||||||
|
* This key must be static for the element & not change
|
||||||
|
* while the component is mounted.
|
||||||
|
*/
|
||||||
scrollKey: string;
|
scrollKey: string;
|
||||||
shouldUpdateScroll?: ShouldUpdateScrollFn;
|
shouldUpdateScroll?: ShouldUpdateScrollFn;
|
||||||
children: React.ReactElement;
|
children: React.ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ScrollContainer` is used to automatically scroll to the top of the page
|
* `ScrollContainer` is used to manage the scroll position of elements on the page
|
||||||
* when pushing a new history state, and remembering the scroll position
|
* that can be scrolled independently of the page body.
|
||||||
* when going back.
|
|
||||||
* This component is a port of the unmaintained https://github.com/ytase/react-router-scroll/
|
* This component is a port of the unmaintained https://github.com/ytase/react-router-scroll/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -26,6 +29,9 @@ export const ScrollContainer: React.FC<ScrollContainerProps> = ({
|
||||||
|
|
||||||
const containerRef = useRef<HTMLElement>();
|
const containerRef = useRef<HTMLElement>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register/unregister scrollable element with ScrollBehavior
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!scrollBehaviorContext || !containerRef.current) {
|
if (!scrollBehaviorContext || !containerRef.current) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -41,6 +41,14 @@ interface ScrollContextProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A top-level wrapper that provides the app with an instance of the
|
||||||
|
* ScrollBehavior object. scroll-behavior is a library for managing the
|
||||||
|
* scroll position of a single-page app in the same way the browser would
|
||||||
|
* normally do for a multi-page app. This means it'll scroll back to top
|
||||||
|
* when navigating to a new page, and will restore the scroll position
|
||||||
|
* when navigating e.g. using `history.back`.
|
||||||
|
* The library keeps a record of scroll positions in session storage.
|
||||||
|
*
|
||||||
* This component is a port of the unmaintained https://github.com/ytase/react-router-scroll/
|
* This component is a port of the unmaintained https://github.com/ytase/react-router-scroll/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -51,16 +59,24 @@ export const ScrollContext: React.FC<ScrollContextProps> = ({
|
||||||
const location = useLocation<LocationState>();
|
const location = useLocation<LocationState>();
|
||||||
const history = useHistory<LocationState>();
|
const history = useHistory<LocationState>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep the current location in a mutable ref so that ScrollBehavior's
|
||||||
|
* `getCurrentLocation` can access it without having to recreate the
|
||||||
|
* whole ScrollBehavior object
|
||||||
|
*/
|
||||||
const currentLocationRef = useRef(location);
|
const currentLocationRef = useRef(location);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
currentLocationRef.current = location;
|
currentLocationRef.current = location;
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise ScrollBehavior object once – using state rather
|
||||||
|
* than a ref to simplify the types and ensure it's defined immediately.
|
||||||
|
*/
|
||||||
const [scrollBehavior] = useState(
|
const [scrollBehavior] = useState(
|
||||||
(): ScrollBehaviorInstance =>
|
(): ScrollBehaviorInstance =>
|
||||||
new ScrollBehavior({
|
new ScrollBehavior({
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
addNavigationListener: history.listen.bind(history),
|
||||||
addNavigationListener: history.listen,
|
|
||||||
stateStorage: new SessionStorage(),
|
stateStorage: new SessionStorage(),
|
||||||
getCurrentLocation: () =>
|
getCurrentLocation: () =>
|
||||||
currentLocationRef.current as unknown as LocationBase,
|
currentLocationRef.current as unknown as LocationBase,
|
||||||
|
@ -77,18 +93,28 @@ export const ScrollContext: React.FC<ScrollContextProps> = ({
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle scroll update when location changes
|
/**
|
||||||
|
* Handle scroll update when location changes
|
||||||
|
*/
|
||||||
const prevLocation = usePrevious(location) ?? null;
|
const prevLocation = usePrevious(location) ?? null;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollBehavior.updateScroll(prevLocation, location);
|
scrollBehavior.updateScroll(prevLocation, location);
|
||||||
}, [location, prevLocation, scrollBehavior]);
|
}, [location, prevLocation, scrollBehavior]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop Scrollbehavior on unmount
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
scrollBehavior.stop();
|
scrollBehavior.stop();
|
||||||
};
|
};
|
||||||
}, [scrollBehavior]);
|
}, [scrollBehavior]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the app with a way to register separately scrollable
|
||||||
|
* elements to also be tracked by ScrollBehavior. (By default
|
||||||
|
* ScrollBehavior only handles scrolling on the main document body.)
|
||||||
|
*/
|
||||||
const contextValue = useMemo<ScrollBehaviorContextType>(
|
const contextValue = useMemo<ScrollBehaviorContextType>(
|
||||||
() => ({
|
() => ({
|
||||||
registerElement: (key, element, shouldUpdateScroll) => {
|
registerElement: (key, element, shouldUpdateScroll) => {
|
||||||
|
|
|
@ -8,6 +8,10 @@ interface LocationBaseWithKey extends LocationBase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module is part of our port of https://github.com/ytase/react-router-scroll/
|
* This module is part of our port of https://github.com/ytase/react-router-scroll/
|
||||||
|
* and handles storing scroll positions in SessionStorage.
|
||||||
|
* Stored positions (`[x, y]`) are keyed by the location key and an optional
|
||||||
|
* `scrollKey` that's used for to track separately scrollable elements other
|
||||||
|
* than the document body.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class SessionStorage {
|
export class SessionStorage {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user