Skip to content

Commit

Permalink
update side nav
Browse files Browse the repository at this point in the history
  • Loading branch information
Gkrumbach07 committed Jul 11, 2024
1 parent 926ab74 commit 53b6fd4
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 86 deletions.
2 changes: 1 addition & 1 deletion src/components/shared/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const Layout = ({
className={!!sidebar ? "pf-u-h-100vh" : undefined}
isManagedSidebar
sidebar={
sidebar ? <PageSidebar nav={sidebar} theme="light" /> : undefined
sidebar ? <PageSidebar style={{minWidth: "400px"}} nav={sidebar} theme="light" /> : undefined
}
header={
<Navbar
Expand Down
298 changes: 213 additions & 85 deletions src/components/shared/SideNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,222 @@
import {
Nav,
NavExpandable,
NavItem,
NavList,
} from "@patternfly/react-core";
import { Link } from "gatsby";
import { Nav, NavExpandable, NavItem, NavList } from "@patternfly/react-core";
import { css } from "@patternfly/react-styles";
import navStyles from "@patternfly/react-styles/css/components/Nav/nav";

import { Link, navigate } from "gatsby";
import * as React from "react";
import { AsciiDocSection, NestedAsciiDocSection, SideNavItemConfig } from "../../types";
import {
AsciiDocSection,
NestedAsciiDocSection,
SideNavItemConfig,
} from "../../types";

type SideNavigationProps = {
config: SideNavItemConfig[]
location: Location
toc: { [key: string]: AsciiDocSection[] }
}


export const SideNavigation = ({ config, location, toc = {} }: SideNavigationProps) => {
function nestSections(sections: AsciiDocSection[]): NestedAsciiDocSection[] {
const nestedSections: NestedAsciiDocSection[] = [];

const sectionMap: Record<string, NestedAsciiDocSection> = {};
// create a map of sections using their `id` as key
sections.forEach((section) => {
sectionMap[section.id] = { ...section, sections: [] };
});

// assign child sections to their parent
sections.forEach((section) => {
const parentId = section.parentId;
if (parentId && sectionMap[parentId]) {
sectionMap[parentId].sections.push(sectionMap[section.id]);
} else {
nestedSections.push(sectionMap[section.id]);
}
});

return nestedSections;
}
config: SideNavItemConfig[];
location: Location;
toc: { [key: string]: AsciiDocSection[] };
};

const renderSection = (section: NestedAsciiDocSection, slug: string) => {
if (section.sections.length > 0) {
return (
<NavExpandable key={section.id} title={section.name} isExpanded isActive={location.hash.includes(section.id)}>
{section.sections.map(section => renderSection(section, slug))}
</NavExpandable>
)
}
else {
return (
<NavItem key={section.id} isActive={location.hash.includes(section.id)}>
<Link to={`${slug}#${section.id}`}>
{section.name}
</Link>
</NavItem>
)
}
const NavSection = ({
section,
slug,
location,
toc,
}: {
section: NestedAsciiDocSection;
slug: string;
location: Location;
toc: {
[key: string]: AsciiDocSection[];
};
}) => {
function isParentOfCurrentSection(
currentSection: AsciiDocSection,
parentSection: AsciiDocSection,
page: AsciiDocSection[]
): boolean {
if (currentSection.id === parentSection.id) {
return true;
}
if (!currentSection.parentId) {
return false;
}
const currentSectionsParent = page.find(
(s) => s.id === currentSection.parentId
);
if (!currentSectionsParent) {
return false;
}

return isParentOfCurrentSection(currentSectionsParent, parentSection, page);
}

const renderNavItem = (item: SideNavItemConfig) => {
if (toc[item.slug]) {
return (
<NavExpandable key={item.slug + item.title} title={item.title} isExpanded={location.pathname.includes(item.slug)} isActive={location.pathname.includes(item.slug)}>
{nestSections(toc[item.slug]).map(section => renderSection(section, item.slug))}
</NavExpandable>
)
}
else if (item.children) {
return (
<NavExpandable key={item.slug + item.title} title={item.title} isExpanded={location.pathname.includes(item.slug)} isActive={location.pathname.includes(item.slug)}>
{item.children.map(i => renderNavItem(i))}
</NavExpandable>
)
}
else {
return (
<NavItem key={item.slug + item.title} isActive={location.pathname.includes(item.slug)}>
<Link to={item.slug}>
{item.title}
</Link>
</NavItem>
)
}
function isChildSectionActive(
slug: string,
section: AsciiDocSection
): boolean {
if (location.pathname.includes(slug)) {
const page = toc[slug];
const currentSection = page.find((s) => location.hash.includes(s.id));
if (!currentSection) {
return false;
}
return isParentOfCurrentSection(currentSection, section, page);
}

return false;
}

const [isExpanded, setIsExpanded] = React.useState(true);

const isActive =
location.hash.includes(section.id) || isChildSectionActive(slug, section);
if (section.sections.length > 0) {
return (
<NavExpandable
key={section.id}
title={section.name}
isActive={isActive}
isExpanded={isExpanded}
onExpand={() => {
navigate(`${slug}#${section.id}`);
setIsExpanded(!isExpanded);
}}
buttonProps={{
className: css(
navStyles.navLink,
isActive && navStyles.modifiers.current
),
}}
>
{section.sections.map((section) => (
<NavSection
key={section.id}
section={section}
slug={slug}
location={location}
toc={toc}
/>
))}
</NavExpandable>
);
} else {
return (
<Nav theme="light">
<NavList>
{config.map(item => renderNavItem(item))}
</NavList>
</Nav>
)

}
<NavItem key={section.id} isActive={isActive}>
<Link to={`${slug}#${section.id}`}>{section.name}</Link>
</NavItem>
);
}
};

const CustomNavExpandable = ({
item,
toc,
location,
}: {
item: SideNavItemConfig;
toc: { [key: string]: AsciiDocSection[] };
location: Location;
}) => {
const isActive = location.pathname.includes(item.slug);
const [isExpanded, setIsExpanded] = React.useState(isActive);

function nestSections(sections: AsciiDocSection[]): NestedAsciiDocSection[] {
const nestedSections: NestedAsciiDocSection[] = [];

const sectionMap: Record<string, NestedAsciiDocSection> = {};
// create a map of sections using their `id` as key
sections.forEach((section) => {
sectionMap[section.id] = { ...section, sections: [] };
});

// assign child sections to their parent
sections.forEach((section) => {
const parentId = section.parentId;
if (parentId && sectionMap[parentId]) {
sectionMap[parentId].sections.push(sectionMap[section.id]);
} else {
nestedSections.push(sectionMap[section.id]);
}
});

return nestedSections;
}

return (
<NavExpandable
key={item.slug + item.title}
title={item.title}
isExpanded={isExpanded}
isActive={isActive}
onExpand={() => {
navigate(item.slug);
setIsExpanded(!isExpanded);
}}
buttonProps={{
className: css(
navStyles.navLink,
isActive && navStyles.modifiers.current
),
}}
>
{nestSections(toc[item.slug]).map((section) => (
<NavSection
key={section.id}
section={section}
slug={item.slug}
location={location}
toc={toc}
/>
))}
</NavExpandable>
);
};

export const SideNavigation = ({
config,
location,
toc = {},
}: SideNavigationProps) => {
const renderNavItem = (item: SideNavItemConfig) => {
const isActive = location.pathname.includes(item.slug);
if (toc[item.slug]) {
return (
<CustomNavExpandable
key={item.slug + item.title}
item={item}
toc={toc}
location={location}
/>
);
} else if (item.children) {
return (
<NavExpandable
key={item.slug + item.title}
title={item.title}
isExpanded={isActive}
isActive={isActive}
buttonProps={{
className: css(
navStyles.navLink,
isActive && navStyles.modifiers.current
),
}}
>
{item.children.map((i) => renderNavItem(i))}
</NavExpandable>
);
} else {
return (
<NavItem key={item.slug + item.title} isActive={isActive}>
<Link to={item.slug}>{item.title}</Link>
</NavItem>
);
}
};

return (
<Nav theme="light">
<NavList>{config.map((item) => renderNavItem(item))}</NavList>
</Nav>
);
};

0 comments on commit 53b6fd4

Please sign in to comment.