Improved the wiki section
This commit is contained in:
parent
bb42e2a56f
commit
d1d055de29
@ -1,4 +1,10 @@
|
||||
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
||||
import {
|
||||
Dispatch,
|
||||
Fragment,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useToggle } from "hooks/useToggle";
|
||||
@ -29,6 +35,11 @@ export const Select = ({
|
||||
const [opened, setOpened] = useState(false);
|
||||
const toggleOpened = useToggle(setOpened);
|
||||
|
||||
const tryToggling = useCallback(() => {
|
||||
const optionCount = options.length + (state === -1 ? 1 : 0);
|
||||
if (optionCount > 1) toggleOpened();
|
||||
}, [options.length, state, toggleOpened]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
@ -45,7 +56,7 @@ export const Select = ({
|
||||
cIf(opened, "rounded-b-none bg-highlight outline-[transparent]")
|
||||
)}
|
||||
>
|
||||
<p onClick={toggleOpened} className="w-full">
|
||||
<p onClick={tryToggling} className="w-full">
|
||||
{state === -1 ? "—" : options[state]}
|
||||
</p>
|
||||
{state >= 0 && allowEmpty && (
|
||||
@ -59,7 +70,7 @@ export const Select = ({
|
||||
/>
|
||||
)}
|
||||
<Ico
|
||||
onClick={toggleOpened}
|
||||
onClick={tryToggling}
|
||||
icon={opened ? Icon.ArrowDropUp : Icon.ArrowDropDown}
|
||||
/>
|
||||
</div>
|
||||
|
@ -4,6 +4,8 @@ import { ToolTip } from "components/ToolTip";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import Link from "next/link";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
@ -11,7 +13,10 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
source?: string;
|
||||
source?: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
};
|
||||
translations: {
|
||||
language: string | undefined;
|
||||
definition: string | null | undefined;
|
||||
@ -79,9 +84,16 @@ const DefinitionCard = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="italic">{`${langui.source}: ${source}`}</p>
|
||||
|
||||
<p>{selectedTranslation?.definition}</p>
|
||||
|
||||
{source?.url && source.name && (
|
||||
<Link href={source.url}>
|
||||
<div className="flex place-items-center gap-2 mt-3">
|
||||
<p>{langui.source}: </p>
|
||||
<Button size="small" text={source.name} />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
24
src/graphql/fragments/source.graphql
Normal file
24
src/graphql/fragments/source.graphql
Normal file
@ -0,0 +1,24 @@
|
||||
fragment source on Source {
|
||||
name
|
||||
content {
|
||||
data {
|
||||
attributes {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
ranged_content {
|
||||
data {
|
||||
attributes {
|
||||
slug
|
||||
library_item {
|
||||
data {
|
||||
attributes {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,24 @@ query getWikiPage($slug: String, $language_code: String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
tags {
|
||||
data {
|
||||
id
|
||||
attributes {
|
||||
slug
|
||||
titles(filters: { language: { code: { eq: $language_code } } }) {
|
||||
language {
|
||||
data {
|
||||
attributes {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
translations {
|
||||
title
|
||||
aliases {
|
||||
@ -73,7 +91,7 @@ query getWikiPage($slug: String, $language_code: String) {
|
||||
source {
|
||||
data {
|
||||
attributes {
|
||||
name
|
||||
...source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
query getWikiPagesPreviews {
|
||||
query getWikiPagesPreviews($language_code: String) {
|
||||
wikiPages(pagination: { limit: -1 }) {
|
||||
data {
|
||||
id
|
||||
@ -20,6 +20,24 @@ query getWikiPagesPreviews {
|
||||
}
|
||||
}
|
||||
}
|
||||
tags {
|
||||
data {
|
||||
id
|
||||
attributes {
|
||||
slug
|
||||
titles(filters: { language: { code: { eq: $language_code } } }) {
|
||||
language {
|
||||
data {
|
||||
attributes {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
translations {
|
||||
title
|
||||
aliases {
|
||||
|
@ -23,6 +23,9 @@ import {
|
||||
} from "helpers/others";
|
||||
import { WikiPageWithTranslations } from "helpers/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
page: WikiPageWithTranslations;
|
||||
@ -45,6 +48,8 @@ const WikiPage = ({
|
||||
),
|
||||
});
|
||||
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
@ -63,6 +68,8 @@ const WikiPage = ({
|
||||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Large}>
|
||||
<LightBox />
|
||||
|
||||
<ReturnButton
|
||||
href={`/wiki`}
|
||||
title={langui.wiki}
|
||||
@ -78,7 +85,7 @@ const WikiPage = ({
|
||||
<p className="mr-3 text-center text-2xl">
|
||||
{`(${selectedTranslation.aliases
|
||||
.map((alias) => alias?.alias)
|
||||
.join(", ")})`}
|
||||
.join("・")})`}
|
||||
</p>
|
||||
)}
|
||||
<LanguageSwitcher {...languageSwitcherProps} />
|
||||
@ -93,19 +100,63 @@ const WikiPage = ({
|
||||
text-center"
|
||||
>
|
||||
{page.thumbnail?.data?.attributes && (
|
||||
<Img image={page.thumbnail.data.attributes} />
|
||||
<Img
|
||||
image={page.thumbnail.data.attributes}
|
||||
quality={ImageQuality.Medium}
|
||||
className="w-full cursor-pointer"
|
||||
onClick={() => {
|
||||
if (page.thumbnail?.data?.attributes?.url) {
|
||||
openLightBox([
|
||||
getAssetURL(
|
||||
page.thumbnail.data.attributes.url,
|
||||
ImageQuality.Large
|
||||
),
|
||||
]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="my-4 grid gap-4 p-4">
|
||||
<p className="font-headers text-xl font-bold">
|
||||
{langui.categories}
|
||||
</p>
|
||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||
{filterHasAttributes(page.categories?.data, [
|
||||
"attributes",
|
||||
] as const).map((category) => (
|
||||
<Chip key={category.id} text={category.attributes.name} />
|
||||
))}
|
||||
</div>
|
||||
{page.categories?.data && page.categories.data.length > 0 && (
|
||||
<>
|
||||
<p className="font-headers text-xl font-bold">
|
||||
{langui.categories}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||
{filterHasAttributes(page.categories.data, [
|
||||
"attributes",
|
||||
] as const).map((category) => (
|
||||
<Chip
|
||||
key={category.id}
|
||||
text={category.attributes.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{page.tags?.data && page.tags.data.length > 0 && (
|
||||
<>
|
||||
<p className="font-headers text-xl font-bold">
|
||||
{/* TODO: Add Tags to langui */}
|
||||
{"Tags"}
|
||||
</p>
|
||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||
{filterHasAttributes(page.tags?.data, [
|
||||
"attributes",
|
||||
] as const).map((tag) => (
|
||||
<Chip
|
||||
key={tag.id}
|
||||
text={
|
||||
tag.attributes.titles?.[0]?.title ??
|
||||
prettySlug(tag.attributes.slug)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -124,7 +175,13 @@ const WikiPage = ({
|
||||
<>
|
||||
<DefinitionCard
|
||||
key={index}
|
||||
source={definition.source?.data?.attributes?.name}
|
||||
source={{
|
||||
name: definition.source?.data?.attributes?.name,
|
||||
url: definition.source?.data?.attributes?.content?.data
|
||||
?.attributes?.slug
|
||||
? `/contents/${definition.source.data.attributes.content.data.attributes.slug}`
|
||||
: `/library/${definition.source?.data?.attributes?.ranged_content?.data?.attributes?.library_item?.data?.attributes?.slug}`,
|
||||
}}
|
||||
translations={definition.translations.map((translation) => ({
|
||||
language: translation?.language?.data?.attributes?.code,
|
||||
definition: translation?.definition,
|
||||
@ -149,9 +206,7 @@ const WikiPage = ({
|
||||
languageSwitcherProps,
|
||||
languages,
|
||||
langui,
|
||||
page.categories?.data,
|
||||
page.definitions,
|
||||
page.thumbnail?.data?.attributes,
|
||||
page,
|
||||
selectedTranslation,
|
||||
]
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { AppLayout } from "components/AppLayout";
|
||||
import { NavOption } from "components/PanelComponents/NavOption";
|
||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||
@ -7,7 +7,7 @@ import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { Icon } from "components/Ico";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { GetWikiPagesPreviewsQuery } from "graphql/generated";
|
||||
import { GetWikiPageQuery, GetWikiPagesPreviewsQuery } from "graphql/generated";
|
||||
import {
|
||||
ContentPanel,
|
||||
ContentPanelWidthSizes,
|
||||
@ -19,8 +19,12 @@ import { Switch } from "components/Inputs/Switch";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterDefined, filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { Select } from "components/Inputs/Select";
|
||||
import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
@ -30,6 +34,7 @@ import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholde
|
||||
const DEFAULT_FILTERS_STATE = {
|
||||
searchName: "",
|
||||
keepInfoVisible: true,
|
||||
groupingMethod: -1,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -52,13 +57,13 @@ const Wiki = ({
|
||||
const [searchName, setSearchName] = useState(
|
||||
DEFAULT_FILTERS_STATE.searchName
|
||||
);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
|
||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||
DEFAULT_FILTERS_STATE.groupingMethod
|
||||
);
|
||||
|
||||
const filteredPages = useMemo(
|
||||
() => filterPages(pages, searchName),
|
||||
[pages, searchName]
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
);
|
||||
|
||||
const subPanel = useMemo(
|
||||
@ -77,6 +82,19 @@ const Wiki = ({
|
||||
setState={setSearchName}
|
||||
/>
|
||||
|
||||
<WithLabel
|
||||
label={langui.group_by}
|
||||
input={
|
||||
<Select
|
||||
className="w-full"
|
||||
options={[langui.category ?? ""]}
|
||||
state={groupingMethod}
|
||||
setState={setGroupingMethod}
|
||||
allowEmpty
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{hoverable && (
|
||||
<WithLabel
|
||||
label={langui.always_show_info}
|
||||
@ -104,59 +122,105 @@ const Wiki = ({
|
||||
<NavOption title={langui.chronology} url="/wiki/chronology" border />
|
||||
</SubPanel>
|
||||
),
|
||||
[hoverable, keepInfoVisible, langui, searchName]
|
||||
[groupingMethod, hoverable, keepInfoVisible, langui, searchName]
|
||||
);
|
||||
|
||||
const groupingFunction = useCallback(
|
||||
(
|
||||
item: SelectiveNonNullable<
|
||||
NonNullable<GetWikiPageQuery["wikiPages"]>["data"][number],
|
||||
"attributes" | "id"
|
||||
>
|
||||
): string[] => {
|
||||
switch (groupingMethod) {
|
||||
case 0: {
|
||||
const categories = filterHasAttributes(
|
||||
item.attributes.categories?.data,
|
||||
["attributes"] as const
|
||||
);
|
||||
if (categories.length > 0) {
|
||||
return categories.map((category) => category.attributes.name);
|
||||
}
|
||||
return [langui.no_category ?? "No category"];
|
||||
}
|
||||
default: {
|
||||
return [""];
|
||||
}
|
||||
}
|
||||
},
|
||||
[groupingMethod, langui]
|
||||
);
|
||||
|
||||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
<div
|
||||
className="grid grid-cols-2 items-end gap-8
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))] mobile:gap-4"
|
||||
>
|
||||
{filteredPages.length === 0 && (
|
||||
<SmartList
|
||||
items={filterHasAttributes(pages, ["id", "attributes"] as const)}
|
||||
getItemId={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<>
|
||||
{isDefined(item.attributes.translations) && (
|
||||
<TranslatedPreviewCard
|
||||
href={`/wiki/${item.attributes.slug}`}
|
||||
translations={item.attributes.translations.map(
|
||||
(translation) => ({
|
||||
title: translation?.title,
|
||||
subtitle:
|
||||
translation?.aliases && translation.aliases.length > 0
|
||||
? translation.aliases
|
||||
.map((alias) => alias?.alias)
|
||||
.join("・")
|
||||
: undefined,
|
||||
description: translation?.summary,
|
||||
language: translation?.language?.data?.attributes?.code,
|
||||
})
|
||||
)}
|
||||
thumbnail={item.attributes.thumbnail?.data?.attributes}
|
||||
thumbnailAspectRatio={"4/3"}
|
||||
thumbnailRounded
|
||||
thumbnailForceAspectRatio
|
||||
languages={languages}
|
||||
slug={item.attributes.slug}
|
||||
keepInfoVisible={keepInfoVisible}
|
||||
topChips={filterHasAttributes(item.attributes.tags?.data, [
|
||||
"attributes",
|
||||
] as const).map(
|
||||
(tag) =>
|
||||
tag.attributes.titles?.[0]?.title ??
|
||||
prettySlug(tag.attributes.slug)
|
||||
)}
|
||||
bottomChips={filterHasAttributes(
|
||||
item.attributes.categories?.data,
|
||||
["attributes"] as const
|
||||
).map((category) => category.attributes.short)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
renderWhenEmpty={() => (
|
||||
<ContentPlaceholder
|
||||
message={langui.no_results_message ?? "No results"}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
{filterHasAttributes(filteredPages, [
|
||||
"id",
|
||||
"attributes.translations",
|
||||
] as const).map((page) => (
|
||||
<Fragment key={page.id}>
|
||||
<TranslatedPreviewCard
|
||||
href={`/wiki/${page.attributes.slug}`}
|
||||
translations={page.attributes.translations.map(
|
||||
(translation) => ({
|
||||
title: translation?.title,
|
||||
subtitle:
|
||||
translation?.aliases && translation.aliases.length > 0
|
||||
? translation.aliases
|
||||
.map((alias) => alias?.alias)
|
||||
.join(" | ")
|
||||
: undefined,
|
||||
description: translation?.summary,
|
||||
language: translation?.language?.data?.attributes?.code,
|
||||
})
|
||||
)}
|
||||
thumbnail={page.attributes.thumbnail?.data?.attributes}
|
||||
thumbnailAspectRatio={"4/3"}
|
||||
thumbnailRounded
|
||||
thumbnailForceAspectRatio
|
||||
languages={languages}
|
||||
slug={page.attributes.slug}
|
||||
keepInfoVisible={keepInfoVisible}
|
||||
bottomChips={page.attributes.categories?.data.map(
|
||||
(category) => category.attributes?.short ?? ""
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
langui={langui}
|
||||
className="grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
|
||||
searchingTerm={searchName}
|
||||
searchingBy={(item) =>
|
||||
filterDefined(item.attributes.translations)
|
||||
.map(
|
||||
(translation) =>
|
||||
`${translation.title} ${filterDefined(translation.aliases)
|
||||
.map((alias) => alias.alias)
|
||||
.join(" ")}`
|
||||
)
|
||||
.join(" ")
|
||||
}
|
||||
groupingFunction={groupingFunction}
|
||||
/>
|
||||
</ContentPanel>
|
||||
),
|
||||
[filteredPages, keepInfoVisible, languages, langui]
|
||||
[groupingFunction, keepInfoVisible, languages, langui, pages, searchName]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -180,7 +244,9 @@ export default Wiki;
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const pages = await sdk.getWikiPagesPreviews({});
|
||||
const pages = await sdk.getWikiPagesPreviews({
|
||||
language_code: context.locale ?? "en",
|
||||
});
|
||||
if (!pages.wikiPages?.data) return { notFound: true };
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
@ -202,20 +268,3 @@ const sortPages = (pages: Props["pages"]): Props["pages"] =>
|
||||
const slugB = b.attributes?.slug ?? "";
|
||||
return slugA.localeCompare(slugB);
|
||||
});
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const filterPages = (posts: Props["pages"], searchName: string) =>
|
||||
posts.filter((post) => {
|
||||
if (searchName.length > 1) {
|
||||
if (
|
||||
post.attributes?.translations?.[0]?.title
|
||||
.toLowerCase()
|
||||
.includes(searchName.toLowerCase()) === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user