Use react-collapsible for chronicles
This commit is contained in:
parent
bf6bf2e8a8
commit
a52cb1fe54
|
@ -28,6 +28,7 @@
|
|||
"patch-package": "^7.0.0",
|
||||
"rc-slider": "^10.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-collapsible": "^2.10.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hotkeys-hook": "^3.4.7",
|
||||
"react-swipeable": "^7.0.0",
|
||||
|
@ -9214,6 +9215,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-collapsible": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-collapsible/-/react-collapsible-2.10.0.tgz",
|
||||
"integrity": "sha512-kEVsmlFfXBMTCnU5gwIv19MdmPAhbIPzz5Er37TiJSzRKS0IHrqAKQyQeHEmtoGIQMTcVI46FzE4z3NlVTx77A==",
|
||||
"peerDependencies": {
|
||||
"react": "~15 || ~16 || ~17 || ~18",
|
||||
"react-dom": "~15 || ~16 || ~17 || ~18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
|
@ -17699,6 +17709,12 @@
|
|||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-collapsible": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-collapsible/-/react-collapsible-2.10.0.tgz",
|
||||
"integrity": "sha512-kEVsmlFfXBMTCnU5gwIv19MdmPAhbIPzz5Er37TiJSzRKS0IHrqAKQyQeHEmtoGIQMTcVI46FzE4z3NlVTx77A==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"patch-package": "^7.0.0",
|
||||
"rc-slider": "^10.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-collapsible": "^2.10.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hotkeys-hook": "^3.4.7",
|
||||
"react-swipeable": "^7.0.0",
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { useCallback } from "react";
|
||||
import { useBoolean } from "usehooks-ts";
|
||||
import Collapsible from "react-collapsible";
|
||||
import { TranslatedChroniclePreview } from "./ChroniclePreview";
|
||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { prettyInlineTitle, prettySlug, sJoin } from "helpers/formatters";
|
||||
import { Ico } from "components/Ico";
|
||||
import { compareDate } from "helpers/date";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { useAtomSetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -24,27 +24,41 @@ interface Props {
|
|||
>["data"];
|
||||
currentSlug?: string;
|
||||
title: string;
|
||||
open?: boolean;
|
||||
onTriggerClosing?: () => void;
|
||||
onOpening?: () => void;
|
||||
}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element => {
|
||||
const ChroniclesList = ({
|
||||
chronicles,
|
||||
currentSlug,
|
||||
title,
|
||||
open,
|
||||
onTriggerClosing,
|
||||
onOpening,
|
||||
}: Props): JSX.Element => {
|
||||
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
|
||||
const { value: isOpen, toggle: toggleOpen } = useBoolean(
|
||||
chronicles.some((chronicle) => chronicle.attributes?.slug === currentSlug)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="grid place-content-center">
|
||||
<div className="grid cursor-pointer grid-cols-[1em_1fr] gap-4" onClick={toggleOpen}>
|
||||
<Ico className="!text-xl" icon={isOpen ? "arrow_drop_up" : "arrow_drop_down"} />
|
||||
<p className="mb-4 font-headers text-xl">{title}</p>
|
||||
<Collapsible
|
||||
open={open}
|
||||
accordionPosition={title}
|
||||
contentInnerClassName="grid gap-4 pt-4"
|
||||
onTriggerClosing={onTriggerClosing}
|
||||
onOpening={onOpening}
|
||||
easing="ease-in-out"
|
||||
transitionTime={400}
|
||||
lazyRender
|
||||
contentHiddenWhenClosed
|
||||
trigger={
|
||||
<div className="flex place-content-center place-items-center gap-4">
|
||||
<h2 className="text-center text-xl">{title}</h2>
|
||||
<Button icon={open ? "expand_less" : "expand_more"} active={open} size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="grid gap-4 overflow-hidden transition-height duration-500"
|
||||
style={{ maxHeight: isOpen ? `${8 * chronicles.length}rem` : 0 }}>
|
||||
}>
|
||||
{filterHasAttributes(chronicles, ["attributes.contents", "attributes.translations"])
|
||||
.sort((a, b) => compareDate(a.attributes.date_start, b.attributes.date_start))
|
||||
.map((chronicle) => (
|
||||
|
@ -104,7 +118,7 @@ const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element
|
|||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { useState } from "react";
|
||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
chapters: NonNullable<GetChroniclesChaptersQuery["chroniclesChapters"]>["data"];
|
||||
currentChronicleSlug?: string;
|
||||
}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ChroniclesLists = ({ chapters, currentChronicleSlug }: Props): JSX.Element => {
|
||||
const [openedIndex, setOpenedIndex] = useState(
|
||||
currentChronicleSlug
|
||||
? chapters.findIndex((chapter) =>
|
||||
chapter.attributes?.chronicles?.data.some(
|
||||
(chronicle) => chronicle.attributes?.slug === currentChronicleSlug
|
||||
)
|
||||
)
|
||||
: -1
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid gap-16">
|
||||
{filterHasAttributes(chapters, ["attributes.chronicles", "id"]).map(
|
||||
(chapter, chapterIndex) => (
|
||||
<TranslatedChroniclesList
|
||||
currentSlug={currentChronicleSlug}
|
||||
open={openedIndex === chapterIndex}
|
||||
onOpening={() => setOpenedIndex(chapterIndex)}
|
||||
onTriggerClosing={() => setOpenedIndex(-1)}
|
||||
key={chapter.id}
|
||||
chronicles={chapter.attributes.chronicles.data}
|
||||
translations={filterHasAttributes(chapter.attributes.titles, [
|
||||
"language.data.attributes.code",
|
||||
]).map((translation) => ({
|
||||
title: translation.title,
|
||||
language: translation.language.data.attributes.code,
|
||||
}))}
|
||||
fallback={{ title: prettySlug(chapter.attributes.slug) }}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -22,6 +22,7 @@ import { Ids } from "types/ids";
|
|||
import { useFormat } from "hooks/useFormat";
|
||||
import { getFormat } from "helpers/i18n";
|
||||
import { ElementsSeparator } from "helpers/component";
|
||||
import { ChroniclesLists } from "components/Chronicles/ChroniclesLists";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -65,6 +66,18 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
|
|||
),
|
||||
});
|
||||
|
||||
const subPanel = (
|
||||
<SubPanel>
|
||||
<ReturnButton
|
||||
displayOnlyOn={"3ColumnsLayout"}
|
||||
href="/chronicles"
|
||||
title={format("chronicles")}
|
||||
/>
|
||||
<HorizontalLine />
|
||||
<ChroniclesLists chapters={chapters} currentChronicleSlug={chronicle.slug} />
|
||||
</SubPanel>
|
||||
);
|
||||
|
||||
const contentPanel = (
|
||||
<ContentPanel>
|
||||
<ReturnButton
|
||||
|
@ -116,35 +129,6 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
|
|||
</ContentPanel>
|
||||
);
|
||||
|
||||
const subPanel = (
|
||||
<SubPanel>
|
||||
<ReturnButton
|
||||
displayOnlyOn={"3ColumnsLayout"}
|
||||
href="/chronicles"
|
||||
title={format("chronicles")}
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<div className="grid gap-16">
|
||||
{filterHasAttributes(chapters, ["attributes.chronicles", "id"]).map((chapter) => (
|
||||
<TranslatedChroniclesList
|
||||
key={chapter.id}
|
||||
chronicles={chapter.attributes.chronicles.data}
|
||||
translations={filterHasAttributes(chapter.attributes.titles, [
|
||||
"language.data.attributes.code",
|
||||
]).map((translation) => ({
|
||||
title: translation.title,
|
||||
language: translation.language.data.attributes.code,
|
||||
}))}
|
||||
fallback={{ title: prettySlug(chapter.attributes.slug) }}
|
||||
currentSlug={chronicle.slug}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</SubPanel>
|
||||
);
|
||||
|
||||
return (
|
||||
<AppLayout
|
||||
contentPanel={contentPanel}
|
||||
|
|
|
@ -4,13 +4,11 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
|||
import { SubPanel } from "components/Containers/SubPanel";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { useFormat } from "hooks/useFormat";
|
||||
import { getFormat } from "helpers/i18n";
|
||||
import { ChroniclesLists } from "components/Chronicles/ChroniclesLists";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -23,6 +21,7 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||
const { format } = useFormat();
|
||||
|
||||
const subPanel = (
|
||||
<SubPanel>
|
||||
<PanelHeader
|
||||
|
@ -30,24 +29,8 @@ const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
|||
title={format("chronicles")}
|
||||
description={format("chronicles_description")}
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<div className="grid gap-16">
|
||||
{filterHasAttributes(chapters, ["attributes.chronicles", "id"]).map((chapter) => (
|
||||
<TranslatedChroniclesList
|
||||
key={chapter.id}
|
||||
chronicles={chapter.attributes.chronicles.data}
|
||||
translations={filterHasAttributes(chapter.attributes.titles, [
|
||||
"language.data.attributes.code",
|
||||
]).map((translation) => ({
|
||||
title: translation.title,
|
||||
language: translation.language.data.attributes.code,
|
||||
}))}
|
||||
fallback={{ title: prettySlug(chapter.attributes.slug) }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<ChroniclesLists chapters={chapters} />
|
||||
</SubPanel>
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue