Skip to content

Commit

Permalink
Fixed website scroll perf issue (#3698)
Browse files Browse the repository at this point in the history
* Added dead link analysis

* Fixed scroll perf issues

* Fixed a link
  • Loading branch information
rstaib authored May 17, 2021
1 parent 59908cd commit 32a48c5
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 131 deletions.
1 change: 1 addition & 0 deletions website/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = {
backgroundColor: "transparent",
},
},
"gatsby-remark-check-links",
],
},
},
Expand Down
4 changes: 3 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"gatsby-plugin-styled-components": "^3.10.0",
"gatsby-plugin-ts": "^2.2.3",
"gatsby-plugin-web-font-loader": "^1.0.4",
"gatsby-remark-check-links": "^2.1.0",
"gatsby-remark-images": "^3.11.0",
"gatsby-remark-mermaid": "^2.1.0",
"gatsby-remark-reading-time": "^1.1.0",
Expand All @@ -61,6 +62,7 @@
"react-redux": "^7.2.1",
"react-responsive-carousel": "^3.2.11",
"react-share": "^4.2.1",
"rxjs": "^7.0.1",
"styled-components": "^5.2.3"
},
"devDependencies": {
Expand All @@ -83,7 +85,7 @@
"husky": "^4.3.8",
"prettier": "^2.2.1",
"pretty-quick": "^3.0.2",
"typescript": "^4.1.3",
"typescript": "^4.2.4",
"typescript-styled-plugin": "^0.15.0"
},
"husky": {
Expand Down
142 changes: 87 additions & 55 deletions website/src/components/articles/article-sections.tsx
Original file line number Diff line number Diff line change
@@ -1,114 +1,147 @@
import { graphql, Link } from "gatsby";
import React, {
Fragment,
FunctionComponent,
useCallback,
useEffect,
useState,
} from "react";
import { useDispatch, useSelector } from "react-redux";
import React, { FunctionComponent, useCallback, useEffect } from "react";
import { useDispatch } from "react-redux";
import { Subscription } from "rxjs";
import styled from "styled-components";
import { ArticleSectionsFragment } from "../../../graphql-types";
import { State } from "../../state";
import { useObservable } from "../../state";
import { closeAside } from "../../state/common";
import { MostProminentSection } from "../doc-page/doc-page-elements";

interface ArticleSectionsProperties {
data: ArticleSectionsFragment;
readonly data: ArticleSectionsFragment;
}

export const ArticleSections: FunctionComponent<ArticleSectionsProperties> = ({
data,
}) => {
const dispatch = useDispatch();
const [activeHeadingId, setActiveHeadingId] = useState<string>();
const [headings, setHeadings] = useState<Heading[]>([]);
const yScrollPosition = useSelector<State, number>(
const yScrollPosition$ = useObservable(
(state) => state.common.yScrollPosition
);

const handleCloseClick = useCallback(() => {
dispatch(closeAside());
}, []);
}, [dispatch]);

useEffect(() => {
const result = ((data.tableOfContents.items as TableOfContentItem[]) ?? [])
const headings = (
(data.tableOfContents.items as TableOfContentItem[]) ?? []
)
.flatMap((item) => [item, ...(item.items ?? [])])
.map((item) => ({
.map<Heading>((item) => ({
id: item.url,
title: item.title,
position:
(document.getElementById(item.url.substring(1))?.offsetTop ?? 80) -
80,
}))
.reverse();

setHeadings(result);
let currentActiveId: string | undefined;
let currentActiveClass: string = "";
let timeoutHandler: number | undefined;
let subscription: Subscription | undefined;

if (headings.length > 0) {
subscription = yScrollPosition$.subscribe((yScrollPosition) => {
let newActiveId: string | undefined;
let title: string | undefined;

for (let i = 0; i < headings.length; i++) {
if (yScrollPosition >= headings[i].position) {
newActiveId = headings[i].id;
title = headings[i].title;
break;
}
}

if (currentActiveId !== newActiveId) {
if (currentActiveId) {
document.getElementById(
harmonizeId(currentActiveId)
)!.className = currentActiveClass;
}

currentActiveId = newActiveId;
clearTimeout(timeoutHandler);

if (currentActiveId) {
const element = document.getElementById(
harmonizeId(currentActiveId)
)!;

currentActiveClass = element.className;
element.className = currentActiveClass + " active";
timeoutHandler = window.setTimeout(() => {
window.history.pushState(
undefined,
title ?? "ChilliCream Docs", // todo: default heading should be the doc title
`./${currentActiveId ?? ""}`
);
}, 250);
} else {
timeoutHandler = window.setTimeout(() => {
window.history.pushState(
undefined,
"ChilliCream Docs", // todo: default heading should be the doc title
"./"
);
}, 250);
}
}
});
}

return () => {
subscription?.unsubscribe();
};
}, [data]);

useEffect(() => {
const activeHeading = headings.find((id) => yScrollPosition >= id.position)
?.id;
window.history.pushState(
undefined,
activeHeading ?? "ChilliCream Docs",
"./" + (activeHeading ?? "")
);
setActiveHeadingId(activeHeading);
}, [headings, yScrollPosition]);

const tocItems: TableOfContentItem[] = data.tableOfContents.items ?? [];

return tocItems.length > 0 ? (
<Container>
<Title>In this article</Title>
<MostProminentSection>
<div onClick={handleCloseClick}>
<TableOfContent items={tocItems} activeHeadingId={activeHeadingId} />
<TableOfContent items={tocItems} />
</div>
</MostProminentSection>
</Container>
) : null;
};

interface Heading {
id: string;
position: number;
readonly id: string;
readonly title: string;
readonly position: number;
}

interface TableOfContentProps {
items: TableOfContentItem[];
activeHeadingId: string | undefined;
readonly items: TableOfContentItem[];
}

const TableOfContent: FunctionComponent<TableOfContentProps> = ({
items,
activeHeadingId,
}) => {
const TableOfContent: FunctionComponent<TableOfContentProps> = ({ items }) => {
return (
<TocItemContainer>
{items.map((item) => (
<Fragment key={item.url}>
<TocListItem
className={activeHeadingId === item.url ? "active" : undefined}
>
<TocLink to={item.url}>{item.title}</TocLink>
</TocListItem>
{item.items && (
<TableOfContent
items={item.items ?? []}
activeHeadingId={activeHeadingId}
/>
)}
</Fragment>
<TocListItem key={item.url} id={harmonizeId(item.url)}>
<TocLink to={item.url}>{item.title}</TocLink>
{item.items && <TableOfContent items={item.items ?? []} />}
</TocListItem>
))}
</TocItemContainer>
);
};

function harmonizeId(id: string): string {
return id.replace("#", "link-");
}

interface TableOfContentItem {
title: string;
url: string;
items?: TableOfContentItem[];
readonly title: string;
readonly url: string;
readonly items?: TableOfContentItem[];
}

export const ArticleSectionsGraphQLFragment = graphql`
Expand All @@ -131,7 +164,6 @@ const Title = styled.h6`
`;

const TocItemContainer = styled.ul`
position: absolute;
display: block;
margin: 0;
padding: 0 25px 10px;
Expand Down
59 changes: 41 additions & 18 deletions website/src/components/doc-page/doc-page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import React, { FunctionComponent, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import React, {
FunctionComponent,
useCallback,
useEffect,
useRef,
} from "react";
import { useDispatch } from "react-redux";
import styled from "styled-components";
import { DocPageFragment } from "../../../graphql-types";
import ListAltIconSvg from "../../images/list-alt.svg";
Expand All @@ -13,7 +18,7 @@ import {
IsSmallDesktop,
IsTablet,
} from "../../shared-style";
import { State } from "../../state";
import { useObservable } from "../../state";
import { toggleAside, toggleTOC } from "../../state/common";
import { Article } from "../articles/article";
import { ArticleComments } from "../articles/article-comments";
Expand All @@ -33,8 +38,8 @@ import { DocPageLegacy } from "./doc-page-legacy";
import { DocPageNavigation, Navigation } from "./doc-page-navigation";

interface DocPageProperties {
data: DocPageFragment;
originPath: string;
readonly data: DocPageFragment;
readonly originPath: string;
}

export const DocPage: FunctionComponent<DocPageProperties> = ({
Expand All @@ -50,9 +55,10 @@ export const DocPage: FunctionComponent<DocPageProperties> = ({
const selectedProduct = result![1]! || "";
const selectedVersion = (result && result[2]) || "";
const title = frontmatter!.title!;
const responsiveMenuRef = useRef<HTMLDivElement>(null);

const hasScrolled = useSelector<State, boolean>((state) => {
return state.common.yScrollPosition > 10;
const hasScrolled$ = useObservable((state) => {
return state.common.yScrollPosition > 20;
});

const handleToggleTOC = useCallback(() => {
Expand All @@ -63,6 +69,21 @@ export const DocPage: FunctionComponent<DocPageProperties> = ({
dispatch(toggleAside());
}, []);

useEffect(() => {
const classes = responsiveMenuRef.current?.className ?? "";

const subscription = hasScrolled$.subscribe((hasScrolled) => {
if (responsiveMenuRef.current) {
responsiveMenuRef.current.className =
classes + (hasScrolled ? " scrolled" : "");
}
});

return () => {
subscription.unsubscribe();
};
}, [hasScrolled$]);

return (
<Container>
<DocPageNavigation
Expand All @@ -77,7 +98,7 @@ export const DocPage: FunctionComponent<DocPageProperties> = ({
{false && <DocPageLegacy />}
<ArticleHeader kind="doc">
<ResponsiveMenuWrapper>
<ResponsiveMenu hasScrolled={hasScrolled}>
<ResponsiveMenu ref={responsiveMenuRef}>
<Button onClick={handleToggleTOC} className="toc-toggle">
<ListAltIconSvg /> Table of contents
</Button>
Expand Down Expand Up @@ -196,27 +217,29 @@ const Container = styled.div`
}
`;

const ResponsiveMenu = styled.div<{ readonly hasScrolled: boolean }>`
const ResponsiveMenu = styled.div`
position: fixed;
transition: all 100ms linear 0s;
${({ hasScrolled }) => (hasScrolled ? "top: 60px;" : "top: 80px;")}
box-sizing: border-box;
z-index: 3;
display: flex;
z-index: 3;
box-sizing: border-box;
flex-direction: row;
align-items: center;
top: 80px;
margin: 0 auto;
width: 820px;
height: 60px;
padding: 0 20px;
border-radius: 4px 4px 0 0;
background: linear-gradient(
180deg,
#ffffff 30%,
rgba(255, 255, 255, 0.75) 100%
);
transition: all 100ms linear 0s;
width: 820px;
height: 60px;
margin-left: auto;
margin-right: auto;
padding: 0 20px;
&.scrolled {
top: 60px;
}
${IsPhablet(`
left: 0;
Expand Down
Loading

0 comments on commit 32a48c5

Please sign in to comment.