feat: allow user to set TOC display preference (#6943)

Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
Hemachandar
2024-06-16 21:51:08 +05:30
committed by GitHub
parent 3d0160463c
commit 05c1bee412
12 changed files with 222 additions and 150 deletions

View File

@@ -3,15 +3,14 @@ import * as React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { s } from "@shared/styles";
import { EditorStyleHelper } from "@shared/editor/styles/EditorStyleHelper";
import { depths, s } from "@shared/styles";
import Text from "~/components/Text";
import useWindowScrollPosition from "~/hooks/useWindowScrollPosition";
const HEADING_OFFSET = 20;
type Props = {
/** Whether the document is rendering full width or not. */
isFullWidth: boolean;
/** The headings to render in the contents. */
headings: {
title: string;
@@ -20,9 +19,9 @@ type Props = {
}[];
};
export default function Contents({ headings, isFullWidth }: Props) {
export default function Contents({ headings }: Props) {
const [activeSlug, setActiveSlug] = React.useState<string>();
const position = useWindowScrollPosition({
const scrollPosition = useWindowScrollPosition({
throttle: 100,
});
@@ -43,7 +42,7 @@ export default function Contents({ headings, isFullWidth }: Props) {
}
}
}
}, [position, headings]);
}, [scrollPosition, headings]);
// calculate the minimum heading level and adjust all the headings to make
// that the top-most. This prevents the contents from being weirdly indented
@@ -56,70 +55,53 @@ export default function Contents({ headings, isFullWidth }: Props) {
const { t } = useTranslation();
return (
<Wrapper isFullWidth={isFullWidth}>
<Sticky>
<Heading>{t("Contents")}</Heading>
{headings.length ? (
<List>
{headings
.filter((heading) => heading.level < 4)
.map((heading) => (
<ListItem
key={heading.id}
level={heading.level - headingAdjustment}
active={activeSlug === heading.id}
>
<Link href={`#${heading.id}`}>{heading.title}</Link>
</ListItem>
))}
</List>
) : (
<Empty>
{t("Headings you add to the document will appear here")}
</Empty>
)}
</Sticky>
</Wrapper>
<StickyWrapper>
<Heading>{t("Contents")}</Heading>
{headings.length ? (
<List>
{headings
.filter((heading) => heading.level < 4)
.map((heading) => (
<ListItem
key={heading.id}
level={heading.level - headingAdjustment}
active={activeSlug === heading.id}
>
<Link href={`#${heading.id}`}>{heading.title}</Link>
</ListItem>
))}
</List>
) : (
<Empty>{t("Headings you add to the document will appear here")}</Empty>
)}
</StickyWrapper>
);
}
const Wrapper = styled.div<{ isFullWidth: boolean }>`
width: 256px;
const StickyWrapper = styled.div`
display: none;
${breakpoint("tablet")`
display: block;
`};
${(props) =>
!props.isFullWidth &&
breakpoint("desktopLarge")`
transform: translateX(-256px);
width: 0;
`}
`;
const Sticky = styled.div`
position: sticky;
top: 80px;
max-height: calc(100vh - 80px);
top: 90px;
max-height: calc(100vh - 90px);
width: ${EditorStyleHelper.tocWidth}px;
padding: 0 16px;
overflow-y: auto;
border-radius: 8px;
background: ${s("background")};
transition: ${s("backgroundTransition")};
margin-top: calc(50px + 6vh);
margin-right: 52px;
min-width: 204px;
width: 228px;
min-height: 40px;
overflow-y: auto;
padding: 0 16px;
border-radius: 8px;
@supports (backdrop-filter: blur(20px)) {
backdrop-filter: blur(20px);
background: ${(props) => transparentize(0.2, props.theme.background)};
}
${breakpoint("tablet")`
display: block;
z-index: ${depths.toc};
`};
`;
const Heading = styled.h3`
@@ -131,15 +113,12 @@ const Heading = styled.h3`
`;
const Empty = styled(Text)`
margin: 1em 0 4em;
padding-right: 2em;
font-size: 14px;
`;
const ListItem = styled.li<{ level: number; active?: boolean }>`
margin-left: ${(props) => (props.level - 1) * 10}px;
margin-bottom: 8px;
padding-right: 2em;
line-height: 1.3;
word-break: break-word;