feat: allow user to set TOC display preference (#6943)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user