Skip to content

Commit

Permalink
Lots more ToC tweaks notably including hiding it on small screens
Browse files Browse the repository at this point in the history
Also reformatting bits of related code for readability and tweaking
positioning & when each part is considered 'active'.
  • Loading branch information
pimterry committed Nov 21, 2024
1 parent f0cc730 commit fa044df
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 41 deletions.
5 changes: 4 additions & 1 deletion src/components/layout/documentation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ export async function DocumentationLayout({ title, children, links }: Component<
{children}
</StyledDocumentationLayoutContentWrapper>
<StyledDocumentationLayoutNavigationWrapper>
{links?.length ? <NavigationSidebarLinks title="On this page" links={links} /> : null}
{links?.length
? <NavigationSidebarLinks title="On this page" links={links} />
: null
}
</StyledDocumentationLayoutNavigationWrapper>
</StyledDocumentationLayoutWrapper>
</StyledDocumentationGlobalWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export const StyledNavigationSidebarLinksWrapper = styled.div`
position: sticky;
top: 10px;
align-self: self-start;
@media (max-width: ${({ theme }) => theme.screens['xl']}) {
display: none;
}
`;

export const StyledNavigationSidebarLinksTitle = styled(Heading)``;
Expand Down
56 changes: 31 additions & 25 deletions src/components/modules/table-content/hooks/use-active-toc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,53 @@ const getHeadings = () =>

function useActiveToc() {
useEffect(() => {
const setCurrent: IntersectionObserverCallback = entries => {
const isBigScreen = matchMedia(`(min-width: ${screens.xl})`);

const updateActiveHeading = (
container: HTMLElement,
headingElem: HTMLElement
) => {
// Remove active class from all toc items
container.querySelectorAll('a')
.forEach(e => e.classList.remove('active'));

// Add active to the correct toc
headingElem.classList.add('active');

if (isBigScreen.matches) {
const elementTop = headingElem.offsetTop;
const containerHeight = container.clientHeight;
const scrollOffset = elementTop - (containerHeight / 2);

container.scroll({
top: scrollOffset,
behavior: 'smooth'
});
}
}

const updateFromIntersection: IntersectionObserverCallback = entries => {
const scrollContainer = document
.querySelector('#table-of-content-headings')
?.parentElement;
if (!scrollContainer) return;

for (const entry of entries) {
const { id } = entry.target as HTMLElement;
const isMobile = matchMedia(`(max-width: ${screens.lg})`);

const tocHeadingEl = scrollContainer.querySelector<HTMLElement>(`a[href="#${id}"]`);
if (!tocHeadingEl) return;

if (entry.isIntersecting) {
// Remove active class from all toc items
scrollContainer.querySelectorAll('a')
.forEach(e => e.classList.remove('active'));

// Add active to the correct toc
tocHeadingEl.classList.add('active');

if (!isMobile.matches) {
const elementTop = tocHeadingEl.offsetTop;
const containerHeight = scrollContainer.clientHeight;
const scrollOffset = elementTop - (containerHeight / 2);

scrollContainer.scroll({
top: scrollOffset,
behavior: 'smooth'
});
}

if (tocHeadingEl && entry.isIntersecting) {
updateActiveHeading(scrollContainer, tocHeadingEl);
}
}
};

const observerOptions: IntersectionObserverInit = {
rootMargin: '0px 0px -66%',
rootMargin: '0px 0px -50%',
threshold: 1,
};

const headingObserver = new IntersectionObserver(setCurrent, observerOptions);
const headingObserver = new IntersectionObserver(updateFromIntersection, observerOptions);

getHeadings().forEach(heading => headingObserver.observe(heading));

Expand Down
21 changes: 15 additions & 6 deletions src/components/modules/table-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,25 @@ export const TableContent = ({ isCollapsible, links }: TableContentProps) => {
/>
));

if (!isCollapsible) return <StyledTableContentWrapper data-match-scroll>{content}</StyledTableContentWrapper>;
if (!isCollapsible) {
return <StyledTableContentWrapper data-match-scroll>{content}</StyledTableContentWrapper>;
}

const defaultOpenItem = links.find(item => {
return item.subItems?.some(subItem => getPathWithoutHash(subItem.href) === currentPath);
});
const defaultOpenItem = links.find(item =>
item.subItems?.some(subItem =>
getPathWithoutHash(subItem.href) === currentPath
)
);

return (
<StyledTableContentWrapper>
<Accordion.Root asChild type="single" defaultValue={defaultOpenItem?.text || links[0].text} collapsible>
<>{content}</>
<Accordion.Root
asChild
type="single"
defaultValue={defaultOpenItem?.text || links[0].text}
collapsible
>
<>{ content }</>
</Accordion.Root>
</StyledTableContentWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,25 @@ export const StyledContentWithTableContentWrapper = styled.div`
padding: 64px 0 32px 0;
max-width: initial;
gap: 45px;
grid-template-columns: 1fr ${({ theme }) => theme.screens.content} 1fr;
grid-template-columns: ${({ theme }) => theme.screens.content};
}
@media (min-width: ${({ theme }) => theme.screens.xl}) {
grid-template-columns: 1fr ${({ theme }) => theme.screens.content} 1fr;
}
`;

export const StyledContentWithTableTableWrapper = styled.aside`
@media (min-width: ${({ theme }) => theme.screens.lg}) {
position: sticky;
top: 10px;
}
display: none;
@media (min-width: ${({ theme }) => theme.screens['2xl']}) {
@media (min-width: ${({ theme }) => theme.screens.xl}) {
display: flex;
justify-content: center;
align-self: flex-start;
position: sticky;
top: 16px;
& > div {
max-width: 296px;
min-width: 296px;
Expand Down
10 changes: 7 additions & 3 deletions src/components/sections/content-with-table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ export const ContentWithTable = ({
<Container>
<StyledContentWithTableContentWrapper>
<StyledContentWithTableTableWrapper>
{Array.isArray(links) && links?.length === 1 && links[0]?.subItems?.length === 0 ? null : (
<TableContent isCollapsible={false} links={links} />
)}
{
Array.isArray(links) &&
links?.length === 1 &&
links[0]?.subItems?.length === 0
? null
: <TableContent isCollapsible={false} links={links} />
}
</StyledContentWithTableTableWrapper>

<StyledContentRichText>
Expand Down

0 comments on commit fa044df

Please sign in to comment.