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;
useMemo(() => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
router.events?.on("routeChangeStart", () => {
appLayout.setConfigPanelOpen(false);
appLayout.setMainPanelOpen(false);
appLayout.setSubPanelOpen(false);
});
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
router.events?.on("hashChangeStart", () => {
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
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>
</div>
</div>

View File

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

View File

@ -1,6 +1,6 @@
import { arrayMove } from "helpers/others";
import { Immutable } from "helpers/types";
import { useEffect, useState } from "react";
import { Fragment, useEffect, useState } from "react";
interface Props {
className?: string;
@ -25,7 +25,7 @@ export function OrderableList(props: Immutable<Props>): JSX.Element {
return (
<div className="grid gap-2">
{[...items].map(([key, value], index) => (
<>
<Fragment key={key}>
{props.insertLabels?.get(index) && (
<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
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"
key={key}
draggable
>
<div className="grid grid-rows-[.8em_.8em] place-items-center">
@ -87,7 +86,7 @@ export function OrderableList(props: Immutable<Props>): JSX.Element {
</div>
{value}
</div>
</>
</Fragment>
))}
</div>
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ import { RecorderChipFragment } from "graphql/generated";
import { AppStaticProps } from "graphql/getAppStaticProps";
import { ImageQuality } from "helpers/img";
import { Immutable } from "helpers/types";
import { Fragment } from "react";
import { Img } from "./Img";
import { Markdawn } from "./Markdown/Markdawn";
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">
<p>{langui.languages}:</p>
{recorder.languages.data.map((language) => (
<>
<Fragment key={language.attributes?.code}>
{language.attributes && (
<Chip key={language.attributes.code}>
{language.attributes.code.toUpperCase()}
</Chip>
<Chip>{language.attributes.code.toUpperCase()}</Chip>
)}
</>
</Fragment>
))}
</div>
)}

View File

@ -7,6 +7,7 @@ import {
import { AppStaticProps } from "graphql/getAppStaticProps";
import { getStatusDescription } from "helpers/others";
import { Immutable } from "helpers/types";
import { Fragment } from "react";
interface Props {
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">
{props.item.attributes.events?.map((event) => (
<>
<Fragment key={event?.id}>
{event && (
<div className="m-0" key={event.id}>
{event.translations?.map((translation) => (
<>
<div className="m-0">
{event.translations?.map((translation, translationIndex) => (
<Fragment key={translationIndex}>
{translation && (
<>
<Fragment>
<div
className="place-items-start
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">
@ -157,7 +158,7 @@ export function ChronologyItemComponent(props: Immutable<Props>): JSX.Element {
</p>
</div>
)}
</>
</Fragment>
))}
</div>
</div>

View File

@ -20,7 +20,7 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { useState } from "react";
import { Fragment, useState } from "react";
interface Props extends AppStaticProps {
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"
>
{channel?.videos?.data.map((video) => (
<>
<Fragment key={video.id}>
{video.attributes && (
<PreviewCard
key={video.id}
href={`/archives/videos/v/${video.attributes.uid}`}
title={video.attributes.title}
thumbnail={getVideoThumbnailURL(video.attributes.uid)}
@ -88,7 +87,7 @@ export default function Channel(props: Props): JSX.Element {
}}
/>
)}
</>
</Fragment>
))}
</div>
</ContentPanel>

View File

@ -18,7 +18,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyDate } from "helpers/formatters";
import { getVideoThumbnailURL } from "helpers/videos";
import { GetStaticPropsContext } from "next";
import { useState } from "react";
import { Fragment, useState } from "react";
interface Props extends AppStaticProps {
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"
>
{paginatedVideos[page].map((video) => (
<>
<Fragment key={video.id}>
{video.attributes && (
<PreviewCard
key={video.id}
href={`/archives/videos/v/${video.attributes.uid}`}
title={video.attributes.title}
thumbnail={getVideoThumbnailURL(video.attributes.uid)}
@ -109,7 +108,7 @@ export default function Videos(props: Props): JSX.Element {
}}
/>
)}
</>
</Fragment>
))}
</div>

View File

@ -31,6 +31,7 @@ import {
GetStaticPathsResult,
GetStaticPropsContext,
} from "next";
import { Fragment } from "react";
interface Props extends AppStaticProps {
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">
{selectedTranslation.text_set.transcribers.data.map(
(recorder) => (
<>
<Fragment key={recorder.id}>
{recorder.attributes && (
<RecorderChip
key={recorder.id}
langui={langui}
recorder={recorder.attributes}
/>
)}
</>
</Fragment>
)
)}
</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">
{selectedTranslation.text_set.translators.data.map(
(recorder) => (
<>
<Fragment key={recorder.id}>
{recorder.attributes && (
<RecorderChip
key={recorder.id}
langui={langui}
recorder={recorder.attributes}
/>
)}
</>
</Fragment>
)
)}
</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">
{selectedTranslation.text_set.proofreaders.data.map(
(recorder) => (
<>
<Fragment key={recorder.id}>
{recorder.attributes && (
<RecorderChip
key={recorder.id}
langui={langui}
recorder={recorder.attributes}
/>
)}
</>
</Fragment>
)
)}
</div>

View File

@ -15,7 +15,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
import { Immutable } from "helpers/types";
import { GetStaticPropsContext } from "next";
import { useEffect, useState } from "react";
import { Fragment, useEffect, useState } from "react";
interface Props extends AppStaticProps {
contents: NonNullable<GetContentsQuery["contents"]>["data"];
@ -30,9 +30,12 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
const [keepInfoVisible, setKeepInfoVisible] = useState(false);
const [combineRelatedContent, setCombineRelatedContent] = useState(true);
const [effectiveCombineRelatedContent, setEffectiveCombineRelatedContent] =
useState(true);
const [searchName, setSearchName] = useState("");
const [filteredItems, setFilteredItems] = useState(
filterContents(combineRelatedContent, contents)
filterContents(contents, combineRelatedContent, searchName)
);
const [groups, setGroups] = useState<GroupContentItems>(
@ -40,8 +43,20 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
);
useEffect(() => {
setFilteredItems(filterContents(combineRelatedContent, contents));
}, [combineRelatedContent, contents]);
if (searchName.length > 1) {
setEffectiveCombineRelatedContent(false);
} else {
setEffectiveCombineRelatedContent(combineRelatedContent);
}
setFilteredItems(
filterContents(contents, effectiveCombineRelatedContent, searchName)
);
}, [
effectiveCombineRelatedContent,
contents,
searchName,
combineRelatedContent,
]);
useEffect(() => {
setGroups(getGroups(langui, groupingMethod, filteredItems));
@ -55,6 +70,18 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
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">
<p className="flex-shrink-0">{langui.group_by}:</p>
<Select
@ -66,11 +93,18 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
/>
</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>
<Switch
setState={setCombineRelatedContent}
state={combineRelatedContent}
disabled={searchName.length > 1}
/>
</div>
@ -83,9 +117,9 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
{[...groups].map(([name, items]) => (
<>
<Fragment key={name}>
{items.length > 0 && (
<>
<Fragment>
{name && (
<h2
key={`h2${name}`}
@ -94,7 +128,7 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
>
{name}
<Chip>{`${items.reduce((currentSum, item) => {
if (combineRelatedContent) {
if (effectiveCombineRelatedContent) {
if (item.attributes?.group?.data?.attributes?.combine) {
return (
currentSum +
@ -117,10 +151,9 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"
>
{items.map((item) => (
<>
<Fragment key={item.id}>
{item.attributes && (
<PreviewCard
key={item.id}
href={`/contents/${item.attributes.slug}`}
pre_title={item.attributes.translations?.[0]?.pre_title}
title={
@ -131,7 +164,7 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
thumbnail={item.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2"
stackNumber={
combineRelatedContent &&
effectiveCombineRelatedContent &&
item.attributes.group?.data?.attributes?.combine
? item.attributes.group.data.attributes.contents
?.data.length
@ -155,12 +188,12 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
keepInfoVisible={keepInfoVisible}
/>
)}
</>
</Fragment>
))}
</div>
</>
</Fragment>
)}
</>
</Fragment>
))}
</ContentPanel>
);
@ -271,17 +304,33 @@ function getGroups(
}
function filterContents(
contents: Immutable<Props["contents"]>,
combineRelatedContent: boolean,
contents: Immutable<Props["contents"]>
searchName: string
): Immutable<Props["contents"]> {
if (combineRelatedContent) {
return [...contents].filter(
(content) =>
!content.attributes?.group?.data?.attributes ||
!content.attributes.group.data.attributes.combine ||
content.attributes.group.data.attributes.contents?.data[0].id ===
return contents.filter((content) => {
if (
combineRelatedContent &&
content.attributes?.group?.data?.attributes?.combine &&
content.attributes.group.data.attributes.contents?.data[0].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,
GetStaticPropsContext,
} from "next";
import { useState } from "react";
import { Fragment, useState } from "react";
interface Props extends AppStaticProps {
item: NonNullable<
@ -185,18 +185,14 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
{item?.urls && item.urls.length ? (
<div className="flex flex-row place-items-center gap-3">
<p>{langui.available_at}</p>
{item.urls.map((url) => (
<>
{item.urls.map((url, index) => (
<Fragment key={index}>
{url?.url && (
<Button
href={url.url}
key={url.url}
target={"_blank"}
>
<Button href={url.url} target={"_blank"}>
{prettyURL(url.url)}
</Button>
)}
</>
</Fragment>
))}
</div>
) : (
@ -215,10 +211,9 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"
>
{item.gallery.data.map((galleryItem, index) => (
<>
<Fragment key={galleryItem.id}>
{galleryItem.attributes && (
<div
key={galleryItem.id}
className="relative aspect-square hover:scale-[1.02]
transition-transform cursor-pointer"
onClick={() => {
@ -244,7 +239,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
/>
</div>
)}
</>
</Fragment>
))}
</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"
>
{item.subitems.data.map((subitem) => (
<>
<Fragment key={subitem.id}>
{subitem.attributes && (
<PreviewCard
key={subitem.id}
href={`/library/${subitem.attributes.slug}`}
title={subitem.attributes.title}
subtitle={subitem.attributes.subtitle}
@ -433,7 +427,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element {
}}
/>
)}
</>
</Fragment>
))}
</div>
</div>

View File

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

View File

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

View File

@ -13,7 +13,7 @@ import { getReadySdk } from "graphql/sdk";
import { prettyDate, prettySlug } from "helpers/formatters";
import { Immutable } from "helpers/types";
import { GetStaticPropsContext } from "next";
import { useState } from "react";
import { Fragment, useState } from "react";
interface Props extends AppStaticProps {
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))]"
>
{posts.map((post) => (
<>
<Fragment key={post.id}>
{post.attributes && (
<PreviewCard
key={post.id}
href={`/news/${post.attributes.slug}`}
title={
post.attributes.translations?.[0]?.title ??
@ -69,7 +68,7 @@ export default function News(props: Immutable<Props>): JSX.Element {
}}
/>
)}
</>
</Fragment>
))}
</div>
</ContentPanel>

View File

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