Continued using hooks

This commit is contained in:
DrMint 2022-06-23 00:39:59 +02:00
parent efcf01e8a0
commit d0b91f9db6
29 changed files with 2640 additions and 2356 deletions

View File

@ -161,30 +161,30 @@ export function AppLayout(props: Props): JSX.Element {
}, [fontSize]);
const defaultPreferredLanguages = useMemo(() => {
let list: string[] = [];
let memo: string[] = [];
if (isDefinedAndNotEmpty(router.locale) && router.locales) {
if (router.locale === "en") {
list = [router.locale];
memo = [router.locale];
router.locales.map((locale) => {
if (locale !== router.locale) list.push(locale);
if (locale !== router.locale) memo.push(locale);
});
} else {
list = [router.locale, "en"];
memo = [router.locale, "en"];
router.locales.map((locale) => {
if (locale !== router.locale && locale !== "en") list.push(locale);
if (locale !== router.locale && locale !== "en") memo.push(locale);
});
}
}
return list;
return memo;
}, [router.locale, router.locales]);
const currencyOptions = useMemo(() => {
const list: string[] = [];
const memo: string[] = [];
filterHasAttributes(currencies).map((currentCurrency) => {
if (isDefinedAndNotEmpty(currentCurrency.attributes.code))
list.push(currentCurrency.attributes.code);
memo.push(currentCurrency.attributes.code);
});
return list;
return memo;
}, [currencies]);
const [currencySelect, setCurrencySelect] = useState<number>(-1);

View File

@ -157,12 +157,10 @@ export function ScanSet(props: Props): JSX.Element {
{filterHasAttributes(selectedScan.cleaners.data).map(
(cleaner) => (
<Fragment key={cleaner.id}>
{cleaner.attributes && (
<RecorderChip
langui={langui}
recorder={cleaner.attributes}
/>
)}
</Fragment>
)
)}
@ -178,12 +176,10 @@ export function ScanSet(props: Props): JSX.Element {
{filterHasAttributes(selectedScan.typesetters.data).map(
(typesetter) => (
<Fragment key={typesetter.id}>
{typesetter.attributes && (
<RecorderChip
langui={langui}
recorder={typesetter.attributes}
/>
)}
</Fragment>
)
)}
@ -218,9 +214,7 @@ export function ScanSet(props: Props): JSX.Element {
openLightBox(images, index);
}}
>
{page.attributes && (
<Img image={page.attributes} quality={ImageQuality.Small} />
)}
</div>
))}
</div>

View File

@ -11,7 +11,7 @@ import { getAssetURL, ImageQuality } from "helpers/img";
import { filterHasAttributes, getStatusDescription } from "helpers/others";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { Fragment } from "react";
import { Fragment, useMemo } from "react";
interface Props {
openLightBox: (images: string[], index?: number) => void;
@ -35,19 +35,22 @@ export function ScanSetCover(props: Props): JSX.Element {
languageExtractor: (item) => item.language?.data?.attributes?.code,
});
const coverImages: UploadImageFragment[] = [];
const coverImages = useMemo(() => {
const memo: UploadImageFragment[] = [];
if (selectedScan?.obi_belt?.full?.data?.attributes)
coverImages.push(selectedScan.obi_belt.full.data.attributes);
memo.push(selectedScan.obi_belt.full.data.attributes);
if (selectedScan?.obi_belt?.inside_full?.data?.attributes)
coverImages.push(selectedScan.obi_belt.inside_full.data.attributes);
memo.push(selectedScan.obi_belt.inside_full.data.attributes);
if (selectedScan?.dust_jacket?.full?.data?.attributes)
coverImages.push(selectedScan.dust_jacket.full.data.attributes);
memo.push(selectedScan.dust_jacket.full.data.attributes);
if (selectedScan?.dust_jacket?.inside_full?.data?.attributes)
coverImages.push(selectedScan.dust_jacket.inside_full.data.attributes);
memo.push(selectedScan.dust_jacket.inside_full.data.attributes);
if (selectedScan?.cover?.full?.data?.attributes)
coverImages.push(selectedScan.cover.full.data.attributes);
memo.push(selectedScan.cover.full.data.attributes);
if (selectedScan?.cover?.inside_full?.data?.attributes)
coverImages.push(selectedScan.cover.inside_full.data.attributes);
memo.push(selectedScan.cover.inside_full.data.attributes);
return memo;
}, [selectedScan]);
if (coverImages.length > 0) {
return (

View File

@ -67,7 +67,8 @@ export function PostPage(props: Props): JSX.Element {
[post.slug, post.thumbnail, selectedTranslation]
);
const subPanel =
const subPanel = useMemo(
() =>
returnHref || returnTitle || displayCredits || displayToc ? (
<SubPanel>
{returnHref && returnTitle && (
@ -120,9 +121,22 @@ export function PostPage(props: Props): JSX.Element {
{displayToc && <TOC text={body} title={title} />}
</SubPanel>
) : undefined;
) : undefined,
[
body,
displayCredits,
displayToc,
langui,
post.authors,
returnHref,
returnTitle,
selectedTranslation,
title,
]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel>
{returnHref && returnTitle && (
<ReturnButton
@ -166,6 +180,23 @@ export function PostPage(props: Props): JSX.Element {
<Markdawn text={body} />
{appendBody}
</ContentPanel>
),
[
LanguageSwitcher,
appendBody,
body,
displayLanguageSwitcher,
displayThumbnailHeader,
displayTitle,
excerpt,
langui,
post.categories,
prependBody,
returnHref,
returnTitle,
thumbnail,
title,
]
);
return (

View File

@ -85,7 +85,6 @@ export function ChronologyItemComponent(props: Props): JSX.Element {
{translation.description && (
<p
className={
event.translations &&
event.translations.length > 1
? `mt-2 whitespace-pre-line before:ml-[-1em] before:inline-block
before:w-4 before:text-dark before:content-['-']`

View File

@ -208,7 +208,7 @@ export function sortBy(
orderByType: number,
items: Items,
currencies: AppStaticProps["currencies"]
): Items {
) {
switch (orderByType) {
case 0:
return items.sort((a, b) => {

View File

@ -19,9 +19,7 @@ type SortContentProps =
>["contents"];
export function sortContent(contents: SortContentProps) {
if (contents) {
const newContent = { ...contents };
newContent?.data.sort((a, b) => {
contents?.data.sort((a, b) => {
if (
a.attributes?.range[0]?.__typename === "ComponentRangePageRange" &&
b.attributes?.range[0]?.__typename === "ComponentRangePageRange"
@ -33,9 +31,6 @@ export function sortContent(contents: SortContentProps) {
}
return 0;
});
return newContent;
}
return contents;
}
export function getStatusDescription(

View File

@ -38,12 +38,12 @@ export function useSmartLanguage<T>(
const router = useRouter();
const availableLocales = useMemo(() => {
const map = new Map<string, number>();
const memo = new Map<string, number>();
filterDefined(items).map((elem, index) => {
const result = languageExtractor(elem);
if (isDefined(result)) map.set(result, index);
if (isDefined(result)) memo.set(result, index);
});
return map;
return memo;
}, [items, languageExtractor]);
const [selectedTranslationIndex, setSelectedTranslationIndex] = useState<

View File

@ -7,12 +7,14 @@ import { ContentPanel } from "components/Panels/ContentPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function FourOhFour(props: Props): JSX.Element {
const { langui } = props;
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel>
<h1>404 - {langui.page_not_found}</h1>
<ReturnButton
@ -22,6 +24,8 @@ export default function FourOhFour(props: Props): JSX.Element {
displayOn={ReturnButtonType.Both}
/>
</ContentPanel>
),
[langui]
);
return <AppLayout navTitle="404" contentPanel={contentPanel} {...props} />;
}

View File

@ -7,12 +7,14 @@ import { ContentPanel } from "components/Panels/ContentPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function FiveHundred(props: Props): JSX.Element {
const { langui } = props;
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel>
<h1>500 - Internal Server Error</h1>
<ReturnButton
@ -22,6 +24,8 @@ export default function FiveHundred(props: Props): JSX.Element {
displayOn={ReturnButtonType.Both}
/>
</ContentPanel>
),
[langui]
);
return <AppLayout navTitle="500" contentPanel={contentPanel} {...props} />;
}

View File

@ -6,12 +6,14 @@ import { SubPanel } from "components/Panels/SubPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function AboutUs(props: Props): JSX.Element {
const { langui } = props;
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.Info}
@ -24,7 +26,6 @@ export default function AboutUs(props: Props): JSX.Element {
border
/>
<NavOption title={langui.legality} url="/about-us/legality" border />
{/* <NavOption title={langui.members} url="/about-us/members" border /> */}
<NavOption
title={langui.sharing_policy}
url="/about-us/sharing-policy"
@ -32,6 +33,8 @@ export default function AboutUs(props: Props): JSX.Element {
/>
<NavOption title={langui.contact_us} url="/about-us/contact" border />
</SubPanel>
),
[langui]
);
return (
<AppLayout navTitle={langui.about_us} subPanel={subPanel} {...props} />

View File

@ -6,12 +6,14 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { Icon } from "components/Ico";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function Archives(props: Props): JSX.Element {
const { langui } = props;
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.Inventory}
@ -20,6 +22,8 @@ export default function Archives(props: Props): JSX.Element {
/>
<NavOption title={"Videos"} url="/archives/videos/" border />
</SubPanel>
),
[langui]
);
return (
<AppLayout navTitle={langui.archives} subPanel={subPanel} {...props} />

View File

@ -20,7 +20,7 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { Fragment, useState } from "react";
import { Fragment, useState, useMemo } from "react";
import { Icon } from "components/Ico";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { WithLabel } from "components/Inputs/WithLabel";
@ -37,7 +37,8 @@ export default function Channel(props: Props): JSX.Element {
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
const hoverable = useMediaHoverable();
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/archives/videos/"
@ -62,9 +63,12 @@ export default function Channel(props: Props): JSX.Element {
/>
)}
</SubPanel>
),
[hoverable, keepInfoVisible, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div className="mb-8">
<h1 className="text-3xl">{channel?.title}</h1>
@ -97,7 +101,15 @@ export default function Channel(props: Props): JSX.Element {
))}
</div>
</ContentPanel>
),
[
channel?.subscribers,
channel?.title,
channel?.videos?.data,
keepInfoVisible,
]
);
return (
<AppLayout
navTitle={langui.archives}

View File

@ -22,40 +22,33 @@ import { filterHasAttributes } from "helpers/others";
import { getVideoThumbnailURL } from "helpers/videos";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { GetStaticPropsContext } from "next";
import { Fragment, useState } from "react";
import { Fragment, useMemo, useState } from "react";
interface Props extends AppStaticProps {
videos: NonNullable<GetVideosPreviewQuery["videos"]>["data"];
}
const ITEM_PER_PAGE = 50;
export default function Videos(props: Props): JSX.Element {
const { langui, videos } = props;
const hoverable = useMediaHoverable();
videos
.sort((a, b) => {
const dateA = a.attributes?.published_date
? prettyDate(a.attributes.published_date)
: "9999";
const dateB = b.attributes?.published_date
? prettyDate(b.attributes.published_date)
: "9999";
return dateA.localeCompare(dateB);
})
.reverse();
const itemPerPage = 50;
const paginatedVideos: Props["videos"][] = [];
for (let index = 0; itemPerPage * index < videos.length; index += 1) {
paginatedVideos.push(
videos.slice(index * itemPerPage, (index + 1) * itemPerPage)
const paginatedVideos = useMemo(() => {
const memo = [];
for (let index = 0; ITEM_PER_PAGE * index < videos.length; index += 1) {
memo.push(
videos.slice(index * ITEM_PER_PAGE, (index + 1) * ITEM_PER_PAGE)
);
}
return memo;
}, [videos]);
const [page, setPage] = useState(0);
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/archives/"
@ -80,12 +73,15 @@ export default function Videos(props: Props): JSX.Element {
/>
)}
</SubPanel>
),
[hoverable, keepInfoVisible, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<PageSelector
maxPage={Math.floor(videos.length / itemPerPage)}
maxPage={Math.floor(videos.length / ITEM_PER_PAGE)}
page={page}
setPage={setPage}
className="mb-12"
@ -120,12 +116,14 @@ export default function Videos(props: Props): JSX.Element {
</div>
<PageSelector
maxPage={Math.floor(videos.length / itemPerPage)}
maxPage={Math.floor(videos.length / ITEM_PER_PAGE)}
page={page}
setPage={setPage}
className="mt-12"
/>
</ContentPanel>
),
[keepInfoVisible, page, paginatedVideos, videos.length]
);
return (
<AppLayout
@ -143,6 +141,17 @@ export async function getStaticProps(
const sdk = getReadySdk();
const videos = await sdk.getVideosPreview();
if (!videos.videos) return { notFound: true };
videos.videos.data
.sort((a, b) => {
const dateA = a.attributes?.published_date
? prettyDate(a.attributes.published_date)
: "9999";
const dateB = b.attributes?.published_date
? prettyDate(b.attributes.published_date)
: "9999";
return dateA.localeCompare(dateB);
})
.reverse();
const props: Props = {
...(await getAppStaticProps(context)),
videos: videos.videos.data,

View File

@ -26,6 +26,7 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {
video: NonNullable<
@ -37,7 +38,8 @@ export default function Video(props: Props): JSX.Element {
const { langui, video } = props;
const isMobile = useMediaMobile();
const appLayout = useAppLayout();
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/archives/videos/"
@ -70,9 +72,12 @@ export default function Video(props: Props): JSX.Element {
onClick={() => appLayout.setSubPanelOpen(false)}
/>
</SubPanel>
),
[appLayout, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<ReturnButton
href="/library/"
@ -175,7 +180,22 @@ export default function Video(props: Props): JSX.Element {
</InsetBox>
</div>
</ContentPanel>
),
[
isMobile,
langui,
video.channel?.data?.attributes,
video.description,
video.gone,
video.likes,
video.published_date,
video.source,
video.title,
video.uid,
video.views,
]
);
return (
<AppLayout
navTitle={langui.archives}

View File

@ -5,12 +5,14 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { Icon } from "components/Ico";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function Chronicles(props: Props): JSX.Element {
const { langui } = props;
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.WatchLater}
@ -18,7 +20,10 @@ export default function Chronicles(props: Props): JSX.Element {
description={langui.chronicles_description}
/>
</SubPanel>
),
[langui]
);
return (
<AppLayout navTitle={langui.chronicles} subPanel={subPanel} {...props} />
);

View File

@ -76,7 +76,8 @@ export default function Content(props: Props): JSX.Element {
[content.group, content.slug]
);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href={`/contents`}
@ -96,7 +97,8 @@ export default function Content(props: Props): JSX.Element {
: langui.translation_notice}
</h2>
{selectedTranslation.text_set.source_language.data.attributes.code !==
{selectedTranslation.text_set.source_language.data.attributes
.code !==
selectedTranslation.language?.data?.attributes?.code && (
<div className="grid place-items-center gap-2">
<p className="font-headers">{langui.source_language}:</p>
@ -272,8 +274,18 @@ export default function Content(props: Props): JSX.Element {
</>
)}
</SubPanel>
),
[
content.ranged_contents?.data,
currencies,
languages,
langui,
selectedTranslation,
]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel>
<ReturnButton
href={`/contents`}
@ -313,7 +325,9 @@ export default function Content(props: Props): JSX.Element {
)}
slug={previousContent.attributes.slug}
languages={languages}
thumbnail={previousContent.attributes.thumbnail?.data?.attributes}
thumbnail={
previousContent.attributes.thumbnail?.data?.attributes
}
thumbnailAspectRatio="3/2"
topChips={
isMobile
@ -325,7 +339,8 @@ export default function Content(props: Props): JSX.Element {
? previousContent.attributes.type.data.attributes
.titles[0]?.title
: prettySlug(
previousContent.attributes.type.data.attributes.slug
previousContent.attributes.type.data.attributes
.slug
),
]
: undefined
@ -371,8 +386,8 @@ export default function Content(props: Props): JSX.Element {
: nextContent.attributes.type?.data?.attributes
? [
nextContent.attributes.type.data.attributes.titles?.[0]
? nextContent.attributes.type.data.attributes.titles[0]
?.title
? nextContent.attributes.type.data.attributes
.titles[0]?.title
: prettySlug(
nextContent.attributes.type.data.attributes.slug
),
@ -391,6 +406,19 @@ export default function Content(props: Props): JSX.Element {
)}
</div>
</ContentPanel>
),
[
LanguageSwitcher,
content.categories,
content.thumbnail?.data?.attributes,
content.type,
isMobile,
languages,
langui,
nextContent?.attributes,
previousContent?.attributes,
selectedTranslation,
]
);
return (

View File

@ -71,7 +71,8 @@ export default function Contents(props: Props): JSX.Element {
[combineRelatedContent, searchName.length]
);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.Workspaces}
@ -131,8 +132,19 @@ export default function Contents(props: Props): JSX.Element {
}}
/>
</SubPanel>
),
[
effectiveCombineRelatedContent,
groupingMethod,
hoverable,
keepInfoVisible,
langui,
searchName,
]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
{/* TODO: Add to langui */}
{groups.size === 0 && (
@ -162,8 +174,8 @@ export default function Contents(props: Props): JSX.Element {
) {
return (
currentSum +
(item.attributes.group.data.attributes.contents?.data
.length ?? 1)
(item.attributes.group.data.attributes.contents
?.data.length ?? 1)
);
}
}
@ -202,16 +214,16 @@ export default function Contents(props: Props): JSX.Element {
effectiveCombineRelatedContent &&
item.attributes.group?.data?.attributes?.combine ===
true
? item.attributes.group.data.attributes.contents?.data
.length
? item.attributes.group.data.attributes.contents
?.data.length
: 0
}
topChips={
item.attributes.type?.data?.attributes
? [
item.attributes.type.data.attributes.titles?.[0]
? item.attributes.type.data.attributes.titles[0]
?.title
? item.attributes.type.data.attributes
.titles[0]?.title
: prettySlug(
item.attributes.type.data.attributes.slug
),
@ -230,7 +242,17 @@ export default function Contents(props: Props): JSX.Element {
)
)}
</ContentPanel>
),
[
effectiveCombineRelatedContent,
groups,
keepInfoVisible,
languages,
langui.result,
langui.results,
]
);
return (
<AppLayout
navTitle={langui.contents}

View File

@ -12,6 +12,7 @@ import { getReadySdk } from "graphql/sdk";
import { filterDefined, filterHasAttributes } from "helpers/others";
import { GetStaticPropsContext } from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {
contents: DevGetContentsQuery;
@ -21,7 +22,8 @@ export default function CheckupContents(props: Props): JSX.Element {
const { contents } = props;
const testReport = testingContent(contents);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
{<h2 className="text-2xl">{testReport.title}</h2>}
@ -75,7 +77,10 @@ export default function CheckupContents(props: Props): JSX.Element {
</div>
))}
</ContentPanel>
),
[testReport.lines, testReport.title]
);
return (
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
);

View File

@ -14,6 +14,7 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getReadySdk } from "graphql/sdk";
import { GetStaticPropsContext } from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {
libraryItems: DevGetLibraryItemsQuery;
@ -23,7 +24,8 @@ export default function CheckupLibraryItems(props: Props): JSX.Element {
const { libraryItems } = props;
const testReport = testingLibraryItem(libraryItems);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
{<h2 className="text-2xl">{testReport.title}</h2>}
@ -77,7 +79,10 @@ export default function CheckupLibraryItems(props: Props): JSX.Element {
</div>
))}
</ContentPanel>
),
[testReport.lines, testReport.title]
);
return (
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
);

View File

@ -10,7 +10,7 @@ import { ToolTip } from "components/ToolTip";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import TurndownService from "turndown";
import { Icon } from "components/Ico";
import { TOC } from "components/Markdown/TOC";
@ -25,11 +25,42 @@ export default function Editor(props: Props): JSX.Element {
const [markdown, setMarkdown] = useState("");
const [converterOpened, setConverterOpened] = useState(false);
function wrap(
const transformationWrapper = useCallback(
(
transformation: (
value: string,
selectionStart: number,
selectedEnd: number
) => { prependLength: number; transformedValue: string }
) => {
const textarea =
document.querySelector<HTMLTextAreaElement>("#editorTextArea");
if (textarea) {
const { value, selectionStart, selectionEnd } = textarea;
const { prependLength, transformedValue } = transformation(
value,
selectionStart,
selectionEnd
);
textarea.value = transformedValue;
handleInput(textarea.value);
textarea.focus();
textarea.selectionStart = selectionStart + prependLength;
textarea.selectionEnd = selectionEnd + prependLength;
}
},
[handleInput]
);
const wrap = useCallback(
(
wrapper: string,
properties?: Record<string, string>,
addInnerNewLines?: boolean
) {
) => {
transformationWrapper((value, selectionStart, selectionEnd) => {
let prepend = wrapper;
let append = wrapper;
@ -55,13 +86,29 @@ export default function Editor(props: Props): JSX.Element {
newValue += value.slice(selectionEnd);
return { prependLength: prepend.length, transformedValue: newValue };
});
}
},
[transformationWrapper]
);
function toggleWrap(
const unwrap = useCallback(
(wrapper: string) => {
transformationWrapper((value, selectionStart, selectionEnd) => {
let newValue = "";
newValue += value.slice(0, selectionStart - wrapper.length);
newValue += value.slice(selectionStart, selectionEnd);
newValue += value.slice(wrapper.length + selectionEnd);
return { prependLength: -wrapper.length, transformedValue: newValue };
});
},
[transformationWrapper]
);
const toggleWrap = useCallback(
(
wrapper: string,
properties?: Record<string, string>,
addInnerNewLines?: boolean
) {
) => {
const textarea =
document.querySelector<HTMLTextAreaElement>("#editorTextArea");
if (textarea) {
@ -77,21 +124,15 @@ export default function Editor(props: Props): JSX.Element {
wrap(wrapper, properties, addInnerNewLines);
}
}
}
},
[unwrap, wrap]
);
function unwrap(wrapper: string) {
transformationWrapper((value, selectionStart, selectionEnd) => {
let newValue = "";
newValue += value.slice(0, selectionStart - wrapper.length);
newValue += value.slice(selectionStart, selectionEnd);
newValue += value.slice(wrapper.length + selectionEnd);
return { prependLength: -wrapper.length, transformedValue: newValue };
});
}
function preline(prepend: string) {
const preline = useCallback(
(prepend: string) => {
transformationWrapper((value, selectionStart) => {
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
const lastNewLine =
value.slice(0, selectionStart).lastIndexOf("\n") + 1;
let newValue = "";
newValue += value.slice(0, lastNewLine);
@ -100,9 +141,12 @@ export default function Editor(props: Props): JSX.Element {
return { prependLength: prepend.length, transformedValue: newValue };
});
}
},
[transformationWrapper]
);
function insert(prepend: string) {
const insert = useCallback(
(prepend: string) => {
transformationWrapper((value, selectionStart) => {
let newValue = "";
newValue += value.slice(0, selectionStart);
@ -111,54 +155,33 @@ export default function Editor(props: Props): JSX.Element {
return { prependLength: prepend.length, transformedValue: newValue };
});
}
},
[transformationWrapper]
);
function appendDoc(append: string) {
const appendDoc = useCallback(
(append: string) => {
transformationWrapper((value) => {
const newValue = value + append;
return { prependLength: 0, transformedValue: newValue };
});
}
function transformationWrapper(
transformation: (
value: string,
selectionStart: number,
selectedEnd: number
) => { prependLength: number; transformedValue: string }
) {
const textarea =
document.querySelector<HTMLTextAreaElement>("#editorTextArea");
if (textarea) {
const { value, selectionStart, selectionEnd } = textarea;
const { prependLength, transformedValue } = transformation(
value,
selectionStart,
selectionEnd
},
[transformationWrapper]
);
textarea.value = transformedValue;
handleInput(textarea.value);
textarea.focus();
textarea.selectionStart = selectionStart + prependLength;
textarea.selectionEnd = selectionEnd + prependLength;
}
}
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<Popup setState={setConverterOpened} state={converterOpened}>
<div className="text-center">
<h2 className="mt-4">Convert HTML to markdown</h2>
<p>
Copy and paste any HTML content (content from web pages) here.{" "}
Copy and paste any HTML content (content from web pages) here.
<br />
The text will immediatly be converted to valid Markdown.
<br />
You can then copy the converted text and paste it anywhere you want
in the editor
You can then copy the converted text and paste it anywhere you
want in the editor
</p>
</div>
<textarea
@ -258,8 +281,8 @@ export default function Editor(props: Props): JSX.Element {
<>
<h3 className="text-lg">Transcripts</h3>
<p>
Use this to create dialogues and transcripts. Start by adding a
container, then add transcript speech line within.
Use this to create dialogues and transcripts. Start by adding
a container, then add transcript speech line within.
</p>
<div className="grid gap-2">
<ToolTip
@ -381,7 +404,9 @@ export default function Editor(props: Props): JSX.Element {
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Player&rsquo;s name placeholder</h3>}
content={
<h3 className="text-lg">Player&rsquo;s name placeholder</h3>
}
>
<Button onClick={() => insert("<player>")} icon={Icon.Person} />
</ToolTip>
@ -426,7 +451,19 @@ export default function Editor(props: Props): JSX.Element {
<TOC text={markdown} />
</div>
</ContentPanel>
),
[
appendDoc,
converterOpened,
handleInput,
insert,
markdown,
preline,
toggleWrap,
wrap,
]
);
return (
<AppLayout
navTitle="Markdawn Editor"

View File

@ -49,15 +49,17 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { Fragment, useState } from "react";
import { Fragment, useMemo, useState } from "react";
import { isUntangibleGroupItem } from "helpers/libraryItem";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { WithLabel } from "components/Inputs/WithLabel";
interface Props extends AppStaticProps {
item: NonNullable<
NonNullable<
GetLibraryItemQuery["libraryItems"]
>["data"][number]["attributes"];
>["data"][number]["attributes"]
>;
itemId: NonNullable<
GetLibraryItemQuery["libraryItems"]
>["data"][number]["id"];
@ -67,29 +69,29 @@ export default function LibrarySlug(props: Props): JSX.Element {
const { item, itemId, langui, currencies } = props;
const appLayout = useAppLayout();
const hoverable = useMediaHoverable();
useScrollTopOnChange(AnchorIds.ContentPanel, [item]);
const isVariantSet =
item?.metadata?.[0]?.__typename === "ComponentMetadataGroup" &&
item.metadata[0].subtype?.data?.attributes?.slug === "variant-set";
sortContent(item?.contents);
const [openLightBox, LightBox] = useLightBox();
const [keepInfoVisible, setKeepInfoVisible] = useState(false);
let displayOpenScans = false;
if (item?.contents?.data)
for (const content of item.contents.data) {
if (
content.attributes?.scan_set &&
content.attributes.scan_set.length > 0
)
displayOpenScans = true;
}
useScrollTopOnChange(AnchorIds.ContentPanel, [item]);
const subPanel = (
const isVariantSet = useMemo(
() =>
item.metadata?.[0]?.__typename === "ComponentMetadataGroup" &&
item.metadata[0].subtype?.data?.attributes?.slug === "variant-set",
[item.metadata]
);
const displayOpenScans = useMemo(
() =>
item.contents?.data.some(
(content) =>
content.attributes?.scan_set && content.attributes.scan_set.length > 0
),
[item.contents?.data]
);
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/library/"
@ -102,13 +104,13 @@ export default function LibrarySlug(props: Props): JSX.Element {
<div className="grid gap-4">
<NavOption title={langui.summary} url="#summary" border />
{item?.gallery && item.gallery.data.length > 0 && (
{item.gallery && item.gallery.data.length > 0 && (
<NavOption title={langui.gallery} url="#gallery" border />
)}
<NavOption title={langui.details} url="#details" border />
{item?.subitems && item.subitems.data.length > 0 && (
{item.subitems && item.subitems.data.length > 0 && (
<NavOption
title={isVariantSet ? langui.variants : langui.subitems}
url={isVariantSet ? "#variants" : "#subitems"}
@ -116,14 +118,17 @@ export default function LibrarySlug(props: Props): JSX.Element {
/>
)}
{item?.contents && item.contents.data.length > 0 && (
{item.contents && item.contents.data.length > 0 && (
<NavOption title={langui.contents} url="#contents" border />
)}
</div>
</SubPanel>
),
[isVariantSet, item.contents, item.gallery, item.subitems, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<LightBox />
@ -139,7 +144,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
className="relative h-[50vh] w-full
cursor-pointer drop-shadow-shade-xl desktop:mb-16 mobile:h-[60vh]"
onClick={() => {
if (item?.thumbnail?.data?.attributes) {
if (item.thumbnail?.data?.attributes) {
openLightBox([
getAssetURL(
item.thumbnail.data.attributes.url,
@ -149,7 +154,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
}
}}
>
{item?.thumbnail?.data?.attributes ? (
{item.thumbnail?.data?.attributes ? (
<Img
image={item.thumbnail.data.attributes}
quality={ImageQuality.Large}
@ -162,7 +167,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
<InsetBox id="summary" className="grid place-items-center">
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-8">
{item?.subitem_of?.data[0]?.attributes && (
{item.subitem_of?.data[0]?.attributes && (
<div className="grid place-items-center">
<p>{langui.subitem_of}</p>
<Button
@ -176,23 +181,25 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
)}
<div className="grid place-items-center text-center">
<h1 className="text-3xl">{item?.title}</h1>
{item && isDefinedAndNotEmpty(item.subtitle) && (
<h1 className="text-3xl">{item.title}</h1>
{isDefinedAndNotEmpty(item.subtitle) && (
<h2 className="text-2xl">{item.subtitle}</h2>
)}
</div>
<PreviewCardCTAs
id={itemId}
displayCTAs={!isUntangibleGroupItem(item?.metadata?.[0])}
displayCTAs={!isUntangibleGroupItem(item.metadata?.[0])}
langui={langui}
expand
/>
{item?.descriptions?.[0] && (
<p className="text-justify">{item.descriptions[0].description}</p>
{item.descriptions?.[0] && (
<p className="text-justify">
{item.descriptions[0].description}
</p>
)}
{!(
item?.metadata &&
item.metadata &&
item.metadata[0]?.__typename === "ComponentMetadataGroup" &&
(item.metadata[0].subtype?.data?.attributes?.slug ===
"variant-set" ||
@ -200,7 +207,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
"relation-set")
) && (
<>
{item?.urls && item.urls.length ? (
{item.urls?.length ? (
<div className="flex flex-row place-items-center gap-3">
<p>{langui.available_at}</p>
{filterHasAttributes(item.urls).map((url, index) => (
@ -221,7 +228,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
</InsetBox>
{item?.gallery && item.gallery.data.length > 0 && (
{item.gallery && item.gallery.data.length > 0 && (
<div id="gallery" className="grid w-full place-items-center gap-8">
<h2 className="text-2xl">{langui.gallery}</h2>
<div
@ -238,7 +245,10 @@ export default function LibrarySlug(props: Props): JSX.Element {
const images: string[] = filterHasAttributes(
item.gallery?.data
).map((image) =>
getAssetURL(image.attributes.url, ImageQuality.Large)
getAssetURL(
image.attributes.url,
ImageQuality.Large
)
);
openLightBox(images, index);
}}
@ -263,7 +273,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
className="grid place-items-center gap-y-8
desktop:grid-flow-col desktop:place-content-between"
>
{item?.metadata?.[0] && (
{item.metadata?.[0] && (
<div className="grid place-content-start place-items-center">
<h3 className="text-xl">{langui.type}</h3>
<div className="grid grid-flow-col gap-1">
@ -274,14 +284,14 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
)}
{item?.release_date && (
{item.release_date && (
<div className="grid place-content-start place-items-center">
<h3 className="text-xl">{langui.release_date}</h3>
<p>{prettyDate(item.release_date)}</p>
</div>
)}
{item?.price && (
{item.price && (
<div className="grid place-content-start place-items-center text-center">
<h3 className="text-xl">{langui.price}</h3>
<p>
@ -294,7 +304,11 @@ export default function LibrarySlug(props: Props): JSX.Element {
{item.price.currency?.data?.attributes?.code !==
appLayout.currency && (
<p>
{prettyPrice(item.price, currencies, appLayout.currency)}{" "}
{prettyPrice(
item.price,
currencies,
appLayout.currency
)}{" "}
<br />({langui.calculated?.toLowerCase()})
</p>
)}
@ -302,7 +316,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
)}
</div>
{item?.categories && item.categories.data.length > 0 && (
{item.categories && item.categories.data.length > 0 && (
<div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{langui.categories}</h3>
<div className="flex flex-row flex-wrap place-content-center gap-2">
@ -313,7 +327,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
)}
{item?.size && (
{item.size && (
<div className="grid gap-8 mobile:place-items-center">
<h3 className="text-xl">{langui.size}</h3>
<div
@ -356,12 +370,12 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
)}
{item?.metadata?.[0]?.__typename !== "ComponentMetadataGroup" &&
item?.metadata?.[0]?.__typename !== "ComponentMetadataOther" && (
{item.metadata?.[0]?.__typename !== "ComponentMetadataGroup" &&
item.metadata?.[0]?.__typename !== "ComponentMetadataOther" && (
<>
<h3 className="text-xl">{langui.type_information}</h3>
<div className="grid w-full grid-cols-2 place-content-between">
{item?.metadata?.[0]?.__typename ===
{item.metadata?.[0]?.__typename ===
"ComponentMetadataBooks" && (
<>
<div className="flex flex-row place-content-start gap-4">
@ -408,7 +422,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
</InsetBox>
{item?.subitems && item.subitems.data.length > 0 && (
{item.subitems && item.subitems.data.length > 0 && (
<div
id={isVariantSet ? "variants" : "subitems"}
className="grid w-full place-items-center gap-8"
@ -477,7 +491,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
</div>
)}
{item?.contents && item.contents.data.length > 0 && (
{item.contents && item.contents.data.length > 0 && (
<div id="contents" className="grid w-full place-items-center gap-8">
<h2 className="-mb-6 text-2xl">{langui.contents}</h2>
{displayOpenScans && (
@ -500,15 +514,29 @@ export default function LibrarySlug(props: Props): JSX.Element {
)}
</div>
</ContentPanel>
),
[
LightBox,
openLightBox,
appLayout.currency,
currencies,
displayOpenScans,
hoverable,
isVariantSet,
item,
itemId,
keepInfoVisible,
langui,
]
);
return (
<AppLayout
navTitle={prettyinlineTitle("", item?.title, item?.subtitle)}
navTitle={prettyinlineTitle("", item.title, item.subtitle)}
contentPanel={contentPanel}
subPanel={subPanel}
thumbnail={item?.thumbnail?.data?.attributes ?? undefined}
description={item?.descriptions?.[0]?.description ?? undefined}
thumbnail={item.thumbnail?.data?.attributes ?? undefined}
description={item.descriptions?.[0]?.description ?? undefined}
{...props}
/>
);
@ -526,6 +554,7 @@ export async function getStaticProps(
language_code: context.locale ?? "en",
});
if (!item.libraryItems?.data[0]?.attributes) return { notFound: true };
sortContent(item.libraryItems.data[0].attributes.contents);
const props: Props = {
...(await getAppStaticProps(context)),
item: item.libraryItems.data[0].attributes,
@ -544,7 +573,7 @@ export async function getStaticPaths(
const paths: GetStaticPathsResult["paths"] = [];
filterHasAttributes(libraryItems.libraryItems?.data).map((item) => {
context.locales?.map((local) =>
paths.push({ params: { slug: item.attributes?.slug }, locale: local })
paths.push({ params: { slug: item.attributes.slug }, locale: local })
);
});

View File

@ -23,33 +23,36 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { Fragment } from "react";
import { Fragment, useMemo } from "react";
interface Props extends AppStaticProps {
item: NonNullable<
NonNullable<
GetLibraryItemScansQuery["libraryItems"]
>["data"][number]["attributes"];
>["data"][number]["attributes"]
>;
itemId: NonNullable<
GetLibraryItemScansQuery["libraryItems"]
>["data"][number]["id"];
NonNullable<GetLibraryItemScansQuery["libraryItems"]>["data"][number]["id"]
>;
}
export default function LibrarySlug(props: Props): JSX.Element {
const { item, langui, languages } = props;
const [openLightBox, LightBox] = useLightBox();
sortContent(item?.contents);
sortContent(item.contents);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href={`/library/${item?.slug}`}
href={`/library/${item.slug}`}
title={langui.item}
langui={langui}
displayOn={ReturnButtonType.Desktop}
horizontalLine
/>
{item?.contents?.data.map((content) => (
{item.contents?.data.map((content) => (
<NavOption
key={content.id}
url={`#${content.attributes?.slug}`}
@ -66,21 +69,24 @@ export default function LibrarySlug(props: Props): JSX.Element {
/>
))}
</SubPanel>
),
[item.contents?.data, item.slug, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<LightBox />
<ReturnButton
href={`/library/${item?.slug}`}
href={`/library/${item.slug}`}
title={langui.item}
langui={langui}
displayOn={ReturnButtonType.Mobile}
className="mb-10"
/>
{item?.images && (
{item.images && (
<ScanSetCover
images={item.images}
openLightBox={openLightBox}
@ -89,7 +95,7 @@ export default function LibrarySlug(props: Props): JSX.Element {
/>
)}
{item?.contents?.data.map((content) => (
{item.contents?.data.map((content) => (
<Fragment key={content.id}>
{content.attributes?.scan_set?.[0] && (
<ScanSet
@ -105,14 +111,24 @@ export default function LibrarySlug(props: Props): JSX.Element {
</Fragment>
))}
</ContentPanel>
),
[
LightBox,
openLightBox,
item.contents?.data,
item.images,
item.slug,
languages,
langui,
]
);
return (
<AppLayout
navTitle={prettyinlineTitle("", item?.title, item?.subtitle)}
navTitle={prettyinlineTitle("", item.title, item.subtitle)}
contentPanel={contentPanel}
subPanel={subPanel}
thumbnail={item?.thumbnail?.data?.attributes ?? undefined}
thumbnail={item.thumbnail?.data?.attributes ?? undefined}
{...props}
/>
);
@ -129,7 +145,8 @@ export async function getStaticProps(
: "",
language_code: context.locale ?? "en",
});
if (!item.libraryItems?.data[0]?.attributes) return { notFound: true };
if (!item.libraryItems?.data[0]?.attributes || !item.libraryItems.data[0]?.id)
return { notFound: true };
const props: Props = {
...(await getAppStaticProps(context)),
item: item.libraryItems.data[0].attributes,

View File

@ -114,7 +114,8 @@ export default function Library(props: Props): JSX.Element {
[langui, groupingMethod, sortedItems]
);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.LibraryBooks}
@ -177,7 +178,10 @@ export default function Library(props: Props): JSX.Element {
<WithLabel
label={langui.show_secondary_items}
input={
<Switch state={showSecondaryItems} setState={setShowSecondaryItems} />
<Switch
state={showSecondaryItems}
setState={setShowSecondaryItems}
/>
}
/>
@ -237,8 +241,23 @@ export default function Library(props: Props): JSX.Element {
}}
/>
</SubPanel>
),
[
filterUserStatus,
groupingMethod,
hoverable,
keepInfoVisible,
langui,
searchName,
showPrimaryItems,
showSecondaryItems,
showSubitems,
sortingMethod,
]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
{/* TODO: Add to langui */}
{groups.size === 0 && (
@ -311,7 +330,10 @@ export default function Library(props: Props): JSX.Element {
</Fragment>
))}
</ContentPanel>
),
[currencies, groups, keepInfoVisible, langui]
);
return (
<AppLayout
navTitle={langui.library}

View File

@ -5,11 +5,13 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { GetStaticPropsContext } from "next";
import { Icon } from "components/Ico";
import { useMemo } from "react";
interface Props extends AppStaticProps {}
export default function Merch(props: Props): JSX.Element {
const { langui } = props;
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.Store}
@ -17,6 +19,8 @@ export default function Merch(props: Props): JSX.Element {
description={langui.merch_description}
/>
</SubPanel>
),
[langui]
);
return <AppLayout navTitle={langui.merch} subPanel={subPanel} {...props} />;

View File

@ -46,7 +46,8 @@ export default function News(props: Props): JSX.Element {
[posts, searchName]
);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.Feed}
@ -80,9 +81,12 @@ export default function News(props: Props): JSX.Element {
}}
/>
</SubPanel>
),
[hoverable, keepInfoVisible, langui, searchName]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div
className="grid grid-cols-1 items-end gap-8
@ -113,6 +117,8 @@ export default function News(props: Props): JSX.Element {
))}
</div>
</ContentPanel>
),
[filteredItems, keepInfoVisible]
);
return (

View File

@ -26,6 +26,7 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { useMemo } from "react";
interface Props extends AppStaticProps {
page: WikiPageWithTranslations;
@ -40,7 +41,8 @@ export default function WikiPage(props: Props): JSX.Element {
languageExtractor: (item) => item.language?.data?.attributes?.code,
});
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href={`/wiki`}
@ -50,8 +52,12 @@ export default function WikiPage(props: Props): JSX.Element {
horizontalLine
/>
</SubPanel>
),
[langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Large}>
<ReturnButton
href={`/wiki`}
@ -98,13 +104,13 @@ export default function WikiPage(props: Props): JSX.Element {
<DefinitionCard
key={index}
source={definition.source?.data?.attributes?.name}
translations={filterHasAttributes(definition.translations).map(
(translation) => ({
translations={filterHasAttributes(
definition.translations
).map((translation) => ({
language: translation.language.data?.attributes?.code,
definition: translation.definition,
status: translation.status,
})
)}
}))}
index={index + 1}
languages={languages}
langui={langui}
@ -114,6 +120,16 @@ export default function WikiPage(props: Props): JSX.Element {
</div>
)}
</ContentPanel>
),
[
LanguageSwitcher,
languages,
langui,
page.categories?.data,
page.definitions,
page.thumbnail?.data?.attributes,
selectedTranslation,
]
);
return (

View File

@ -14,7 +14,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettySlug } from "helpers/formatters";
import { filterHasAttributes, isDefined } from "helpers/others";
import { GetStaticPropsContext } from "next";
import { Fragment } from "react";
import { Fragment, useMemo } from "react";
interface Props extends AppStaticProps {
chronologyItems: NonNullable<
@ -27,10 +27,10 @@ export default function Chronology(props: Props): JSX.Element {
const { chronologyItems, chronologyEras, langui } = props;
// Group by year the Chronology items
const chronologyItemYearGroups: Props["chronologyItems"][number][][][] = [];
const chronologyItemYearGroups = useMemo(() => {
const memo: Props["chronologyItems"][number][][][] = [];
chronologyEras.map(() => {
chronologyItemYearGroups.push([]);
memo.push([]);
});
let currentChronologyEraIndex = 0;
@ -45,22 +45,21 @@ export default function Chronology(props: Props): JSX.Element {
}
if (
Object.prototype.hasOwnProperty.call(
chronologyItemYearGroups[currentChronologyEraIndex],
memo[currentChronologyEraIndex],
item.attributes.year
)
) {
chronologyItemYearGroups[currentChronologyEraIndex][
item.attributes.year
].push(item);
memo[currentChronologyEraIndex][item.attributes.year].push(item);
} else {
chronologyItemYearGroups[currentChronologyEraIndex][
item.attributes.year
] = [item];
memo[currentChronologyEraIndex][item.attributes.year] = [item];
}
}
});
return memo;
}, [chronologyEras, chronologyItems]);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/wiki"
@ -87,9 +86,12 @@ export default function Chronology(props: Props): JSX.Element {
</Fragment>
))}
</SubPanel>
),
[chronologyEras, langui]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel>
<ReturnButton
href="/wiki"
@ -130,6 +132,8 @@ export default function Chronology(props: Props): JSX.Element {
</Fragment>
))}
</ContentPanel>
),
[chronologyEras, chronologyItemYearGroups, langui]
);
return (

View File

@ -47,7 +47,8 @@ export default function Wiki(props: Props): JSX.Element {
[pages, searchName]
);
const subPanel = (
const subPanel = useMemo(
() => (
<SubPanel>
<PanelHeader
icon={Icon.TravelExplore}
@ -87,9 +88,12 @@ export default function Wiki(props: Props): JSX.Element {
<NavOption title={langui.chronology} url="/wiki/chronology" border />
</SubPanel>
),
[hoverable, keepInfoVisible, langui, searchName]
);
const contentPanel = (
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div
className="grid grid-cols-2 items-end gap-8
@ -130,6 +134,8 @@ export default function Wiki(props: Props): JSX.Element {
))}
</div>
</ContentPanel>
),
[filteredPages, keepInfoVisible, languages]
);
return (