Use react-collapsible for chronicles
This commit is contained in:
parent
bf6bf2e8a8
commit
a52cb1fe54
|
@ -28,6 +28,7 @@
|
||||||
"patch-package": "^7.0.0",
|
"patch-package": "^7.0.0",
|
||||||
"rc-slider": "^10.1.1",
|
"rc-slider": "^10.1.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-collapsible": "^2.10.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hotkeys-hook": "^3.4.7",
|
"react-hotkeys-hook": "^3.4.7",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
@ -9214,6 +9215,15 @@
|
||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
@ -17699,6 +17709,12 @@
|
||||||
"loose-envify": "^1.1.0"
|
"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": {
|
"react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"patch-package": "^7.0.0",
|
"patch-package": "^7.0.0",
|
||||||
"rc-slider": "^10.1.1",
|
"rc-slider": "^10.1.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-collapsible": "^2.10.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hotkeys-hook": "^3.4.7",
|
"react-hotkeys-hook": "^3.4.7",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useBoolean } from "usehooks-ts";
|
import Collapsible from "react-collapsible";
|
||||||
import { TranslatedChroniclePreview } from "./ChroniclePreview";
|
import { TranslatedChroniclePreview } from "./ChroniclePreview";
|
||||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||||
import { filterHasAttributes } from "helpers/asserts";
|
import { filterHasAttributes } from "helpers/asserts";
|
||||||
import { prettyInlineTitle, prettySlug, sJoin } from "helpers/formatters";
|
import { prettyInlineTitle, prettySlug, sJoin } from "helpers/formatters";
|
||||||
import { Ico } from "components/Ico";
|
|
||||||
import { compareDate } from "helpers/date";
|
import { compareDate } from "helpers/date";
|
||||||
import { TranslatedProps } from "types/TranslatedProps";
|
import { TranslatedProps } from "types/TranslatedProps";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { useAtomSetter } from "helpers/atoms";
|
import { useAtomSetter } from "helpers/atoms";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
|
import { Button } from "components/Inputs/Button";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -24,27 +24,41 @@ interface Props {
|
||||||
>["data"];
|
>["data"];
|
||||||
currentSlug?: string;
|
currentSlug?: string;
|
||||||
title: 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 setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
|
||||||
const { value: isOpen, toggle: toggleOpen } = useBoolean(
|
|
||||||
chronicles.some((chronicle) => chronicle.attributes?.slug === currentSlug)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="grid place-content-center">
|
<Collapsible
|
||||||
<div className="grid cursor-pointer grid-cols-[1em_1fr] gap-4" onClick={toggleOpen}>
|
open={open}
|
||||||
<Ico className="!text-xl" icon={isOpen ? "arrow_drop_up" : "arrow_drop_down"} />
|
accordionPosition={title}
|
||||||
<p className="mb-4 font-headers text-xl">{title}</p>
|
contentInnerClassName="grid gap-4 pt-4"
|
||||||
</div>
|
onTriggerClosing={onTriggerClosing}
|
||||||
</div>
|
onOpening={onOpening}
|
||||||
<div
|
easing="ease-in-out"
|
||||||
className="grid gap-4 overflow-hidden transition-height duration-500"
|
transitionTime={400}
|
||||||
style={{ maxHeight: isOpen ? `${8 * chronicles.length}rem` : 0 }}>
|
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>
|
||||||
|
}>
|
||||||
{filterHasAttributes(chronicles, ["attributes.contents", "attributes.translations"])
|
{filterHasAttributes(chronicles, ["attributes.contents", "attributes.translations"])
|
||||||
.sort((a, b) => compareDate(a.attributes.date_start, b.attributes.date_start))
|
.sort((a, b) => compareDate(a.attributes.date_start, b.attributes.date_start))
|
||||||
.map((chronicle) => (
|
.map((chronicle) => (
|
||||||
|
@ -104,7 +118,7 @@ const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Collapsible>
|
||||||
</div>
|
</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 { useFormat } from "hooks/useFormat";
|
||||||
import { getFormat } from "helpers/i18n";
|
import { getFormat } from "helpers/i18n";
|
||||||
import { ElementsSeparator } from "helpers/component";
|
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 = (
|
const contentPanel = (
|
||||||
<ContentPanel>
|
<ContentPanel>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
|
@ -116,35 +129,6 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
|
||||||
</ContentPanel>
|
</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 (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
contentPanel={contentPanel}
|
contentPanel={contentPanel}
|
||||||
|
|
|
@ -4,13 +4,11 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Containers/SubPanel";
|
import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||||
import { filterHasAttributes } from "helpers/asserts";
|
|
||||||
import { prettySlug } from "helpers/formatters";
|
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { useFormat } from "hooks/useFormat";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { getFormat } from "helpers/i18n";
|
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 Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
const { format } = useFormat();
|
const { format } = useFormat();
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
|
@ -30,24 +29,8 @@ const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
title={format("chronicles")}
|
title={format("chronicles")}
|
||||||
description={format("chronicles_description")}
|
description={format("chronicles_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
<ChroniclesLists chapters={chapters} />
|
||||||
<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>
|
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue