Merge pull request #10 from Accords-Library/develop

Better Markdawn and auto-generated tables of content
This commit is contained in:
DrMint 2022-03-12 14:04:12 +01:00 committed by GitHub
commit 56c07b715d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 837 additions and 259 deletions

50
package-lock.json generated
View File

@ -20,7 +20,6 @@
"turndown": "^7.1.1" "turndown": "^7.1.1"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.2",
"@types/node": "17.0.21", "@types/node": "17.0.21",
"@types/react": "17.0.40", "@types/react": "17.0.40",
"@types/react-dom": "^17.0.13", "@types/react-dom": "^17.0.13",
@ -455,20 +454,6 @@
"integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==",
"dev": true "dev": true
}, },
"node_modules/@tailwindcss/typography": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
"integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==",
"dev": true,
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders"
}
},
"node_modules/@types/json5": { "node_modules/@types/json5": {
"version": "0.0.29", "version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -2405,18 +2390,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
"dev": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
"dev": true
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -3950,17 +3923,6 @@
"integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==",
"dev": true "dev": true
}, },
"@tailwindcss/typography": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
"integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==",
"dev": true,
"requires": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2"
}
},
"@types/json5": { "@types/json5": {
"version": "0.0.29", "version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -5394,18 +5356,6 @@
"path-exists": "^3.0.0" "path-exists": "^3.0.0"
} }
}, },
"lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
"dev": true
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
"dev": true
},
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",

View File

@ -22,7 +22,6 @@
"turndown": "^7.1.1" "turndown": "^7.1.1"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.2",
"@types/node": "17.0.21", "@types/node": "17.0.21",
"@types/react": "17.0.40", "@types/react": "17.0.40",
"@types/react-dom": "^17.0.13", "@types/react-dom": "^17.0.13",

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

@ -1,6 +1,9 @@
import HorizontalLine from "components/HorizontalLine";
import InsetBox from "components/InsetBox";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import Markdown from "markdown-to-jsx"; import Markdown from "markdown-to-jsx";
import SceneBreak from "./SceneBreak"; import { slugify } from "queries/helpers";
import React from "react";
type ScenBreakProps = { type ScenBreakProps = {
className?: string; className?: string;
@ -9,15 +12,31 @@ type ScenBreakProps = {
export default function Markdawn(props: ScenBreakProps): JSX.Element { export default function Markdawn(props: ScenBreakProps): JSX.Element {
const appLayout = useAppLayout(); const appLayout = useAppLayout();
const text = preprocessMarkDawn(props.text);
if (props.text) { if (text) {
return ( return (
<Markdown <Markdown
className={`prose prose-p:text-justify text-black ${props.className}`} className={`formatted ${props.className}`}
options={{ options={{
slugify: slugify,
overrides: { overrides: {
hr: { Sep: {
component: SceneBreak, component: () => {
return <div className="my-24"></div>;
},
},
SceneBreak: {
component: (props: { id: string }) => {
return (
<div
id={props.id}
className={"h-0 text-center text-3xl text-dark mt-16 mb-20"}
>
* * *
</div>
);
},
}, },
player: { player: {
component: () => { component: () => {
@ -28,12 +47,80 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element {
); );
}, },
}, },
Transcript: {
component: (props) => {
return (
<div className="grid grid-cols-[auto_1fr] gap-x-6 gap-y-2">
{props.children}
</div>
);
},
},
Line: {
component: (props) => {
return (
<>
<strong className="text-dark opacity-60">
{props.name}
</strong>
<p className="whitespace-pre-line">{props.children}</p>
</>
);
},
},
InsetBox: {
component: (props) => {
return <InsetBox>{props.children}</InsetBox>;
},
},
li: {
component: (props: { children: React.ReactNode }) => {
return (
<li
className={
props.children && props.children?.toString().length > 100
? "my-4"
: ""
}
>
{props.children}
</li>
);
},
},
Highlight: {
component: (props: { children: React.ReactNode }) => {
return <mark>{props.children}</mark>;
},
},
footer: {
component: (props: { children: React.ReactNode }) => {
return (
<>
<HorizontalLine />
<div>{props.children}</div>
</>
);
},
},
}, },
}} }}
> >
{props.text} {text}
</Markdown> </Markdown>
); );
} }
return <></>; return <></>;
} }
export function preprocessMarkDawn(text: string): string {
let scenebreakIndex = 0;
const result = text.split("\n").map((line) => {
if (line === "* * *" || line === "---") {
scenebreakIndex++;
return `<SceneBreak id="scene-break-${scenebreakIndex}">`;
}
return line;
});
return result.join("\n");
}

View File

@ -1,15 +0,0 @@
type ScenBreakProps = {
className?: string;
};
export default function SceneBreak(props: ScenBreakProps): JSX.Element {
return (
<div
className={
"h-0 text-center text-3xl text-dark mt-16 mb-20" + " " + props.className
}
>
* * *
</div>
);
}

View File

@ -0,0 +1,153 @@
import { slugify } from "queries/helpers";
import { preprocessMarkDawn } from "./Markdawn";
type TOCProps = {
text: string;
title?: string;
};
export default function TOC(props: TOCProps): JSX.Element {
const toc = getTocFromMarkdawn(preprocessMarkDawn(props.text), props.title);
return (
<div>
<h3 className="text-xl">Table of content</h3>
<ol className="text-left">
<li className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap">
<a className="" href={`#${toc.slug}`}>
{<abbr title={toc.title}>{toc.title}</abbr>}
</a>
</li>
{toc.children.map((h2, h2Index) => (
<>
<li
key={h2.slug}
className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap"
>
<span className="text-dark">{`${h2Index + 1}. `}</span>
<a href={`#${h2.slug}`}>
{<abbr title={h2.title}>{h2.title}</abbr>}
</a>
</li>
<ol className="pl-4 text-left">
{h2.children.map((h3, h3Index) => (
<li
key={h3.slug}
className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap"
>
<span className="text-dark">{`${h2Index + 1}.${
h3Index + 1
}. `}</span>
<a href={`#${h3.slug}`}>
{<abbr title={h3.title}>{h3.title}</abbr>}
</a>
</li>
))}
</ol>
</>
))}
</ol>
</div>
);
}
export type TOC = {
title: string;
slug: string;
children: TOC[];
};
export function getTocFromMarkdawn(text: string, title?: string): TOC {
if (!title) title = "Return to top";
let toc: TOC = { title: title, slug: slugify(title) || "", children: [] };
let h2 = -1;
let h3 = -1;
let h4 = -1;
let h5 = -1;
let scenebreak = 0;
let scenebreakIndex = 0;
text.split("\n").map((line) => {
if (line.startsWith("# ")) {
toc.slug = slugify(line);
} else if (line.startsWith("## ")) {
toc.children.push({
title: line.slice("## ".length),
slug: slugify(line),
children: [],
});
h2++;
h3 = -1;
h4 = -1;
h5 = -1;
scenebreak = 0;
} else if (line.startsWith("### ")) {
toc.children[h2].children.push({
title: line.slice("### ".length),
slug: slugify(line),
children: [],
});
h3++;
h4 = -1;
h5 = -1;
scenebreak = 0;
} else if (line.startsWith("#### ")) {
toc.children[h2].children[h3].children.push({
title: line.slice("#### ".length),
slug: slugify(line),
children: [],
});
h4++;
h5 = -1;
scenebreak = 0;
} else if (line.startsWith("##### ")) {
toc.children[h2].children[h3].children[h4].children.push({
title: line.slice("##### ".length),
slug: slugify(line),
children: [],
});
h5++;
scenebreak = 0;
} else if (line.startsWith("###### ")) {
toc.children[h2].children[h3].children[h4].children[h5].children.push({
title: line.slice("###### ".length),
slug: slugify(line),
children: [],
});
} else if (line.startsWith(`<SceneBreak`)) {
scenebreak++;
scenebreakIndex++;
if (h5 >= 0) {
toc.children[h2].children[h3].children[h4].children[h5].children.push({
title: `Scene break ${scenebreak}`,
slug: slugify(`scene-break-${scenebreakIndex}`),
children: [],
});
} else if (h4 >= 0) {
toc.children[h2].children[h3].children[h4].children.push({
title: `Scene break ${scenebreak}`,
slug: slugify(`scene-break-${scenebreakIndex}`),
children: [],
});
} else if (h3 >= 0) {
toc.children[h2].children[h3].children.push({
title: `Scene break ${scenebreak}`,
slug: slugify(`scene-break-${scenebreakIndex}`),
children: [],
});
} else if (h2 >= 0) {
toc.children[h2].children.push({
title: `Scene break ${scenebreak}`,
slug: slugify(`scene-break-${scenebreakIndex}`),
children: [],
});
} else {
toc.children.push({
title: `Scene break ${scenebreak}`,
slug: slugify(`scene-break-${scenebreakIndex}`),
children: [],
});
}
}
});
return toc;
}

View File

@ -12,12 +12,15 @@ export enum ContentPanelWidthSizes {
export default function ContentPanel(props: ContentPanelProps): JSX.Element { export default function ContentPanel(props: ContentPanelProps): JSX.Element {
const width = props.width ? props.width : ContentPanelWidthSizes.default; const width = props.width ? props.width : ContentPanelWidthSizes.default;
const widthCSS = const widthCSS =
width === ContentPanelWidthSizes.default ? "max-w-[45rem]" : "w-full"; width === ContentPanelWidthSizes.default ? "max-w-2xl" : "w-full";
const prose = props.autoformat ? "prose text-justify" : "";
return ( return (
<div className={`grid pt-10 pb-20 px-6 desktop:py-20 desktop:px-10`}> <div className={`grid pt-10 pb-20 px-6 desktop:py-20 desktop:px-10`}>
<main className={`${prose} ${widthCSS} place-self-center`}> <main
className={`${
props.autoformat && "formatted"
} ${widthCSS} place-self-center`}
>
{props.children} {props.children}
</main> </main>
</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"
@ -137,6 +139,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
*/}
<HorizontalLine /> <HorizontalLine />
<NavOption <NavOption
@ -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"
@ -157,6 +161,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
onClick={() => appLayout.setMainPanelOpen(false)} onClick={() => appLayout.setMainPanelOpen(false)}
/> />
*/}
<NavOption <NavOption
url="/gallery" url="/gallery"
icon="collections" icon="collections"
@ -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

@ -6,7 +6,7 @@ export type SwitchProps = {
className?: string; className?: string;
}; };
export default function Select(props: SwitchProps): JSX.Element { export default function Switch(props: SwitchProps): JSX.Element {
return ( return (
<div <div
className={`h-6 w-12 rounded-full border-2 border-mid grid transition-colors relative cursor-pointer ${ className={`h-6 w-12 rounded-full border-2 border-mid grid transition-colors relative cursor-pointer ${

View File

@ -109,6 +109,12 @@ query getWebsiteInterface($language_code: String) {
translation_notice translation_notice
source_language source_language
pronouns pronouns
no_category
item
items
content
result
results
} }
} }
} }
@ -1142,3 +1148,77 @@ query getLanguages {
} }
} }
} }
query getPost($slug: String, $language_code: String) {
posts(filters: { slug: { eq: $slug } }) {
data {
id
attributes {
slug
publishedAt
updatedAt
authors {
data {
id
attributes {
username
anonymize
anonymous_code
pronouns
bio(filters: { language: { code: { eq: $language_code } } }) {
bio
}
languages {
data {
attributes {
code
}
}
}
avatar {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
}
}
}
categories {
data {
id
attributes {
name
short
}
}
}
hidden
translations(filters: { language: { code: { eq: $language_code } } }) {
Status
title
excerpt
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
body
}
}
}
}
}

View File

@ -48,6 +48,13 @@ export enum Enum_Componentmetadatavideo_Resolution {
QuadHd_2160p = "QuadHD_2160p", QuadHd_2160p = "QuadHD_2160p",
} }
export enum Enum_Componenttranslationsposts_Status {
Incomplete = "Incomplete",
Draft = "Draft",
Review = "Review",
Done = "Done",
}
export enum Enum_Componenttranslationschronologyitem_Status { export enum Enum_Componenttranslationschronologyitem_Status {
Incomplete = "Incomplete", Incomplete = "Incomplete",
Draft = "Draft", Draft = "Draft",
@ -191,6 +198,12 @@ export type GetWebsiteInterfaceQuery = {
translation_notice: string; translation_notice: string;
source_language: string; source_language: string;
pronouns: string; pronouns: string;
no_category: string;
item: string;
items: string;
content: string;
result: string;
results: string;
}; };
}>; }>;
}; };
@ -1525,3 +1538,100 @@ export type GetLanguagesQuery = {
}>; }>;
}; };
}; };
export type GetPostQueryVariables = Exact<{
slug: InputMaybe<Scalars["String"]>;
language_code: InputMaybe<Scalars["String"]>;
}>;
export type GetPostQuery = {
__typename: "Query";
posts: {
__typename: "PostEntityResponseCollection";
data: Array<{
__typename: "PostEntity";
id: string;
attributes: {
__typename: "Post";
slug: string;
publishedAt: any;
updatedAt: any;
hidden: boolean;
authors: {
__typename: "RecorderRelationResponseCollection";
data: Array<{
__typename: "RecorderEntity";
id: string;
attributes: {
__typename: "Recorder";
username: string;
anonymize: boolean;
anonymous_code: string;
pronouns: string;
bio: Array<{
__typename: "ComponentTranslationsBio";
bio: string;
}>;
languages: {
__typename: "LanguageRelationResponseCollection";
data: Array<{
__typename: "LanguageEntity";
attributes: { __typename: "Language"; code: string };
}>;
};
avatar: {
__typename: "UploadFileEntityResponse";
data: {
__typename: "UploadFileEntity";
attributes: {
__typename: "UploadFile";
name: string;
alternativeText: string;
caption: string;
width: number;
height: number;
url: string;
};
};
};
};
}>;
};
categories: {
__typename: "CategoryRelationResponseCollection";
data: Array<{
__typename: "CategoryEntity";
id: string;
attributes: {
__typename: "Category";
name: string;
short: string;
};
}>;
};
translations: Array<{
__typename: "ComponentTranslationsPosts";
Status: Enum_Componenttranslationsposts_Status;
title: string;
excerpt: string;
body: string;
thumbnail: {
__typename: "UploadFileEntityResponse";
data: {
__typename: "UploadFileEntity";
attributes: {
__typename: "UploadFile";
name: string;
alternativeText: string;
caption: string;
width: number;
height: number;
url: string;
};
};
};
}>;
};
}>;
};
};

View File

@ -23,6 +23,8 @@ import {
GetLibraryItemsPreviewQueryVariables, GetLibraryItemsPreviewQueryVariables,
GetLibraryItemsSlugsQuery, GetLibraryItemsSlugsQuery,
GetLibraryItemsSlugsQueryVariables, GetLibraryItemsSlugsQueryVariables,
GetPostQuery,
GetPostQueryVariables,
GetWebsiteInterfaceQuery, GetWebsiteInterfaceQuery,
GetWebsiteInterfaceQueryVariables, GetWebsiteInterfaceQueryVariables,
} from "graphql/operations-types"; } from "graphql/operations-types";
@ -141,3 +143,10 @@ export async function getLanguages(
const query = getQueryFromOperations("getLanguages"); const query = getQueryFromOperations("getLanguages");
return await graphQL(query, JSON.stringify(variables)); return await graphQL(query, JSON.stringify(variables));
} }
export async function getPost(
variables: GetPostQueryVariables
): Promise<GetPostQuery> {
const query = getQueryFromOperations("getPost");
return await graphQL(query, JSON.stringify(variables));
}

View File

@ -1060,6 +1060,8 @@ input ComponentTranslationsPostsFiltersInput {
Status: StringFilterInput Status: StringFilterInput
title: StringFilterInput title: StringFilterInput
excerpt: StringFilterInput excerpt: StringFilterInput
body: StringFilterInput
language: LanguageFiltersInput
and: [ComponentTranslationsPostsFiltersInput] and: [ComponentTranslationsPostsFiltersInput]
or: [ComponentTranslationsPostsFiltersInput] or: [ComponentTranslationsPostsFiltersInput]
not: ComponentTranslationsPostsFiltersInput not: ComponentTranslationsPostsFiltersInput
@ -1071,6 +1073,8 @@ input ComponentTranslationsPostsInput {
title: String title: String
excerpt: String excerpt: String
thumbnail: ID thumbnail: ID
body: String
language: ID
} }
type ComponentTranslationsPosts { type ComponentTranslationsPosts {
@ -1079,6 +1083,8 @@ type ComponentTranslationsPosts {
title: String! title: String!
excerpt: String excerpt: String
thumbnail: UploadFileEntityResponse thumbnail: UploadFileEntityResponse
body: String
language: LanguageEntityResponse
} }
enum ENUM_COMPONENTTRANSLATIONSSCANSET_STATUS { enum ENUM_COMPONENTTRANSLATIONSSCANSET_STATUS {
@ -1648,6 +1654,7 @@ input CurrencyFiltersInput {
symbol: StringFilterInput symbol: StringFilterInput
code: StringFilterInput code: StringFilterInput
rate_to_usd: FloatFilterInput rate_to_usd: FloatFilterInput
display_decimals: BooleanFilterInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
and: [CurrencyFiltersInput] and: [CurrencyFiltersInput]
@ -1659,12 +1666,14 @@ input CurrencyInput {
symbol: String symbol: String
code: String code: String
rate_to_usd: Float rate_to_usd: Float
display_decimals: Boolean
} }
type Currency { type Currency {
symbol: String! symbol: String!
code: String! code: String!
rate_to_usd: Float rate_to_usd: Float!
display_decimals: Boolean!
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
} }
@ -1916,6 +1925,7 @@ input LibraryItemFiltersInput {
digital: BooleanFilterInput digital: BooleanFilterInput
primary: BooleanFilterInput primary: BooleanFilterInput
submerchs: MerchItemFiltersInput submerchs: MerchItemFiltersInput
categories: CategoryFiltersInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
and: [LibraryItemFiltersInput] and: [LibraryItemFiltersInput]
@ -1942,6 +1952,7 @@ input LibraryItemInput {
digital: Boolean digital: Boolean
primary: Boolean primary: Boolean
submerchs: [ID] submerchs: [ID]
categories: [ID]
} }
type LibraryItem { type LibraryItem {
@ -1987,6 +1998,11 @@ type LibraryItem {
pagination: PaginationArg = {} pagination: PaginationArg = {}
sort: [String] = [] sort: [String] = []
): MerchItemRelationResponseCollection ): MerchItemRelationResponseCollection
categories(
filters: CategoryFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): CategoryRelationResponseCollection
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
} }
@ -2107,6 +2123,7 @@ input PostFiltersInput {
authors: RecorderFiltersInput authors: RecorderFiltersInput
slug: StringFilterInput slug: StringFilterInput
categories: CategoryFiltersInput categories: CategoryFiltersInput
hidden: BooleanFilterInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
publishedAt: DateTimeFilterInput publishedAt: DateTimeFilterInput
@ -2120,6 +2137,7 @@ input PostInput {
slug: String slug: String
categories: [ID] categories: [ID]
translations: [ComponentTranslationsPostsInput] translations: [ComponentTranslationsPostsInput]
hidden: Boolean
publishedAt: DateTime publishedAt: DateTime
} }
@ -2140,6 +2158,7 @@ type Post {
pagination: PaginationArg = {} pagination: PaginationArg = {}
sort: [String] = [] sort: [String] = []
): [ComponentTranslationsPosts] ): [ComponentTranslationsPosts]
hidden: Boolean!
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
publishedAt: DateTime publishedAt: DateTime
@ -2619,6 +2638,38 @@ input WebsiteInterfaceFiltersInput {
group_by: StringFilterInput group_by: StringFilterInput
select_option_sidebar: StringFilterInput select_option_sidebar: StringFilterInput
group: StringFilterInput group: StringFilterInput
settings: StringFilterInput
theme: StringFilterInput
light: StringFilterInput
auto: StringFilterInput
dark: StringFilterInput
font_size: StringFilterInput
player_name: StringFilterInput
currency: StringFilterInput
font: StringFilterInput
calculated: StringFilterInput
status_incomplete: StringFilterInput
status_draft: StringFilterInput
status_review: StringFilterInput
status_done: StringFilterInput
incomplete: StringFilterInput
draft: StringFilterInput
review: StringFilterInput
done: StringFilterInput
status: StringFilterInput
transcribers: StringFilterInput
translators: StringFilterInput
proofreaders: StringFilterInput
transcript_notice: StringFilterInput
translation_notice: StringFilterInput
source_language: StringFilterInput
pronouns: StringFilterInput
no_category: StringFilterInput
item: StringFilterInput
items: StringFilterInput
content: StringFilterInput
result: StringFilterInput
results: StringFilterInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
and: [WebsiteInterfaceFiltersInput] and: [WebsiteInterfaceFiltersInput]
@ -2707,6 +2758,38 @@ input WebsiteInterfaceInput {
group_by: String group_by: String
select_option_sidebar: String select_option_sidebar: String
group: String group: String
settings: String
theme: String
light: String
auto: String
dark: String
font_size: String
player_name: String
currency: String
font: String
calculated: String
status_incomplete: String
status_draft: String
status_review: String
status_done: String
incomplete: String
draft: String
review: String
done: String
status: String
transcribers: String
translators: String
proofreaders: String
transcript_notice: String
translation_notice: String
source_language: String
pronouns: String
no_category: String
item: String
items: String
content: String
result: String
results: String
} }
type WebsiteInterface { type WebsiteInterface {
@ -2790,6 +2873,38 @@ type WebsiteInterface {
group_by: String group_by: String
select_option_sidebar: String select_option_sidebar: String
group: String group: String
settings: String
theme: String
light: String
auto: String
dark: String
font_size: String
player_name: String
currency: String
font: String
calculated: String
status_incomplete: String
status_draft: String
status_review: String
status_done: String
incomplete: String
draft: String
review: String
done: String
status: String
transcribers: String
translators: String
proofreaders: String
transcript_notice: String
translation_notice: String
source_language: String
pronouns: String
no_category: String
item: String
items: String
content: String
result: String
results: String
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
} }

View File

@ -3,6 +3,7 @@ import PanelHeader from "components/PanelComponents/PanelHeader";
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import AppLayout from "components/AppLayout"; import AppLayout from "components/AppLayout";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import NavOption from "components/PanelComponents/NavOption";
interface AboutUsProps extends AppStaticProps {} interface AboutUsProps extends AppStaticProps {}
@ -15,6 +16,15 @@ export default function AboutUs(props: AboutUsProps): JSX.Element {
title={langui.about_us} title={langui.about_us}
description={langui.about_us_description} description={langui.about_us_description}
/> />
<NavOption title="Accords Handbook" url="/about-us/handbook" border />
<NavOption
title="Site information"
url="/about-us/site-information"
border
/>
<NavOption title="FAQ" url="/about-us/faq" border />
<NavOption title="Sharing Policy" url="/about-us/sharing-policy" border />
<NavOption title="Contact us" url="/about-us/contact" border />
</SubPanel> </SubPanel>
); );
return ( return (

View File

@ -0,0 +1,60 @@
import AppLayout from "components/AppLayout";
import Markdawn from "components/Markdown/Markdawn";
import ReturnButton, {
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import ContentPanel from "components/Panels/ContentPanel";
import { getPost } from "graphql/operations";
import { GetPostQuery } from "graphql/operations-types";
import { GetStaticProps } from "next";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import { prettySlug } from "queries/helpers";
interface SiteInfoProps extends AppStaticProps {
post: GetPostQuery["posts"]["data"][number]["attributes"];
}
export default function SiteInformation(props: SiteInfoProps): JSX.Element {
const { langui, post } = props;
const contentPanel = (
<ContentPanel>
<ReturnButton
href="/about-us"
displayOn={ReturnButtonType.Both}
langui={langui}
title={langui.about_us}
className="mb-10"
/>
{post.translations.length > 0 && (
<Markdawn text={post.translations[0].body} />
)}
</ContentPanel>
);
return (
<AppLayout
navTitle={
post.translations.length > 0
? post.translations[0].title
: prettySlug(post.slug)
}
contentPanel={contentPanel}
{...props}
/>
);
}
export const getStaticProps: GetStaticProps = async (context) => {
const props: SiteInfoProps = {
...(await getAppStaticProps(context)),
post: (
await getPost({
slug: "site-information",
language_code: context.locale || "en",
})
).posts.data[0].attributes,
};
return {
props: props,
};
};

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,24 @@ export default function ContentRead(props: ContentReadProps): JSX.Element {
)} )}
</div> </div>
)} )}
{content.text_set.length > 0 && content.text_set[0].text && (
<>
<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 = (
@ -157,7 +176,7 @@ export default function ContentRead(props: ContentReadProps): JSX.Element {
<HorizontalLine /> <HorizontalLine />
{content.text_set.length > 0 && ( {content.text_set.length > 0 && content.text_set[0].text && (
<Markdawn text={content.text_set[0].text} /> <Markdawn text={content.text_set[0].text} />
)} )}
</div> </div>

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

@ -1,146 +1,48 @@
import AppLayout from "components/AppLayout"; import AppLayout from "components/AppLayout";
import Markdawn from "components/Markdown/Markdawn";
import ContentPanel from "components/Panels/ContentPanel"; import ContentPanel from "components/Panels/ContentPanel";
import { getPost } from "graphql/operations";
import { GetPostQuery } from "graphql/operations-types";
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import { prettySlug } from "queries/helpers";
interface HomeProps extends AppStaticProps {} interface HomeProps extends AppStaticProps {
post: GetPostQuery["posts"]["data"][number]["attributes"];
}
export default function Home(props: HomeProps): JSX.Element { export default function Home(props: HomeProps): JSX.Element {
const { post } = props;
const contentPanel = ( const contentPanel = (
<ContentPanel autoformat> <ContentPanel>
<div className="grid place-items-center place-content-center w-full gap-5 text-center"> {post.translations.length > 0 && (
<div className="[mask:url('/icons/accords.svg')] [mask-size:contain] [mask-repeat:no-repeat] [mask-position:center] w-32 aspect-square mobile:w-[50vw] bg-black" /> <Markdawn text={post.translations[0].body} />
<h1 className="text-5xl mb-0">Accord&rsquo;s Library</h1> )}
<h2 className="mt-0">Discover Analyse Translate Archive</h2>
</div>
<h2>What is this?</h2>
<p>
Accord&rsquo;s Library aims at gathering and archiving all of Yoko
Taro&rsquo;s work. Yoko Taro is a Japanese video game director and
scenario writer. He is best-known for his work on the NieR and
Drakengard (Drag-on Dragoon) franchises. To complement his games, Yoko
Taro likes to publish side materials in the form of books, novellas,
artbooks, stage plays, manga, drama CDs, and comics. Those side
materials can be very difficult to find. His work goes all the way back
to 2003, and most of them are out of print after having been released
solely in Japan, sometimes in limited quantities. Their prices on the
second hand market have skyrocketed, ranging all the way to hundreds if
not thousand of dollars for the rarest items.&nbsp;
</p>
<p>
This is where this library takes its meaning, in trying to help the
community grow by providing translators, writers, and wiki&rsquo;s
contributors a simple way to access these records filled with stories,
artworks, and knowledge.
</p>
<p>
We are a small group of Yoko Taro&rsquo;s fans that decided to join
forces and create a website and a community. Our motto is{" "}
<strong>Discover Analyze Translate Archive</strong> (D.A.T.A. for
short). We started with the goal of gathering and archiving as much
side-materials/merch as possible. But since then, our ambition grew and
we decided to create a full-fledged website that will also include news
articles, lore, summaries, translations, and transcriptions. Hopefully
one day, we will be up there in the list of notable resources for
Drakengard and NieR fans.
</p>
<h2>What&rsquo;s on this website?</h2>
<p>
<strong>
<a href="https://accords-library.com/compendium/">The Compendium</a>
</strong>
: This is where we will list every NieR/DOD/other Yoko Tato merch,
games, books, novel, stage play, CD... well everything! For each, we
will provide photos and/or scans of the content, information about what
it is, when and how it was released, size, initial price...
</p>
<p>
<strong>
<a href="https://accords-library.com/news/">News</a>
</strong>
: Yes because we also want to create our own content! So there you will
find translations, transcriptions, unboxing, news about future
merch/game releases, maybe some guides. We don&rsquo;t see this website
as being purely a showcase of our work, but also of the community, and
as such, we will be accepting applications for becoming contributors on
the website. For the applicant, there is no deadline or article quota,
it merely means that we will have access to the website Post Writing
tools and will be able to submit a draft that can be published once
verified by an editor. Anyway, that&rsquo;s at least the plan, we will
think more about this until the website&rsquo;s official launch.
</p>
<p>
<strong>
<a href="https://accords-library.com/data/">Data</a>
</strong>
: There we will publish lore/knowledge about the Yokoverse: Dictionary,
Timeline, Weapons Stories, Game summaries... We have not yet decided how
deep we want to go as they are already quite a few resources out there.{" "}
</p>
<p>
<strong>
<a
href="https://gallery.accords-library.com/posts"
target="_blank"
rel="noreferrer noopener"
>
Gallery
</a>
</strong>
: A fully tagged Danbooru-styled gallery with currently more than a
thousand unique artworks. If you are unfamiliar with this kind of
gallery, it comes with a powerful search function that allows you to
search for specific images: want to search for images with both Caim and
Inuart, just type{" "}
<kbd>
<a
href="https://gallery.accords-library.com/posts/query=Caim%20Inuart"
target="_blank"
rel="noreferrer noopener"
>
Caim Inuart
</a>
</kbd>
. If you want images of Devola OR Popola, you can use a comma{" "}
<kbd>
<a
href="https://gallery.accords-library.com/posts/query=Popola%2CDevola"
data-type="URL"
data-id="https://gallery.accords-library.com/posts/query=Popola%2CDevola"
target="_blank"
rel="noreferrer noopener"
>
Popola,Devola
</a>
</kbd>
. You can also negate a tag: i.e. images of 9S without any pods around,
search for{" "}
<kbd>
<a
href="https://gallery.accords-library.com/posts/query=9S%20-Pods"
target="_blank"
rel="noreferrer noopener"
>
9S -Pods
</a>
</kbd>
. Anyway, there is a lot more to it, you can click on &quot;Syntax
help&quot; next to the Search button for even neater functions. Btw, you
can create an account to favorite, upvote/downvote posts, or if you want
to help tagging them. There isn&rsquo;t currently a way for new users to
upload images, you&rsquo;ll have to contact us first and we can decide
to enable this function on your account.
</p>
</ContentPanel> </ContentPanel>
); );
return <AppLayout navTitle={"Home"} contentPanel={contentPanel} {...props} />; return (
<AppLayout
navTitle={
post.translations.length > 0
? post.translations[0].title
: prettySlug(post.slug)
}
contentPanel={contentPanel}
{...props}
/>
);
} }
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const props: HomeProps = { const props: HomeProps = {
...(await getAppStaticProps(context)), ...(await getAppStaticProps(context)),
post: (
await getPost({
slug: "home",
language_code: context.locale || "en",
})
).posts.data[0].attributes,
}; };
return { return {
props: props, props: props,

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

@ -19,14 +19,12 @@ import { useRouter } from "next/router";
import ReactTooltip from "react-tooltip"; import ReactTooltip from "react-tooltip";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
interface DataChronologyProps extends AppStaticProps { interface ChronologyProps extends AppStaticProps {
chronologyItems: GetChronologyItemsQuery["chronologyItems"]["data"]; chronologyItems: GetChronologyItemsQuery["chronologyItems"]["data"];
chronologyEras: GetErasQuery["chronologyEras"]["data"]; chronologyEras: GetErasQuery["chronologyEras"]["data"];
} }
export default function DataChronology( export default function Chronology(props: ChronologyProps): JSX.Element {
props: DataChronologyProps
): JSX.Element {
useTesting(props); useTesting(props);
const { chronologyItems, chronologyEras } = props; const { chronologyItems, chronologyEras } = props;
@ -133,7 +131,7 @@ export default function DataChronology(
} }
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const props: DataChronologyProps = { const props: ChronologyProps = {
...(await getAppStaticProps(context)), ...(await getAppStaticProps(context)),
chronologyItems: ( chronologyItems: (
await getChronologyItems({ await getChronologyItems({
@ -148,7 +146,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
}; };
}; };
function useTesting(props: DataChronologyProps) { function useTesting(props: ChronologyProps) {
const router = useRouter(); const router = useRouter();
const { chronologyItems, chronologyEras } = props; const { chronologyItems, chronologyEras } = props;
chronologyEras.map((era) => { chronologyEras.map((era) => {

View File

@ -6,7 +6,7 @@ import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
interface WikiProps extends AppStaticProps {} interface WikiProps extends AppStaticProps {}
export default function Hubs(props: WikiProps): JSX.Element { export default function Wiki(props: WikiProps): JSX.Element {
const { langui } = props; const { langui } = props;
const subPanel = ( const subPanel = (
<SubPanel> <SubPanel>

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();
}

View File

@ -24,6 +24,10 @@
@apply bg-dark text-light; @apply bg-dark text-light;
} }
mark {
@apply bg-mid px-2
}
/* SCROLLBARS STYLING */ /* SCROLLBARS STYLING */
* { * {
@ -42,45 +46,82 @@
@apply bg-dark rounded-full border-[3px] border-solid border-light; @apply bg-dark rounded-full border-[3px] border-solid border-light;
} }
/* CHANGE PROSE DEFAULTS */ /* CHANGE FORMATTED DEFAULTS */
.prose, .formatted h1,
.prose p, .formatted h2,
.prose h1, .formatted h3,
.prose h2, .formatted h4,
.prose h3, .formatted h5,
.prose h4, .formatted h6 {
.prose h5, @apply text-center;
.prose h6,
.prose a,
.prose strong {
@apply text-black;
} }
.prose a { .formatted h1 {
@apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark hover:text-dark; @apply text-4xl my-16;
} }
.prose footer { .formatted h1 + h2 {
@apply -mt-10;
}
.formatted h2 {
@apply text-3xl my-12;
}
.formatted h2 + h3 {
@apply -mt-8;
}
.formatted h3 {
@apply text-2xl my-8;
}
.formatted h3 + h4 {
@apply -mt-6;
}
.formatted h4 {
@apply text-xl my-6;
}
.formatted h5 {
@apply text-lg my-4;
}
.formatted p,
.formatted strong {
@apply my-2 text-justify;
}
.formatted footer {
@apply border-t-[3px] border-dotted pt-6; @apply border-t-[3px] border-dotted pt-6;
} }
.prose footer > div { .formatted footer > div {
@apply my-2 px-6 py-4 rounded-xl; @apply my-2 px-6 py-4 rounded-xl;
} }
.prose footer > div:target { .formatted footer > div:target {
@apply bg-mid shadow-inner-sm shadow-shade; @apply bg-mid shadow-inner-sm shadow-shade;
} }
.prose li::marker { .formatted li::marker {
@apply text-dark; @apply text-dark;
} }
.prose blockquote { .formatted blockquote {
@apply border-l-dark; @apply border-l-dark;
} }
.formatted ul {
@apply list-disc pl-4;
}
.formatted ol {
@apply list-decimal pl-4;
}
/* INPUT */ /* INPUT */
input { input {

View File

@ -72,7 +72,6 @@ module.exports = {
}, },
}, },
plugins: [ plugins: [
require("@tailwindcss/typography"),
plugin(function ({ addUtilities }) { plugin(function ({ addUtilities }) {
addUtilities({ addUtilities({