Autogenerate read all section
This commit is contained in:
parent
0762d640c3
commit
99acb0f470
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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".
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
export const metadata = {
|
|
||||||
title: "Read All",
|
|
||||||
url: "0"
|
|
||||||
};
|
|
||||||
|
|
||||||
You can leave readall blank, it is auto generated.
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue