Added intersection + added container query
This commit is contained in:
parent
acd2d7d482
commit
3afaea7027
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}`}
|
||||
>
|
||||
|
|
|
@ -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} />
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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 />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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,35 +17,28 @@ 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}
|
||||
|
@ -52,6 +46,8 @@ export const ReturnButton = ({
|
|||
icon={Icon.NavigateBefore}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -23,11 +24,14 @@ export const ContentPanel = ({
|
|||
width = ContentPanelWidthSizes.Default,
|
||||
children,
|
||||
className,
|
||||
}: Props): JSX.Element => (
|
||||
}: 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 desktop:px-10 desktop:pt-20 desktop:pb-32",
|
||||
"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
|
||||
|
@ -39,4 +43,5 @@ export const ContentPanel = ({
|
|||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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,13 +34,14 @@ 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 */}
|
||||
{is3ColumnsLayout && (
|
||||
<div
|
||||
className={cJoin(
|
||||
"fixed top-1/2 mobile:hidden",
|
||||
"fixed top-1/2",
|
||||
cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
|
||||
)}
|
||||
onClick={toggleMainPanelReduced}
|
||||
|
@ -51,6 +51,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
|
|||
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’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) && (
|
||||
|
|
|
@ -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">
|
||||
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>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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")
|
||||
)}
|
||||
|
|
|
@ -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}
|
||||
{body && (
|
||||
<>
|
||||
{displayThumbnailHeader && <HorizontalLine />}
|
||||
<Markdawn text={body} langui={langui} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{appendBody}
|
||||
</ContentPanel>
|
||||
),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) => (
|
||||
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"
|
||||
>
|
||||
<Ico icon={Icon.ChevronLeft} className="!text-[300%] mobile:hidden" />
|
||||
{is3ColumnsLayout && (
|
||||
<Ico icon={Icon.ChevronLeft} className="!text-[300%]" />
|
||||
)}
|
||||
<p className="max-w-xs text-2xl">{langui.no_results_message}</p>
|
||||
<Ico icon={Icon.ChevronRight} className="!text-[300%] desktop:hidden" />
|
||||
{!is3ColumnsLayout && (
|
||||
<Ico icon={Icon.ChevronRight} className="!text-[300%]" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -23,9 +23,7 @@ query getVideoChannel($channel: String) {
|
|||
}
|
||||
}
|
||||
published_date {
|
||||
year
|
||||
month
|
||||
day
|
||||
...datePicker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const DEFAULT_OG_THUMBNAIL = {
|
|||
};
|
||||
|
||||
export const TITLE_PREFIX = "Accord’s 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 ?? "",
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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)");
|
||||
|
|
|
@ -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]);
|
||||
};
|
|
@ -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]);
|
||||
};
|
|
@ -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]);
|
||||
};
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
),
|
||||
|
|
|
@ -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}>
|
||||
] as const)}
|
||||
getItemId={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<PreviewCard
|
||||
href={`/archives/videos/v/${video.attributes.uid}`}
|
||||
title={video.attributes.title}
|
||||
thumbnail={getVideoThumbnailURL(video.attributes.uid)}
|
||||
href={`/archives/videos/v/${item.attributes.uid}`}
|
||||
title={item.attributes.title}
|
||||
thumbnail={getVideoThumbnailURL(item.attributes.uid)}
|
||||
thumbnailAspectRatio="16/9"
|
||||
thumbnailForceAspectRatio
|
||||
keepInfoVisible={keepInfoVisible}
|
||||
metadata={{
|
||||
releaseDate: video.attributes.published_date,
|
||||
views: video.attributes.views,
|
||||
releaseDate: item.attributes.published_date,
|
||||
views: item.attributes.views,
|
||||
author: channel?.title,
|
||||
position: "Top",
|
||||
}}
|
||||
hoverlay={{
|
||||
__typename: "Video",
|
||||
duration: video.attributes.duration,
|
||||
duration: item.attributes.duration,
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
langui={langui}
|
||||
className={cIf(
|
||||
isContentPanelAtLeast4xl,
|
||||
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
|
||||
"grid-cols-2 gap-x-3 gap-y-5"
|
||||
)}
|
||||
groupingFunction={() => [channel?.title ?? ""]}
|
||||
paginationItemPerPage={25}
|
||||
searchingTerm={searchName}
|
||||
searchingBy={(item) => item.attributes.title}
|
||||
/>
|
||||
</ContentPanel>
|
||||
),
|
||||
[
|
||||
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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 && (
|
||||
<>
|
||||
<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}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
||||
{selectedTranslation?.text_set?.text && (
|
||||
<>
|
||||
<HorizontalLine />
|
||||
|
||||
<Markdawn
|
||||
text={selectedTranslation?.text_set?.text ?? ""}
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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’s Library</h1>
|
||||
<h2 className="-mt-5 text-xl">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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,13 +101,15 @@ const WikiPage = ({
|
|||
<LanguageSwitcher {...languageSwitcherProps} />
|
||||
</div>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
{selectedTranslation && (
|
||||
<>
|
||||
<HorizontalLine />
|
||||
<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]"
|
||||
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
|
||||
|
@ -190,32 +192,40 @@ const WikiPage = ({
|
|||
? `/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) => ({
|
||||
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)}
|
||||
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,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -35,7 +35,7 @@ mark {
|
|||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
@apply w-3 mobile:w-0;
|
||||
@apply w-3;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
|
|
|
@ -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)" },
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue