Fixed key problems + search function

This commit is contained in:
DrMint 2022-05-17 23:16:22 +02:00
parent 50e988f64f
commit fa96469ebf
20 changed files with 214 additions and 149 deletions

View File

@ -46,11 +46,13 @@ export function AppLayout(props: Immutable<Props>): JSX.Element {
const sensibilitySwipe = 1.1; const sensibilitySwipe = 1.1;
useMemo(() => { useMemo(() => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
router.events?.on("routeChangeStart", () => { router.events?.on("routeChangeStart", () => {
appLayout.setConfigPanelOpen(false); appLayout.setConfigPanelOpen(false);
appLayout.setMainPanelOpen(false); appLayout.setMainPanelOpen(false);
appLayout.setSubPanelOpen(false); appLayout.setSubPanelOpen(false);
}); });
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
router.events?.on("hashChangeStart", () => { router.events?.on("hashChangeStart", () => {
appLayout.setSubPanelOpen(false); appLayout.setSubPanelOpen(false);
}); });
@ -425,7 +427,9 @@ export function AppLayout(props: Immutable<Props>): JSX.Element {
) )
} }
> >
<span className="material-icons !text-base">text_decrease</span> <span className="material-icons !text-base">
text_decrease
</span>
</Button> </Button>
<Button <Button
className="rounded-l-none rounded-r-none border-x-0" className="rounded-l-none rounded-r-none border-x-0"
@ -449,7 +453,9 @@ export function AppLayout(props: Immutable<Props>): JSX.Element {
) )
} }
> >
<span className="material-icons !text-base">text_increase</span> <span className="material-icons !text-base">
text_increase
</span>
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
import { AppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps } from "graphql/getAppStaticProps";
import { prettyLanguage } from "helpers/formatters"; import { prettyLanguage } from "helpers/formatters";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { Dispatch, SetStateAction } from "react"; import { Dispatch, Fragment, SetStateAction } from "react";
import { ToolTip } from "../ToolTip"; import { ToolTip } from "../ToolTip";
import { Button } from "./Button"; import { Button } from "./Button";
@ -21,17 +21,16 @@ export function LanguageSwitcher(props: Immutable<Props>): JSX.Element {
content={ content={
<div className={`flex flex-col gap-2 ${className}`}> <div className={`flex flex-col gap-2 ${className}`}>
{[...locales].map(([locale, value], index) => ( {[...locales].map(([locale, value], index) => (
<> <Fragment key={index}>
{locale && ( {locale && (
<Button <Button
key={index}
active={value === localesIndex} active={value === localesIndex}
onClick={() => setLocalesIndex(value)} onClick={() => setLocalesIndex(value)}
> >
{prettyLanguage(locale, props.languages)} {prettyLanguage(locale, props.languages)}
</Button> </Button>
)} )}
</> </Fragment>
))} ))}
</div> </div>
} }

View File

@ -1,6 +1,6 @@
import { arrayMove } from "helpers/others"; import { arrayMove } from "helpers/others";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { useEffect, useState } from "react"; import { Fragment, useEffect, useState } from "react";
interface Props { interface Props {
className?: string; className?: string;
@ -25,7 +25,7 @@ export function OrderableList(props: Immutable<Props>): JSX.Element {
return ( return (
<div className="grid gap-2"> <div className="grid gap-2">
{[...items].map(([key, value], index) => ( {[...items].map(([key, value], index) => (
<> <Fragment key={key}>
{props.insertLabels?.get(index) && ( {props.insertLabels?.get(index) && (
<p>{props.insertLabels.get(index)}</p> <p>{props.insertLabels.get(index)}</p>
)} )}
@ -60,7 +60,6 @@ export function OrderableList(props: Immutable<Props>): JSX.Element {
border-[1px] transition-all hover:text-light hover:bg-dark border-[1px] transition-all hover:text-light hover:bg-dark
hover:drop-shadow-shade-lg border-dark bg-light text-dark hover:drop-shadow-shade-lg border-dark bg-light text-dark
rounded-full cursor-grab select-none px-1 py-2 pr-4 gap-2" rounded-full cursor-grab select-none px-1 py-2 pr-4 gap-2"
key={key}
draggable draggable
> >
<div className="grid grid-rows-[.8em_.8em] place-items-center"> <div className="grid grid-rows-[.8em_.8em] place-items-center">
@ -87,7 +86,7 @@ export function OrderableList(props: Immutable<Props>): JSX.Element {
</div> </div>
{value} {value}
</div> </div>
</> </Fragment>
))} ))}
</div> </div>
); );

View File

@ -1,5 +1,5 @@
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { Dispatch, SetStateAction, useState } from "react"; import { Dispatch, Fragment, SetStateAction, useState } from "react";
interface Props { interface Props {
setState: Dispatch<SetStateAction<number>>; setState: Dispatch<SetStateAction<number>>;
@ -47,12 +47,12 @@ export function Select(props: Immutable<Props>): JSX.Element {
}`} }`}
> >
{props.options.map((option, index) => ( {props.options.map((option, index) => (
<> <Fragment key={index}>
{index !== props.state && ( {index !== props.state && (
<div <div
className="bg-light hover:bg-mid transition-colors className="bg-light hover:bg-mid transition-colors
cursor-pointer p-1 last-of-type:rounded-b-[1em]" cursor-pointer p-1 last-of-type:rounded-b-[1em]"
key={index}
id={option} id={option}
onClick={() => { onClick={() => {
setOpened(false); setOpened(false);
@ -62,7 +62,7 @@ export function Select(props: Immutable<Props>): JSX.Element {
{option} {option}
</div> </div>
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -5,23 +5,25 @@ interface Props {
setState: Dispatch<SetStateAction<boolean>>; setState: Dispatch<SetStateAction<boolean>>;
state: boolean; state: boolean;
className?: string; className?: string;
disabled?: boolean;
} }
export function Switch(props: Immutable<Props>): JSX.Element { export function Switch(props: Immutable<Props>): JSX.Element {
const { state, setState, className, disabled } = props;
return ( return (
<div <div
className={`h-6 w-12 rounded-full border-2 border-mid grid className={`h-6 w-12 rounded-full border-2 border-mid grid
transition-colors relative cursor-pointer ${props.className} ${ transition-colors relative ${
props.state ? "bg-mid" : "bg-light" disabled ? "cursor-not-allowed" : "cursor-pointer"
}`} } ${className} ${state ? "bg-mid" : "bg-light"}`}
onClick={() => { onClick={() => {
props.setState(!props.state); if (!disabled) setState(!state);
}} }}
> >
<div <div
className={`bg-dark aspect-square rounded-full absolute className={`bg-dark aspect-square rounded-full absolute
top-0 bottom-0 left-0 transition-transform ${ top-0 bottom-0 left-0 transition-transform ${
props.state && "translate-x-[115%]" state && "translate-x-[115%]"
}`} }`}
></div> ></div>
</div> </div>

View File

@ -10,6 +10,7 @@ import { isInteger } from "helpers/numbers";
import { getStatusDescription } from "helpers/others"; import { getStatusDescription } from "helpers/others";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { Fragment } from "react";
interface Props { interface Props {
openLightBox: (images: string[], index?: number) => void; openLightBox: (images: string[], index?: number) => void;
@ -121,15 +122,14 @@ export function ScanSet(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Scanners"}:</p> <p className="font-headers">{"Scanners"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.scanners.data.map((scanner) => ( {selectedScan.scanners.data.map((scanner) => (
<> <Fragment key={scanner.id}>
{scanner.attributes && ( {scanner.attributes && (
<RecorderChip <RecorderChip
key={scanner.id}
langui={langui} langui={langui}
recorder={scanner.attributes} recorder={scanner.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>
@ -140,15 +140,14 @@ export function ScanSet(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Cleaners"}:</p> <p className="font-headers">{"Cleaners"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.cleaners.data.map((cleaner) => ( {selectedScan.cleaners.data.map((cleaner) => (
<> <Fragment key={cleaner.id}>
{cleaner.attributes && ( {cleaner.attributes && (
<RecorderChip <RecorderChip
key={cleaner.id}
langui={langui} langui={langui}
recorder={cleaner.attributes} recorder={cleaner.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>
@ -160,15 +159,14 @@ export function ScanSet(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Typesetters"}:</p> <p className="font-headers">{"Typesetters"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.typesetters.data.map((typesetter) => ( {selectedScan.typesetters.data.map((typesetter) => (
<> <Fragment key={typesetter.id}>
{typesetter.attributes && ( {typesetter.attributes && (
<RecorderChip <RecorderChip
key={typesetter.id}
langui={langui} langui={langui}
recorder={typesetter.attributes} recorder={typesetter.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@ import { getAssetURL, ImageQuality } from "helpers/img";
import { getStatusDescription } from "helpers/others"; import { getStatusDescription } from "helpers/others";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { Fragment } from "react";
interface Props { interface Props {
openLightBox: (images: string[], index?: number) => void; openLightBox: (images: string[], index?: number) => void;
@ -87,15 +88,14 @@ export function ScanSetCover(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Scanners"}:</p> <p className="font-headers">{"Scanners"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.scanners.data.map((scanner) => ( {selectedScan.scanners.data.map((scanner) => (
<> <Fragment key={scanner.id}>
{scanner.attributes && ( {scanner.attributes && (
<RecorderChip <RecorderChip
key={scanner.id}
langui={langui} langui={langui}
recorder={scanner.attributes} recorder={scanner.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>
@ -106,15 +106,14 @@ export function ScanSetCover(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Cleaners"}:</p> <p className="font-headers">{"Cleaners"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.cleaners.data.map((cleaner) => ( {selectedScan.cleaners.data.map((cleaner) => (
<> <Fragment key={cleaner.id}>
{cleaner.attributes && ( {cleaner.attributes && (
<RecorderChip <RecorderChip
key={cleaner.id}
langui={langui} langui={langui}
recorder={cleaner.attributes} recorder={cleaner.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>
@ -126,15 +125,14 @@ export function ScanSetCover(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Typesetters"}:</p> <p className="font-headers">{"Typesetters"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedScan.typesetters.data.map((typesetter) => ( {selectedScan.typesetters.data.map((typesetter) => (
<> <Fragment key={typesetter.id}>
{typesetter.attributes && ( {typesetter.attributes && (
<RecorderChip <RecorderChip
key={typesetter.id}
langui={langui} langui={langui}
recorder={typesetter.attributes} recorder={typesetter.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
import { slugify } from "helpers/formatters"; import { slugify } from "helpers/formatters";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Fragment } from "react";
import { preprocessMarkDawn } from "./Markdawn"; import { preprocessMarkDawn } from "./Markdawn";
interface Props { interface Props {
@ -39,11 +40,8 @@ function TOCLevel(props: LevelProps): JSX.Element {
return ( return (
<ol className="pl-4 text-left"> <ol className="pl-4 text-left">
{tocchildren.map((child, childIndex) => ( {tocchildren.map((child, childIndex) => (
<> <Fragment key={child.slug}>
<li <li className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap">
key={child.slug}
className="my-2 overflow-x-hidden w-full text-ellipsis whitespace-nowrap"
>
<span className="text-dark">{`${parentNumbering}${ <span className="text-dark">{`${parentNumbering}${
childIndex + 1 childIndex + 1
}.`}</span>{" "} }.`}</span>{" "}
@ -55,7 +53,7 @@ function TOCLevel(props: LevelProps): JSX.Element {
tocchildren={child.children} tocchildren={child.children}
parentNumbering={`${parentNumbering}${childIndex + 1}.`} parentNumbering={`${parentNumbering}${childIndex + 1}.`}
/> />
</> </Fragment>
))} ))}
</ol> </ol>
); );

View File

@ -3,6 +3,7 @@ import { prettySlug } from "helpers/formatters";
import { getStatusDescription } from "helpers/others"; import { getStatusDescription } from "helpers/others";
import { Immutable, PostWithTranslations } from "helpers/types"; import { Immutable, PostWithTranslations } from "helpers/types";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { Fragment } from "react";
import { AppLayout } from "./AppLayout"; import { AppLayout } from "./AppLayout";
import { Chip } from "./Chip"; import { Chip } from "./Chip";
import { HorizontalLine } from "./HorizontalLine"; import { HorizontalLine } from "./HorizontalLine";
@ -97,15 +98,14 @@ export function PostPage(props: Immutable<Props>): JSX.Element {
<p className="font-headers">{"Authors"}:</p> <p className="font-headers">{"Authors"}:</p>
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{post.authors.data.map((author) => ( {post.authors.data.map((author) => (
<> <Fragment key={author.id}>
{author.attributes && ( {author.attributes && (
<RecorderChip <RecorderChip
key={author.id}
langui={langui} langui={langui}
recorder={author.attributes} recorder={author.attributes}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -3,6 +3,7 @@ import { RecorderChipFragment } from "graphql/generated";
import { AppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps } from "graphql/getAppStaticProps";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { Fragment } from "react";
import { Img } from "./Img"; import { Img } from "./Img";
import { Markdawn } from "./Markdown/Markdawn"; import { Markdawn } from "./Markdown/Markdawn";
import { ToolTip } from "./ToolTip"; import { ToolTip } from "./ToolTip";
@ -33,13 +34,11 @@ export function RecorderChip(props: Immutable<Props>): JSX.Element {
<div className="flex flex-row flex-wrap gap-1"> <div className="flex flex-row flex-wrap gap-1">
<p>{langui.languages}:</p> <p>{langui.languages}:</p>
{recorder.languages.data.map((language) => ( {recorder.languages.data.map((language) => (
<> <Fragment key={language.attributes?.code}>
{language.attributes && ( {language.attributes && (
<Chip key={language.attributes.code}> <Chip>{language.attributes.code.toUpperCase()}</Chip>
{language.attributes.code.toUpperCase()}
</Chip>
)} )}
</> </Fragment>
))} ))}
</div> </div>
)} )}

View File

@ -7,6 +7,7 @@ import {
import { AppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps } from "graphql/getAppStaticProps";
import { getStatusDescription } from "helpers/others"; import { getStatusDescription } from "helpers/others";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { Fragment } from "react";
interface Props { interface Props {
item: NonNullable<GetChronologyItemsQuery["chronologyItems"]>["data"][number]; item: NonNullable<GetChronologyItemsQuery["chronologyItems"]>["data"][number];
@ -92,13 +93,13 @@ export function ChronologyItemComponent(props: Immutable<Props>): JSX.Element {
<div className="col-start-2 row-start-1 row-span-2 grid gap-4"> <div className="col-start-2 row-start-1 row-span-2 grid gap-4">
{props.item.attributes.events?.map((event) => ( {props.item.attributes.events?.map((event) => (
<> <Fragment key={event?.id}>
{event && ( {event && (
<div className="m-0" key={event.id}> <div className="m-0">
{event.translations?.map((translation) => ( {event.translations?.map((translation, translationIndex) => (
<> <Fragment key={translationIndex}>
{translation && ( {translation && (
<> <Fragment>
<div <div
className="place-items-start className="place-items-start
place-content-start grid grid-flow-col gap-2" place-content-start grid grid-flow-col gap-2"
@ -140,9 +141,9 @@ export function ChronologyItemComponent(props: Immutable<Props>): JSX.Element {
) : ( ) : (
"" ""
)} )}
</> </Fragment>
)} )}
</> </Fragment>
))} ))}
<p className="text-dark text-xs grid place-self-start grid-flow-col gap-1 mt-1"> <p className="text-dark text-xs grid place-self-start grid-flow-col gap-1 mt-1">
@ -157,7 +158,7 @@ export function ChronologyItemComponent(props: Immutable<Props>): JSX.Element {
</p> </p>
</div> </div>
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -20,7 +20,7 @@ import {
GetStaticPathsResult, GetStaticPathsResult,
GetStaticPropsContext, GetStaticPropsContext,
} from "next"; } from "next";
import { useState } from "react"; import { Fragment, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
channel: NonNullable< channel: NonNullable<
@ -67,10 +67,9 @@ export default function Channel(props: Props): JSX.Element {
pb-12 border-b-[3px] border-dotted last-of-type:border-0" pb-12 border-b-[3px] border-dotted last-of-type:border-0"
> >
{channel?.videos?.data.map((video) => ( {channel?.videos?.data.map((video) => (
<> <Fragment key={video.id}>
{video.attributes && ( {video.attributes && (
<PreviewCard <PreviewCard
key={video.id}
href={`/archives/videos/v/${video.attributes.uid}`} href={`/archives/videos/v/${video.attributes.uid}`}
title={video.attributes.title} title={video.attributes.title}
thumbnail={getVideoThumbnailURL(video.attributes.uid)} thumbnail={getVideoThumbnailURL(video.attributes.uid)}
@ -88,7 +87,7 @@ export default function Channel(props: Props): JSX.Element {
}} }}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</ContentPanel> </ContentPanel>

View File

@ -18,7 +18,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyDate } from "helpers/formatters"; import { prettyDate } from "helpers/formatters";
import { getVideoThumbnailURL } from "helpers/videos"; import { getVideoThumbnailURL } from "helpers/videos";
import { GetStaticPropsContext } from "next"; import { GetStaticPropsContext } from "next";
import { useState } from "react"; import { Fragment, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
videos: NonNullable<GetVideosPreviewQuery["videos"]>["data"]; videos: NonNullable<GetVideosPreviewQuery["videos"]>["data"];
@ -88,10 +88,9 @@ export default function Videos(props: Props): JSX.Element {
pb-12 border-b-[3px] border-dotted last-of-type:border-0" pb-12 border-b-[3px] border-dotted last-of-type:border-0"
> >
{paginatedVideos[page].map((video) => ( {paginatedVideos[page].map((video) => (
<> <Fragment key={video.id}>
{video.attributes && ( {video.attributes && (
<PreviewCard <PreviewCard
key={video.id}
href={`/archives/videos/v/${video.attributes.uid}`} href={`/archives/videos/v/${video.attributes.uid}`}
title={video.attributes.title} title={video.attributes.title}
thumbnail={getVideoThumbnailURL(video.attributes.uid)} thumbnail={getVideoThumbnailURL(video.attributes.uid)}
@ -109,7 +108,7 @@ export default function Videos(props: Props): JSX.Element {
}} }}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>

View File

@ -31,6 +31,7 @@ import {
GetStaticPathsResult, GetStaticPathsResult,
GetStaticPropsContext, GetStaticPropsContext,
} from "next"; } from "next";
import { Fragment } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
content: ContentWithTranslations; content: ContentWithTranslations;
@ -116,15 +117,14 @@ export default function Content(props: Immutable<Props>): JSX.Element {
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedTranslation.text_set.transcribers.data.map( {selectedTranslation.text_set.transcribers.data.map(
(recorder) => ( (recorder) => (
<> <Fragment key={recorder.id}>
{recorder.attributes && ( {recorder.attributes && (
<RecorderChip <RecorderChip
key={recorder.id}
langui={langui} langui={langui}
recorder={recorder.attributes} recorder={recorder.attributes}
/> />
)} )}
</> </Fragment>
) )
)} )}
</div> </div>
@ -138,15 +138,14 @@ export default function Content(props: Immutable<Props>): JSX.Element {
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedTranslation.text_set.translators.data.map( {selectedTranslation.text_set.translators.data.map(
(recorder) => ( (recorder) => (
<> <Fragment key={recorder.id}>
{recorder.attributes && ( {recorder.attributes && (
<RecorderChip <RecorderChip
key={recorder.id}
langui={langui} langui={langui}
recorder={recorder.attributes} recorder={recorder.attributes}
/> />
)} )}
</> </Fragment>
) )
)} )}
</div> </div>
@ -160,15 +159,14 @@ export default function Content(props: Immutable<Props>): JSX.Element {
<div className="grid place-items-center place-content-center gap-2"> <div className="grid place-items-center place-content-center gap-2">
{selectedTranslation.text_set.proofreaders.data.map( {selectedTranslation.text_set.proofreaders.data.map(
(recorder) => ( (recorder) => (
<> <Fragment key={recorder.id}>
{recorder.attributes && ( {recorder.attributes && (
<RecorderChip <RecorderChip
key={recorder.id}
langui={langui} langui={langui}
recorder={recorder.attributes} recorder={recorder.attributes}
/> />
)} )}
</> </Fragment>
) )
)} )}
</div> </div>

View File

@ -15,7 +15,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyinlineTitle, prettySlug } from "helpers/formatters"; import { prettyinlineTitle, prettySlug } from "helpers/formatters";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { GetStaticPropsContext } from "next"; import { GetStaticPropsContext } from "next";
import { useEffect, useState } from "react"; import { Fragment, useEffect, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
contents: NonNullable<GetContentsQuery["contents"]>["data"]; contents: NonNullable<GetContentsQuery["contents"]>["data"];
@ -30,9 +30,12 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
const [keepInfoVisible, setKeepInfoVisible] = useState(false); const [keepInfoVisible, setKeepInfoVisible] = useState(false);
const [combineRelatedContent, setCombineRelatedContent] = useState(true); const [combineRelatedContent, setCombineRelatedContent] = useState(true);
const [effectiveCombineRelatedContent, setEffectiveCombineRelatedContent] =
useState(true);
const [searchName, setSearchName] = useState("");
const [filteredItems, setFilteredItems] = useState( const [filteredItems, setFilteredItems] = useState(
filterContents(combineRelatedContent, contents) filterContents(contents, combineRelatedContent, searchName)
); );
const [groups, setGroups] = useState<GroupContentItems>( const [groups, setGroups] = useState<GroupContentItems>(
@ -40,8 +43,20 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
); );
useEffect(() => { useEffect(() => {
setFilteredItems(filterContents(combineRelatedContent, contents)); if (searchName.length > 1) {
}, [combineRelatedContent, contents]); setEffectiveCombineRelatedContent(false);
} else {
setEffectiveCombineRelatedContent(combineRelatedContent);
}
setFilteredItems(
filterContents(contents, effectiveCombineRelatedContent, searchName)
);
}, [
effectiveCombineRelatedContent,
contents,
searchName,
combineRelatedContent,
]);
useEffect(() => { useEffect(() => {
setGroups(getGroups(langui, groupingMethod, filteredItems)); setGroups(getGroups(langui, groupingMethod, filteredItems));
@ -55,6 +70,18 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
description={langui.contents_description} description={langui.contents_description}
/> />
<input
className="w-full mb-6"
type="text"
name="name"
id="name"
placeholder="Search title..."
onChange={(event) => {
const input = event.target as HTMLInputElement;
setSearchName(input.value);
}}
/>
<div className="flex flex-row gap-2 place-items-center"> <div className="flex flex-row gap-2 place-items-center">
<p className="flex-shrink-0">{langui.group_by}:</p> <p className="flex-shrink-0">{langui.group_by}:</p>
<Select <Select
@ -66,11 +93,18 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
/> />
</div> </div>
<div className="flex flex-row gap-2 place-items-center coarse:hidden"> <div
className={`flex flex-row gap-2 place-items-center coarse:hidden ${
searchName.length > 1
? "text-dark grayscale contrast-75 brightness-150"
: ""
}`}
>
<p className="flex-shrink-0">{langui.combine_related_contents}:</p> <p className="flex-shrink-0">{langui.combine_related_contents}:</p>
<Switch <Switch
setState={setCombineRelatedContent} setState={setCombineRelatedContent}
state={combineRelatedContent} state={combineRelatedContent}
disabled={searchName.length > 1}
/> />
</div> </div>
@ -83,9 +117,9 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
const contentPanel = ( const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}> <ContentPanel width={ContentPanelWidthSizes.large}>
{[...groups].map(([name, items]) => ( {[...groups].map(([name, items]) => (
<> <Fragment key={name}>
{items.length > 0 && ( {items.length > 0 && (
<> <Fragment>
{name && ( {name && (
<h2 <h2
key={`h2${name}`} key={`h2${name}`}
@ -94,7 +128,7 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
> >
{name} {name}
<Chip>{`${items.reduce((currentSum, item) => { <Chip>{`${items.reduce((currentSum, item) => {
if (combineRelatedContent) { if (effectiveCombineRelatedContent) {
if (item.attributes?.group?.data?.attributes?.combine) { if (item.attributes?.group?.data?.attributes?.combine) {
return ( return (
currentSum + currentSum +
@ -117,10 +151,9 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"
> >
{items.map((item) => ( {items.map((item) => (
<> <Fragment key={item.id}>
{item.attributes && ( {item.attributes && (
<PreviewCard <PreviewCard
key={item.id}
href={`/contents/${item.attributes.slug}`} href={`/contents/${item.attributes.slug}`}
pre_title={item.attributes.translations?.[0]?.pre_title} pre_title={item.attributes.translations?.[0]?.pre_title}
title={ title={
@ -131,7 +164,7 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
thumbnail={item.attributes.thumbnail?.data?.attributes} thumbnail={item.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
stackNumber={ stackNumber={
combineRelatedContent && effectiveCombineRelatedContent &&
item.attributes.group?.data?.attributes?.combine item.attributes.group?.data?.attributes?.combine
? item.attributes.group.data.attributes.contents ? item.attributes.group.data.attributes.contents
?.data.length ?.data.length
@ -155,12 +188,12 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</> </Fragment>
)} )}
</> </Fragment>
))} ))}
</ContentPanel> </ContentPanel>
); );
@ -271,17 +304,33 @@ function getGroups(
} }
function filterContents( function filterContents(
contents: Immutable<Props["contents"]>,
combineRelatedContent: boolean, combineRelatedContent: boolean,
contents: Immutable<Props["contents"]> searchName: string
): Immutable<Props["contents"]> { ): Immutable<Props["contents"]> {
if (combineRelatedContent) { return contents.filter((content) => {
return [...contents].filter( if (
(content) => combineRelatedContent &&
!content.attributes?.group?.data?.attributes || content.attributes?.group?.data?.attributes?.combine &&
!content.attributes.group.data.attributes.combine || content.attributes.group.data.attributes.contents?.data[0].id !==
content.attributes.group.data.attributes.contents?.data[0].id ===
content.id content.id
); ) {
return false;
} }
return contents; if (searchName.length > 1) {
if (
prettyinlineTitle(
content.attributes?.translations?.[0]?.pre_title,
content.attributes?.translations?.[0]?.title,
content.attributes?.translations?.[0]?.subtitle
)
.toLowerCase()
.includes(searchName.toLowerCase())
) {
return true;
}
return false;
}
return true;
});
} }

View File

@ -43,7 +43,7 @@ import {
GetStaticPathsResult, GetStaticPathsResult,
GetStaticPropsContext, GetStaticPropsContext,
} from "next"; } from "next";
import { useState } from "react"; import { Fragment, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
item: NonNullable< item: NonNullable<
@ -185,18 +185,14 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
{item?.urls && item.urls.length ? ( {item?.urls && item.urls.length ? (
<div className="flex flex-row place-items-center gap-3"> <div className="flex flex-row place-items-center gap-3">
<p>{langui.available_at}</p> <p>{langui.available_at}</p>
{item.urls.map((url) => ( {item.urls.map((url, index) => (
<> <Fragment key={index}>
{url?.url && ( {url?.url && (
<Button <Button href={url.url} target={"_blank"}>
href={url.url}
key={url.url}
target={"_blank"}
>
{prettyURL(url.url)} {prettyURL(url.url)}
</Button> </Button>
)} )}
</> </Fragment>
))} ))}
</div> </div>
) : ( ) : (
@ -215,10 +211,9 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"
> >
{item.gallery.data.map((galleryItem, index) => ( {item.gallery.data.map((galleryItem, index) => (
<> <Fragment key={galleryItem.id}>
{galleryItem.attributes && ( {galleryItem.attributes && (
<div <div
key={galleryItem.id}
className="relative aspect-square hover:scale-[1.02] className="relative aspect-square hover:scale-[1.02]
transition-transform cursor-pointer" transition-transform cursor-pointer"
onClick={() => { onClick={() => {
@ -244,7 +239,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
/> />
</div> </div>
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>
@ -405,10 +400,9 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full" grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full"
> >
{item.subitems.data.map((subitem) => ( {item.subitems.data.map((subitem) => (
<> <Fragment key={subitem.id}>
{subitem.attributes && ( {subitem.attributes && (
<PreviewCard <PreviewCard
key={subitem.id}
href={`/library/${subitem.attributes.slug}`} href={`/library/${subitem.attributes.slug}`}
title={subitem.attributes.title} title={subitem.attributes.title}
subtitle={subitem.attributes.subtitle} subtitle={subitem.attributes.subtitle}
@ -433,7 +427,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
}} }}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</div> </div>

View File

@ -23,6 +23,7 @@ import {
GetStaticPathsResult, GetStaticPathsResult,
GetStaticPropsContext, GetStaticPropsContext,
} from "next"; } from "next";
import { Fragment } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
item: NonNullable< item: NonNullable<
@ -89,10 +90,9 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
)} )}
{item?.contents?.data.map((content) => ( {item?.contents?.data.map((content) => (
<> <Fragment key={content.id}>
{content.attributes?.scan_set?.[0] && ( {content.attributes?.scan_set?.[0] && (
<ScanSet <ScanSet
key={content.id}
scanSet={content.attributes.scan_set} scanSet={content.attributes.scan_set}
openLightBox={openLightBox} openLightBox={openLightBox}
slug={content.attributes.slug} slug={content.attributes.slug}
@ -102,7 +102,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
content={content.attributes.content} content={content.attributes.content}
/> />
)} )}
</> </Fragment>
))} ))}
</ContentPanel> </ContentPanel>
); );

View File

@ -20,7 +20,7 @@ import {
import { convertPrice } from "helpers/numbers"; import { convertPrice } from "helpers/numbers";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { GetStaticPropsContext } from "next"; import { GetStaticPropsContext } from "next";
import { useEffect, useState } from "react"; import { Fragment, useEffect, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
items: NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"]; items: NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"];
@ -31,6 +31,7 @@ type GroupLibraryItems = Map<string, Immutable<Props["items"]>>;
export default function Library(props: Immutable<Props>): JSX.Element { export default function Library(props: Immutable<Props>): JSX.Element {
const { langui, items: libraryItems, currencies } = props; const { langui, items: libraryItems, currencies } = props;
const [searchName, setSearchName] = useState("");
const [showSubitems, setShowSubitems] = useState<boolean>(false); const [showSubitems, setShowSubitems] = useState<boolean>(false);
const [showPrimaryItems, setShowPrimaryItems] = useState<boolean>(true); const [showPrimaryItems, setShowPrimaryItems] = useState<boolean>(true);
const [showSecondaryItems, setShowSecondaryItems] = useState<boolean>(false); const [showSecondaryItems, setShowSecondaryItems] = useState<boolean>(false);
@ -40,10 +41,11 @@ export default function Library(props: Immutable<Props>): JSX.Element {
const [filteredItems, setFilteredItems] = useState( const [filteredItems, setFilteredItems] = useState(
filterItems( filterItems(
libraryItems,
searchName,
showSubitems, showSubitems,
showPrimaryItems, showPrimaryItems,
showSecondaryItems, showSecondaryItems
libraryItems
) )
); );
@ -58,13 +60,20 @@ export default function Library(props: Immutable<Props>): JSX.Element {
useEffect(() => { useEffect(() => {
setFilteredItems( setFilteredItems(
filterItems( filterItems(
libraryItems,
searchName,
showSubitems, showSubitems,
showPrimaryItems, showPrimaryItems,
showSecondaryItems, showSecondaryItems
libraryItems
) )
); );
}, [showSubitems, libraryItems, showPrimaryItems, showSecondaryItems]); }, [
showSubitems,
libraryItems,
showPrimaryItems,
showSecondaryItems,
searchName,
]);
useEffect(() => { useEffect(() => {
setSortedItem(sortBy(sortingMethod, filteredItems, currencies)); setSortedItem(sortBy(sortingMethod, filteredItems, currencies));
@ -82,6 +91,18 @@ export default function Library(props: Immutable<Props>): JSX.Element {
description={langui.library_description} description={langui.library_description}
/> />
<input
className="w-full mb-6"
type="text"
name="name"
id="name"
placeholder="Search title..."
onChange={(event) => {
const input = event.target as HTMLInputElement;
setSearchName(input.value);
}}
/>
<div className="flex flex-row gap-2 place-items-center"> <div className="flex flex-row gap-2 place-items-center">
<p className="flex-shrink-0">{langui.group_by}:</p> <p className="flex-shrink-0">{langui.group_by}:</p>
<Select <Select
@ -135,12 +156,11 @@ export default function Library(props: Immutable<Props>): JSX.Element {
const contentPanel = ( const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}> <ContentPanel width={ContentPanelWidthSizes.large}>
{[...groups].map(([name, items]) => ( {[...groups].map(([name, items]) => (
<> <Fragment key={name}>
{items.length > 0 && ( {items.length > 0 && (
<> <>
{name && ( {name && (
<h2 <h2
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" flex flex-row place-items-center gap-2"
> >
@ -153,16 +173,14 @@ export default function Library(props: Immutable<Props>): JSX.Element {
</h2> </h2>
)} )}
<div <div
key={`items${name}`}
className="grid gap-8 mobile:gap-4 items-end mobile:grid-cols-2 className="grid gap-8 mobile:gap-4 items-end mobile:grid-cols-2
desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))] desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]
pb-12 border-b-[3px] border-dotted last-of-type:border-0" pb-12 border-b-[3px] border-dotted last-of-type:border-0"
> >
{items.map((item) => ( {items.map((item) => (
<> <Fragment key={item.id}>
{item.attributes && ( {item.attributes && (
<PreviewCard <PreviewCard
key={item.id}
href={`/library/${item.attributes.slug}`} href={`/library/${item.attributes.slug}`}
title={item.attributes.title} title={item.attributes.title}
subtitle={item.attributes.subtitle} subtitle={item.attributes.subtitle}
@ -187,12 +205,12 @@ export default function Library(props: Immutable<Props>): JSX.Element {
}} }}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</> </>
)} )}
</> </Fragment>
))} ))}
</ContentPanel> </ContentPanel>
); );
@ -362,10 +380,11 @@ function getGroups(
} }
function filterItems( function filterItems(
items: Immutable<Props["items"]>,
searchName: string,
showSubitems: boolean, showSubitems: boolean,
showPrimaryItems: boolean, showPrimaryItems: boolean,
showSecondaryItems: boolean, showSecondaryItems: boolean
items: Immutable<Props["items"]>
): Immutable<Props["items"]> { ): Immutable<Props["items"]> {
return [...items].filter((item) => { return [...items].filter((item) => {
if (!showSubitems && !item.attributes?.root_item) return false; if (!showSubitems && !item.attributes?.root_item) return false;
@ -376,10 +395,21 @@ function filterItems(
"variant-set" || "variant-set" ||
item.attributes.metadata[0].subtype?.data?.attributes?.slug === item.attributes.metadata[0].subtype?.data?.attributes?.slug ===
"relation-set") "relation-set")
) ) {
return false; return false;
}
if (item.attributes?.primary && !showPrimaryItems) return false; if (item.attributes?.primary && !showPrimaryItems) return false;
if (!item.attributes?.primary && !showSecondaryItems) return false; if (!item.attributes?.primary && !showSecondaryItems) return false;
if (searchName.length > 1) {
if (
prettyinlineTitle("", item.attributes?.title, item.attributes?.subtitle)
.toLowerCase()
.includes(searchName.toLowerCase())
) {
return true;
}
return false;
}
return true; return true;
}); });
} }

View File

@ -13,7 +13,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyDate, prettySlug } from "helpers/formatters"; import { prettyDate, prettySlug } from "helpers/formatters";
import { Immutable } from "helpers/types"; import { Immutable } from "helpers/types";
import { GetStaticPropsContext } from "next"; import { GetStaticPropsContext } from "next";
import { useState } from "react"; import { Fragment, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"]; posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
@ -47,10 +47,9 @@ export default function News(props: Immutable<Props>): JSX.Element {
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]" desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
> >
{posts.map((post) => ( {posts.map((post) => (
<> <Fragment key={post.id}>
{post.attributes && ( {post.attributes && (
<PreviewCard <PreviewCard
key={post.id}
href={`/news/${post.attributes.slug}`} href={`/news/${post.attributes.slug}`}
title={ title={
post.attributes.translations?.[0]?.title ?? post.attributes.translations?.[0]?.title ??
@ -69,7 +68,7 @@ export default function News(props: Immutable<Props>): JSX.Element {
}} }}
/> />
)} )}
</> </Fragment>
))} ))}
</div> </div>
</ContentPanel> </ContentPanel>

View File

@ -8,12 +8,12 @@ import {
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Panels/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Panels/SubPanel";
import { ChronologyYearComponent } from "components/Wiki/Chronology/ChronologyYearComponent"; import { ChronologyYearComponent } from "components/Wiki/Chronology/ChronologyYearComponent";
import { useAppLayout } from "contexts/AppLayoutContext";
import { GetChronologyItemsQuery, GetErasQuery } from "graphql/generated"; import { GetChronologyItemsQuery, GetErasQuery } from "graphql/generated";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";
import { GetStaticPropsContext } from "next"; import { GetStaticPropsContext } from "next";
import { Fragment } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
chronologyItems: NonNullable< chronologyItems: NonNullable<
@ -24,7 +24,6 @@ interface Props extends AppStaticProps {
export default function Chronology(props: Props): JSX.Element { export default function Chronology(props: Props): JSX.Element {
const { chronologyItems, chronologyEras, langui } = props; const { chronologyItems, chronologyEras, langui } = props;
const appLayout = useAppLayout();
// Group by year the Chronology items // Group by year the Chronology items
const chronologyItemYearGroups: Props["chronologyItems"][number][][][] = []; const chronologyItemYearGroups: Props["chronologyItems"][number][][][] = [];
@ -71,10 +70,9 @@ export default function Chronology(props: Props): JSX.Element {
/> />
{chronologyEras.map((era) => ( {chronologyEras.map((era) => (
<> <Fragment key={era.id}>
{era.attributes && ( {era.attributes && (
<NavOption <NavOption
key={era.id}
url={`#${era.attributes.slug}`} url={`#${era.attributes.slug}`}
title={ title={
era.attributes.title && era.attributes.title &&
@ -87,7 +85,7 @@ export default function Chronology(props: Props): JSX.Element {
border border
/> />
)} )}
</> </Fragment>
))} ))}
</SubPanel> </SubPanel>
); );
@ -103,7 +101,7 @@ export default function Chronology(props: Props): JSX.Element {
/> />
{chronologyItemYearGroups.map((era, eraIndex) => ( {chronologyItemYearGroups.map((era, eraIndex) => (
<> <Fragment key={eraIndex}>
<InsetBox <InsetBox
id={chronologyEras[eraIndex].attributes?.slug} id={chronologyEras[eraIndex].attributes?.slug}
className="grid text-center my-8 gap-4" className="grid text-center my-8 gap-4"
@ -120,18 +118,17 @@ export default function Chronology(props: Props): JSX.Element {
</p> </p>
</InsetBox> </InsetBox>
{era.map((items, index) => ( {era.map((items, index) => (
<> <Fragment key={index}>
{items[0].attributes?.year && ( {items[0].attributes?.year && (
<ChronologyYearComponent <ChronologyYearComponent
key={`${eraIndex}-${index}`}
year={items[0].attributes.year} year={items[0].attributes.year}
items={items} items={items}
langui={langui} langui={langui}
/> />
)} )}
</> </Fragment>
))} ))}
</> </Fragment>
))} ))}
</ContentPanel> </ContentPanel>
); );