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!
- Autogenerated multi-level table of content and anchor links for the different headers
- 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 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)
- Persistent app state using LocalStorage
- Accessibility
@ -56,7 +57,7 @@
- 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
- 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
- Automatic generation of the sitemap using [next-sitemap](https://www.npmjs.com/package/next-sitemap)
- Data quality testing
@ -105,6 +106,6 @@ Run in dev mode:
OR build and run in production mode
```bash
./run_accords_build.sh
npm run build
./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 = {
openDyslexic: "OpenDyslexic",
vollkorn: "Vollkorn",
@ -43,9 +37,16 @@ const fontFamilies = {
},
};
const layout = {
// all values in rem
mainMenuReduced: 6,
mainMenu: 20,
subMenu: 20,
};
module.exports = {
colors,
breaks,
layout,
fonts,
fontFamilies,
};

28
package-lock.json generated
View File

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

View File

@ -44,7 +44,7 @@
"@graphql-codegen/typescript": "2.7.3",
"@graphql-codegen/typescript-graphql-request": "^4.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/react": "18.0.17",
"@types/react-dom": "^18.0.6",
@ -56,7 +56,7 @@
"eslint-config-next": "12.2.5",
"eslint-plugin-import": "^2.26.0",
"graphql": "^16.6.0",
"next-sitemap": "^3.1.20",
"next-sitemap": "^3.1.21",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.1.8",

View File

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

View File

@ -27,7 +27,7 @@ export const PreviewCardCTAs = ({
return (
<>
<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"
}`}
>

View File

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

View File

@ -15,6 +15,8 @@ import { useLightBox } from "hooks/useLightBox";
import { AnchorShare } from "components/AnchorShare";
import { useIntersectionList } from "hooks/useIntersectionList";
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 => {
const { playerName } = useAppLayout();
const router = useRouter();
const isContentPanelAtLeastLg = useIsContentPanelAtLeast("lg");
const [openLightBox, LightBox] = useLightBox();
/* eslint-disable no-irregular-whitespace */
@ -131,7 +134,16 @@ export const Markdawn = ({
Transcript: {
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}
</div>
),
@ -140,7 +152,12 @@ export const Markdawn = ({
Line: {
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} />
</strong>
<p className="whitespace-pre-line">{compProps.children}</p>
@ -301,12 +318,13 @@ interface HeaderProps {
}
const Header = ({ level, title, slug, langui }: HeaderProps): JSX.Element => {
const isHoverable = useDeviceSupportsHover();
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 === "* * *" ? (
<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} />
@ -315,52 +333,53 @@ const Header = ({ level, title, slug, langui }: HeaderProps): JSX.Element => {
<div className="font-headers">{title}</div>
)}
<AnchorShare
className="opacity-0 transition-opacity group-hover:opacity-100"
className={cIf(
isHoverable,
"opacity-0 transition-opacity group-hover:opacity-100"
)}
id={slug}
langui={langui}
/>
</div>
</>
),
[langui, slug, title]
[isHoverable, langui, slug, title]
);
const className = "group";
switch (level) {
case 1:
return (
<h1 id={slug} className={className}>
<h1 id={slug} className="group">
{innerComponent}
</h1>
);
case 2:
return (
<h2 id={slug} className={className}>
<h2 id={slug} className="group">
{innerComponent}
</h2>
);
case 3:
return (
<h3 id={slug} className={className}>
<h3 id={slug} className="group">
{innerComponent}
</h3>
);
case 4:
return (
<h4 id={slug} className={className}>
<h4 id={slug} className="group">
{innerComponent}
</h4>
);
case 5:
return (
<h5 id={slug} className={className}>
<h5 id={slug} className="group">
{innerComponent}
</h5>
);
default:
return (
<h6 id={slug} className={className}>
<h6 id={slug} className="group">
{innerComponent}
</h6>
);

View File

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

View File

@ -3,9 +3,10 @@ import { Icon } from "components/Ico";
import { Button } from "components/Inputs/Button";
import { useAppLayout } from "contexts/AppLayoutContext";
import { AppStaticProps } from "graphql/getAppStaticProps";
import { cIf, cJoin } from "helpers/className";
import { TranslatedProps } from "helpers/types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
import { isDefined } from "helpers/others";
/*
*
@ -16,42 +17,37 @@ interface Props {
href: string;
title: string | null | undefined;
langui: AppStaticProps["langui"];
displayOn: ReturnButtonType;
displayOnlyOn?: "1ColumnLayout" | "3ColumnsLayout";
className?: string;
}
export enum ReturnButtonType {
Mobile = "mobile",
Desktop = "desktop",
Both = "both",
}
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const ReturnButton = ({
href,
title,
langui,
displayOn,
displayOnlyOn,
className,
}: Props): JSX.Element => {
const { setSubPanelOpen } = useAppLayout();
const is3ColumnsLayout = useIs3ColumnsLayout();
return (
<div
className={cJoin(
cIf(displayOn === ReturnButtonType.Mobile, "desktop:hidden"),
cIf(displayOn === ReturnButtonType.Desktop, "mobile:hidden"),
className
<>
{((is3ColumnsLayout && displayOnlyOn === "3ColumnsLayout") ||
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
!isDefined(displayOnlyOn)) && (
<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,
children,
className,
}: Props): JSX.Element => (
<div className="grid h-full">
<main
className={cJoin(
"justify-self-center px-4 pt-10 pb-20 desktop:px-10 desktop:pt-20 desktop:pb-32",
width === ContentPanelWidthSizes.Default
? "max-w-2xl"
: width === ContentPanelWidthSizes.Large
? "max-w-4xl"
: "w-full",
className
)}
>
{children}
</main>
</div>
);
}: Props): JSX.Element => {
const isContentPanelAtLeast3xl = useIsContentPanelAtLeast("3xl");
return (
<div className="grid h-full">
<main
className={cJoin(
"justify-self-center px-4 pt-10 pb-20",
cIf(isContentPanelAtLeast3xl, "px-10 pt-20 pb-32"),
width === ContentPanelWidthSizes.Default
? "max-w-2xl"
: width === ContentPanelWidthSizes.Large
? "max-w-4xl"
: "w-full",
className
)}
>
{children}
</main>
</div>
);
};

View File

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

View File

@ -1,3 +1,6 @@
import { cIf, cJoin } from "helpers/className";
import { useIsSubPanelAtLeast } from "hooks/useContainerQuery";
/*
*
* COMPONENT
@ -9,8 +12,16 @@ interface Props {
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
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">
{children}
</div>
);
export const SubPanel = ({ children }: Props): JSX.Element => {
const isSubPanelAtLeastSm = useIsSubPanelAtLeast("2xs");
return (
<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
className={cJoin(
"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(
fillViewport,
"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")
)}

View File

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

View File

@ -19,7 +19,7 @@ import {
prettyShortenNumber,
} from "helpers/formatters";
import { ImageQuality } from "helpers/img";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedProps } from "helpers/types/TranslatedProps";
@ -81,7 +81,7 @@ export const PreviewCard = ({
infoAppend,
}: Props): JSX.Element => {
const { currency } = useAppLayout();
const isHoverable = useMediaHoverable();
const isHoverable = useDeviceSupportsHover();
const router = useRouter();
const metadataJSX = useMemo(
@ -90,7 +90,7 @@ export const PreviewCard = ({
{metadata && (metadata.releaseDate || metadata.price) && (
<div className="flex w-full flex-row flex-wrap gap-x-3">
{metadata.releaseDate && (
<p className="text-sm mobile:text-xs">
<p className="text-sm">
<Ico
icon={Icon.Event}
className="mr-1 translate-y-[.15em] !text-base"
@ -99,7 +99,7 @@ export const PreviewCard = ({
</p>
)}
{metadata.price && metadata.currencies && (
<p className="justify-self-end text-sm mobile:text-xs">
<p className="justify-self-end text-sm">
<Ico
icon={Icon.ShoppingCart}
className="mr-1 translate-y-[.15em] !text-base"
@ -108,7 +108,7 @@ export const PreviewCard = ({
</p>
)}
{metadata.views && (
<p className="text-sm mobile:text-xs">
<p className="text-sm">
<Ico
icon={Icon.Visibility}
className="mr-1 translate-y-[.15em] !text-base"
@ -117,7 +117,7 @@ export const PreviewCard = ({
</p>
)}
{metadata.author && (
<p className="text-sm mobile:text-xs">
<p className="text-sm">
<Ico
icon={Icon.Person}
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 { isDefined, isDefinedAndNotEmpty } from "helpers/others";
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
interface Group<T> {
name: string;
@ -205,7 +206,7 @@ export const SmartList = <T,>({
<div
className={cJoin(
`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
)}
>
@ -244,15 +245,22 @@ interface DefaultRenderWhenEmptyProps {
langui: AppStaticProps["langui"];
}
const DefaultRenderWhenEmpty = ({ langui }: DefaultRenderWhenEmptyProps) => (
<div className="grid h-full place-content-center">
<div
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
border-dark p-8 text-dark opacity-40"
>
<Ico icon={Icon.ChevronLeft} className="!text-[300%] mobile:hidden" />
<p className="max-w-xs text-2xl">{langui.no_results_message}</p>
<Ico icon={Icon.ChevronRight} className="!text-[300%] desktop:hidden" />
const DefaultRenderWhenEmpty = ({ langui }: DefaultRenderWhenEmptyProps) => {
const is3ColumnsLayout = useIs3ColumnsLayout();
return (
<div className="grid h-full place-content-center">
<div
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
border-dark p-8 text-dark opacity-40"
>
{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>
);
);
};

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ const DEFAULT_OG_THUMBNAIL = {
};
export const TITLE_PREFIX = "Accords Library";
export const TITLE_SEPARATOR = " - "
export const TITLE_SEPARATOR = " - ";
export interface OpenGraph {
title: string;
@ -30,7 +30,9 @@ export const getOpenGraph = (
description?: string | null | undefined,
thumbnail?: UploadImageFragment | null | undefined
): OpenGraph => ({
title: `${TITLE_PREFIX}${isDefinedAndNotEmpty(title) ? `${TITLE_SEPARATOR}${title}` : ""}`,
title: `${TITLE_PREFIX}${
isDefinedAndNotEmpty(title) ? `${TITLE_SEPARATOR}${title}` : ""
}`,
description: isDefinedAndNotEmpty(description)
? 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 { throttle } from "throttle-debounce";
import { useIsClient } from "usehooks-ts";
import { useOnScroll, AnchorIds } from "./useScrollTopOnChange";
import { useOnScroll } from "./useOnScroll";
import { AnchorIds } from "./useScrollTopOnChange";
import { isDefined } from "helpers/others";
export const useIntersectionList = (ids: string[]): number => {
@ -16,7 +17,7 @@ export const useIntersectionList = (ids: string[]): number => {
const refreshCurrentIntersection = useCallback(
(scroll: number) => {
console.log("update");
console.log("useIntersectionList");
if (!isDefined(contentPanel)) {
setCurrentIntersection(-1);

View File

@ -1,14 +1,7 @@
import { useMediaQuery } from "usehooks-ts";
import { breaks } from "../../design.config";
// ts-unused-exports:disable-next-line
export const useMediaThin = (): boolean => useMediaQuery(breaks.thin.raw);
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 useDeviceSupportsHover = (): boolean =>
useMediaQuery("(hover: hover)");
export const usePrefersDarkMode = (): boolean =>
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 { useIsClient } from "usehooks-ts";
import { DependencyList, useEffect } from "react";
export enum AnchorIds {
Body = "bodyqs65d4a98d56az48z64d",
ContentPanel = "contentPanel495922447721572",
SubPanel = "subPanelz9e8rs2d3f18zer98ze",
}
// Scroll to top of element "id" when "deps" update.
@ -19,23 +20,3 @@ export const useScrollTopOnChange = (
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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 { AppLayout, AppLayoutRequired } from "components/AppLayout";
import {
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getOpenGraph } from "helpers/openGraph";
@ -24,12 +21,7 @@ const FourOhFour = ({
contentPanel={
<ContentPanel>
<h1>{openGraph.title}</h1>
<ReturnButton
href="/"
title="Home"
langui={langui}
displayOn={ReturnButtonType.Both}
/>
<ReturnButton href="/" title="Home" langui={langui} />
</ContentPanel>
}
openGraph={openGraph}

View File

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

View File

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

View File

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

View File

@ -1,13 +1,10 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { Fragment, useMemo } from "react";
import { useMemo, useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
import {
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import {
ContentPanel,
ContentPanelWidthSizes,
@ -19,10 +16,25 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { getReadySdk } from "graphql/sdk";
import { getVideoThumbnailURL } from "helpers/videos";
import { Icon } from "components/Ico";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { WithLabel } from "components/Inputs/WithLabel";
import { filterHasAttributes, isDefined } from "helpers/others";
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 { value: keepInfoVisible, toggle: toggleKeepInfoVisible } =
useBoolean(true);
const hoverable = useMediaHoverable();
const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
);
const subPanel = useMemo(
() => (
@ -47,7 +64,7 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
href="/archives/videos/"
title={langui.videos}
langui={langui}
displayOn={ReturnButtonType.Desktop}
displayOnlyOn={"3ColumnsLayout"}
className="mb-10"
/>
@ -57,6 +74,15 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
description={langui.archives_description}
/>
<HorizontalLine />
<TextInput
className="mb-6 w-full"
placeholder={langui.search_title ?? "Search title..."}
value={searchName}
onChange={setSearchName}
/>
{hoverable && (
<WithLabel label={langui.always_show_info}>
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
@ -64,51 +90,58 @@ const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
)}
</SubPanel>
),
[hoverable, keepInfoVisible, langui, toggleKeepInfoVisible]
[hoverable, keepInfoVisible, langui, searchName, toggleKeepInfoVisible]
);
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div className="mb-8">
<h1 className="text-3xl">{channel?.title}</h1>
<p>{channel?.subscribers.toLocaleString()} subscribers</p>
</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, [
<SmartList
items={filterHasAttributes(channel?.videos?.data, [
"id",
"attributes",
] as const).map((video) => (
<Fragment key={video.id}>
<PreviewCard
href={`/archives/videos/v/${video.attributes.uid}`}
title={video.attributes.title}
thumbnail={getVideoThumbnailURL(video.attributes.uid)}
thumbnailAspectRatio="16/9"
keepInfoVisible={keepInfoVisible}
metadata={{
releaseDate: video.attributes.published_date,
views: video.attributes.views,
author: channel?.title,
position: "Top",
}}
hoverlay={{
__typename: "Video",
duration: video.attributes.duration,
}}
/>
</Fragment>
))}
</div>
] as const)}
getItemId={(item) => item.id}
renderItem={({ item }) => (
<PreviewCard
href={`/archives/videos/v/${item.attributes.uid}`}
title={item.attributes.title}
thumbnail={getVideoThumbnailURL(item.attributes.uid)}
thumbnailAspectRatio="16/9"
thumbnailForceAspectRatio
keepInfoVisible={keepInfoVisible}
metadata={{
releaseDate: item.attributes.published_date,
views: item.attributes.views,
author: channel?.title,
position: "Top",
}}
hoverlay={{
__typename: "Video",
duration: item.attributes.duration,
}}
/>
)}
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>
),
[
channel?.subscribers,
channel?.title,
channel?.videos?.data,
isContentPanelAtLeast4xl,
keepInfoVisible,
langui,
searchName,
]
);
@ -137,6 +170,13 @@ export const getStaticProps: GetStaticProps = async (context) => {
: "",
});
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 props: Props = {
...appStaticProps,
@ -157,6 +197,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
const sdk = getReadySdk();
const channels = await sdk.getVideoChannelsSlugs();
const paths: GetStaticPathsResult["paths"] = [];
if (channels.videoChannels?.data)
filterHasAttributes(channels.videoChannels.data, [
"attributes",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,9 @@ import { SubPanel } from "components/Panels/SubPanel";
import { TranslatedProps } from "helpers/types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage";
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,
...otherProps
}: Props): JSX.Element => {
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const subPanel = useMemo(
() => (
<SubPanel>
@ -53,6 +58,8 @@ const ContentsFolder = ({
description={langui.contents_description}
/>
<HorizontalLine />
<Button
href="/contents/all"
text={"Switch to grid view"}
@ -126,8 +133,14 @@ const ContentsFolder = ({
fallback={{ title: prettySlug(item.attributes.slug) }}
/>
)}
className="grid-cols-2 items-stretch
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-4"
)
)}
renderWhenEmpty={() => <></>}
langui={langui}
groupingFunction={() => [langui.folders ?? "Folders"]}
@ -169,7 +182,11 @@ const ContentsFolder = ({
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={() => <></>}
langui={langui}
groupingFunction={() => [langui.contents ?? "Contents"]}
@ -182,11 +199,12 @@ const ContentsFolder = ({
</ContentPanel>
),
[
folder.contents,
folder.contents?.data,
folder.parent_folder?.data?.attributes,
folder.slug,
folder.subfolders,
folder.subfolders?.data,
folder.titles,
isContentPanelAtLeast4xl,
langui,
]
);
@ -284,15 +302,11 @@ interface PreviewFolderProps {
title: string | null | undefined;
}
export const PreviewFolder = ({
href,
title,
}: PreviewFolderProps): JSX.Element => (
const PreviewFolder = ({ href, title }: PreviewFolderProps): JSX.Element => (
<Link
href={href}
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]
mobile:px-6 mobile:py-6"
rounded-md bg-light p-6 transition-transform drop-shadow-shade-xl hover:scale-[1.02]"
>
{title && (
<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,
fallback,
...otherProps

View File

@ -210,7 +210,7 @@ const Editor = ({ langui, ...otherProps }: Props): JSX.Element => {
target.select();
event.preventDefault();
}}
className="h-[50vh] w-[50vw] mobile:w-[75vw]"
className="h-[50vh] w-[50vw]"
/>
</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="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>
<h2 className="-mt-5 text-xl">

View File

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

View File

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

View File

@ -22,7 +22,7 @@ import { Button } from "components/Inputs/Button";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { isUntangibleGroupItem } from "helpers/libraryItem";
import { PreviewCard } from "components/PreviewCard";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { ButtonGroup } from "components/Inputs/ButtonGroup";
import { filterHasAttributes, isDefined, isUndefined } from "helpers/others";
import { useAppLayout } from "contexts/AppLayoutContext";
@ -31,6 +31,9 @@ import { SmartList } from "components/SmartList";
import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { getOpenGraph } from "helpers/openGraph";
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,
...otherProps
}: Props): JSX.Element => {
const hoverable = useMediaHoverable();
const hoverable = useDeviceSupportsHover();
const { libraryItemUserStatus } = useAppLayout();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
@ -261,6 +265,8 @@ const Library = ({
description={langui.library_description}
/>
<HorizontalLine />
<TextInput
className="mb-6 w-full"
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}
sortingFunction={sortingFunction}
groupingFunction={groupingFunction}
@ -444,6 +456,7 @@ const Library = ({
currencies,
filteringFunction,
groupingFunction,
isContentPanelAtLeast4xl,
items,
keepInfoVisible,
langui,

View File

@ -17,12 +17,15 @@ import { Icon } from "components/Ico";
import { WithLabel } from "components/Inputs/WithLabel";
import { TextInput } from "components/Inputs/TextInput";
import { Button } from "components/Inputs/Button";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { filterHasAttributes } from "helpers/others";
import { SmartList } from "components/SmartList";
import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date";
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 hoverable = useMediaHoverable();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const hoverable = useDeviceSupportsHover();
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
);
@ -63,6 +67,8 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
description={langui.news_description}
/>
<HorizontalLine />
<TextInput
className="mb-6 w-full"
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}
searchingBy={(post) =>
`${prettySlug(post.attributes.slug)} ${post.attributes.translations
@ -140,7 +150,7 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
/>
</ContentPanel>
),
[keepInfoVisible, langui, posts, searchName]
[keepInfoVisible, langui, posts, searchName, isContentPanelAtLeast4xl]
);
return (

View File

@ -4,10 +4,7 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip";
import { HorizontalLine } from "components/HorizontalLine";
import { Img } from "components/Img";
import {
ReturnButton,
ReturnButtonType,
} from "components/PanelComponents/ReturnButton";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import {
ContentPanel,
ContentPanelWidthSizes,
@ -32,6 +29,8 @@ import {
staticSmartLanguage,
} from "helpers/locales";
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 is3ColumnsLayout = useIs3ColumnsLayout();
const subPanel = useMemo(
() => (
@ -68,7 +68,7 @@ const WikiPage = ({
href={`/wiki`}
title={langui.wiki}
langui={langui}
displayOn={ReturnButtonType.Desktop}
displayOnlyOn={"3ColumnsLayout"}
/>
</SubPanel>
),
@ -84,7 +84,7 @@ const WikiPage = ({
href={`/wiki`}
title={langui.wiki}
langui={langui}
displayOn={ReturnButtonType.Mobile}
displayOnlyOn={"1ColumnLayout"}
className="mb-10"
/>
@ -101,121 +101,131 @@ const WikiPage = ({
<LanguageSwitcher {...languageSwitcherProps} />
</div>
<HorizontalLine />
{selectedTranslation && (
<div className="text-justify">
<div
className="mb-8 overflow-hidden rounded-lg bg-mid text-center desktop:float-right
desktop:ml-8 desktop:w-[25rem]"
>
{page.thumbnail?.data?.attributes && (
<Img
src={page.thumbnail.data.attributes}
quality={ImageQuality.Medium}
className="w-full cursor-pointer"
onClick={() => {
if (page.thumbnail?.data?.attributes?.url) {
openLightBox([
getAssetURL(
page.thumbnail.data.attributes.url,
ImageQuality.Large
),
]);
}
}}
/>
<>
<HorizontalLine />
<div className="text-justify">
<div
className={cJoin(
"mb-8 overflow-hidden rounded-lg bg-mid text-center",
cIf(is3ColumnsLayout, "float-right ml-8 w-[25rem]")
)}
>
{page.thumbnail?.data?.attributes && (
<Img
src={page.thumbnail.data.attributes}
quality={ImageQuality.Medium}
className="w-full cursor-pointer"
onClick={() => {
if (page.thumbnail?.data?.attributes?.url) {
openLightBox([
getAssetURL(
page.thumbnail.data.attributes.url,
ImageQuality.Large
),
]);
}
}}
/>
)}
<div className="my-4 grid gap-4 p-4">
{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.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>
{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>
{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>
),
[
LanguageSwitcher,
LightBox,
is3ColumnsLayout,
languageSwitcherProps,
languages,
langui,
openLightBox,
page,
page.categories?.data,
page.definitions,
page.tags?.data,
page.thumbnail?.data?.attributes,
selectedTranslation,
]
);

View File

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

View File

@ -18,7 +18,7 @@ import { Button } from "components/Inputs/Button";
import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { filterDefined, filterHasAttributes } from "helpers/others";
import { SmartList } from "components/SmartList";
import { Select } from "components/Inputs/Select";
@ -26,6 +26,8 @@ import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { prettySlug } from "helpers/formatters";
import { getOpenGraph } from "helpers/openGraph";
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 hoverable = useMediaHoverable();
const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
@ -73,6 +76,8 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
description={langui.wiki_description}
/>
<HorizontalLine />
<TextInput
className="mb-6 w-full"
placeholder={langui.search_title ?? "Search..."}
@ -105,6 +110,7 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
}}
/>
<HorizontalLine />
<p className="mb-4 font-headers text-xl font-bold">
@ -193,7 +199,11 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
/>
)}
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}
searchingBy={(item) =>
filterDefined(item.attributes.translations)
@ -210,7 +220,14 @@ const Wiki = ({ langui, pages, ...otherProps }: Props): JSX.Element => {
/>
</ContentPanel>
),
[groupingFunction, keepInfoVisible, langui, pages, searchName]
[
groupingFunction,
keepInfoVisible,
langui,
pages,
searchName,
isContentPanelAtLeast4xl,
]
);
return (

View File

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

View File

@ -1,5 +1,5 @@
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(" ");
@ -23,9 +23,6 @@ module.exports = {
...fonts,
},
screens: {
desktop: breaks.desktop,
mobile: breaks.mobile,
thin: breaks.thin,
hoverable: { raw: "(hover: hover)" },
notHoverable: { raw: "(hover: none)" },
},