fix: Retain scroll position when navigating through document history
closes #4087
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
CompositeStateReturn,
|
||||
} from "reakit/Composite";
|
||||
|
||||
type Props = {
|
||||
type Props = React.HTMLAttributes<HTMLDivElement> & {
|
||||
children: (composite: CompositeStateReturn) => React.ReactNode;
|
||||
onEscape?: (ev: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LocationDescriptor } from "history";
|
||||
import { observer } from "mobx-react";
|
||||
import {
|
||||
TrashIcon,
|
||||
@@ -39,7 +40,7 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => {
|
||||
userName: event.actor.name,
|
||||
};
|
||||
const isRevision = event.name === "revisions.create";
|
||||
let meta, icon, to;
|
||||
let meta, icon, to: LocationDescriptor | undefined;
|
||||
|
||||
const ref = React.useRef<HTMLAnchorElement>(null);
|
||||
// the time component tends to steal focus when clicked
|
||||
@@ -58,13 +59,19 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => {
|
||||
case "revisions.create":
|
||||
icon = <EditIcon color="currentColor" size={16} />;
|
||||
meta = t("{{userName}} edited", opts);
|
||||
to = documentHistoryUrl(document, event.modelId || "");
|
||||
to = {
|
||||
pathname: documentHistoryUrl(document, event.modelId || ""),
|
||||
state: { retainScrollPosition: true },
|
||||
};
|
||||
break;
|
||||
|
||||
case "documents.live_editing":
|
||||
icon = <LightningIcon color="currentColor" size={16} />;
|
||||
meta = t("Live editing");
|
||||
to = documentHistoryUrl(document);
|
||||
meta = t("Latest");
|
||||
to = {
|
||||
pathname: documentHistoryUrl(document),
|
||||
state: { retainScrollPosition: true },
|
||||
};
|
||||
break;
|
||||
|
||||
case "documents.archive":
|
||||
@@ -108,7 +115,10 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isActive = location.pathname === to;
|
||||
const isActive =
|
||||
typeof to === "string"
|
||||
? location.pathname === to
|
||||
: location.pathname === to?.pathname;
|
||||
|
||||
if (document.isDeleted) {
|
||||
to = undefined;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LocationDescriptor } from "history";
|
||||
import * as React from "react";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import Flex from "~/components/Flex";
|
||||
@@ -5,7 +6,7 @@ import NavLink from "~/components/NavLink";
|
||||
|
||||
export type Props = Omit<React.HTMLAttributes<HTMLAnchorElement>, "title"> & {
|
||||
image?: React.ReactNode;
|
||||
to?: string;
|
||||
to?: LocationDescriptor;
|
||||
exact?: boolean;
|
||||
title: React.ReactNode;
|
||||
subtitle?: React.ReactNode;
|
||||
@@ -72,7 +73,7 @@ const ListItem = (
|
||||
const Wrapper = styled.a<{
|
||||
$small?: boolean;
|
||||
$border?: boolean;
|
||||
to?: string;
|
||||
to?: LocationDescriptor;
|
||||
}>`
|
||||
display: flex;
|
||||
padding: ${(props) => (props.$border === false ? 0 : "8px 0")};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LocationDescriptor } from "history";
|
||||
import * as React from "react";
|
||||
import { match, NavLink, Route } from "react-router-dom";
|
||||
|
||||
@@ -12,7 +13,7 @@ type Props = React.ComponentProps<typeof NavLink> & {
|
||||
) => React.ReactNode;
|
||||
exact?: boolean;
|
||||
activeStyle?: React.CSSProperties;
|
||||
to: string;
|
||||
to: LocationDescriptor;
|
||||
};
|
||||
|
||||
function NavLinkWithChildrenFunc(
|
||||
@@ -20,7 +21,7 @@ function NavLinkWithChildrenFunc(
|
||||
ref?: React.Ref<HTMLAnchorElement>
|
||||
) {
|
||||
return (
|
||||
<Route path={to} exact={exact}>
|
||||
<Route path={typeof to === "string" ? to : to?.pathname} exact={exact}>
|
||||
{({ match, location }) => (
|
||||
<NavLink {...rest} to={to} exact={exact} ref={ref}>
|
||||
{children
|
||||
|
||||
@@ -8,11 +8,14 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function ScrollToTop({ children }: Props) {
|
||||
const location = useLocation();
|
||||
const location = useLocation<{ retainScrollPosition?: boolean }>();
|
||||
const previousLocationPathname = usePrevious(location.pathname);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (location.pathname === previousLocationPathname) {
|
||||
if (
|
||||
location.pathname === previousLocationPathname ||
|
||||
location.state?.retainScrollPosition
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// exception for when entering or exiting document edit, scroll position should not reset
|
||||
@@ -23,7 +26,11 @@ export default function ScrollToTop({ children }: Props) {
|
||||
return;
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
}, [location.pathname, previousLocationPathname]);
|
||||
}, [
|
||||
location.pathname,
|
||||
previousLocationPathname,
|
||||
location.state?.retainScrollPosition,
|
||||
]);
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user