Content pages now display the table of content

This commit is contained in:
DrMint 2022-03-12 13:27:21 +01:00
parent 6d3a9c84b3
commit b213447c49
7 changed files with 100 additions and 27 deletions

View File

@ -2,10 +2,11 @@ import {
GetContentQuery, GetContentQuery,
GetWebsiteInterfaceQuery, GetWebsiteInterfaceQuery,
} from "graphql/operations-types"; } from "graphql/operations-types";
import { prettySlug } from "queries/helpers"; import { prettyinlineTitle, prettySlug, slugify } from "queries/helpers";
import Button from "components/Button"; import Button from "components/Button";
import Img, { ImageQuality } from "components/Img"; import Img, { ImageQuality } from "components/Img";
import InsetBox from "components/InsetBox"; import InsetBox from "components/InsetBox";
import Chip from "components/Chip";
export type ThumbnailHeaderProps = { export type ThumbnailHeaderProps = {
content: { content: {
@ -39,7 +40,18 @@ export default function ThumbnailHeader(
<div className="w-full aspect-[4/3] bg-light rounded-xl"></div> <div className="w-full aspect-[4/3] bg-light rounded-xl"></div>
)} )}
</div> </div>
<div className="grid place-items-center text-center"> <div
id={slugify(
content.titles.length > 0
? prettyinlineTitle(
content.titles[0].pre_title,
content.titles[0].title,
content.titles[0].subtitle
)
: prettySlug(content.slug)
)}
className="grid place-items-center text-center"
>
{content.titles.length > 0 ? ( {content.titles.length > 0 ? (
<> <>
<p className="text-2xl">{content.titles[0].pre_title}</p> <p className="text-2xl">{content.titles[0].pre_title}</p>
@ -54,22 +66,26 @@ export default function ThumbnailHeader(
<div className="grid grid-flow-col gap-8"> <div className="grid grid-flow-col gap-8">
{content.type && ( {content.type && (
<div className="grid place-items-center place-content-start gap-2"> <div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{langui.type}</h3> <h3 className="text-xl">{langui.type}</h3>
<Button> <div className="flex flex-row flex-wrap">
{content.type.data.attributes.titles.length > 0 <Chip>
? content.type.data.attributes.titles[0].title {content.type.data.attributes.titles.length > 0
: prettySlug(content.type.data.attributes.slug)} ? content.type.data.attributes.titles[0].title
</Button> : prettySlug(content.type.data.attributes.slug)}
</Chip>
</div>
</div> </div>
)} )}
{content.categories.data.length > 0 && ( {content.categories.data.length > 0 && (
<div className="grid place-items-center place-content-start gap-2"> <div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{langui.categories}</h3> <h3 className="text-xl">{langui.categories}</h3>
{content.categories.data.map((category) => ( <div className="flex flex-row flex-wrap place-content-center gap-2">
<Button key={category.id}>{category.attributes.name}</Button> {content.categories.data.map((category) => (
))} <Chip key={category.id}>{category.attributes.name}</Chip>
))}
</div>
</div> </div>
)} )}
</div> </div>

View File

@ -117,6 +117,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
{/*
<NavOption <NavOption
url="/wiki" url="/wiki"
icon="travel_explore" icon="travel_explore"
@ -136,6 +138,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
reduced={appLayout.mainPanelReduced && isDesktop} reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
*/}
<HorizontalLine /> <HorizontalLine />
@ -147,7 +151,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
reduced={appLayout.mainPanelReduced && isDesktop} reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
{/*
<NavOption <NavOption
url="/merch" url="/merch"
icon="store" icon="store"
@ -156,6 +160,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
reduced={appLayout.mainPanelReduced && isDesktop} reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
*/}
<NavOption <NavOption
url="/gallery" url="/gallery"
@ -166,6 +172,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
{/*
<NavOption <NavOption
url="/archives" url="/archives"
icon="inventory" icon="inventory"
@ -175,6 +183,9 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
*/}
<NavOption <NavOption
url="/about-us" url="/about-us"
icon="info" icon="info"

View File

@ -26,6 +26,7 @@ import Chip from "components/Chip";
import ReactTooltip from "react-tooltip"; import ReactTooltip from "react-tooltip";
import RecorderChip from "components/RecorderChip"; import RecorderChip from "components/RecorderChip";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import TOC from "components/Markdown/TOC";
interface ContentReadProps extends AppStaticProps { interface ContentReadProps extends AppStaticProps {
content: GetContentTextQuery["contents"]["data"][number]["attributes"]; content: GetContentTextQuery["contents"]["data"][number]["attributes"];
@ -141,6 +142,20 @@ export default function ContentRead(props: ContentReadProps): JSX.Element {
)} )}
</div> </div>
)} )}
<HorizontalLine />
<TOC
text={content.text_set[0].text}
title={
content.titles.length > 0
? prettyinlineTitle(
content.titles[0].pre_title,
content.titles[0].title,
content.titles[0].subtitle
)
: prettySlug(content.slug)
}
/>
</SubPanel> </SubPanel>
); );
const contentPanel = ( const contentPanel = (

View File

@ -3,7 +3,10 @@ import SubPanel from "components/Panels/SubPanel";
import ContentPanel, { import ContentPanel, {
ContentPanelWidthSizes, ContentPanelWidthSizes,
} from "components/Panels/ContentPanel"; } from "components/Panels/ContentPanel";
import { GetContentsQuery } from "graphql/operations-types"; import {
GetContentsQuery,
GetWebsiteInterfaceQuery,
} from "graphql/operations-types";
import { getContents } from "graphql/operations"; import { getContents } from "graphql/operations";
import PanelHeader from "components/PanelComponents/PanelHeader"; import PanelHeader from "components/PanelComponents/PanelHeader";
import AppLayout from "components/AppLayout"; import AppLayout from "components/AppLayout";
@ -12,6 +15,7 @@ import { prettyinlineTitle, prettySlug } from "queries/helpers";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import Select from "components/Select"; import Select from "components/Select";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Chip from "components/Chip";
interface ContentsProps extends AppStaticProps { interface ContentsProps extends AppStaticProps {
contents: GetContentsQuery["contents"]["data"]; contents: GetContentsQuery["contents"]["data"];
@ -25,11 +29,11 @@ export default function Contents(props: ContentsProps): JSX.Element {
const [groupingMethod, setGroupingMethod] = useState<number>(-1); const [groupingMethod, setGroupingMethod] = useState<number>(-1);
const [groups, setGroups] = useState<GroupContentItems>( const [groups, setGroups] = useState<GroupContentItems>(
getGroups(groupingMethod, contents) getGroups(langui, groupingMethod, contents)
); );
useEffect(() => { useEffect(() => {
setGroups(getGroups(groupingMethod, contents)); setGroups(getGroups(langui, groupingMethod, contents));
}, [langui, groupingMethod, contents]); }, [langui, groupingMethod, contents]);
const subPanel = ( const subPanel = (
@ -61,9 +65,14 @@ export default function Contents(props: ContentsProps): JSX.Element {
{name && ( {name && (
<h2 <h2
key={"h2" + name} key={"h2" + name}
className="text-2xl pb-2 pt-10 first-of-type:pt-0" className="text-2xl pb-2 pt-10 first-of-type:pt-0 flex flex-row place-items-center gap-2"
> >
{name} {name}
<Chip>{`${items.length} ${
items.length <= 1
? langui.result.toLowerCase()
: langui.results.toLowerCase()
}`}</Chip>
</h2> </h2>
)} )}
<div <div
@ -127,6 +136,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
}; };
function getGroups( function getGroups(
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"],
groupByType: number, groupByType: number,
items: ContentsProps["contents"] items: ContentsProps["contents"]
): GroupContentItems { ): GroupContentItems {
@ -150,11 +160,11 @@ function getGroups(
typeGroup.set("Bakuken", []); typeGroup.set("Bakuken", []);
typeGroup.set("YoRHa", []); typeGroup.set("YoRHa", []);
typeGroup.set("YoRHa Boys", []); typeGroup.set("YoRHa Boys", []);
typeGroup.set("No category", []); typeGroup.set(langui.no_category, []);
items.map((item) => { items.map((item) => {
if (item.attributes.categories.data.length === 0) { if (item.attributes.categories.data.length === 0) {
typeGroup.get("No category")?.push(item); typeGroup.get(langui.no_category)?.push(item);
} else { } else {
item.attributes.categories.data.map((category) => { item.attributes.categories.data.map((category) => {
typeGroup.get(category.attributes.name)?.push(item); typeGroup.get(category.attributes.name)?.push(item);

View File

@ -218,13 +218,11 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
</div> </div>
{item.categories.data.length > 0 && ( {item.categories.data.length > 0 && (
<div> <div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{langui.categories}</h3> <h3 className="text-xl">{langui.categories}</h3>
<div className="flex flex-row flex-wrap place-items-center place-content-start gap-2"> <div className="flex flex-row flex-wrap place-content-center gap-2">
{item.categories.data.map((category) => ( {item.categories.data.map((category) => (
<Chip key={category.id}> <Chip key={category.id}>{category.attributes.name}</Chip>
{category.attributes.short}
</Chip>
))} ))}
</div> </div>
</div> </div>

View File

@ -17,6 +17,7 @@ import { useEffect, useState } from "react";
import { convertPrice, prettyDate, prettyinlineTitle } from "queries/helpers"; import { convertPrice, prettyDate, prettyinlineTitle } from "queries/helpers";
import Switch from "components/Switch"; import Switch from "components/Switch";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import Chip from "components/Chip";
interface LibraryProps extends AppStaticProps { interface LibraryProps extends AppStaticProps {
items: GetLibraryItemsPreviewQuery["libraryItems"]["data"]; items: GetLibraryItemsPreviewQuery["libraryItems"]["data"];
@ -116,9 +117,14 @@ export default function Library(props: LibraryProps): JSX.Element {
{name && ( {name && (
<h2 <h2
key={"h2" + name} key={"h2" + name}
className="text-2xl pb-2 pt-10 first-of-type:pt-0" className="text-2xl pb-2 pt-10 first-of-type:pt-0 flex flex-row place-items-center gap-2"
> >
{name} {name}
<Chip>{`${items.length} ${
items.length <= 1
? langui.result.toLowerCase()
: langui.results.toLowerCase()
}`}</Chip>
</h2> </h2>
)} )}
<div <div
@ -188,11 +194,11 @@ function getGroups(
typeGroup.set("Bakuken", []); typeGroup.set("Bakuken", []);
typeGroup.set("YoRHa", []); typeGroup.set("YoRHa", []);
typeGroup.set("YoRHa Boys", []); typeGroup.set("YoRHa Boys", []);
typeGroup.set("No category", []); typeGroup.set(langui.no_category, []);
items.map((item) => { items.map((item) => {
if (item.attributes.categories.data.length === 0) { if (item.attributes.categories.data.length === 0) {
typeGroup.get("No category")?.push(item); typeGroup.get(langui.no_category)?.push(item);
} else { } else {
item.attributes.categories.data.map((category) => { item.attributes.categories.data.map((category) => {
typeGroup.get(category.attributes.name)?.push(item); typeGroup.get(category.attributes.name)?.push(item);

View File

@ -256,3 +256,20 @@ export function sortContent(
return 0; return 0;
}); });
} }
export function slugify(str: string): string {
return str
.replace(/[ÀÁÂÃÄÅàáâãäåæÆ]/g, "a")
.replace(/[çÇ]/g, "c")
.replace(/[ðÐ]/g, "d")
.replace(/[ÈÉÊËéèêë]/g, "e")
.replace(/[ÏïÎîÍíÌì]/g, "i")
.replace(/[Ññ]/g, "n")
.replace(/[øØœŒÕõÔôÓóÒò]/g, "o")
.replace(/[ÜüÛûÚúÙù]/g, "u")
.replace(/[ŸÿÝý]/g, "y")
.replace(/[^a-z0-9- ]/gi, "")
.trim()
.replace(/ /gi, "-")
.toLowerCase();
}