Added intersection + added container query

This commit is contained in:
DrMint 2022-08-22 21:56:00 +02:00
parent acd2d7d482
commit 3afaea7027
51 changed files with 1044 additions and 549 deletions

View File

@ -34,10 +34,11 @@
- Support for Arbitrary React Components and Component Props! - Support for Arbitrary React Components and Component Props!
- Autogenerated multi-level table of content and anchor links for the different headers - Autogenerated multi-level table of content and anchor links for the different headers
- Styling: [Tailwind CSS](https://tailwindcss.com/) - Styling: [Tailwind CSS](https://tailwindcss.com/)
- Manually added support for scrollbar styling - Manually added support for scrollbar styling to Tailwind CSS
- Support for [Material Icons](https://fonts.google.com/icons) - Support for [Material Icons](https://fonts.google.com/icons)
- Support for creating any arbitrary theming mode by swapping CSS variables - Support for creating any arbitrary theming mode by swapping CSS variables
- Support for many screen sizes and resolutions - Support for Container Queries (media queries at the element level)
- The website has a three-column layout, which turns into one-column + 2 toggleable side-menus if the screen is too narrow.
- State Management: [React Context](https://reactjs.org/docs/context.html) - State Management: [React Context](https://reactjs.org/docs/context.html)
- Persistent app state using LocalStorage - Persistent app state using LocalStorage
- Accessibility - Accessibility
@ -56,7 +57,7 @@
- Performances are great, and possibility to deploy the app using a CDN - Performances are great, and possibility to deploy the app using a CDN
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted - On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
- SEO - SEO
- Good defaults for the metadate and OpenGraph properties - Good defaults for the metadata and OpenGraph properties
- Each page can provide the thumbnail, title, description to be used - Each page can provide the thumbnail, title, description to be used
- Automatic generation of the sitemap using [next-sitemap](https://www.npmjs.com/package/next-sitemap) - Automatic generation of the sitemap using [next-sitemap](https://www.npmjs.com/package/next-sitemap)
- Data quality testing - Data quality testing
@ -105,6 +106,6 @@ Run in dev mode:
OR build and run in production mode OR build and run in production mode
```bash ```bash
./run_accords_build.sh npm run build
./run_accords_prod.sh ./run_accords_prod.sh
``` ```

View File

@ -17,12 +17,6 @@ const colors = {
}, },
}; };
const breaks = {
thin: { raw: "(max-width: 25rem)" },
mobile: { raw: "(max-width: 60rem)" },
desktop: { raw: "(min-width: 60rem)" },
};
const fonts = { const fonts = {
openDyslexic: "OpenDyslexic", openDyslexic: "OpenDyslexic",
vollkorn: "Vollkorn", vollkorn: "Vollkorn",
@ -43,9 +37,16 @@ const fontFamilies = {
}, },
}; };
const layout = {
// all values in rem
mainMenuReduced: 6,
mainMenu: 20,
subMenu: 20,
};
module.exports = { module.exports = {
colors, colors,
breaks, layout,
fonts, fonts,
fontFamilies, fontFamilies,
}; };

28
package-lock.json generated
View File

@ -34,7 +34,7 @@
"@graphql-codegen/typescript": "2.7.3", "@graphql-codegen/typescript": "2.7.3",
"@graphql-codegen/typescript-graphql-request": "^4.5.3", "@graphql-codegen/typescript-graphql-request": "^4.5.3",
"@graphql-codegen/typescript-operations": "^2.5.3", "@graphql-codegen/typescript-operations": "^2.5.3",
"@types/node": "18.7.8", "@types/node": "18.7.9",
"@types/nodemailer": "^6.4.5", "@types/nodemailer": "^6.4.5",
"@types/react": "18.0.17", "@types/react": "18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
@ -46,7 +46,7 @@
"eslint-config-next": "12.2.5", "eslint-config-next": "12.2.5",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"next-sitemap": "^3.1.20", "next-sitemap": "^3.1.21",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13", "prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.1.8", "tailwindcss": "^3.1.8",
@ -2411,9 +2411,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.7.8", "version": "18.7.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.9.tgz",
"integrity": "sha512-/YP55EMK2341JkODUb8DM9O0x1SIz2aBvyF33Uf1c76St3VpsMXEIW0nxuKkq/5cxnbz0RD9cfwNZHEAZQD3ag==", "integrity": "sha512-0N5Y1XAdcl865nDdjbO0m3T6FdmQ4ijE89/urOHLREyTXbpMWbSafx9y7XIsgWGtwUP2iYTinLyyW3FatAxBLQ==",
"dev": true "dev": true
}, },
"node_modules/@types/nodemailer": { "node_modules/@types/nodemailer": {
@ -6287,9 +6287,9 @@
} }
}, },
"node_modules/next-sitemap": { "node_modules/next-sitemap": {
"version": "3.1.20", "version": "3.1.21",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-3.1.20.tgz", "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-3.1.21.tgz",
"integrity": "sha512-ugcSQpAtwc9fk7fsr5to0yrlA/RjY0kw2wHOUqcrRXnZDwFfBwGnEVvaYbI7C4ZlhvxjJTA3G15QMpvpmvPquw==", "integrity": "sha512-8upGCtI91FvjNBpeKDZzrzRBlY4BH6qjG7dMe89sdBzJ9B++PVLTzRPlpLHJYOEPukBROsxi2zVuAvePBtsrdw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -10334,9 +10334,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "18.7.8", "version": "18.7.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.9.tgz",
"integrity": "sha512-/YP55EMK2341JkODUb8DM9O0x1SIz2aBvyF33Uf1c76St3VpsMXEIW0nxuKkq/5cxnbz0RD9cfwNZHEAZQD3ag==", "integrity": "sha512-0N5Y1XAdcl865nDdjbO0m3T6FdmQ4ijE89/urOHLREyTXbpMWbSafx9y7XIsgWGtwUP2iYTinLyyW3FatAxBLQ==",
"dev": true "dev": true
}, },
"@types/nodemailer": { "@types/nodemailer": {
@ -13263,9 +13263,9 @@
} }
}, },
"next-sitemap": { "next-sitemap": {
"version": "3.1.20", "version": "3.1.21",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-3.1.20.tgz", "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-3.1.21.tgz",
"integrity": "sha512-ugcSQpAtwc9fk7fsr5to0yrlA/RjY0kw2wHOUqcrRXnZDwFfBwGnEVvaYbI7C4ZlhvxjJTA3G15QMpvpmvPquw==", "integrity": "sha512-8upGCtI91FvjNBpeKDZzrzRBlY4BH6qjG7dMe89sdBzJ9B++PVLTzRPlpLHJYOEPukBROsxi2zVuAvePBtsrdw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@corex/deepmerge": "^4.0.29", "@corex/deepmerge": "^4.0.29",

View File

@ -44,7 +44,7 @@
"@graphql-codegen/typescript": "2.7.3", "@graphql-codegen/typescript": "2.7.3",
"@graphql-codegen/typescript-graphql-request": "^4.5.3", "@graphql-codegen/typescript-graphql-request": "^4.5.3",
"@graphql-codegen/typescript-operations": "^2.5.3", "@graphql-codegen/typescript-operations": "^2.5.3",
"@types/node": "18.7.8", "@types/node": "18.7.9",
"@types/nodemailer": "^6.4.5", "@types/nodemailer": "^6.4.5",
"@types/react": "18.0.17", "@types/react": "18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
@ -56,7 +56,7 @@
"eslint-config-next": "12.2.5", "eslint-config-next": "12.2.5",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"next-sitemap": "^3.1.20", "next-sitemap": "^3.1.21",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13", "prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.1.8", "tailwindcss": "^3.1.8",

View File

@ -4,6 +4,7 @@ import { useEffect, useLayoutEffect, useMemo, useState } from "react";
import { useSwipeable } from "react-swipeable"; import { useSwipeable } from "react-swipeable";
import UAParser from "ua-parser-js"; import UAParser from "ua-parser-js";
import { useBoolean, useIsClient } from "usehooks-ts"; import { useBoolean, useIsClient } from "usehooks-ts";
import { layout } from "../../design.config";
import { Ico, Icon } from "./Ico"; import { Ico, Icon } from "./Ico";
import { ButtonGroup } from "./Inputs/ButtonGroup"; import { ButtonGroup } from "./Inputs/ButtonGroup";
import { OrderableList } from "./Inputs/OrderableList"; import { OrderableList } from "./Inputs/OrderableList";
@ -12,7 +13,6 @@ import { TextInput } from "./Inputs/TextInput";
import { MainPanel } from "./Panels/MainPanel"; import { MainPanel } from "./Panels/MainPanel";
import { Popup } from "./Popup"; import { Popup } from "./Popup";
import { AnchorIds } from "hooks/useScrollTopOnChange"; import { AnchorIds } from "hooks/useScrollTopOnChange";
import { useMediaMobile } from "hooks/useMediaQuery";
import { import {
filterHasAttributes, filterHasAttributes,
isDefined, isDefined,
@ -27,6 +27,11 @@ import { useAppLayout } from "contexts/AppLayoutContext";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph"; import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
import { getDefaultPreferredLanguages } from "helpers/locales"; import { getDefaultPreferredLanguages } from "helpers/locales";
import {
useIs1ColumnLayout,
useIsScreenAtLeast,
} from "hooks/useContainerQuery";
import { useOnResize } from "hooks/useOnResize";
/* /*
* *
@ -88,10 +93,18 @@ export const AppLayout = ({
setSubPanelOpen, setSubPanelOpen,
toggleMainPanelOpen, toggleMainPanelOpen,
toggleSubPanelOpen, toggleSubPanelOpen,
setScreenWidth,
setContentPanelWidth,
setSubPanelWidth,
} = useAppLayout(); } = useAppLayout();
const router = useRouter(); const router = useRouter();
const isMobile = useMediaMobile(); const is1ColumnLayout = useIs1ColumnLayout();
const isScreenAtLeastXs = useIsScreenAtLeast("xs");
useOnResize(AnchorIds.Body, (width) => setScreenWidth(width));
useOnResize(AnchorIds.ContentPanel, (width) => setContentPanelWidth(width));
useOnResize(AnchorIds.SubPanel, (width) => setSubPanelWidth(width));
useEffect(() => { useEffect(() => {
router.events.on("routeChangeStart", () => { router.events.on("routeChangeStart", () => {
@ -178,18 +191,6 @@ export const AppLayout = ({
setPreferredLanguages, setPreferredLanguages,
]); ]);
const gridCol = useMemo(() => {
if (isDefined(subPanel)) {
if (mainPanelReduced) {
return "grid-cols-[6rem_20rem_1fr]";
}
return "grid-cols-[20rem_20rem_1fr]";
} else if (mainPanelReduced) {
return "grid-cols-[6rem_0px_1fr]";
}
return "grid-cols-[20rem_0px_1fr]";
}, [mainPanelReduced, subPanel]);
const isClient = useIsClient(); const isClient = useIsClient();
const { value: hasDisgardSafariWarning, setTrue: disgardSafariWarning } = const { value: hasDisgardSafariWarning, setTrue: disgardSafariWarning } =
useBoolean(false); useBoolean(false);
@ -212,12 +213,22 @@ export const AppLayout = ({
> >
<div <div
{...handlers} {...handlers}
id={AnchorIds.Body}
className={cJoin( className={cJoin(
`fixed inset-0 m-0 grid touch-pan-y bg-light p-0 text-black `fixed inset-0 m-0 grid touch-pan-y bg-light p-0 text-black
[grid-template-areas:'main_sub_content'] mobile:grid-cols-[1fr] [grid-template-areas:'main_sub_content']`,
mobile:grid-rows-[1fr_5rem] mobile:[grid-template-areas:'content''navbar']`, cIf(
gridCol is1ColumnLayout,
"grid-rows-[1fr_5rem] [grid-template-areas:'content''navbar']"
)
)} )}
style={{
gridTemplateColumns: is1ColumnLayout
? "1fr"
: `${
mainPanelReduced ? layout.mainMenuReduced : layout.mainMenu
}rem ${isDefined(subPanel) ? layout.subMenu : 0}rem 1fr`,
}}
> >
<Head> <Head>
<title>{openGraph.title}</title> <title>{openGraph.title}</title>
@ -257,11 +268,11 @@ export const AppLayout = ({
{/* Background when navbar is opened */} {/* Background when navbar is opened */}
<div <div
className={cJoin( className={cJoin(
`absolute inset-0 transition-[backdrop-filter] duration-500 [grid-area:content] `absolute inset-0 transition-[backdrop-filter] duration-500
mobile:z-10`, [grid-area:content]`,
cIf( cIf(
(mainPanelOpen || subPanelOpen) && isMobile, (mainPanelOpen || subPanelOpen) && is1ColumnLayout,
"[backdrop-filter:blur(2px)]", "z-10 [backdrop-filter:blur(2px)]",
"pointer-events-none touch-none" "pointer-events-none touch-none"
) )
)} )}
@ -270,7 +281,7 @@ export const AppLayout = ({
className={cJoin( className={cJoin(
"absolute inset-0 bg-shade transition-opacity duration-500", "absolute inset-0 bg-shade transition-opacity duration-500",
cIf( cIf(
(mainPanelOpen || subPanelOpen) && isMobile, (mainPanelOpen || subPanelOpen) && is1ColumnLayout,
"opacity-60", "opacity-60",
"opacity-0" "opacity-0"
) )
@ -303,16 +314,26 @@ export const AppLayout = ({
{/* Sub panel */} {/* Sub panel */}
{isDefined(subPanel) && ( {isDefined(subPanel) && (
<div <div
id={AnchorIds.SubPanel}
className={cJoin( className={cJoin(
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light `texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light
transition-transform duration-300 [grid-area:sub] [scrollbar-width:none] transition-transform duration-300 [scrollbar-width:none]
webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%] mobile:justify-self-end webkit-scrollbar:w-0`,
mobile:border-r-0 mobile:border-l-[1px] mobile:[grid-area:content]`, cIf(
turnSubIntoContent is1ColumnLayout,
? "mobile:w-full mobile:border-l-0" `z-10 justify-self-end border-r-0
: subPanelOpen [grid-area:content]`,
? "" "[grid-area:sub]"
: "mobile:translate-x-[100vw]" ),
cIf(
is1ColumnLayout && isScreenAtLeastXs,
"w-[min(30rem,90%)] border-l-[1px]"
),
cIf(
is1ColumnLayout && !subPanelOpen && !turnSubIntoContent,
"translate-x-[100vw]"
),
cIf(is1ColumnLayout && turnSubIntoContent, "w-full border-l-0")
)} )}
> >
{subPanel} {subPanel}
@ -323,10 +344,14 @@ export const AppLayout = ({
<div <div
className={cJoin( className={cJoin(
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light `texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light
transition-transform duration-300 [grid-area:main] [scrollbar-width:none] transition-transform duration-300 [scrollbar-width:none] webkit-scrollbar:w-0`,
webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%] mobile:justify-self-start cIf(
mobile:[grid-area:content]`, is1ColumnLayout,
cIf(!mainPanelOpen, "mobile:-translate-x-full") "z-10 justify-self-start [grid-area:content]",
"[grid-area:main]"
),
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
cIf(!mainPanelOpen && is1ColumnLayout, "-translate-x-full")
)} )}
> >
<MainPanel langui={langui} /> <MainPanel langui={langui} />
@ -334,8 +359,11 @@ export const AppLayout = ({
{/* Navbar */} {/* Navbar */}
<div <div
className="texture-paper-dots grid grid-cols-[5rem_1fr_5rem] place-items-center className={cJoin(
border-t-[1px] border-dotted border-black bg-light [grid-area:navbar] desktop:hidden" `texture-paper-dots grid grid-cols-[5rem_1fr_5rem] place-items-center
border-t-[1px] border-dotted border-black bg-light [grid-area:navbar]`,
cIf(!is1ColumnLayout, "hidden")
)}
> >
<Ico <Ico
icon={mainPanelOpen ? Icon.Close : Icon.Menu} icon={mainPanelOpen ? Icon.Close : Icon.Menu}
@ -410,8 +438,10 @@ export const AppLayout = ({
<h2 className="text-2xl">{langui.settings}</h2> <h2 className="text-2xl">{langui.settings}</h2>
<div <div
className="mt-4 grid justify-items-center gap-16 className={cJoin(
text-center desktop:grid-cols-[auto_auto]" `mt-4 grid justify-items-center gap-16 text-center`,
cIf(!is1ColumnLayout, "grid-cols-[auto_auto]")
)}
> >
{router.locales && ( {router.locales && (
<div> <div>
@ -443,7 +473,12 @@ export const AppLayout = ({
)} )}
</div> </div>
)} )}
<div className="grid place-items-center gap-8 text-center desktop:grid-cols-2"> <div
className={cJoin(
"grid place-items-center gap-8 text-center",
cIf(!is1ColumnLayout, "grid-cols-2")
)}
>
<div> <div>
<h3 className="text-xl">{langui.theme}</h3> <h3 className="text-xl">{langui.theme}</h3>
<ButtonGroup <ButtonGroup

View File

@ -27,7 +27,7 @@ export const PreviewCardCTAs = ({
return ( return (
<> <>
<div <div
className={`flex flex-row place-content-center place-items-center ${ className={`flex flex-row flex-wrap place-content-center place-items-center ${
expand ? "gap-4" : "gap-2" expand ? "gap-4" : "gap-2"
}`} }`}
> >

View File

@ -5,6 +5,8 @@ import { Img } from "./Img";
import { Button } from "./Inputs/Button"; import { Button } from "./Inputs/Button";
import { Popup } from "./Popup"; import { Popup } from "./Popup";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
import { cIf, cJoin } from "helpers/className";
/* /*
* *
@ -40,6 +42,7 @@ export const LightBox = ({
const handlePrevious = useCallback(() => { const handlePrevious = useCallback(() => {
if (index > 0) setIndex(index - 1); if (index > 0) setIndex(index - 1);
}, [index, setIndex]); }, [index, setIndex]);
const is3ColumnsLayout = useIs3ColumnsLayout();
const handleNext = useCallback(() => { const handleNext = useCallback(() => {
if (index < images.length - 1) setIndex(index + 1); if (index < images.length - 1) setIndex(index + 1);
@ -78,11 +81,16 @@ export const LightBox = ({
> >
<div <div
{...handlers} {...handlers}
className={`grid h-full w-full grid-cols-[4em,1fr,4em] place-items-center className={cJoin(
overflow-hidden [grid-template-areas:"left_image_right"] first-letter:gap-4 `grid h-full w-full place-items-center overflow-hidden first-letter:gap-4`,
mobile:grid-cols-2 mobile:[grid-template-areas:"image_image""left_right"]`} cIf(
is3ColumnsLayout,
`grid-cols-[4em,1fr,4em] [grid-template-areas:"left_image_right"]`,
`grid-cols-2 [grid-template-areas:"image_image""left_right"]`
)
)}
> >
<div className="[grid-area:left]"> <div className="ml-4 [grid-area:left]">
{index > 0 && ( {index > 0 && (
<Button onClick={handlePrevious} icon={Icon.ChevronLeft} /> <Button onClick={handlePrevious} icon={Icon.ChevronLeft} />
)} )}
@ -93,7 +101,7 @@ export const LightBox = ({
src={images[index]} src={images[index]}
/> />
<div className="[grid-area:right]"> <div className="mr-4 [grid-area:right]">
{index < images.length - 1 && ( {index < images.length - 1 && (
<Button onClick={handleNext} icon={Icon.ChevronRight} /> <Button onClick={handleNext} icon={Icon.ChevronRight} />
)} )}

View File

@ -15,6 +15,8 @@ import { useLightBox } from "hooks/useLightBox";
import { AnchorShare } from "components/AnchorShare"; import { AnchorShare } from "components/AnchorShare";
import { useIntersectionList } from "hooks/useIntersectionList"; import { useIntersectionList } from "hooks/useIntersectionList";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
/* /*
* *
@ -36,6 +38,7 @@ export const Markdawn = ({
}: MarkdawnProps): JSX.Element => { }: MarkdawnProps): JSX.Element => {
const { playerName } = useAppLayout(); const { playerName } = useAppLayout();
const router = useRouter(); const router = useRouter();
const isContentPanelAtLeastLg = useIsContentPanelAtLeast("lg");
const [openLightBox, LightBox] = useLightBox(); const [openLightBox, LightBox] = useLightBox();
/* eslint-disable no-irregular-whitespace */ /* eslint-disable no-irregular-whitespace */
@ -131,7 +134,16 @@ export const Markdawn = ({
Transcript: { Transcript: {
component: (compProps) => ( component: (compProps) => (
<div className="grid grid-cols-[auto_1fr] gap-x-6 gap-y-2 mobile:grid-cols-1"> <div
className={cJoin(
"grid gap-x-6 gap-y-2",
cIf(
isContentPanelAtLeastLg,
"grid-cols-[auto_1fr]",
"grid-cols-1"
)
)}
>
{compProps.children} {compProps.children}
</div> </div>
), ),
@ -140,7 +152,12 @@ export const Markdawn = ({
Line: { Line: {
component: (compProps) => ( component: (compProps) => (
<> <>
<strong className="!my-0 text-dark/60 mobile:!-mb-4"> <strong
className={cJoin(
"!my-0 text-dark/60",
cIf(!isContentPanelAtLeastLg, "!-mb-4")
)}
>
<Markdawn text={compProps.name} langui={langui} /> <Markdawn text={compProps.name} langui={langui} />
</strong> </strong>
<p className="whitespace-pre-line">{compProps.children}</p> <p className="whitespace-pre-line">{compProps.children}</p>
@ -301,12 +318,13 @@ interface HeaderProps {
} }
const Header = ({ level, title, slug, langui }: HeaderProps): JSX.Element => { const Header = ({ level, title, slug, langui }: HeaderProps): JSX.Element => {
const isHoverable = useDeviceSupportsHover();
const innerComponent = useMemo( const innerComponent = useMemo(
() => ( () => (
<> <>
<div className="mt-8 mr-2 mb-12 flex place-items-center gap-4"> <div className="ml-10 flex place-items-center gap-4">
{title === "* * *" ? ( {title === "* * *" ? (
<div className="space-x-3 text-dark"> <div className="mt-8 mb-12 space-x-3 text-dark">
<Ico icon={Icon.Emergency} /> <Ico icon={Icon.Emergency} />
<Ico icon={Icon.Emergency} /> <Ico icon={Icon.Emergency} />
<Ico icon={Icon.Emergency} /> <Ico icon={Icon.Emergency} />
@ -315,52 +333,53 @@ const Header = ({ level, title, slug, langui }: HeaderProps): JSX.Element => {
<div className="font-headers">{title}</div> <div className="font-headers">{title}</div>
)} )}
<AnchorShare <AnchorShare
className="opacity-0 transition-opacity group-hover:opacity-100" className={cIf(
isHoverable,
"opacity-0 transition-opacity group-hover:opacity-100"
)}
id={slug} id={slug}
langui={langui} langui={langui}
/> />
</div> </div>
</> </>
), ),
[langui, slug, title] [isHoverable, langui, slug, title]
); );
const className = "group";
switch (level) { switch (level) {
case 1: case 1:
return ( return (
<h1 id={slug} className={className}> <h1 id={slug} className="group">
{innerComponent} {innerComponent}
</h1> </h1>
); );
case 2: case 2:
return ( return (
<h2 id={slug} className={className}> <h2 id={slug} className="group">
{innerComponent} {innerComponent}
</h2> </h2>
); );
case 3: case 3:
return ( return (
<h3 id={slug} className={className}> <h3 id={slug} className="group">
{innerComponent} {innerComponent}
</h3> </h3>
); );
case 4: case 4:
return ( return (
<h4 id={slug} className={className}> <h4 id={slug} className="group">
{innerComponent} {innerComponent}
</h4> </h4>
); );
case 5: case 5:
return ( return (
<h5 id={slug} className={className}> <h5 id={slug} className="group">
{innerComponent} {innerComponent}
</h5> </h5>
); );
default: default:
return ( return (
<h6 id={slug} className={className}> <h6 id={slug} className="group">
{innerComponent} {innerComponent}
</h6> </h6>
); );

View File

@ -1,4 +1,3 @@
import { HorizontalLine } from "components/HorizontalLine";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { isDefinedAndNotEmpty } from "helpers/others"; import { isDefinedAndNotEmpty } from "helpers/others";
@ -26,6 +25,5 @@ export const PanelHeader = ({
<h2 className="text-2xl">{title}</h2> <h2 className="text-2xl">{title}</h2>
{isDefinedAndNotEmpty(description) && <p>{description}</p>} {isDefinedAndNotEmpty(description) && <p>{description}</p>}
</div> </div>
<HorizontalLine />
</> </>
); );

View File

@ -3,9 +3,10 @@ import { Icon } from "components/Ico";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import { AppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps } from "graphql/getAppStaticProps";
import { cIf, cJoin } from "helpers/className";
import { TranslatedProps } from "helpers/types/TranslatedProps"; import { TranslatedProps } from "helpers/types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
import { isDefined } from "helpers/others";
/* /*
* *
@ -16,42 +17,37 @@ interface Props {
href: string; href: string;
title: string | null | undefined; title: string | null | undefined;
langui: AppStaticProps["langui"]; langui: AppStaticProps["langui"];
displayOn: ReturnButtonType; displayOnlyOn?: "1ColumnLayout" | "3ColumnsLayout";
className?: string; className?: string;
} }
export enum ReturnButtonType {
Mobile = "mobile",
Desktop = "desktop",
Both = "both",
}
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const ReturnButton = ({ export const ReturnButton = ({
href, href,
title, title,
langui, langui,
displayOn, displayOnlyOn,
className, className,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { setSubPanelOpen } = useAppLayout(); const { setSubPanelOpen } = useAppLayout();
const is3ColumnsLayout = useIs3ColumnsLayout();
return ( return (
<div <>
className={cJoin( {((is3ColumnsLayout && displayOnlyOn === "3ColumnsLayout") ||
cIf(displayOn === ReturnButtonType.Mobile, "desktop:hidden"), (!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
cIf(displayOn === ReturnButtonType.Desktop, "mobile:hidden"), !isDefined(displayOnlyOn)) && (
className <div className={className}>
<Button
onClick={() => setSubPanelOpen(false)}
href={href}
text={`${langui.return_to} ${title}`}
icon={Icon.NavigateBefore}
/>
</div>
)} )}
> </>
<Button
onClick={() => setSubPanelOpen(false)}
href={href}
text={`${langui.return_to} ${title}`}
icon={Icon.NavigateBefore}
/>
</div>
); );
}; };

View File

@ -1,4 +1,5 @@
import { cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
/* /*
* *
@ -23,20 +24,24 @@ export const ContentPanel = ({
width = ContentPanelWidthSizes.Default, width = ContentPanelWidthSizes.Default,
children, children,
className, className,
}: Props): JSX.Element => ( }: Props): JSX.Element => {
<div className="grid h-full"> const isContentPanelAtLeast3xl = useIsContentPanelAtLeast("3xl");
<main return (
className={cJoin( <div className="grid h-full">
"justify-self-center px-4 pt-10 pb-20 desktop:px-10 desktop:pt-20 desktop:pb-32", <main
width === ContentPanelWidthSizes.Default className={cJoin(
? "max-w-2xl" "justify-self-center px-4 pt-10 pb-20",
: width === ContentPanelWidthSizes.Large cIf(isContentPanelAtLeast3xl, "px-10 pt-20 pb-32"),
? "max-w-4xl" width === ContentPanelWidthSizes.Default
: "w-full", ? "max-w-2xl"
className : width === ContentPanelWidthSizes.Large
)} ? "max-w-4xl"
> : "w-full",
{children} className
</main> )}
</div> >
); {children}
</main>
</div>
);
};

View File

@ -5,12 +5,11 @@ import { NavOption } from "components/PanelComponents/NavOption";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import { AppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps } from "graphql/getAppStaticProps";
import { useMediaDesktop } from "hooks/useMediaQuery";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { isDefinedAndNotEmpty } from "helpers/others"; import { isDefinedAndNotEmpty } from "helpers/others";
import { Link } from "components/Inputs/Link"; import { Link } from "components/Inputs/Link";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
/* /*
* *
@ -24,7 +23,7 @@ interface Props {
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const MainPanel = ({ langui }: Props): JSX.Element => { export const MainPanel = ({ langui }: Props): JSX.Element => {
const isDesktop = useMediaDesktop(); const is3ColumnsLayout = useIs3ColumnsLayout();
const { const {
mainPanelReduced = false, mainPanelReduced = false,
toggleMainPanelReduced, toggleMainPanelReduced,
@ -35,22 +34,24 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
<div <div
className={cJoin( className={cJoin(
"grid content-start justify-center gap-y-2 p-8 text-center", "grid content-start justify-center gap-y-2 p-8 text-center",
cIf(mainPanelReduced && isDesktop, "px-4") cIf(mainPanelReduced && is3ColumnsLayout, "px-4")
)} )}
> >
{/* Reduce/expand main menu */} {/* Reduce/expand main menu */}
<div {is3ColumnsLayout && (
className={cJoin( <div
"fixed top-1/2 mobile:hidden", className={cJoin(
cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]") "fixed top-1/2",
)} cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
onClick={toggleMainPanelReduced} )}
> onClick={toggleMainPanelReduced}
<Button >
className="bg-light !px-2" <Button
icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft} className="bg-light !px-2"
/> icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft}
</div> />
</div>
)}
<div> <div>
<div className="grid place-items-center"> <div className="grid place-items-center">
@ -60,19 +61,23 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
`mb-4 aspect-square cursor-pointer bg-black transition-colors `mb-4 aspect-square cursor-pointer bg-black transition-colors
[mask:url('/icons/accords.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] [mask:url('/icons/accords.svg')] ![mask-size:contain] ![mask-repeat:no-repeat]
![mask-position:center] hover:bg-dark`, ![mask-position:center] hover:bg-dark`,
cIf(mainPanelReduced && isDesktop, "w-12", "w-1/2") cIf(mainPanelReduced && is3ColumnsLayout, "w-12", "w-1/2")
)} )}
></div> ></div>
</Link> </Link>
{(!mainPanelReduced || !isDesktop) && ( {(!mainPanelReduced || !is3ColumnsLayout) && (
<h2 className="mb-4 text-3xl">Accord&rsquo;s Library</h2> <h2 className="mb-4 text-3xl">Accord&rsquo;s Library</h2>
)} )}
<div <div
className={cJoin( className={cJoin(
"flex flex-wrap gap-2", "flex flex-wrap gap-2",
cIf(mainPanelReduced && isDesktop, "flex-col gap-3", "flex-row") cIf(
mainPanelReduced && is3ColumnsLayout,
"flex-col gap-3",
"flex-row"
)
)} )}
> >
<ToolTip <ToolTip
@ -113,7 +118,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
icon={Icon.LibraryBooks} icon={Icon.LibraryBooks}
title={langui.library} title={langui.library}
subtitle={langui.library_short_description} subtitle={langui.library_short_description}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<NavOption <NavOption
@ -121,7 +126,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
icon={Icon.Workspaces} icon={Icon.Workspaces}
title={langui.contents} title={langui.contents}
subtitle={langui.contents_short_description} subtitle={langui.contents_short_description}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<NavOption <NavOption
@ -129,7 +134,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
icon={Icon.TravelExplore} icon={Icon.TravelExplore}
title={langui.wiki} title={langui.wiki}
subtitle={langui.wiki_short_description} subtitle={langui.wiki_short_description}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<NavOption <NavOption
@ -137,7 +142,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
icon={Icon.WatchLater} icon={Icon.WatchLater}
title={langui.chronicles} title={langui.chronicles}
subtitle={langui.chronicles_short_description} subtitle={langui.chronicles_short_description}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<HorizontalLine /> <HorizontalLine />
@ -146,7 +151,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
url="/news" url="/news"
icon={Icon.Feed} icon={Icon.Feed}
title={langui.news} title={langui.news}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
{/* {/*
@ -154,7 +159,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
url="/merch" url="/merch"
icon={Icon.Store} icon={Icon.Store}
title={langui.merch} title={langui.merch}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
*/} */}
@ -162,29 +167,29 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
url="https://gallery.accords-library.com/posts/" url="https://gallery.accords-library.com/posts/"
icon={Icon.Collections} icon={Icon.Collections}
title={langui.gallery} title={langui.gallery}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<NavOption <NavOption
url="/archives" url="/archives"
icon={Icon.Inventory} icon={Icon.Inventory}
title={langui.archives} title={langui.archives}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
<NavOption <NavOption
url="/about-us" url="/about-us"
icon={Icon.Info} icon={Icon.Info}
title={langui.about_us} title={langui.about_us}
reduced={mainPanelReduced && isDesktop} reduced={mainPanelReduced && is3ColumnsLayout}
/> />
{mainPanelReduced && isDesktop ? "" : <HorizontalLine />} {mainPanelReduced && is3ColumnsLayout ? "" : <HorizontalLine />}
<div <div
className={cJoin( className={cJoin(
"text-center", "text-center",
cIf(mainPanelReduced && isDesktop, "hidden") cIf(mainPanelReduced && is3ColumnsLayout, "hidden")
)} )}
> >
{isDefinedAndNotEmpty(langui.licensing_notice) && ( {isDefinedAndNotEmpty(langui.licensing_notice) && (

View File

@ -1,3 +1,6 @@
import { cIf, cJoin } from "helpers/className";
import { useIsSubPanelAtLeast } from "hooks/useContainerQuery";
/* /*
* *
* COMPONENT * COMPONENT
@ -9,8 +12,16 @@ interface Props {
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const SubPanel = ({ children }: Props): JSX.Element => ( export const SubPanel = ({ children }: Props): JSX.Element => {
<div className="grid gap-y-2 px-6 pt-10 pb-20 text-center desktop:py-8 desktop:px-10"> const isSubPanelAtLeastSm = useIsSubPanelAtLeast("2xs");
{children} return (
</div> <div
); className={cJoin(
"grid gap-y-2 text-center",
cIf(isSubPanelAtLeastSm, "px-10 pt-10 pb-20", "p-4")
)}
>
{children}
</div>
);
};

View File

@ -56,12 +56,12 @@ export const Popup = ({
<div <div
className={cJoin( className={cJoin(
"grid place-items-center gap-4 transition-transform", "grid place-items-center gap-4 transition-transform",
cIf(padding, "p-10 mobile:p-6"), cIf(padding, "p-10"),
cIf(state, "scale-100", "scale-0"), cIf(state, "scale-100", "scale-0"),
cIf( cIf(
fillViewport, fillViewport,
"absolute inset-10", "absolute inset-10",
"relative max-h-[80vh] overflow-y-auto mobile:w-[85vw]" "relative max-h-[80vh] overflow-y-auto"
), ),
cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade") cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade")
)} )}

View File

@ -3,7 +3,7 @@ import { AppLayout, AppLayoutRequired } from "./AppLayout";
import { Chip } from "./Chip"; import { Chip } from "./Chip";
import { HorizontalLine } from "./HorizontalLine"; import { HorizontalLine } from "./HorizontalLine";
import { Markdawn, TableOfContents } from "./Markdown/Markdawn"; import { Markdawn, TableOfContents } from "./Markdown/Markdawn";
import { ReturnButton, ReturnButtonType } from "./PanelComponents/ReturnButton"; import { ReturnButton } from "./PanelComponents/ReturnButton";
import { ContentPanel } from "./Panels/ContentPanel"; import { ContentPanel } from "./Panels/ContentPanel";
import { SubPanel } from "./Panels/SubPanel"; import { SubPanel } from "./Panels/SubPanel";
import { RecorderChip } from "./RecorderChip"; import { RecorderChip } from "./RecorderChip";
@ -85,7 +85,7 @@ export const PostPage = ({
href={returnHref} href={returnHref}
title={returnTitle} title={returnTitle}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
/> />
)} )}
@ -161,7 +161,7 @@ export const PostPage = ({
href={returnHref} href={returnHref}
title={returnTitle} title={returnTitle}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn={"1ColumnLayout"}
className="mb-10" className="mb-10"
/> />
)} )}
@ -180,8 +180,6 @@ export const PostPage = ({
) : undefined ) : undefined
} }
/> />
<HorizontalLine />
</> </>
) : ( ) : (
<> <>
@ -199,7 +197,13 @@ export const PostPage = ({
)} )}
{prependBody} {prependBody}
<Markdawn text={body} langui={langui} /> {body && (
<>
{displayThumbnailHeader && <HorizontalLine />}
<Markdawn text={body} langui={langui} />
</>
)}
{appendBody} {appendBody}
</ContentPanel> </ContentPanel>
), ),

View File

@ -19,7 +19,7 @@ import {
prettyShortenNumber, prettyShortenNumber,
} from "helpers/formatters"; } from "helpers/formatters";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedProps } from "helpers/types/TranslatedProps"; import { TranslatedProps } from "helpers/types/TranslatedProps";
@ -81,7 +81,7 @@ export const PreviewCard = ({
infoAppend, infoAppend,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { currency } = useAppLayout(); const { currency } = useAppLayout();
const isHoverable = useMediaHoverable(); const isHoverable = useDeviceSupportsHover();
const router = useRouter(); const router = useRouter();
const metadataJSX = useMemo( const metadataJSX = useMemo(
@ -90,7 +90,7 @@ export const PreviewCard = ({
{metadata && (metadata.releaseDate || metadata.price) && ( {metadata && (metadata.releaseDate || metadata.price) && (
<div className="flex w-full flex-row flex-wrap gap-x-3"> <div className="flex w-full flex-row flex-wrap gap-x-3">
{metadata.releaseDate && ( {metadata.releaseDate && (
<p className="text-sm mobile:text-xs"> <p className="text-sm">
<Ico <Ico
icon={Icon.Event} icon={Icon.Event}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"
@ -99,7 +99,7 @@ export const PreviewCard = ({
</p> </p>
)} )}
{metadata.price && metadata.currencies && ( {metadata.price && metadata.currencies && (
<p className="justify-self-end text-sm mobile:text-xs"> <p className="justify-self-end text-sm">
<Ico <Ico
icon={Icon.ShoppingCart} icon={Icon.ShoppingCart}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"
@ -108,7 +108,7 @@ export const PreviewCard = ({
</p> </p>
)} )}
{metadata.views && ( {metadata.views && (
<p className="text-sm mobile:text-xs"> <p className="text-sm">
<Ico <Ico
icon={Icon.Visibility} icon={Icon.Visibility}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"
@ -117,7 +117,7 @@ export const PreviewCard = ({
</p> </p>
)} )}
{metadata.author && ( {metadata.author && (
<p className="text-sm mobile:text-xs"> <p className="text-sm">
<Ico <Ico
icon={Icon.Person} icon={Icon.Person}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"

View File

@ -6,6 +6,7 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
import { cJoin } from "helpers/className"; import { cJoin } from "helpers/className";
import { isDefined, isDefinedAndNotEmpty } from "helpers/others"; import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange"; import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
interface Group<T> { interface Group<T> {
name: string; name: string;
@ -205,7 +206,7 @@ export const SmartList = <T,>({
<div <div
className={cJoin( className={cJoin(
`grid items-start gap-8 border-b-[3px] border-dotted pb-12 `grid items-start gap-8 border-b-[3px] border-dotted pb-12
last-of-type:border-0 mobile:gap-4`, last-of-type:border-0`,
className className
)} )}
> >
@ -244,15 +245,22 @@ interface DefaultRenderWhenEmptyProps {
langui: AppStaticProps["langui"]; langui: AppStaticProps["langui"];
} }
const DefaultRenderWhenEmpty = ({ langui }: DefaultRenderWhenEmptyProps) => ( const DefaultRenderWhenEmpty = ({ langui }: DefaultRenderWhenEmptyProps) => {
<div className="grid h-full place-content-center"> const is3ColumnsLayout = useIs3ColumnsLayout();
<div return (
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted <div className="grid h-full place-content-center">
border-dark p-8 text-dark opacity-40" <div
> className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
<Ico icon={Icon.ChevronLeft} className="!text-[300%] mobile:hidden" /> border-dark p-8 text-dark opacity-40"
<p className="max-w-xs text-2xl">{langui.no_results_message}</p> >
<Ico icon={Icon.ChevronRight} className="!text-[300%] desktop:hidden" /> {is3ColumnsLayout && (
<Ico icon={Icon.ChevronLeft} className="!text-[300%]" />
)}
<p className="max-w-xs text-2xl">{langui.no_results_message}</p>
{!is3ColumnsLayout && (
<Ico icon={Icon.ChevronRight} className="!text-[300%]" />
)}
</div>
</div> </div>
</div> );
); };

View File

@ -5,6 +5,8 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
import { getStatusDescription } from "helpers/others"; import { getStatusDescription } from "helpers/others";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { useIsContentPanelNoMoreThan } from "hooks/useContainerQuery";
import { cIf, cJoin } from "helpers/className";
/* /*
* *
@ -37,6 +39,7 @@ const DefinitionCard = ({
index, index,
categories, categories,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const isContentPanelNoMoreThanMd = useIsContentPanelNoMoreThan("md");
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
useSmartLanguage({ useSmartLanguage({
items: translations, items: translations,
@ -86,7 +89,12 @@ const DefinitionCard = ({
<p>{selectedTranslation?.definition}</p> <p>{selectedTranslation?.definition}</p>
{source?.url && source.name && ( {source?.url && source.name && (
<div className="mt-3 flex place-items-center gap-2 mobile:flex-col mobile:text-center"> <div
className={cJoin(
"mt-3 flex place-items-center gap-2",
cIf(isContentPanelNoMoreThanMd, "flex-col text-center")
)}
>
<p>{langui.source}: </p> <p>{langui.source}: </p>
<Button href={source.url} size="small" text={source.name} /> <Button href={source.url} size="small" text={source.name} />
</div> </div>

View File

@ -75,6 +75,21 @@ interface AppLayoutState {
setLibraryItemUserStatus: React.Dispatch< setLibraryItemUserStatus: React.Dispatch<
React.SetStateAction<AppLayoutState["libraryItemUserStatus"]> React.SetStateAction<AppLayoutState["libraryItemUserStatus"]>
>; >;
screenWidth: number;
setScreenWidth: React.Dispatch<
React.SetStateAction<AppLayoutState["screenWidth"]>
>;
contentPanelWidth: number;
setContentPanelWidth: React.Dispatch<
React.SetStateAction<AppLayoutState["contentPanelWidth"]>
>;
subPanelWidth: number;
setSubPanelWidth: React.Dispatch<
React.SetStateAction<AppLayoutState["subPanelWidth"]>
>;
} }
const initialState: RequiredNonNullable<AppLayoutState> = { const initialState: RequiredNonNullable<AppLayoutState> = {
@ -128,6 +143,15 @@ const initialState: RequiredNonNullable<AppLayoutState> = {
libraryItemUserStatus: {}, libraryItemUserStatus: {},
setLibraryItemUserStatus: () => null, setLibraryItemUserStatus: () => null,
screenWidth: 0,
setScreenWidth: () => null,
contentPanelWidth: 0,
setContentPanelWidth: () => null,
subPanelWidth: 0,
setSubPanelWidth: () => null,
}; };
const AppContext = React.createContext<AppLayoutState>(initialState); const AppContext = React.createContext<AppLayoutState>(initialState);
@ -237,6 +261,10 @@ export const AppContextProvider = (props: Props): JSX.Element => {
setDyslexic((current) => (isDefined(current) ? !current : current)); setDyslexic((current) => (isDefined(current) ? !current : current));
}; };
const [screenWidth, setScreenWidth] = useState(0);
const [contentPanelWidth, setContentPanelWidth] = useState(0);
const [subPanelWidth, setSubPanelWidth] = useState(0);
return ( return (
<AppContext.Provider <AppContext.Provider
value={{ value={{
@ -254,6 +282,9 @@ export const AppContextProvider = (props: Props): JSX.Element => {
preferredLanguages, preferredLanguages,
menuGestures, menuGestures,
libraryItemUserStatus, libraryItemUserStatus,
screenWidth,
contentPanelWidth,
subPanelWidth,
setSubPanelOpen, setSubPanelOpen,
setConfigPanelOpen, setConfigPanelOpen,
setSearchPanelOpen, setSearchPanelOpen,
@ -277,6 +308,9 @@ export const AppContextProvider = (props: Props): JSX.Element => {
toggleMenuGestures, toggleMenuGestures,
toggleSelectedThemeMode, toggleSelectedThemeMode,
toggleDyslexic, toggleDyslexic,
setContentPanelWidth,
setScreenWidth,
setSubPanelWidth,
}} }}
> >
{props.children} {props.children}

View File

@ -23,9 +23,7 @@ query getVideoChannel($channel: String) {
} }
} }
published_date { published_date {
year ...datePicker
month
day
} }
} }
} }

View File

@ -16,7 +16,7 @@ const DEFAULT_OG_THUMBNAIL = {
}; };
export const TITLE_PREFIX = "Accords Library"; export const TITLE_PREFIX = "Accords Library";
export const TITLE_SEPARATOR = " - " export const TITLE_SEPARATOR = " - ";
export interface OpenGraph { export interface OpenGraph {
title: string; title: string;
@ -30,7 +30,9 @@ export const getOpenGraph = (
description?: string | null | undefined, description?: string | null | undefined,
thumbnail?: UploadImageFragment | null | undefined thumbnail?: UploadImageFragment | null | undefined
): OpenGraph => ({ ): OpenGraph => ({
title: `${TITLE_PREFIX}${isDefinedAndNotEmpty(title) ? `${TITLE_SEPARATOR}${title}` : ""}`, title: `${TITLE_PREFIX}${
isDefinedAndNotEmpty(title) ? `${TITLE_SEPARATOR}${title}` : ""
}`,
description: isDefinedAndNotEmpty(description) description: isDefinedAndNotEmpty(description)
? description ? description
: langui.default_description ?? "", : langui.default_description ?? "",

View File

@ -0,0 +1,112 @@
import { useAppLayout } from "contexts/AppLayoutContext";
type MediaQuery = { value: number; unit: "px" | "rem"; rule: "max" | "min" };
type Size =
| "2xl"
| "2xs"
| "3xl"
| "4xl"
| "5xl"
| "6xl"
| "7xl"
| "lg"
| "md"
| "sm"
| "xl"
| "xs";
const sizes: Record<Size, number> = {
"2xl": 42,
"3xl": 48,
"4xl": 56,
"5xl": 64,
"6xl": 72,
"7xl": 80,
lg: 32,
md: 28,
sm: 24,
xl: 36,
xs: 20,
"2xs": 16,
};
export const useIsScreenAtLeast = (size: Size): boolean => {
const { screenWidth } = useAppLayout();
return useApplyContainerQuery(screenWidth, {
value: sizes[size],
unit: "rem",
rule: "min",
});
};
// ts-unused-exports:disable-next-line
export const useIsScreenNoMoreThan = (size: Size): boolean => {
const { screenWidth } = useAppLayout();
return useApplyContainerQuery(screenWidth, {
value: sizes[size],
unit: "rem",
rule: "max",
});
};
export const useIsContentPanelAtLeast = (size: Size): boolean => {
const { contentPanelWidth } = useAppLayout();
return useApplyContainerQuery(contentPanelWidth, {
value: sizes[size],
unit: "rem",
rule: "min",
});
};
export const useIsContentPanelNoMoreThan = (size: Size): boolean => {
const { contentPanelWidth } = useAppLayout();
return useApplyContainerQuery(contentPanelWidth, {
value: sizes[size],
unit: "rem",
rule: "max",
});
};
export const useIsSubPanelAtLeast = (size: Size): boolean => {
const { subPanelWidth } = useAppLayout();
return useApplyContainerQuery(subPanelWidth, {
value: sizes[size],
unit: "rem",
rule: "min",
});
};
// ts-unused-exports:disable-next-line
export const useIsSubPanelNoMoreThan = (size: Size): boolean => {
const { subPanelWidth } = useAppLayout();
return useApplyContainerQuery(subPanelWidth, {
value: sizes[size],
unit: "rem",
rule: "max",
});
};
export const useIs3ColumnsLayout = (): boolean => {
const { screenWidth } = useAppLayout();
return useApplyContainerQuery(screenWidth, {
value: sizes["5xl"],
unit: "rem",
rule: "min",
});
};
export const useIs1ColumnLayout = (): boolean => {
const { screenWidth } = useAppLayout();
return useApplyContainerQuery(screenWidth, {
value: sizes["5xl"],
unit: "rem",
rule: "max",
});
};
const useApplyContainerQuery = (width: number, query: MediaQuery) => {
const { fontSize } = useAppLayout();
const breakpoint = query.value * (query.unit === "rem" ? 16 : 1) * fontSize;
return query.rule === "min" ? width >= breakpoint : width < breakpoint;
};

View File

@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { throttle } from "throttle-debounce"; import { throttle } from "throttle-debounce";
import { useIsClient } from "usehooks-ts"; import { useIsClient } from "usehooks-ts";
import { useOnScroll, AnchorIds } from "./useScrollTopOnChange"; import { useOnScroll } from "./useOnScroll";
import { AnchorIds } from "./useScrollTopOnChange";
import { isDefined } from "helpers/others"; import { isDefined } from "helpers/others";
export const useIntersectionList = (ids: string[]): number => { export const useIntersectionList = (ids: string[]): number => {
@ -16,7 +17,7 @@ export const useIntersectionList = (ids: string[]): number => {
const refreshCurrentIntersection = useCallback( const refreshCurrentIntersection = useCallback(
(scroll: number) => { (scroll: number) => {
console.log("update"); console.log("useIntersectionList");
if (!isDefined(contentPanel)) { if (!isDefined(contentPanel)) {
setCurrentIntersection(-1); setCurrentIntersection(-1);

View File

@ -1,14 +1,7 @@
import { useMediaQuery } from "usehooks-ts"; import { useMediaQuery } from "usehooks-ts";
import { breaks } from "../../design.config";
// ts-unused-exports:disable-next-line export const useDeviceSupportsHover = (): boolean =>
export const useMediaThin = (): boolean => useMediaQuery(breaks.thin.raw); useMediaQuery("(hover: hover)");
export const useMediaMobile = (): boolean => useMediaQuery(breaks.mobile.raw);
export const useMediaDesktop = (): boolean => useMediaQuery(breaks.desktop.raw);
export const useMediaHoverable = (): boolean => useMediaQuery("(hover: hover)");
export const usePrefersDarkMode = (): boolean => export const usePrefersDarkMode = (): boolean =>
useMediaQuery("(prefers-color-scheme: dark)"); useMediaQuery("(prefers-color-scheme: dark)");

29
src/hooks/useOnResize.ts Normal file
View File

@ -0,0 +1,29 @@
import { useMemo, useCallback, useEffect } from "react";
import { useIsClient } from "usehooks-ts";
import { isDefined } from "helpers/others";
export const useOnResize = (
id: string,
onResize: (width: number, height: number) => void
): void => {
const isClient = useIsClient();
const elem = useMemo(
() => (isClient ? document.querySelector(`#${id}`) : null),
[id, isClient]
);
const listener = useCallback(() => {
console.log("useOnResize");
if (elem?.clientWidth && elem.clientHeight) {
onResize(elem.clientWidth, elem.clientHeight);
}
}, [elem?.clientHeight, elem?.clientWidth, onResize]);
useEffect(() => {
const ro = new ResizeObserver(listener);
if (isDefined(elem)) {
ro.observe(elem);
}
return () => ro.disconnect();
}, [elem, listener]);
};

23
src/hooks/useOnScroll.ts Normal file
View File

@ -0,0 +1,23 @@
import { useMemo, useCallback, useEffect } from "react";
import { useIsClient } from "usehooks-ts";
import { AnchorIds } from "./useScrollTopOnChange";
export const useOnScroll = (
id: AnchorIds,
onScroll: (scroll: number) => void
): void => {
const isClient = useIsClient();
const elem = useMemo(
() => (isClient ? document.querySelector(`#${id}`) : null),
[id, isClient]
);
const listener = useCallback(() => {
if (elem?.scrollTop) {
onScroll(elem.scrollTop);
}
}, [elem?.scrollTop, onScroll]);
useEffect(() => {
elem?.addEventListener("scroll", listener);
return () => elem?.removeEventListener("scroll", listener);
}, [elem, listener]);
};

View File

@ -1,8 +1,9 @@
import { DependencyList, useCallback, useEffect, useMemo } from "react"; import { DependencyList, useEffect } from "react";
import { useIsClient } from "usehooks-ts";
export enum AnchorIds { export enum AnchorIds {
Body = "bodyqs65d4a98d56az48z64d",
ContentPanel = "contentPanel495922447721572", ContentPanel = "contentPanel495922447721572",
SubPanel = "subPanelz9e8rs2d3f18zer98ze",
} }
// Scroll to top of element "id" when "deps" update. // Scroll to top of element "id" when "deps" update.
@ -19,23 +20,3 @@ export const useScrollTopOnChange = (
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, ...deps, enabled]); }, [id, ...deps, enabled]);
}; };
export const useOnScroll = (
id: AnchorIds,
onScroll: (scroll: number) => void
): void => {
const isClient = useIsClient();
const elem = useMemo(
() => (isClient ? document.querySelector(`#${id}`) : null),
[id, isClient]
);
const listener = useCallback(() => {
if (elem?.scrollTop) {
onScroll(elem.scrollTop);
}
}, [elem?.scrollTop, onScroll]);
useEffect(() => {
elem?.addEventListener("scroll", listener);
return () => elem?.removeEventListener("scrool", listener);
}, [elem, listener]);
};

View File

@ -1,9 +1,6 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Panels/ContentPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
@ -24,12 +21,7 @@ const FourOhFour = ({
contentPanel={ contentPanel={
<ContentPanel> <ContentPanel>
<h1>{openGraph.title}</h1> <h1>{openGraph.title}</h1>
<ReturnButton <ReturnButton href="/" title="Home" langui={langui} />
href="/"
title="Home"
langui={langui}
displayOn={ReturnButtonType.Both}
/>
</ContentPanel> </ContentPanel>
} }
openGraph={openGraph} openGraph={openGraph}

View File

@ -1,9 +1,6 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Panels/ContentPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
@ -24,12 +21,7 @@ const FiveHundred = ({
contentPanel={ contentPanel={
<ContentPanel> <ContentPanel>
<h1>{openGraph.title}</h1> <h1>{openGraph.title}</h1>
<ReturnButton <ReturnButton href="/" title="Home" langui={langui} />
href="/"
title="Home"
langui={langui}
displayOn={ReturnButtonType.Both}
/>
</ContentPanel> </ContentPanel>
} }
openGraph={openGraph} openGraph={openGraph}

View File

@ -9,6 +9,7 @@ import {
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { randomInt } from "helpers/numbers"; import { randomInt } from "helpers/numbers";
import { RequestMailProps, ResponseMailProps } from "pages/api/mail"; import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
import { useIs1ColumnLayout } from "hooks/useContainerQuery";
/* /*
* *
@ -17,6 +18,7 @@ import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
const AboutUs = ({ langui, ...otherProps }: PostStaticProps): JSX.Element => { const AboutUs = ({ langui, ...otherProps }: PostStaticProps): JSX.Element => {
const router = useRouter(); const router = useRouter();
const is1ColumnLayout = useIs1ColumnLayout();
const [formResponse, setFormResponse] = useState(""); const [formResponse, setFormResponse] = useState("");
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">( const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">(
"stale" "stale"
@ -100,7 +102,7 @@ const AboutUs = ({ langui, ...otherProps }: PostStaticProps): JSX.Element => {
<label htmlFor="name">{langui.name}:</label> <label htmlFor="name">{langui.name}:</label>
<input <input
type="text" type="text"
className="mobile:w-full" className={cIf(is1ColumnLayout, "w-full")}
name="name" name="name"
id="name" id="name"
required required
@ -112,7 +114,7 @@ const AboutUs = ({ langui, ...otherProps }: PostStaticProps): JSX.Element => {
<label htmlFor="email">{langui.email}:</label> <label htmlFor="email">{langui.email}:</label>
<input <input
type="email" type="email"
className="mobile:w-full" className={cIf(is1ColumnLayout, "w-full")}
name="email" name="email"
id="email" id="email"
required required

View File

@ -6,6 +6,7 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Panels/SubPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine";
/* /*
* *
@ -23,6 +24,9 @@ const AboutUs = ({ langui, ...otherProps }: Props): JSX.Element => (
title={langui.about_us} title={langui.about_us}
description={langui.about_us_description} description={langui.about_us_description}
/> />
<HorizontalLine />
<NavOption <NavOption
title={langui.accords_handbook} title={langui.accords_handbook}
url="/about-us/accords-handbook" url="/about-us/accords-handbook"

View File

@ -7,6 +7,7 @@ import { SubPanel } from "components/Panels/SubPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine";
/* /*
* *
@ -24,6 +25,7 @@ const Archives = ({ langui, ...otherProps }: Props): JSX.Element => {
title={langui.archives} title={langui.archives}
description={langui.archives_description} description={langui.archives_description}
/> />
<HorizontalLine />
<NavOption title={"Videos"} url="/archives/videos/" border /> <NavOption title={"Videos"} url="/archives/videos/" border />
</SubPanel> </SubPanel>
), ),

View File

@ -1,13 +1,10 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { Fragment, useMemo } from "react"; import { useMemo, useState } from "react";
import { useBoolean } from "usehooks-ts"; import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -19,10 +16,25 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { getVideoThumbnailURL } from "helpers/videos"; import { getVideoThumbnailURL } from "helpers/videos";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { filterHasAttributes, isDefined } from "helpers/others"; import { filterHasAttributes, isDefined } from "helpers/others";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date";
import { HorizontalLine } from "components/HorizontalLine";
import { SmartList } from "components/SmartList";
import { cIf } from "helpers/className";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { TextInput } from "components/Inputs/TextInput";
/*
*
* CONSTANTS
*/
const DEFAULT_FILTERS_STATE = {
searchName: "",
};
/* /*
* *
@ -38,7 +50,12 @@ interface Props extends AppStaticProps, AppLayoutRequired {
const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => { const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } =
useBoolean(true); useBoolean(true);
const hoverable = useMediaHoverable(); const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
);
const subPanel = useMemo( const subPanel = useMemo(
() => ( () => (
@ -47,7 +64,7 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
href="/archives/videos/" href="/archives/videos/"
title={langui.videos} title={langui.videos}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
className="mb-10" className="mb-10"
/> />
@ -57,6 +74,15 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
description={langui.archives_description} description={langui.archives_description}
/> />
<HorizontalLine />
<TextInput
className="mb-6 w-full"
placeholder={langui.search_title ?? "Search title..."}
value={searchName}
onChange={setSearchName}
/>
{hoverable && ( {hoverable && (
<WithLabel label={langui.always_show_info}> <WithLabel label={langui.always_show_info}>
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} /> <Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
@ -64,51 +90,58 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
)} )}
</SubPanel> </SubPanel>
), ),
[hoverable, keepInfoVisible, langui, toggleKeepInfoVisible] [hoverable, keepInfoVisible, langui, searchName, toggleKeepInfoVisible]
); );
const contentPanel = useMemo( const contentPanel = useMemo(
() => ( () => (
<ContentPanel width={ContentPanelWidthSizes.Full}> <ContentPanel width={ContentPanelWidthSizes.Full}>
<div className="mb-8"> <SmartList
<h1 className="text-3xl">{channel?.title}</h1> items={filterHasAttributes(channel?.videos?.data, [
<p>{channel?.subscribers.toLocaleString()} subscribers</p> "id",
</div>
<div
className="grid items-start gap-8 border-b-[3px] border-dotted pb-12 last-of-type:border-0
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2"
>
{filterHasAttributes(channel?.videos?.data, [
"attributes", "attributes",
] as const).map((video) => ( ] as const)}
<Fragment key={video.id}> getItemId={(item) => item.id}
<PreviewCard renderItem={({ item }) => (
href={`/archives/videos/v/${video.attributes.uid}`} <PreviewCard
title={video.attributes.title} href={`/archives/videos/v/${item.attributes.uid}`}
thumbnail={getVideoThumbnailURL(video.attributes.uid)} title={item.attributes.title}
thumbnailAspectRatio="16/9" thumbnail={getVideoThumbnailURL(item.attributes.uid)}
keepInfoVisible={keepInfoVisible} thumbnailAspectRatio="16/9"
metadata={{ thumbnailForceAspectRatio
releaseDate: video.attributes.published_date, keepInfoVisible={keepInfoVisible}
views: video.attributes.views, metadata={{
author: channel?.title, releaseDate: item.attributes.published_date,
position: "Top", views: item.attributes.views,
}} author: channel?.title,
hoverlay={{ position: "Top",
__typename: "Video", }}
duration: video.attributes.duration, hoverlay={{
}} __typename: "Video",
/> duration: item.attributes.duration,
</Fragment> }}
))} />
</div> )}
langui={langui}
className={cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)}
groupingFunction={() => [channel?.title ?? ""]}
paginationItemPerPage={25}
searchingTerm={searchName}
searchingBy={(item) => item.attributes.title}
/>
</ContentPanel> </ContentPanel>
), ),
[ [
channel?.subscribers,
channel?.title, channel?.title,
channel?.videos?.data, channel?.videos?.data,
isContentPanelAtLeast4xl,
keepInfoVisible, keepInfoVisible,
langui,
searchName,
] ]
); );
@ -137,6 +170,13 @@ export const getStaticProps: GetStaticProps = async (context) => {
: "", : "",
}); });
if (!channel.videoChannels?.data[0].attributes) return { notFound: true }; if (!channel.videoChannels?.data[0].attributes) return { notFound: true };
channel.videoChannels.data[0].attributes.videos?.data
.sort((a, b) =>
compareDate(a.attributes?.published_date, b.attributes?.published_date)
)
.reverse();
const appStaticProps = await getAppStaticProps(context); const appStaticProps = await getAppStaticProps(context);
const props: Props = { const props: Props = {
...appStaticProps, ...appStaticProps,
@ -157,6 +197,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const channels = await sdk.getVideoChannelsSlugs(); const channels = await sdk.getVideoChannelsSlugs();
const paths: GetStaticPathsResult["paths"] = []; const paths: GetStaticPathsResult["paths"] = [];
if (channels.videoChannels?.data) if (channels.videoChannels?.data)
filterHasAttributes(channels.videoChannels.data, [ filterHasAttributes(channels.videoChannels.data, [
"attributes", "attributes",

View File

@ -8,10 +8,7 @@ import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -23,9 +20,12 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes } from "helpers/others"; import { filterHasAttributes } from "helpers/others";
import { getVideoThumbnailURL } from "helpers/videos"; import { getVideoThumbnailURL } from "helpers/videos";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date"; import { compareDate } from "helpers/date";
import { HorizontalLine } from "components/HorizontalLine";
import { cIf } from "helpers/className";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
/* /*
* *
@ -46,7 +46,8 @@ interface Props extends AppStaticProps, AppLayoutRequired {
} }
const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => { const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
const hoverable = useMediaHoverable(); const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } =
useBoolean(true); useBoolean(true);
@ -62,7 +63,7 @@ const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
href="/archives/" href="/archives/"
title={"Archives"} title={"Archives"}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
className="mb-10" className="mb-10"
/> />
@ -72,6 +73,8 @@ const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
description={langui.archives_description} description={langui.archives_description}
/> />
<HorizontalLine />
<TextInput <TextInput
className="mb-6 w-full" className="mb-6 w-full"
placeholder={langui.search_title ?? "Search title..."} placeholder={langui.search_title ?? "Search title..."}
@ -116,15 +119,18 @@ const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
/> />
)} )}
langui={langui} langui={langui}
className="desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2 className={cIf(
thin:grid-cols-1" isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)}
paginationItemPerPage={25} paginationItemPerPage={25}
searchingTerm={searchName} searchingTerm={searchName}
searchingBy={(item) => item.attributes.title} searchingBy={(item) => item.attributes.title}
/> />
</ContentPanel> </ContentPanel>
), ),
[keepInfoVisible, langui, searchName, videos] [isContentPanelAtLeast4xl, keepInfoVisible, langui, searchName, videos]
); );
return ( return (
<AppLayout <AppLayout

View File

@ -7,10 +7,7 @@ import { Ico, Icon } from "components/Ico";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/InsetBox";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -23,8 +20,8 @@ import { getReadySdk } from "graphql/sdk";
import { prettyDate, prettyShortenNumber } from "helpers/formatters"; import { prettyDate, prettyShortenNumber } from "helpers/formatters";
import { filterHasAttributes, isDefined } from "helpers/others"; import { filterHasAttributes, isDefined } from "helpers/others";
import { getVideoFile } from "helpers/videos"; import { getVideoFile } from "helpers/videos";
import { useMediaMobile } from "hooks/useMediaQuery";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
/* /*
* *
@ -38,7 +35,7 @@ interface Props extends AppStaticProps, AppLayoutRequired {
} }
const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => { const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
const isMobile = useMediaMobile(); const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const { setSubPanelOpen } = useAppLayout(); const { setSubPanelOpen } = useAppLayout();
const router = useRouter(); const router = useRouter();
@ -49,8 +46,7 @@ const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
href="/archives/videos/" href="/archives/videos/"
title={langui.videos} title={langui.videos}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
className="mb-10"
/> />
<HorizontalLine /> <HorizontalLine />
@ -87,7 +83,7 @@ const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
href="/library/" href="/library/"
title={langui.library} title={langui.library}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn={"1ColumnLayout"}
className="mb-10" className="mb-10"
/> />
@ -129,9 +125,9 @@ const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
icon={Icon.Visibility} icon={Icon.Visibility}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"
/> />
{isMobile {isContentPanelAtLeast4xl
? prettyShortenNumber(video.views) ? video.views.toLocaleString()
: video.views.toLocaleString()} : prettyShortenNumber(video.views)}
</p> </p>
{video.channel?.data?.attributes && ( {video.channel?.data?.attributes && (
<p> <p>
@ -139,9 +135,9 @@ const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
icon={Icon.ThumbUp} icon={Icon.ThumbUp}
className="mr-1 translate-y-[.15em] !text-base" className="mr-1 translate-y-[.15em] !text-base"
/> />
{isMobile {isContentPanelAtLeast4xl
? prettyShortenNumber(video.likes) ? video.likes.toLocaleString()
: video.likes.toLocaleString()} : prettyShortenNumber(video.likes)}
</p> </p>
)} )}
<a <a
@ -186,7 +182,7 @@ const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
</ContentPanel> </ContentPanel>
), ),
[ [
isMobile, isContentPanelAtLeast4xl,
langui, langui,
router.locale, router.locale,
video.channel?.data?.attributes, video.channel?.data?.attributes,

View File

@ -13,10 +13,7 @@ import { ThumbnailHeader } from "components/ThumbnailHeader";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { GetChroniclesChaptersQuery } from "graphql/generated"; import { GetChroniclesChaptersQuery } from "graphql/generated";
import { prettyInlineTitle, prettySlug } from "helpers/formatters"; import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { import {
@ -93,7 +90,7 @@ const Chronicle = ({
() => ( () => (
<ContentPanel> <ContentPanel>
<ReturnButton <ReturnButton
displayOn={ReturnButtonType.Mobile} displayOnlyOn={"1ColumnLayout"}
href="/chronicles" href="/chronicles"
title={langui.chronicles} title={langui.chronicles}
langui={langui} langui={langui}
@ -136,13 +133,14 @@ const Chronicle = ({
langui={langui} langui={langui}
/> />
<HorizontalLine />
{selectedContentTranslation.text_set?.text && ( {selectedContentTranslation.text_set?.text && (
<Markdawn <>
text={selectedContentTranslation.text_set.text} <HorizontalLine />
langui={langui} <Markdawn
/> text={selectedContentTranslation.text_set.text}
langui={langui}
/>
</>
)} )}
</> </>
)} )}
@ -168,7 +166,7 @@ const Chronicle = ({
() => ( () => (
<SubPanel> <SubPanel>
<ReturnButton <ReturnButton
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
href="/chronicles" href="/chronicles"
title={langui.chronicles} title={langui.chronicles}
langui={langui} langui={langui}

View File

@ -11,6 +11,7 @@ import { filterHasAttributes } from "helpers/others";
import { prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList"; import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
import { HorizontalLine } from "components/HorizontalLine";
/* /*
* *
@ -36,6 +37,9 @@ const Chronicles = ({
title={langui.chronicles} title={langui.chronicles}
description={langui.chronicles_description} description={langui.chronicles_description}
/> />
<HorizontalLine />
<div className="grid gap-16"> <div className="grid gap-16">
{filterHasAttributes(chapters, [ {filterHasAttributes(chapters, [
"attributes.chronicles", "attributes.chronicles",

View File

@ -5,10 +5,7 @@ import { Chip } from "components/Chip";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn"; import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
import { import { TranslatedReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButtonType,
TranslatedReturnButton,
} from "components/PanelComponents/ReturnButton";
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 { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
@ -30,7 +27,6 @@ import {
isDefinedAndNotEmpty, isDefinedAndNotEmpty,
} from "helpers/others"; } from "helpers/others";
import { ContentWithTranslations } from "helpers/types"; import { ContentWithTranslations } from "helpers/types";
import { useMediaMobile } from "hooks/useMediaQuery";
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange"; import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
@ -40,6 +36,11 @@ import {
} from "helpers/locales"; } from "helpers/locales";
import { getDescription } from "helpers/description"; import { getDescription } from "helpers/description";
import { TranslatedPreviewLine } from "components/PreviewLine"; import { TranslatedPreviewLine } from "components/PreviewLine";
import {
useIs1ColumnLayout,
useIsContentPanelAtLeast,
} from "hooks/useContainerQuery";
import { cIf } from "helpers/className";
/* /*
* *
@ -57,7 +58,8 @@ const Content = ({
currencies, currencies,
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const isMobile = useMediaMobile(); const isContentPanelAtLeast2xl = useIsContentPanelAtLeast("2xl");
const is1ColumnLayout = useIs1ColumnLayout();
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
useSmartLanguage({ useSmartLanguage({
@ -122,7 +124,7 @@ const Content = ({
<SubPanel> <SubPanel>
<TranslatedReturnButton <TranslatedReturnButton
{...returnButtonProps} {...returnButtonProps}
displayOn={ReturnButtonType.Desktop} displayOnlyOn="3ColumnsLayout"
/> />
{selectedTranslation?.text_set?.source_language?.data?.attributes {selectedTranslation?.text_set?.source_language?.data?.attributes
@ -282,7 +284,7 @@ const Content = ({
return ( return (
<div <div
key={libraryItem.attributes.slug} key={libraryItem.attributes.slug}
className="mobile:w-[80%]" className={cIf(is1ColumnLayout, "w-3/4")}
> >
<PreviewCard <PreviewCard
href={`/library/${libraryItem.attributes.slug}`} href={`/library/${libraryItem.attributes.slug}`}
@ -341,6 +343,7 @@ const Content = ({
langui, langui,
returnButtonProps, returnButtonProps,
selectedTranslation, selectedTranslation,
is1ColumnLayout,
] ]
); );
@ -349,7 +352,7 @@ const Content = ({
<ContentPanel> <ContentPanel>
<TranslatedReturnButton <TranslatedReturnButton
{...returnButtonProps} {...returnButtonProps}
displayOn={ReturnButtonType.Mobile} displayOnlyOn="1ColumnLayout"
className="mb-10" className="mb-10"
/> />
@ -394,9 +397,8 @@ const Content = ({
} }
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
topChips={ topChips={
isMobile isContentPanelAtLeast2xl &&
? undefined previousContent.attributes.type?.data?.attributes
: previousContent.attributes.type?.data?.attributes
? [ ? [
previousContent.attributes.type.data.attributes previousContent.attributes.type.data.attributes
.titles?.[0] .titles?.[0]
@ -410,22 +412,25 @@ const Content = ({
: undefined : undefined
} }
bottomChips={ bottomChips={
isMobile isContentPanelAtLeast2xl
? undefined ? previousContent.attributes.categories?.data.map(
: previousContent.attributes.categories?.data.map(
(category) => category.attributes?.short ?? "" (category) => category.attributes?.short ?? ""
) )
: undefined
} }
/> />
</div> </div>
)} )}
<HorizontalLine /> {selectedTranslation?.text_set?.text && (
<>
<Markdawn <HorizontalLine />
text={selectedTranslation?.text_set?.text ?? ""} <Markdawn
langui={langui} text={selectedTranslation.text_set.text}
/> langui={langui}
/>
</>
)}
{nextContent?.attributes && ( {nextContent?.attributes && (
<> <>
@ -448,9 +453,8 @@ const Content = ({
thumbnail={nextContent.attributes.thumbnail?.data?.attributes} thumbnail={nextContent.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
topChips={ topChips={
isMobile isContentPanelAtLeast2xl &&
? undefined nextContent.attributes.type?.data?.attributes
: nextContent.attributes.type?.data?.attributes
? [ ? [
nextContent.attributes.type.data.attributes.titles?.[0] nextContent.attributes.type.data.attributes.titles?.[0]
? nextContent.attributes.type.data.attributes ? nextContent.attributes.type.data.attributes
@ -462,11 +466,11 @@ const Content = ({
: undefined : undefined
} }
bottomChips={ bottomChips={
isMobile isContentPanelAtLeast2xl
? undefined ? nextContent.attributes.categories?.data.map(
: nextContent.attributes.categories?.data.map(
(category) => category.attributes?.short ?? "" (category) => category.attributes?.short ?? ""
) )
: undefined
} }
/> />
</> </>
@ -479,7 +483,7 @@ const Content = ({
content.categories, content.categories,
content.thumbnail?.data?.attributes, content.thumbnail?.data?.attributes,
content.type, content.type,
isMobile, isContentPanelAtLeast2xl,
languageSwitcherProps, languageSwitcherProps,
langui, langui,
nextContent?.attributes, nextContent?.attributes,

View File

@ -16,7 +16,7 @@ import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { filterDefined, filterHasAttributes } from "helpers/others"; import { filterDefined, filterHasAttributes } from "helpers/others";
import { GetContentsQuery } from "graphql/generated"; import { GetContentsQuery } from "graphql/generated";
@ -25,6 +25,8 @@ import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { cJoin, cIf } from "helpers/className";
/* /*
* *
@ -52,7 +54,8 @@ const Contents = ({
languages, languages,
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const hoverable = useMediaHoverable(); const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [groupingMethod, setGroupingMethod] = useState<number>( const [groupingMethod, setGroupingMethod] = useState<number>(
DEFAULT_FILTERS_STATE.groupingMethod DEFAULT_FILTERS_STATE.groupingMethod
@ -135,6 +138,8 @@ const Contents = ({
description={langui.contents_description} description={langui.contents_description}
/> />
<HorizontalLine />
<Button <Button
href="/contents" href="/contents"
text={"Switch to folder view"} text={"Switch to folder view"}
@ -232,7 +237,14 @@ const Contents = ({
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
/> />
)} )}
className="grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" className={cJoin(
"items-end",
cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)
)}
groupingFunction={groupingFunction} groupingFunction={groupingFunction}
filteringFunction={filteringFunction} filteringFunction={filteringFunction}
searchingTerm={searchName} searchingTerm={searchName}
@ -255,6 +267,7 @@ const Contents = ({
</ContentPanel> </ContentPanel>
), ),
[ [
isContentPanelAtLeast4xl,
contents, contents,
filteringFunction, filteringFunction,
groupingFunction, groupingFunction,

View File

@ -24,6 +24,9 @@ import { SubPanel } from "components/Panels/SubPanel";
import { TranslatedProps } from "helpers/types/TranslatedProps"; import { TranslatedProps } from "helpers/types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { HorizontalLine } from "components/HorizontalLine";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { cJoin, cIf } from "helpers/className";
/* /*
* *
@ -44,6 +47,8 @@ const ContentsFolder = ({
folder, folder,
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const subPanel = useMemo( const subPanel = useMemo(
() => ( () => (
<SubPanel> <SubPanel>
@ -53,6 +58,8 @@ const ContentsFolder = ({
description={langui.contents_description} description={langui.contents_description}
/> />
<HorizontalLine />
<Button <Button
href="/contents/all" href="/contents/all"
text={"Switch to grid view"} text={"Switch to grid view"}
@ -126,8 +133,14 @@ const ContentsFolder = ({
fallback={{ title: prettySlug(item.attributes.slug) }} fallback={{ title: prettySlug(item.attributes.slug) }}
/> />
)} )}
className="grid-cols-2 items-stretch className={cJoin(
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" "items-end",
cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-4"
)
)}
renderWhenEmpty={() => <></>} renderWhenEmpty={() => <></>}
langui={langui} langui={langui}
groupingFunction={() => [langui.folders ?? "Folders"]} groupingFunction={() => [langui.folders ?? "Folders"]}
@ -169,7 +182,11 @@ const ContentsFolder = ({
keepInfoVisible keepInfoVisible
/> />
)} )}
className="grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" className={cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)}
renderWhenEmpty={() => <></>} renderWhenEmpty={() => <></>}
langui={langui} langui={langui}
groupingFunction={() => [langui.contents ?? "Contents"]} groupingFunction={() => [langui.contents ?? "Contents"]}
@ -182,11 +199,12 @@ const ContentsFolder = ({
</ContentPanel> </ContentPanel>
), ),
[ [
folder.contents, folder.contents?.data,
folder.parent_folder?.data?.attributes, folder.parent_folder?.data?.attributes,
folder.slug, folder.slug,
folder.subfolders, folder.subfolders?.data,
folder.titles, folder.titles,
isContentPanelAtLeast4xl,
langui, langui,
] ]
); );
@ -284,15 +302,11 @@ interface PreviewFolderProps {
title: string | null | undefined; title: string | null | undefined;
} }
export const PreviewFolder = ({ const PreviewFolder = ({ href, title }: PreviewFolderProps): JSX.Element => (
href,
title,
}: PreviewFolderProps): JSX.Element => (
<Link <Link
href={href} href={href}
className="flex w-full cursor-pointer flex-row place-content-center place-items-center gap-4 className="flex w-full cursor-pointer flex-row place-content-center place-items-center gap-4
rounded-md bg-light p-6 px-8 transition-transform drop-shadow-shade-xl hover:scale-[1.02] rounded-md bg-light p-6 transition-transform drop-shadow-shade-xl hover:scale-[1.02]"
mobile:px-6 mobile:py-6"
> >
{title && ( {title && (
<p className="text-center font-headers text-lg font-bold leading-none"> <p className="text-center font-headers text-lg font-bold leading-none">
@ -304,7 +318,7 @@ export const PreviewFolder = ({
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const TranslatedPreviewFolder = ({ const TranslatedPreviewFolder = ({
translations, translations,
fallback, fallback,
...otherProps ...otherProps

View File

@ -210,7 +210,7 @@ const Editor = ({ langui, ...otherProps }: Props): JSX.Element => {
target.select(); target.select();
event.preventDefault(); event.preventDefault();
}} }}
className="h-[50vh] w-[50vw] mobile:w-[75vw]" className="h-[50vh] w-[50vw]"
/> />
</Popup> </Popup>

View File

@ -19,7 +19,7 @@ const Home = ({ langui, ...otherProps }: PostStaticProps): JSX.Element => (
<div className="grid w-full place-content-center place-items-center gap-5 text-center"> <div className="grid w-full place-content-center place-items-center gap-5 text-center">
<div <div
className="aspect-square w-32 bg-black [mask:url('/icons/accords.svg')] className="aspect-square w-32 bg-black [mask:url('/icons/accords.svg')]
[mask-size:contain] [mask-repeat:no-repeat] [mask-position:center] mobile:w-[50vw]" [mask-size:contain] [mask-repeat:no-repeat] [mask-position:center]"
/> />
<h1 className="mb-0 text-5xl">Accord&rsquo;s Library</h1> <h1 className="mb-0 text-5xl">Accord&rsquo;s Library</h1>
<h2 className="-mt-5 text-xl"> <h2 className="-mt-5 text-xl">

View File

@ -10,10 +10,7 @@ import { Switch } from "components/Inputs/Switch";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/InsetBox";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -49,7 +46,7 @@ import {
import { useLightBox } from "hooks/useLightBox"; import { useLightBox } from "hooks/useLightBox";
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange"; import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { isUntangibleGroupItem } from "helpers/libraryItem"; import { isUntangibleGroupItem } from "helpers/libraryItem";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { cJoin, cIf } from "helpers/className"; import { cJoin, cIf } from "helpers/className";
@ -58,6 +55,7 @@ import { getOpenGraph } from "helpers/openGraph";
import { getDescription } from "helpers/description"; import { getDescription } from "helpers/description";
import { useIntersectionList } from "hooks/useIntersectionList"; import { useIntersectionList } from "hooks/useIntersectionList";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { useIsContentPanelNoMoreThan } from "hooks/useContainerQuery";
/* /*
* *
@ -97,14 +95,15 @@ const LibrarySlug = ({
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { currency } = useAppLayout(); const { currency } = useAppLayout();
const hoverable = useMediaHoverable(); const isContentPanelNoMoreThan3xl = useIsContentPanelNoMoreThan("3xl");
const isContentPanelNoMoreThanSm = useIsContentPanelNoMoreThan("sm");
const hoverable = useDeviceSupportsHover();
const router = useRouter(); const router = useRouter();
const [openLightBox, LightBox] = useLightBox(); const [openLightBox, LightBox] = useLightBox();
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } =
useBoolean(false); useBoolean(false);
useScrollTopOnChange(AnchorIds.ContentPanel, [item]); useScrollTopOnChange(AnchorIds.ContentPanel, [item]);
const currentIntersection = useIntersectionList(intersectionIds); const currentIntersection = useIntersectionList(intersectionIds);
const isVariantSet = useMemo( const isVariantSet = useMemo(
@ -130,7 +129,7 @@ const LibrarySlug = ({
href="/library/" href="/library/"
title={langui.library} title={langui.library}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn="3ColumnsLayout"
/> />
<HorizontalLine /> <HorizontalLine />
@ -198,13 +197,15 @@ const LibrarySlug = ({
href="/library/" href="/library/"
title={langui.library} title={langui.library}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn="1ColumnLayout"
className="mb-10" className="mb-10"
/> />
<div className="grid place-items-center gap-12"> <div className="grid place-items-center gap-12">
<div <div
className="relative h-[50vh] w-full className={cJoin(
cursor-pointer drop-shadow-shade-xl desktop:mb-16 mobile:h-[60vh]" "relative h-[50vh] w-full cursor-pointer drop-shadow-shade-xl",
cIf(isContentPanelNoMoreThan3xl, "h-[60vh]", "mb-16")
)}
onClick={() => { onClick={() => {
if (item.thumbnail?.data?.attributes) { if (item.thumbnail?.data?.attributes) {
openLightBox([ openLightBox([
@ -332,11 +333,16 @@ const LibrarySlug = ({
)} )}
<InsetBox id={intersectionIds[2]} className="grid place-items-center"> <InsetBox id={intersectionIds[2]} className="grid place-items-center">
<div className="place-items grid w-[clamp(0px,100%,42rem)] gap-8"> <div className="place-items grid w-[clamp(0px,100%,42rem)] gap-10">
<h2 className="text-center text-2xl">{langui.details}</h2> <h2 className="text-center text-2xl">{langui.details}</h2>
<div <div
className="grid place-items-center gap-y-8 className={cJoin(
desktop:grid-flow-col desktop:place-content-between" "grid place-items-center gap-y-8",
cIf(
!isContentPanelNoMoreThan3xl,
"grid-flow-col place-content-between"
)
)}
> >
{item.metadata?.[0] && ( {item.metadata?.[0] && (
<div className="grid place-content-start place-items-center"> <div className="grid place-content-start place-items-center">
@ -391,15 +397,32 @@ const LibrarySlug = ({
)} )}
{item.size && ( {item.size && (
<div className="grid gap-8 mobile:place-items-center"> <div
className={cJoin(
"grid gap-4",
cIf(isContentPanelNoMoreThan3xl, "place-items-center")
)}
>
<h3 className="text-xl">{langui.size}</h3> <h3 className="text-xl">{langui.size}</h3>
<div <div
className="grid w-full grid-flow-col place-content-between thin:grid-flow-row className={cJoin(
thin:place-content-center thin:gap-8" "grid w-full",
cIf(
isContentPanelNoMoreThanSm,
"grid-flow-row place-content-center gap-8",
"grid-flow-col place-content-between"
)
)}
> >
<div <div
className="grid place-items-center gap-x-4 desktop:grid-flow-col className={cJoin(
desktop:place-items-start" "grid gap-x-4",
cIf(
isContentPanelNoMoreThan3xl,
"place-items-center",
"grid-flow-col place-items-start"
)
)}
> >
<p className="font-bold">{langui.width}:</p> <p className="font-bold">{langui.width}:</p>
<div> <div>
@ -408,8 +431,14 @@ const LibrarySlug = ({
</div> </div>
</div> </div>
<div <div
className="grid place-items-center gap-x-4 desktop:grid-flow-col className={cJoin(
desktop:place-items-start" "grid gap-x-4",
cIf(
isContentPanelNoMoreThan3xl,
"place-items-center",
"grid-flow-col place-items-start"
)
)}
> >
<p className="font-bold">{langui.height}:</p> <p className="font-bold">{langui.height}:</p>
<div> <div>
@ -419,8 +448,14 @@ const LibrarySlug = ({
</div> </div>
{isDefined(item.size.thickness) && ( {isDefined(item.size.thickness) && (
<div <div
className="grid place-items-center gap-x-4 desktop:grid-flow-col className={cJoin(
desktop:place-items-start" "grid gap-x-4",
cIf(
isContentPanelNoMoreThan3xl,
"place-items-center",
"grid-flow-col place-items-start"
)
)}
> >
<p className="font-bold">{langui.thickness}:</p> <p className="font-bold">{langui.thickness}:</p>
<div> <div>
@ -435,7 +470,12 @@ const LibrarySlug = ({
{item.metadata?.[0]?.__typename !== "ComponentMetadataGroup" && {item.metadata?.[0]?.__typename !== "ComponentMetadataGroup" &&
item.metadata?.[0]?.__typename !== "ComponentMetadataOther" && ( item.metadata?.[0]?.__typename !== "ComponentMetadataOther" && (
<> <div
className={cJoin(
"grid gap-4",
cIf(isContentPanelNoMoreThan3xl, "place-items-center")
)}
>
<h3 className="text-xl">{langui.type_information}</h3> <h3 className="text-xl">{langui.type_information}</h3>
<div className="grid w-full grid-cols-2 place-content-between"> <div className="grid w-full grid-cols-2 place-content-between">
{item.metadata?.[0]?.__typename === {item.metadata?.[0]?.__typename ===
@ -480,7 +520,7 @@ const LibrarySlug = ({
</> </>
)} )}
</div> </div>
</> </div>
)} )}
</div> </div>
</InsetBox> </InsetBox>
@ -504,8 +544,8 @@ const LibrarySlug = ({
)} )}
<div <div
className="grid w-full grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] className="grid w-full grid-cols-[repeat(auto-fill,minmax(13rem,1fr))]
items-end gap-8 mobile:grid-cols-2 thin:grid-cols-1" items-end gap-8"
> >
{filterHasAttributes(item.subitems.data, [ {filterHasAttributes(item.subitems.data, [
"id", "id",
@ -560,7 +600,7 @@ const LibrarySlug = ({
text={langui.view_scans} text={langui.view_scans}
/> />
)} )}
<div className="grid w-full gap-4"> <div className="max-w- grid w-full gap-4">
{filterHasAttributes(item.contents.data, [ {filterHasAttributes(item.contents.data, [
"attributes", "attributes",
] as const).map((rangedContent) => ( ] as const).map((rangedContent) => (
@ -610,6 +650,7 @@ const LibrarySlug = ({
isDefined(rangedContent.attributes.scan_set) && isDefined(rangedContent.attributes.scan_set) &&
rangedContent.attributes.scan_set.length > 0 rangedContent.attributes.scan_set.length > 0
} }
condensed={isContentPanelNoMoreThan3xl}
/> />
))} ))}
</div> </div>
@ -621,6 +662,7 @@ const LibrarySlug = ({
[ [
LightBox, LightBox,
langui, langui,
isContentPanelNoMoreThan3xl,
item.thumbnail?.data?.attributes, item.thumbnail?.data?.attributes,
item.subitem_of?.data, item.subitem_of?.data,
item.title, item.title,
@ -640,6 +682,7 @@ const LibrarySlug = ({
router.locale, router.locale,
currencies, currencies,
currency, currency,
isContentPanelNoMoreThanSm,
isVariantSet, isVariantSet,
hoverable, hoverable,
toggleKeepInfoVisible, toggleKeepInfoVisible,
@ -763,6 +806,7 @@ interface ContentLineProps {
langui: AppStaticProps["langui"]; langui: AppStaticProps["langui"];
languages: AppStaticProps["languages"]; languages: AppStaticProps["languages"];
hasScanSet: boolean; hasScanSet: boolean;
condensed: boolean;
} }
const ContentLine = ({ const ContentLine = ({
@ -773,9 +817,9 @@ const ContentLine = ({
hasScanSet, hasScanSet,
slug, slug,
parentSlug, parentSlug,
condensed,
}: ContentLineProps): JSX.Element => { }: ContentLineProps): JSX.Element => {
const { value: isOpened, toggle: toggleOpened } = useBoolean(false); const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
const [selectedTranslation] = useSmartLanguage({ const [selectedTranslation] = useSmartLanguage({
items: content?.translations ?? [], items: content?.translations ?? [],
languages: languages, languages: languages,
@ -787,6 +831,56 @@ const ContentLine = ({
), ),
}); });
if (condensed) {
return (
<div className="my-4 grid gap-2">
<div className="flex gap-2">
{content?.type && <Chip text={content.type} />}
<p className="h-4 w-full border-b-2 border-dotted border-black opacity-30"></p>
<p>{rangeStart}</p>
</div>
<h3 className="flex flex-wrap place-items-center gap-2">
{selectedTranslation
? prettyInlineTitle(
selectedTranslation.pre_title,
selectedTranslation.title,
selectedTranslation.subtitle
)
: content
? prettySlug(content.slug, parentSlug)
: prettySlug(slug, parentSlug)}
</h3>
<div className="flex flex-row flex-wrap gap-1">
{content?.categories?.map((category, index) => (
<Chip key={index} text={category} />
))}
</div>
<div className="grid grid-cols-2 gap-3">
{hasScanSet || isDefined(content) ? (
<>
{hasScanSet && (
<Button
href={`/library/${parentSlug}/scans#${slug}`}
text={langui.view_scans}
/>
)}
{isDefined(content) && (
<Button
href={`/contents/${content.slug}`}
text={langui.open_content}
/>
)}
</>
) : (
/* TODO: Add to langui */
"The content is not available"
)}
</div>
</div>
);
}
return ( return (
<div <div
className={cJoin( className={cJoin(
@ -794,10 +888,7 @@ const ContentLine = ({
cIf(isOpened, "my-2 h-auto bg-mid py-3 shadow-inner-sm shadow-shade") cIf(isOpened, "my-2 h-auto bg-mid py-3 shadow-inner-sm shadow-shade")
)} )}
> >
<div <div className="grid grid-cols-[auto_auto_1fr_auto_12ch] place-items-center gap-4">
className="grid grid-cols-[auto_auto_1fr_auto_12ch] place-items-center
gap-4 thin:grid-cols-[auto_auto_1fr_auto]"
>
<a> <a>
<h3 className="cursor-pointer" onClick={toggleOpened}> <h3 className="cursor-pointer" onClick={toggleOpened}>
{selectedTranslation {selectedTranslation
@ -819,7 +910,7 @@ const ContentLine = ({
<p className="h-4 w-full border-b-2 border-dotted border-black opacity-30"></p> <p className="h-4 w-full border-b-2 border-dotted border-black opacity-30"></p>
<p>{rangeStart}</p> <p>{rangeStart}</p>
{content?.type && ( {content?.type && (
<Chip className="justify-self-end thin:hidden" text={content.type} /> <Chip className="justify-self-end" text={content.type} />
)} )}
</div> </div>
<div <div
@ -845,6 +936,7 @@ const ContentLine = ({
)} )}
</> </>
) : ( ) : (
/* TODO: Add to langui */
"The content is not available" "The content is not available"
)} )}
</div> </div>

View File

@ -1,10 +1,7 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { Fragment, useCallback, useMemo } from "react"; import { Fragment, useCallback, useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -45,6 +42,11 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedProps } from "helpers/types/TranslatedProps"; import { TranslatedProps } from "helpers/types/TranslatedProps";
import { TranslatedNavOption } from "components/PanelComponents/NavOption"; import { TranslatedNavOption } from "components/PanelComponents/NavOption";
import { useIntersectionList } from "hooks/useIntersectionList"; import { useIntersectionList } from "hooks/useIntersectionList";
import {
useIs1ColumnLayout,
useIsContentPanelNoMoreThan,
} from "hooks/useContainerQuery";
import { cIf, cJoin } from "helpers/className";
/* /*
* *
@ -71,6 +73,7 @@ const LibrarySlug = ({
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const [openLightBox, LightBox] = useLightBox(); const [openLightBox, LightBox] = useLightBox();
const is1ColumnLayout = useIs1ColumnLayout();
const ids = useMemo( const ids = useMemo(
() => () =>
@ -89,11 +92,11 @@ const LibrarySlug = ({
title={langui.item} title={langui.item}
langui={langui} langui={langui}
className="mb-4" className="mb-4"
displayOn={ReturnButtonType.Desktop} displayOnlyOn="3ColumnsLayout"
/> />
<div className="grid place-items-center"> <div className="grid place-items-center">
<div className="mobile:w-[80%]"> <div className={cIf(is1ColumnLayout, "w-3/4")}>
<PreviewCard <PreviewCard
href={`/library/${item.slug}`} href={`/library/${item.slug}`}
title={item.title} title={item.title}
@ -190,6 +193,7 @@ const LibrarySlug = ({
item.title, item.title,
itemId, itemId,
langui, langui,
is1ColumnLayout,
] ]
); );
@ -202,7 +206,7 @@ const LibrarySlug = ({
href={`/library/${item.slug}`} href={`/library/${item.slug}`}
title={langui.item} title={langui.item}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn="1ColumnLayout"
className="mb-10" className="mb-10"
/> />
@ -370,6 +374,7 @@ const ScanSet = ({
langui, langui,
content, content,
}: ScanSetProps): JSX.Element => { }: ScanSetProps): JSX.Element => {
const is1ColumnLayout = useIsContentPanelNoMoreThan("2xl");
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = const [selectedScan, LanguageSwitcher, languageSwitcherProps] =
useSmartLanguage({ useSmartLanguage({
items: scanSet, items: scanSet,
@ -533,8 +538,15 @@ const ScanSet = ({
</div> </div>
<div <div
className="grid items-end gap-8 border-b-[3px] border-dotted pb-12 last-of-type:border-0 className={cJoin(
desktop:grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))] mobile:grid-cols-2" `grid items-end gap-8 border-b-[3px] border-dotted pb-12
last-of-type:border-0`,
cIf(
is1ColumnLayout,
"grid-cols-2 gap-[4vmin]",
"grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))]"
)
)}
> >
{pages.map((page, index) => ( {pages.map((page, index) => (
<div <div
@ -602,6 +614,7 @@ const ScanSetCover = ({
languages, languages,
langui, langui,
}: ScanSetCoverProps): JSX.Element => { }: ScanSetCoverProps): JSX.Element => {
const is1ColumnLayout = useIsContentPanelNoMoreThan("4xl");
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = const [selectedScan, LanguageSwitcher, languageSwitcherProps] =
useSmartLanguage({ useSmartLanguage({
items: images, items: images,
@ -727,9 +740,15 @@ const ScanSetCover = ({
</div> </div>
<div <div
className="grid items-end gap-8 border-b-[3px] border-dotted pb-12 className={cJoin(
last-of-type:border-0 desktop:grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))] `grid items-end gap-8 border-b-[3px] border-dotted pb-12
mobile:grid-cols-2" last-of-type:border-0`,
cIf(
is1ColumnLayout,
"grid-cols-2 gap-[4vmin]",
"grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))]"
)
)}
> >
{coverImages.map((image, index) => ( {coverImages.map((image, index) => (
<div <div

View File

@ -22,7 +22,7 @@ import { Button } from "components/Inputs/Button";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { isUntangibleGroupItem } from "helpers/libraryItem"; import { isUntangibleGroupItem } from "helpers/libraryItem";
import { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { ButtonGroup } from "components/Inputs/ButtonGroup"; import { ButtonGroup } from "components/Inputs/ButtonGroup";
import { filterHasAttributes, isDefined, isUndefined } from "helpers/others"; import { filterHasAttributes, isDefined, isUndefined } from "helpers/others";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
@ -31,6 +31,9 @@ import { SmartList } from "components/SmartList";
import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable"; import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date"; import { compareDate } from "helpers/date";
import { HorizontalLine } from "components/HorizontalLine";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { cIf, cJoin } from "helpers/className";
/* /*
* *
@ -63,8 +66,9 @@ const Library = ({
currencies, currencies,
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const hoverable = useMediaHoverable(); const hoverable = useDeviceSupportsHover();
const { libraryItemUserStatus } = useAppLayout(); const { libraryItemUserStatus } = useAppLayout();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState( const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName DEFAULT_FILTERS_STATE.searchName
@ -261,6 +265,8 @@ const Library = ({
description={langui.library_description} description={langui.library_description}
/> />
<HorizontalLine />
<TextInput <TextInput
className="mb-6 w-full" className="mb-6 w-full"
placeholder={langui.search_title ?? "Search..."} placeholder={langui.search_title ?? "Search..."}
@ -423,7 +429,13 @@ const Library = ({
} }
/> />
)} )}
className="grid-cols-2 items-end desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]" className={cJoin(
"grid-cols-2 items-end",
cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]"
)
)}
searchingTerm={searchName} searchingTerm={searchName}
sortingFunction={sortingFunction} sortingFunction={sortingFunction}
groupingFunction={groupingFunction} groupingFunction={groupingFunction}
@ -444,6 +456,7 @@ const Library = ({
currencies, currencies,
filteringFunction, filteringFunction,
groupingFunction, groupingFunction,
isContentPanelAtLeast4xl,
items, items,
keepInfoVisible, keepInfoVisible,
langui, langui,

View File

@ -17,12 +17,15 @@ import { Icon } from "components/Ico";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { filterHasAttributes } from "helpers/others"; import { filterHasAttributes } from "helpers/others";
import { SmartList } from "components/SmartList"; import { SmartList } from "components/SmartList";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date"; import { compareDate } from "helpers/date";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { HorizontalLine } from "components/HorizontalLine";
import { cIf } from "helpers/className";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
/* /*
* *
@ -44,7 +47,8 @@ interface Props extends AppStaticProps, AppLayoutRequired {
} }
const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => { const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
const hoverable = useMediaHoverable(); const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const hoverable = useDeviceSupportsHover();
const [searchName, setSearchName] = useState( const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName DEFAULT_FILTERS_STATE.searchName
); );
@ -63,6 +67,8 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
description={langui.news_description} description={langui.news_description}
/> />
<HorizontalLine />
<TextInput <TextInput
className="mb-6 w-full" className="mb-6 w-full"
placeholder={langui.search_title ?? "Search..."} placeholder={langui.search_title ?? "Search..."}
@ -129,7 +135,11 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
}} }}
/> />
)} )}
className="grid-cols-1 desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]" className={cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-4 gap-y-6"
)}
searchingTerm={searchName} searchingTerm={searchName}
searchingBy={(post) => searchingBy={(post) =>
`${prettySlug(post.attributes.slug)} ${post.attributes.translations `${prettySlug(post.attributes.slug)} ${post.attributes.translations
@ -140,7 +150,7 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
/> />
</ContentPanel> </ContentPanel>
), ),
[keepInfoVisible, langui, posts, searchName] [keepInfoVisible, langui, posts, searchName, isContentPanelAtLeast4xl]
); );
return ( return (

View File

@ -4,10 +4,7 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip"; import { Chip } from "components/Chip";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { import {
ContentPanel, ContentPanel,
ContentPanelWidthSizes, ContentPanelWidthSizes,
@ -32,6 +29,8 @@ import {
staticSmartLanguage, staticSmartLanguage,
} from "helpers/locales"; } from "helpers/locales";
import { getDescription } from "helpers/description"; import { getDescription } from "helpers/description";
import { cIf, cJoin } from "helpers/className";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
/* /*
* *
@ -60,6 +59,7 @@ const WikiPage = ({
}); });
const [openLightBox, LightBox] = useLightBox(); const [openLightBox, LightBox] = useLightBox();
const is3ColumnsLayout = useIs3ColumnsLayout();
const subPanel = useMemo( const subPanel = useMemo(
() => ( () => (
@ -68,7 +68,7 @@ const WikiPage = ({
href={`/wiki`} href={`/wiki`}
title={langui.wiki} title={langui.wiki}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn={"3ColumnsLayout"}
/> />
</SubPanel> </SubPanel>
), ),
@ -84,7 +84,7 @@ const WikiPage = ({
href={`/wiki`} href={`/wiki`}
title={langui.wiki} title={langui.wiki}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn={"1ColumnLayout"}
className="mb-10" className="mb-10"
/> />
@ -101,121 +101,131 @@ const WikiPage = ({
<LanguageSwitcher {...languageSwitcherProps} /> <LanguageSwitcher {...languageSwitcherProps} />
</div> </div>
<HorizontalLine />
{selectedTranslation && ( {selectedTranslation && (
<div className="text-justify"> <>
<div <HorizontalLine />
className="mb-8 overflow-hidden rounded-lg bg-mid text-center desktop:float-right <div className="text-justify">
desktop:ml-8 desktop:w-[25rem]" <div
> className={cJoin(
{page.thumbnail?.data?.attributes && ( "mb-8 overflow-hidden rounded-lg bg-mid text-center",
<Img cIf(is3ColumnsLayout, "float-right ml-8 w-[25rem]")
src={page.thumbnail.data.attributes} )}
quality={ImageQuality.Medium} >
className="w-full cursor-pointer" {page.thumbnail?.data?.attributes && (
onClick={() => { <Img
if (page.thumbnail?.data?.attributes?.url) { src={page.thumbnail.data.attributes}
openLightBox([ quality={ImageQuality.Medium}
getAssetURL( className="w-full cursor-pointer"
page.thumbnail.data.attributes.url, onClick={() => {
ImageQuality.Large if (page.thumbnail?.data?.attributes?.url) {
), openLightBox([
]); getAssetURL(
} page.thumbnail.data.attributes.url,
}} ImageQuality.Large
/> ),
]);
}
}}
/>
)}
<div className="my-4 grid gap-4 p-4">
{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">
{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>
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
<div className="mb-12">
<p className="font-headers text-lg font-bold">
{langui.summary}
</p>
<p>{selectedTranslation.summary}</p>
</div>
)} )}
<div className="my-4 grid gap-4 p-4">
{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.definitions, [
{filterHasAttributes(page.categories.data, [ "translations",
"attributes", ] as const).map((definition, index) => (
] as const).map((category) => ( <div key={index} className="mb-12">
<Chip <DefinitionCard
key={category.id} source={{
text={category.attributes.name} name: definition.source?.data?.attributes?.name,
/> url: definition.source?.data?.attributes?.content?.data
))} ?.attributes?.slug
</div> ? `/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(
{page.tags?.data && page.tags.data.length > 0 && ( (translation) => ({
<> language: translation?.language?.data?.attributes?.code,
<p className="font-headers text-xl font-bold"> definition: translation?.definition,
{langui.tags} status: translation?.status,
</p> })
<div className="flex flex-row flex-wrap place-content-center gap-2"> )}
{filterHasAttributes(page.tags.data, [ index={index + 1}
"attributes", languages={languages}
] as const).map((tag) => ( langui={langui}
<Chip categories={filterHasAttributes(
key={tag.id} definition.categories?.data,
text={ ["attributes"] as const
tag.attributes.titles?.[0]?.title ?? ).map((category) => category.attributes.short)}
prettySlug(tag.attributes.slug) />
} </div>
/> ))}
))}
</div>
</>
)}
</div>
</div> </div>
</>
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
<div className="mb-12">
<p className="font-headers text-lg font-bold">
{langui.summary}
</p>
<p>{selectedTranslation.summary}</p>
</div>
)}
{filterHasAttributes(page.definitions, [
"translations",
] as const).map((definition, index) => (
<div key={index} className="mb-12">
<DefinitionCard
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,
status: translation?.status,
}))}
index={index + 1}
languages={languages}
langui={langui}
categories={filterHasAttributes(definition.categories?.data, [
"attributes",
] as const).map((category) => category.attributes.short)}
/>
</div>
))}
</div>
)} )}
</ContentPanel> </ContentPanel>
), ),
[ [
LanguageSwitcher, LanguageSwitcher,
LightBox, LightBox,
is3ColumnsLayout,
languageSwitcherProps, languageSwitcherProps,
languages, languages,
langui, langui,
openLightBox, openLightBox,
page, page.categories?.data,
page.definitions,
page.tags?.data,
page.thumbnail?.data?.attributes,
selectedTranslation, selectedTranslation,
] ]
); );

View File

@ -3,10 +3,7 @@ import { Fragment, useCallback, useMemo } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/InsetBox";
import { import { ReturnButton } from "components/PanelComponents/ReturnButton";
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
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 { import {
@ -71,7 +68,7 @@ const Chronology = ({
href="/wiki" href="/wiki"
title={langui.wiki} title={langui.wiki}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Desktop} displayOnlyOn="3ColumnsLayout"
/> />
<HorizontalLine /> <HorizontalLine />
@ -110,7 +107,7 @@ const Chronology = ({
href="/wiki" href="/wiki"
title={langui.wiki} title={langui.wiki}
langui={langui} langui={langui}
displayOn={ReturnButtonType.Mobile} displayOnlyOn="1ColumnLayout"
className="mb-10" className="mb-10"
/> />

View File

@ -18,7 +18,7 @@ import { Button } from "components/Inputs/Button";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { useMediaHoverable } from "hooks/useMediaQuery"; import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { filterDefined, filterHasAttributes } from "helpers/others"; import { filterDefined, filterHasAttributes } from "helpers/others";
import { SmartList } from "components/SmartList"; import { SmartList } from "components/SmartList";
import { Select } from "components/Inputs/Select"; import { Select } from "components/Inputs/Select";
@ -26,6 +26,8 @@ import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
import { cIf } from "helpers/className";
/* /*
* *
@ -48,7 +50,8 @@ interface Props extends AppStaticProps, AppLayoutRequired {
} }
const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => { const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
const hoverable = useMediaHoverable(); const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState( const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName DEFAULT_FILTERS_STATE.searchName
@ -73,6 +76,8 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
description={langui.wiki_description} description={langui.wiki_description}
/> />
<HorizontalLine />
<TextInput <TextInput
className="mb-6 w-full" className="mb-6 w-full"
placeholder={langui.search_title ?? "Search..."} placeholder={langui.search_title ?? "Search..."}
@ -105,6 +110,7 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible); setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
}} }}
/> />
<HorizontalLine /> <HorizontalLine />
<p className="mb-4 font-headers text-xl font-bold"> <p className="mb-4 font-headers text-xl font-bold">
@ -193,7 +199,11 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
/> />
)} )}
langui={langui} langui={langui}
className="grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]" className={cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)}
searchingTerm={searchName} searchingTerm={searchName}
searchingBy={(item) => searchingBy={(item) =>
filterDefined(item.attributes.translations) filterDefined(item.attributes.translations)
@ -210,7 +220,14 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
/> />
</ContentPanel> </ContentPanel>
), ),
[groupingFunction, keepInfoVisible, langui, pages, searchName] [
groupingFunction,
keepInfoVisible,
langui,
pages,
searchName,
isContentPanelAtLeast4xl,
]
); );
return ( return (

View File

@ -35,7 +35,7 @@ mark {
} }
*::-webkit-scrollbar { *::-webkit-scrollbar {
@apply w-3 mobile:w-0; @apply w-3;
} }
*::-webkit-scrollbar-track { *::-webkit-scrollbar-track {

View File

@ -1,5 +1,5 @@
const plugin = require("tailwindcss/plugin"); const plugin = require("tailwindcss/plugin");
const { breaks, colors, fonts, fontFamilies } = require("./design.config.js"); const { colors, fonts, fontFamilies } = require("./design.config.js");
const rgb = (color) => [color.r, color.g, color.b].join(" "); const rgb = (color) => [color.r, color.g, color.b].join(" ");
@ -23,9 +23,6 @@ module.exports = {
...fonts, ...fonts,
}, },
screens: { screens: {
desktop: breaks.desktop,
mobile: breaks.mobile,
thin: breaks.thin,
hoverable: { raw: "(hover: hover)" }, hoverable: { raw: "(hover: hover)" },
notHoverable: { raw: "(hover: none)" }, notHoverable: { raw: "(hover: none)" },
}, },