Autogenerate read all section

This commit is contained in:
Aditya Thakral 2021-06-09 04:45:25 -04:00
parent 0762d640c3
commit 99acb0f470
10 changed files with 119 additions and 118 deletions

View File

@ -3,85 +3,79 @@ import styles from "./OrganizedContent.module.css";
export interface LinkProps { export interface LinkProps {
className?: string; className?: string;
url: string; id: string;
children: string | ReactNode | (string | ReactNode)[]; children: ReactNode;
} }
type Link = ComponentType<LinkProps>; type Link = ComponentType<LinkProps>;
interface Heading { interface Section {
id: string;
title: string; title: string;
url: string; Content: ComponentType;
content: ReactNode;
} }
const READ_ALL_TITLE = "Read All";
export const READ_ALL_ID = "read-all";
interface Props { interface Props {
headings: Heading[]; sections: Section[];
currentIndex: number; currentId: string;
link: Link;
children: ReactNode;
}
interface ChildProps {
headings: Heading[];
currentIndex: number;
link: Link; link: Link;
} }
export const OrganizedContent = ({ export function OrganizedContent(props: Props) {
headings, const sections = createSections(props.sections);
currentIndex, const currentIndex = sections.findIndex(({ id }) => id === props.currentId);
link: Link,
children,
}: Props) => {
const isReadAll = headings[currentIndex].title === "Read All";
const readAllContent = headings if (currentIndex < 0) {
.filter((heading: { title: string }) => heading.title !== "Read All") throw new Error(`Section with ID ${props.currentId} was not found`);
.map((heading) => ( }
<div key={heading.url}>
<h1>{heading.title}</h1>
{heading.content}
</div>
));
const childProps: ChildProps = { const section = sections[currentIndex];
headings: headings, const isReadAll = section.id === READ_ALL_ID;
currentIndex: currentIndex,
link: Link,
};
return ( return (
<div className={styles.organizedContent}> <div className={styles.organizedContent}>
<Nav {...childProps} /> <Nav sections={sections} currentIndex={currentIndex} link={props.link} />
<div> <div>
{isReadAll ? ( {isReadAll ? (
<>{readAllContent}</> <section.Content />
) : ( ) : (
<> <>
<h1>{headings[currentIndex].title}</h1> <h1>{section.title}</h1>
{children} <section.Content />
<Footer {...childProps} /> <Footer
sections={sections}
currentIndex={currentIndex}
link={props.link}
/>
</> </>
)} )}
</div> </div>
</div> </div>
); );
}; }
const Nav = ({ headings, currentIndex, link: Link }: ChildProps) => { interface NavProps {
sections: Section[];
currentIndex: number;
link: Link;
}
function Nav({ sections, currentIndex, link: Link }: NavProps) {
return ( return (
<div className={styles.nav}> <div className={styles.nav}>
{headings.map((heading, index) => ( {sections.map((section, index) => (
<div <div
className={index === currentIndex ? styles.selectedHeadingArea : ""} className={index === currentIndex ? styles.selectedHeadingArea : ""}
key={heading.url} key={section.id}
> >
<div <div
className={ className={
styles.navOption + styles.navOption +
" " + " " +
(heading.title === "Read All" ? styles.readAll : "") (section.title === "Read All" ? styles.readAll : "")
} }
> >
{index === currentIndex && ( {index === currentIndex && (
@ -93,9 +87,9 @@ const Nav = ({ headings, currentIndex, link: Link }: ChildProps) => {
" " + " " +
(index === currentIndex ? styles.navLinkSelected : "") (index === currentIndex ? styles.navLinkSelected : "")
} }
url={heading.url} id={section.id}
> >
{heading.title} {section.title}
</Link> </Link>
</div> </div>
<div className={styles.divider}></div> <div className={styles.divider}></div>
@ -103,62 +97,90 @@ const Nav = ({ headings, currentIndex, link: Link }: ChildProps) => {
))} ))}
</div> </div>
); );
}; }
const Footer = ({ headings, currentIndex, link: Link }: ChildProps) => { interface FooterProps {
const prevHeading = sections: Section[];
currentIndex > 0 && headings[currentIndex - 1].title !== "Read All" currentIndex: number;
? headings[currentIndex - 1] link: Link;
}
function Footer({ sections, currentIndex, link: Link }: FooterProps) {
const prevSection =
currentIndex > 0 && sections[currentIndex - 1].id !== READ_ALL_ID
? sections[currentIndex - 1]
: undefined; : undefined;
const nextHeading = const nextSection =
currentIndex < headings.length - 1 && currentIndex < sections.length - 1 &&
headings[currentIndex + 1].title !== "Read All" sections[currentIndex + 1].id !== READ_ALL_ID
? headings[currentIndex + 1] ? sections[currentIndex + 1]
: undefined; : undefined;
return ( return (
<div className={styles.footer}> <div className={styles.footer}>
{prevHeading && ( {prevSection && (
<Link url={prevHeading.url}> <Link id={prevSection.id}>
<div className={styles.footerSection}> <div className={styles.footerSection}>
<svg <Arrow direction="left" />
xmlns="http://www.w3.org/2000/svg"
width="14"
height="9"
viewBox="0 0 14 9"
className={styles.arrow + " " + styles.prevArrow}
>
<path d="M6.24407 8.12713C6.64284 8.58759 7.35716 8.58759 7.75593 8.12713L13.3613 1.65465C13.9221 1.00701 13.4621 0 12.6053 0H1.39467C0.537918 0 0.0778675 1.00701 0.638743 1.65465L6.24407 8.12713Z" />
</svg>
<div> <div>
<div className={styles.prevNext}>Previous</div> <div className={styles.prevNext}>Previous</div>
<div className={styles.arrowHeading}>{prevHeading.title}</div> <div className={styles.arrowHeading}>{prevSection.title}</div>
</div> </div>
</div> </div>
</Link> </Link>
)} )}
<div className={styles.footerDivider}></div> <div className={styles.footerDivider}></div>
{nextHeading && ( {nextSection && (
<Link url={nextHeading.url}> <Link id={nextSection.id}>
<div className={styles.footerSection}> <div className={styles.footerSection}>
<div> <div>
<div className={styles.prevNext + " " + styles.nextText}> <div className={styles.prevNext + " " + styles.nextText}>
Next Next
</div> </div>
<div className={styles.arrowHeading}>{nextHeading.title}</div> <div className={styles.arrowHeading}>{nextSection.title}</div>
</div> </div>
<svg <Arrow direction="right" />
xmlns="http://www.w3.org/2000/svg"
width="14"
height="9"
viewBox="0 0 14 9"
className={styles.arrow + " " + styles.nextArrow}
>
<path d="M6.24407 8.12713C6.64284 8.58759 7.35716 8.58759 7.75593 8.12713L13.3613 1.65465C13.9221 1.00701 13.4621 0 12.6053 0H1.39467C0.537918 0 0.0778675 1.00701 0.638743 1.65465L6.24407 8.12713Z" />
</svg>
</div> </div>
</Link> </Link>
)} )}
</div> </div>
); );
}; }
function createSections(sections: Section[]) {
return [
{
id: READ_ALL_ID,
title: READ_ALL_TITLE,
Content() {
return (
<>
{sections.map(({ id, title, Content: SectionContent }) => (
<div key={id}>
<h1>{title}</h1>
<SectionContent />
</div>
))}
</>
);
},
},
...sections,
];
}
function Arrow({ direction }: { direction: "left" | "right" }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="9"
viewBox="0 0 14 9"
className={`${styles.arrow} ${
direction === "left" ? styles.prevArrow : styles.nextArrow
}`}
>
<path d="M6.24407 8.12713C6.64284 8.58759 7.35716 8.58759 7.75593 8.12713L13.3613 1.65465C13.9221 1.00701 13.4621 0 12.6053 0H1.39467C0.537918 0 0.0778675 1.00701 0.638743 1.65465L6.24407 8.12713Z" />
</svg>
);
}

View File

@ -11,9 +11,6 @@ import AltTab, {
metadata as altTabEventMetadata, metadata as altTabEventMetadata,
} from "../content/playground/alt-tab.event.mdx"; } from "../content/playground/alt-tab.event.mdx";
import ReadAll, {
metadata as readAllOrganizedContentMetadata,
} from "../content/playground/constitution/read-all.organized-content.mdx";
import Name, { import Name, {
metadata as nameOrganizedContentMetadata, metadata as nameOrganizedContentMetadata,
} from "../content/playground/constitution/name.organized-content.mdx"; } from "../content/playground/constitution/name.organized-content.mdx";
@ -56,14 +53,13 @@ const events = [
]; ];
const constitution = [ const constitution = [
{ content: <ReadAll />, ...readAllOrganizedContentMetadata }, { Content: Name, ...nameOrganizedContentMetadata },
{ content: <Name />, ...nameOrganizedContentMetadata }, { Content: Purpose, ...purposeOrganizedContentMetadata },
{ content: <Purpose />, ...purposeOrganizedContentMetadata }, { Content: Membership, ...membershipOrganizedContentMetadata },
{ content: <Membership />, ...membershipOrganizedContentMetadata }, { Content: Officers, ...officersOrganizedContentMetadata },
{ content: <Officers />, ...officersOrganizedContentMetadata }, { Content: Duties, ...dutiesOrganizedContentMetadata },
{ content: <Duties />, ...dutiesOrganizedContentMetadata },
{ {
content: <ExecutiveCouncil />, Content: ExecutiveCouncil,
...executiveCouncilOrganizedContentMetadata, ...executiveCouncilOrganizedContentMetadata,
}, },
]; ];
@ -165,28 +161,17 @@ export function TeamMemberCardDemo() {
export function OrganizedContentDemo() { export function OrganizedContentDemo() {
const sections = constitution; const sections = constitution;
const [index, setIndex] = useState(0); const [id, setId] = useState(sections[0].id);
function FakeLink({ className, url, children }: LinkProps) { function FakeLink({ className, id, children }: LinkProps) {
return ( return (
<div <div className={className} onClick={() => setId(id)}>
className={className}
onClick={() => {
const target = sections.findIndex((section) => section.url === url);
if (target >= 0) {
setIndex(target);
}
}}
>
{children} {children}
</div> </div>
); );
} }
return ( return (
<OrganizedContent headings={sections} currentIndex={index} link={FakeLink}> <OrganizedContent sections={sections} currentId={id} link={FakeLink} />
{sections[index].content}
</OrganizedContent>
); );
} }

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "5. Duties of Officers", title: "5. Duties of Officers",
url: "5" id: "duties-of-officers"
}; };
The duties of the President shall be: The duties of the President shall be:

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "6. Executive Council", title: "6. Executive Council",
url: "6" id: "executive-council"
}; };
The Executive Council shall consist of the present officers of the Club and the Faculty Advisor (as a non-voting member) and has the power to run the affairs of this club within the limits of this constitution. This includes the power to overrule or issue directions to any officer. The Executive Council shall consist of the present officers of the Club and the Faculty Advisor (as a non-voting member) and has the power to run the affairs of this club within the limits of this constitution. This includes the power to overrule or issue directions to any officer.

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "3. Membership", title: "3. Membership",
url: "3" id: "membership"
}; };
In compliance with MathSoc regulations and in recognition of the club being primarily targeted at undergraduate students, full membership is open to all Social Members of the Mathematics Society and restricted to the same. In compliance with MathSoc regulations and in recognition of the club being primarily targeted at undergraduate students, full membership is open to all Social Members of the Mathematics Society and restricted to the same.

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "1. Name", title: "1. Name",
url: "1" id: "name"
}; };
The name of this organization shall be the "Computer Science Club of the University of Waterloo". The name of this organization shall be the "Computer Science Club of the University of Waterloo".

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "4. Officers", title: "4. Officers",
url: "4" id: "officers"
}; };
The officers of the Club shall be: The officers of the Club shall be:

View File

@ -1,6 +1,6 @@
export const metadata = { export const metadata = {
title: "2. Purpose", title: "2. Purpose",
url: "2" id: "purpose"
}; };
The Club is organized and will be operated exclusively for educational and scientific purposes in furtherance of: The Club is organized and will be operated exclusively for educational and scientific purposes in furtherance of:

View File

@ -1,6 +0,0 @@
export const metadata = {
title: "Read All",
url: "0"
};
You can leave readall blank, it is auto generated.

2
next-env.d.ts vendored
View File

@ -54,7 +54,7 @@ declare module "*.organized-content.mdx" {
interface OrganizedContentMetadata { interface OrganizedContentMetadata {
title: string; title: string;
url: string; id: string;
} }
const ReactComponent: ComponentType; const ReactComponent: ComponentType;