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, | ||||
| }; | ||||
|  | ||||
							
								
								
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -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,42 +17,37 @@ interface Props { | ||||
|   href: string; | ||||
|   title: string | null | undefined; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   displayOn: ReturnButtonType; | ||||
|   displayOnlyOn?: "1ColumnLayout" | "3ColumnsLayout"; | ||||
|   className?: string; | ||||
| } | ||||
| 
 | ||||
| export enum ReturnButtonType { | ||||
|   Mobile = "mobile", | ||||
|   Desktop = "desktop", | ||||
|   Both = "both", | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const ReturnButton = ({ | ||||
|   href, | ||||
|   title, | ||||
|   langui, | ||||
|   displayOn, | ||||
|   displayOnlyOn, | ||||
|   className, | ||||
| }: Props): JSX.Element => { | ||||
|   const { setSubPanelOpen } = useAppLayout(); | ||||
|   const is3ColumnsLayout = useIs3ColumnsLayout(); | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={cJoin( | ||||
|         cIf(displayOn === ReturnButtonType.Mobile, "desktop:hidden"), | ||||
|         cIf(displayOn === ReturnButtonType.Desktop, "mobile:hidden"), | ||||
|         className | ||||
|     <> | ||||
|       {((is3ColumnsLayout && displayOnlyOn === "3ColumnsLayout") || | ||||
|         (!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") || | ||||
|         !isDefined(displayOnlyOn)) && ( | ||||
|         <div className={className}> | ||||
|           <Button | ||||
|             onClick={() => setSubPanelOpen(false)} | ||||
|             href={href} | ||||
|             text={`${langui.return_to} ${title}`} | ||||
|             icon={Icon.NavigateBefore} | ||||
|           /> | ||||
|         </div> | ||||
|       )} | ||||
|     > | ||||
|       <Button | ||||
|         onClick={() => setSubPanelOpen(false)} | ||||
|         href={href} | ||||
|         text={`${langui.return_to} ${title}`} | ||||
|         icon={Icon.NavigateBefore} | ||||
|       /> | ||||
|     </div> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { useIsContentPanelAtLeast } from "hooks/useContainerQuery"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -23,20 +24,24 @@ export const ContentPanel = ({ | ||||
|   width = ContentPanelWidthSizes.Default, | ||||
|   children, | ||||
|   className, | ||||
| }: Props): JSX.Element => ( | ||||
|   <div className="grid h-full"> | ||||
|     <main | ||||
|       className={cJoin( | ||||
|         "justify-self-center px-4 pt-10 pb-20 desktop:px-10 desktop:pt-20 desktop:pb-32", | ||||
|         width === ContentPanelWidthSizes.Default | ||||
|           ? "max-w-2xl" | ||||
|           : width === ContentPanelWidthSizes.Large | ||||
|           ? "max-w-4xl" | ||||
|           : "w-full", | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       {children} | ||||
|     </main> | ||||
|   </div> | ||||
| ); | ||||
| }: Props): JSX.Element => { | ||||
|   const isContentPanelAtLeast3xl = useIsContentPanelAtLeast("3xl"); | ||||
|   return ( | ||||
|     <div className="grid h-full"> | ||||
|       <main | ||||
|         className={cJoin( | ||||
|           "justify-self-center px-4 pt-10 pb-20", | ||||
|           cIf(isContentPanelAtLeast3xl, "px-10 pt-20 pb-32"), | ||||
|           width === ContentPanelWidthSizes.Default | ||||
|             ? "max-w-2xl" | ||||
|             : width === ContentPanelWidthSizes.Large | ||||
|             ? "max-w-4xl" | ||||
|             : "w-full", | ||||
|           className | ||||
|         )} | ||||
|       > | ||||
|         {children} | ||||
|       </main> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -5,12 +5,11 @@ import { NavOption } from "components/PanelComponents/NavOption"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| 
 | ||||
| import { useMediaDesktop } from "hooks/useMediaQuery"; | ||||
| import { Icon } from "components/Ico"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { isDefinedAndNotEmpty } from "helpers/others"; | ||||
| import { Link } from "components/Inputs/Link"; | ||||
| import { useIs3ColumnsLayout } from "hooks/useContainerQuery"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -24,7 +23,7 @@ interface Props { | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const MainPanel = ({ langui }: Props): JSX.Element => { | ||||
|   const isDesktop = useMediaDesktop(); | ||||
|   const is3ColumnsLayout = useIs3ColumnsLayout(); | ||||
|   const { | ||||
|     mainPanelReduced = false, | ||||
|     toggleMainPanelReduced, | ||||
| @ -35,22 +34,24 @@ export const MainPanel = ({ langui }: Props): JSX.Element => { | ||||
|     <div | ||||
|       className={cJoin( | ||||
|         "grid content-start justify-center gap-y-2 p-8 text-center", | ||||
|         cIf(mainPanelReduced && isDesktop, "px-4") | ||||
|         cIf(mainPanelReduced && is3ColumnsLayout, "px-4") | ||||
|       )} | ||||
|     > | ||||
|       {/* Reduce/expand main menu */} | ||||
|       <div | ||||
|         className={cJoin( | ||||
|           "fixed top-1/2 mobile:hidden", | ||||
|           cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]") | ||||
|         )} | ||||
|         onClick={toggleMainPanelReduced} | ||||
|       > | ||||
|         <Button | ||||
|           className="bg-light !px-2" | ||||
|           icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft} | ||||
|         /> | ||||
|       </div> | ||||
|       {is3ColumnsLayout && ( | ||||
|         <div | ||||
|           className={cJoin( | ||||
|             "fixed top-1/2", | ||||
|             cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]") | ||||
|           )} | ||||
|           onClick={toggleMainPanelReduced} | ||||
|         > | ||||
|           <Button | ||||
|             className="bg-light !px-2" | ||||
|             icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft} | ||||
|           /> | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       <div> | ||||
|         <div className="grid place-items-center"> | ||||
| @ -60,19 +61,23 @@ export const MainPanel = ({ langui }: Props): JSX.Element => { | ||||
|                 `mb-4 aspect-square cursor-pointer bg-black transition-colors
 | ||||
|                 [mask:url('/icons/accords.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] | ||||
|                 ![mask-position:center] hover:bg-dark`,
 | ||||
|                 cIf(mainPanelReduced && isDesktop, "w-12", "w-1/2") | ||||
|                 cIf(mainPanelReduced && is3ColumnsLayout, "w-12", "w-1/2") | ||||
|               )} | ||||
|             ></div> | ||||
|           </Link> | ||||
| 
 | ||||
|           {(!mainPanelReduced || !isDesktop) && ( | ||||
|           {(!mainPanelReduced || !is3ColumnsLayout) && ( | ||||
|             <h2 className="mb-4 text-3xl">Accord’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"> | ||||
|     {children} | ||||
|   </div> | ||||
| ); | ||||
| export const SubPanel = ({ children }: Props): JSX.Element => { | ||||
|   const isSubPanelAtLeastSm = useIsSubPanelAtLeast("2xs"); | ||||
|   return ( | ||||
|     <div | ||||
|       className={cJoin( | ||||
|         "grid gap-y-2 text-center", | ||||
|         cIf(isSubPanelAtLeastSm, "px-10 pt-10 pb-20", "p-4") | ||||
|       )} | ||||
|     > | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -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} | ||||
|         <Markdawn text={body} langui={langui} /> | ||||
|         {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) => ( | ||||
|   <div className="grid h-full place-content-center"> | ||||
|     <div | ||||
|       className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted | ||||
|       border-dark p-8 text-dark opacity-40" | ||||
|     > | ||||
|       <Ico icon={Icon.ChevronLeft} className="!text-[300%] mobile:hidden" /> | ||||
|       <p className="max-w-xs text-2xl">{langui.no_results_message}</p> | ||||
|       <Ico icon={Icon.ChevronRight} className="!text-[300%] desktop:hidden" /> | ||||
| const DefaultRenderWhenEmpty = ({ langui }: DefaultRenderWhenEmptyProps) => { | ||||
|   const is3ColumnsLayout = useIs3ColumnsLayout(); | ||||
|   return ( | ||||
|     <div className="grid h-full place-content-center"> | ||||
|       <div | ||||
|         className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted | ||||
|         border-dark p-8 text-dark opacity-40" | ||||
|       > | ||||
|         {is3ColumnsLayout && ( | ||||
|           <Ico icon={Icon.ChevronLeft} className="!text-[300%]" /> | ||||
|         )} | ||||
|         <p className="max-w-xs text-2xl">{langui.no_results_message}</p> | ||||
|         {!is3ColumnsLayout && ( | ||||
|           <Ico icon={Icon.ChevronRight} className="!text-[300%]" /> | ||||
|         )} | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| ); | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -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 ?? "", | ||||
|  | ||||
							
								
								
									
										112
									
								
								src/hooks/useContainerQuery.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/hooks/useContainerQuery.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| 
 | ||||
| type MediaQuery = { value: number; unit: "px" | "rem"; rule: "max" | "min" }; | ||||
| 
 | ||||
| type Size = | ||||
|   | "2xl" | ||||
|   | "2xs" | ||||
|   | "3xl" | ||||
|   | "4xl" | ||||
|   | "5xl" | ||||
|   | "6xl" | ||||
|   | "7xl" | ||||
|   | "lg" | ||||
|   | "md" | ||||
|   | "sm" | ||||
|   | "xl" | ||||
|   | "xs"; | ||||
| 
 | ||||
| const sizes: Record<Size, number> = { | ||||
|   "2xl": 42, | ||||
|   "3xl": 48, | ||||
|   "4xl": 56, | ||||
|   "5xl": 64, | ||||
|   "6xl": 72, | ||||
|   "7xl": 80, | ||||
|   lg: 32, | ||||
|   md: 28, | ||||
|   sm: 24, | ||||
|   xl: 36, | ||||
|   xs: 20, | ||||
|   "2xs": 16, | ||||
| }; | ||||
| 
 | ||||
| export const useIsScreenAtLeast = (size: Size): boolean => { | ||||
|   const { screenWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(screenWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "min", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // ts-unused-exports:disable-next-line
 | ||||
| export const useIsScreenNoMoreThan = (size: Size): boolean => { | ||||
|   const { screenWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(screenWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "max", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const useIsContentPanelAtLeast = (size: Size): boolean => { | ||||
|   const { contentPanelWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(contentPanelWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "min", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const useIsContentPanelNoMoreThan = (size: Size): boolean => { | ||||
|   const { contentPanelWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(contentPanelWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "max", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const useIsSubPanelAtLeast = (size: Size): boolean => { | ||||
|   const { subPanelWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(subPanelWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "min", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // ts-unused-exports:disable-next-line
 | ||||
| export const useIsSubPanelNoMoreThan = (size: Size): boolean => { | ||||
|   const { subPanelWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(subPanelWidth, { | ||||
|     value: sizes[size], | ||||
|     unit: "rem", | ||||
|     rule: "max", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const useIs3ColumnsLayout = (): boolean => { | ||||
|   const { screenWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(screenWidth, { | ||||
|     value: sizes["5xl"], | ||||
|     unit: "rem", | ||||
|     rule: "min", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const useIs1ColumnLayout = (): boolean => { | ||||
|   const { screenWidth } = useAppLayout(); | ||||
|   return useApplyContainerQuery(screenWidth, { | ||||
|     value: sizes["5xl"], | ||||
|     unit: "rem", | ||||
|     rule: "max", | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const useApplyContainerQuery = (width: number, query: MediaQuery) => { | ||||
|   const { fontSize } = useAppLayout(); | ||||
|   const breakpoint = query.value * (query.unit === "rem" ? 16 : 1) * fontSize; | ||||
|   return query.rule === "min" ? width >= breakpoint : width < breakpoint; | ||||
| }; | ||||
| @ -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)"); | ||||
|  | ||||
							
								
								
									
										29
									
								
								src/hooks/useOnResize.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/hooks/useOnResize.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| import { useMemo, useCallback, useEffect } from "react"; | ||||
| import { useIsClient } from "usehooks-ts"; | ||||
| import { isDefined } from "helpers/others"; | ||||
| 
 | ||||
| export const useOnResize = ( | ||||
|   id: string, | ||||
|   onResize: (width: number, height: number) => void | ||||
| ): void => { | ||||
|   const isClient = useIsClient(); | ||||
|   const elem = useMemo( | ||||
|     () => (isClient ? document.querySelector(`#${id}`) : null), | ||||
|     [id, isClient] | ||||
|   ); | ||||
| 
 | ||||
|   const listener = useCallback(() => { | ||||
|     console.log("useOnResize"); | ||||
|     if (elem?.clientWidth && elem.clientHeight) { | ||||
|       onResize(elem.clientWidth, elem.clientHeight); | ||||
|     } | ||||
|   }, [elem?.clientHeight, elem?.clientWidth, onResize]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const ro = new ResizeObserver(listener); | ||||
|     if (isDefined(elem)) { | ||||
|       ro.observe(elem); | ||||
|     } | ||||
|     return () => ro.disconnect(); | ||||
|   }, [elem, listener]); | ||||
| }; | ||||
							
								
								
									
										23
									
								
								src/hooks/useOnScroll.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/hooks/useOnScroll.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| import { useMemo, useCallback, useEffect } from "react"; | ||||
| import { useIsClient } from "usehooks-ts"; | ||||
| import { AnchorIds } from "./useScrollTopOnChange"; | ||||
| 
 | ||||
| export const useOnScroll = ( | ||||
|   id: AnchorIds, | ||||
|   onScroll: (scroll: number) => void | ||||
| ): void => { | ||||
|   const isClient = useIsClient(); | ||||
|   const elem = useMemo( | ||||
|     () => (isClient ? document.querySelector(`#${id}`) : null), | ||||
|     [id, isClient] | ||||
|   ); | ||||
|   const listener = useCallback(() => { | ||||
|     if (elem?.scrollTop) { | ||||
|       onScroll(elem.scrollTop); | ||||
|     } | ||||
|   }, [elem?.scrollTop, onScroll]); | ||||
|   useEffect(() => { | ||||
|     elem?.addEventListener("scroll", listener); | ||||
|     return () => elem?.removeEventListener("scroll", listener); | ||||
|   }, [elem, listener]); | ||||
| }; | ||||
| @ -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}> | ||||
|               <PreviewCard | ||||
|                 href={`/archives/videos/v/${video.attributes.uid}`} | ||||
|                 title={video.attributes.title} | ||||
|                 thumbnail={getVideoThumbnailURL(video.attributes.uid)} | ||||
|                 thumbnailAspectRatio="16/9" | ||||
|                 keepInfoVisible={keepInfoVisible} | ||||
|                 metadata={{ | ||||
|                   releaseDate: video.attributes.published_date, | ||||
|                   views: video.attributes.views, | ||||
|                   author: channel?.title, | ||||
|                   position: "Top", | ||||
|                 }} | ||||
|                 hoverlay={{ | ||||
|                   __typename: "Video", | ||||
|                   duration: video.attributes.duration, | ||||
|                 }} | ||||
|               /> | ||||
|             </Fragment> | ||||
|           ))} | ||||
|         </div> | ||||
|           ] as const)} | ||||
|           getItemId={(item) => item.id} | ||||
|           renderItem={({ item }) => ( | ||||
|             <PreviewCard | ||||
|               href={`/archives/videos/v/${item.attributes.uid}`} | ||||
|               title={item.attributes.title} | ||||
|               thumbnail={getVideoThumbnailURL(item.attributes.uid)} | ||||
|               thumbnailAspectRatio="16/9" | ||||
|               thumbnailForceAspectRatio | ||||
|               keepInfoVisible={keepInfoVisible} | ||||
|               metadata={{ | ||||
|                 releaseDate: item.attributes.published_date, | ||||
|                 views: item.attributes.views, | ||||
|                 author: channel?.title, | ||||
|                 position: "Top", | ||||
|               }} | ||||
|               hoverlay={{ | ||||
|                 __typename: "Video", | ||||
|                 duration: item.attributes.duration, | ||||
|               }} | ||||
|             /> | ||||
|           )} | ||||
|           langui={langui} | ||||
|           className={cIf( | ||||
|             isContentPanelAtLeast4xl, | ||||
|             "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8", | ||||
|             "grid-cols-2 gap-x-3 gap-y-5" | ||||
|           )} | ||||
|           groupingFunction={() => [channel?.title ?? ""]} | ||||
|           paginationItemPerPage={25} | ||||
|           searchingTerm={searchName} | ||||
|           searchingBy={(item) => item.attributes.title} | ||||
|         /> | ||||
|       </ContentPanel> | ||||
|     ), | ||||
|     [ | ||||
|       channel?.subscribers, | ||||
|       channel?.title, | ||||
|       channel?.videos?.data, | ||||
|       isContentPanelAtLeast4xl, | ||||
|       keepInfoVisible, | ||||
|       langui, | ||||
|       searchName, | ||||
|     ] | ||||
|   ); | ||||
| 
 | ||||
| @ -137,6 +170,13 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
|         : "", | ||||
|   }); | ||||
|   if (!channel.videoChannels?.data[0].attributes) return { notFound: true }; | ||||
| 
 | ||||
|   channel.videoChannels.data[0].attributes.videos?.data | ||||
|     .sort((a, b) => | ||||
|       compareDate(a.attributes?.published_date, b.attributes?.published_date) | ||||
|     ) | ||||
|     .reverse(); | ||||
| 
 | ||||
|   const appStaticProps = await getAppStaticProps(context); | ||||
|   const props: Props = { | ||||
|     ...appStaticProps, | ||||
| @ -157,6 +197,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => { | ||||
|   const sdk = getReadySdk(); | ||||
|   const channels = await sdk.getVideoChannelsSlugs(); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
| 
 | ||||
|   if (channels.videoChannels?.data) | ||||
|     filterHasAttributes(channels.videoChannels.data, [ | ||||
|       "attributes", | ||||
|  | ||||
| @ -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 && ( | ||||
|                   <Markdawn | ||||
|                     text={selectedContentTranslation.text_set.text} | ||||
|                     langui={langui} | ||||
|                   /> | ||||
|                   <> | ||||
|                     <HorizontalLine /> | ||||
|                     <Markdawn | ||||
|                       text={selectedContentTranslation.text_set.text} | ||||
|                       langui={langui} | ||||
|                     /> | ||||
|                   </> | ||||
|                 )} | ||||
|               </> | ||||
|             )} | ||||
| @ -168,7 +166,7 @@ const Chronicle = ({ | ||||
|     () => ( | ||||
|       <SubPanel> | ||||
|         <ReturnButton | ||||
|           displayOn={ReturnButtonType.Desktop} | ||||
|           displayOnlyOn={"3ColumnsLayout"} | ||||
|           href="/chronicles" | ||||
|           title={langui.chronicles} | ||||
|           langui={langui} | ||||
|  | ||||
| @ -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> | ||||
|           )} | ||||
| 
 | ||||
|           <HorizontalLine /> | ||||
| 
 | ||||
|           <Markdawn | ||||
|             text={selectedTranslation?.text_set?.text ?? ""} | ||||
|             langui={langui} | ||||
|           /> | ||||
|           {selectedTranslation?.text_set?.text && ( | ||||
|             <> | ||||
|               <HorizontalLine /> | ||||
|               <Markdawn | ||||
|                 text={selectedTranslation.text_set.text} | ||||
|                 langui={langui} | ||||
|               /> | ||||
|             </> | ||||
|           )} | ||||
| 
 | ||||
|           {nextContent?.attributes && ( | ||||
|             <> | ||||
| @ -448,9 +453,8 @@ const Content = ({ | ||||
|                 thumbnail={nextContent.attributes.thumbnail?.data?.attributes} | ||||
|                 thumbnailAspectRatio="3/2" | ||||
|                 topChips={ | ||||
|                   isMobile | ||||
|                     ? undefined | ||||
|                     : nextContent.attributes.type?.data?.attributes | ||||
|                   isContentPanelAtLeast2xl && | ||||
|                   nextContent.attributes.type?.data?.attributes | ||||
|                     ? [ | ||||
|                         nextContent.attributes.type.data.attributes.titles?.[0] | ||||
|                           ? nextContent.attributes.type.data.attributes | ||||
| @ -462,11 +466,11 @@ const Content = ({ | ||||
|                     : undefined | ||||
|                 } | ||||
|                 bottomChips={ | ||||
|                   isMobile | ||||
|                     ? undefined | ||||
|                     : nextContent.attributes.categories?.data.map( | ||||
|                   isContentPanelAtLeast2xl | ||||
|                     ? nextContent.attributes.categories?.data.map( | ||||
|                         (category) => category.attributes?.short ?? "" | ||||
|                       ) | ||||
|                     : undefined | ||||
|                 } | ||||
|               /> | ||||
|             </> | ||||
| @ -479,7 +483,7 @@ const Content = ({ | ||||
|       content.categories, | ||||
|       content.thumbnail?.data?.attributes, | ||||
|       content.type, | ||||
|       isMobile, | ||||
|       isContentPanelAtLeast2xl, | ||||
|       languageSwitcherProps, | ||||
|       langui, | ||||
|       nextContent?.attributes, | ||||
|  | ||||
| @ -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,121 +101,131 @@ const WikiPage = ({ | ||||
|           <LanguageSwitcher {...languageSwitcherProps} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <HorizontalLine /> | ||||
| 
 | ||||
|         {selectedTranslation && ( | ||||
|           <div className="text-justify"> | ||||
|             <div | ||||
|               className="mb-8 overflow-hidden rounded-lg bg-mid text-center desktop:float-right | ||||
|               desktop:ml-8 desktop:w-[25rem]" | ||||
|             > | ||||
|               {page.thumbnail?.data?.attributes && ( | ||||
|                 <Img | ||||
|                   src={page.thumbnail.data.attributes} | ||||
|                   quality={ImageQuality.Medium} | ||||
|                   className="w-full cursor-pointer" | ||||
|                   onClick={() => { | ||||
|                     if (page.thumbnail?.data?.attributes?.url) { | ||||
|                       openLightBox([ | ||||
|                         getAssetURL( | ||||
|                           page.thumbnail.data.attributes.url, | ||||
|                           ImageQuality.Large | ||||
|                         ), | ||||
|                       ]); | ||||
|                     } | ||||
|                   }} | ||||
|                 /> | ||||
|           <> | ||||
|             <HorizontalLine /> | ||||
|             <div className="text-justify"> | ||||
|               <div | ||||
|                 className={cJoin( | ||||
|                   "mb-8 overflow-hidden rounded-lg bg-mid text-center", | ||||
|                   cIf(is3ColumnsLayout, "float-right ml-8 w-[25rem]") | ||||
|                 )} | ||||
|               > | ||||
|                 {page.thumbnail?.data?.attributes && ( | ||||
|                   <Img | ||||
|                     src={page.thumbnail.data.attributes} | ||||
|                     quality={ImageQuality.Medium} | ||||
|                     className="w-full cursor-pointer" | ||||
|                     onClick={() => { | ||||
|                       if (page.thumbnail?.data?.attributes?.url) { | ||||
|                         openLightBox([ | ||||
|                           getAssetURL( | ||||
|                             page.thumbnail.data.attributes.url, | ||||
|                             ImageQuality.Large | ||||
|                           ), | ||||
|                         ]); | ||||
|                       } | ||||
|                     }} | ||||
|                   /> | ||||
|                 )} | ||||
|                 <div className="my-4 grid gap-4 p-4"> | ||||
|                   {page.categories?.data && page.categories.data.length > 0 && ( | ||||
|                     <> | ||||
|                       <p className="font-headers text-xl font-bold"> | ||||
|                         {langui.categories} | ||||
|                       </p> | ||||
| 
 | ||||
|                       <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                         {filterHasAttributes(page.categories.data, [ | ||||
|                           "attributes", | ||||
|                         ] as const).map((category) => ( | ||||
|                           <Chip | ||||
|                             key={category.id} | ||||
|                             text={category.attributes.name} | ||||
|                           /> | ||||
|                         ))} | ||||
|                       </div> | ||||
|                     </> | ||||
|                   )} | ||||
| 
 | ||||
|                   {page.tags?.data && page.tags.data.length > 0 && ( | ||||
|                     <> | ||||
|                       <p className="font-headers text-xl font-bold"> | ||||
|                         {langui.tags} | ||||
|                       </p> | ||||
|                       <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                         {filterHasAttributes(page.tags.data, [ | ||||
|                           "attributes", | ||||
|                         ] as const).map((tag) => ( | ||||
|                           <Chip | ||||
|                             key={tag.id} | ||||
|                             text={ | ||||
|                               tag.attributes.titles?.[0]?.title ?? | ||||
|                               prettySlug(tag.attributes.slug) | ||||
|                             } | ||||
|                           /> | ||||
|                         ))} | ||||
|                       </div> | ||||
|                     </> | ||||
|                   )} | ||||
|                 </div> | ||||
|               </div> | ||||
| 
 | ||||
|               {isDefinedAndNotEmpty(selectedTranslation.summary) && ( | ||||
|                 <div className="mb-12"> | ||||
|                   <p className="font-headers text-lg font-bold"> | ||||
|                     {langui.summary} | ||||
|                   </p> | ||||
|                   <p>{selectedTranslation.summary}</p> | ||||
|                 </div> | ||||
|               )} | ||||
|               <div className="my-4 grid gap-4 p-4"> | ||||
|                 {page.categories?.data && page.categories.data.length > 0 && ( | ||||
|                   <> | ||||
|                     <p className="font-headers text-xl font-bold"> | ||||
|                       {langui.categories} | ||||
|                     </p> | ||||
| 
 | ||||
|                     <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                       {filterHasAttributes(page.categories.data, [ | ||||
|                         "attributes", | ||||
|                       ] as const).map((category) => ( | ||||
|                         <Chip | ||||
|                           key={category.id} | ||||
|                           text={category.attributes.name} | ||||
|                         /> | ||||
|                       ))} | ||||
|                     </div> | ||||
|                   </> | ||||
|                 )} | ||||
| 
 | ||||
|                 {page.tags?.data && page.tags.data.length > 0 && ( | ||||
|                   <> | ||||
|                     <p className="font-headers text-xl font-bold"> | ||||
|                       {langui.tags} | ||||
|                     </p> | ||||
|                     <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                       {filterHasAttributes(page.tags.data, [ | ||||
|                         "attributes", | ||||
|                       ] as const).map((tag) => ( | ||||
|                         <Chip | ||||
|                           key={tag.id} | ||||
|                           text={ | ||||
|                             tag.attributes.titles?.[0]?.title ?? | ||||
|                             prettySlug(tag.attributes.slug) | ||||
|                           } | ||||
|                         /> | ||||
|                       ))} | ||||
|                     </div> | ||||
|                   </> | ||||
|                 )} | ||||
|               </div> | ||||
|               {filterHasAttributes(page.definitions, [ | ||||
|                 "translations", | ||||
|               ] as const).map((definition, index) => ( | ||||
|                 <div key={index} className="mb-12"> | ||||
|                   <DefinitionCard | ||||
|                     source={{ | ||||
|                       name: definition.source?.data?.attributes?.name, | ||||
|                       url: definition.source?.data?.attributes?.content?.data | ||||
|                         ?.attributes?.slug | ||||
|                         ? `/contents/${definition.source.data.attributes.content.data.attributes.slug}` | ||||
|                         : `/library/${definition.source?.data?.attributes?.ranged_content?.data?.attributes?.library_item?.data?.attributes?.slug}`, | ||||
|                     }} | ||||
|                     translations={definition.translations.map( | ||||
|                       (translation) => ({ | ||||
|                         language: translation?.language?.data?.attributes?.code, | ||||
|                         definition: translation?.definition, | ||||
|                         status: translation?.status, | ||||
|                       }) | ||||
|                     )} | ||||
|                     index={index + 1} | ||||
|                     languages={languages} | ||||
|                     langui={langui} | ||||
|                     categories={filterHasAttributes( | ||||
|                       definition.categories?.data, | ||||
|                       ["attributes"] as const | ||||
|                     ).map((category) => category.attributes.short)} | ||||
|                   /> | ||||
|                 </div> | ||||
|               ))} | ||||
|             </div> | ||||
| 
 | ||||
|             {isDefinedAndNotEmpty(selectedTranslation.summary) && ( | ||||
|               <div className="mb-12"> | ||||
|                 <p className="font-headers text-lg font-bold"> | ||||
|                   {langui.summary} | ||||
|                 </p> | ||||
|                 <p>{selectedTranslation.summary}</p> | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|             {filterHasAttributes(page.definitions, [ | ||||
|               "translations", | ||||
|             ] as const).map((definition, index) => ( | ||||
|               <div key={index} className="mb-12"> | ||||
|                 <DefinitionCard | ||||
|                   source={{ | ||||
|                     name: definition.source?.data?.attributes?.name, | ||||
|                     url: definition.source?.data?.attributes?.content?.data | ||||
|                       ?.attributes?.slug | ||||
|                       ? `/contents/${definition.source.data.attributes.content.data.attributes.slug}` | ||||
|                       : `/library/${definition.source?.data?.attributes?.ranged_content?.data?.attributes?.library_item?.data?.attributes?.slug}`, | ||||
|                   }} | ||||
|                   translations={definition.translations.map((translation) => ({ | ||||
|                     language: translation?.language?.data?.attributes?.code, | ||||
|                     definition: translation?.definition, | ||||
|                     status: translation?.status, | ||||
|                   }))} | ||||
|                   index={index + 1} | ||||
|                   languages={languages} | ||||
|                   langui={langui} | ||||
|                   categories={filterHasAttributes(definition.categories?.data, [ | ||||
|                     "attributes", | ||||
|                   ] as const).map((category) => category.attributes.short)} | ||||
|                 /> | ||||
|               </div> | ||||
|             ))} | ||||
|           </div> | ||||
|           </> | ||||
|         )} | ||||
|       </ContentPanel> | ||||
|     ), | ||||
|     [ | ||||
|       LanguageSwitcher, | ||||
|       LightBox, | ||||
|       is3ColumnsLayout, | ||||
|       languageSwitcherProps, | ||||
|       languages, | ||||
|       langui, | ||||
|       openLightBox, | ||||
|       page, | ||||
|       page.categories?.data, | ||||
|       page.definitions, | ||||
|       page.tags?.data, | ||||
|       page.thumbnail?.data?.attributes, | ||||
|       selectedTranslation, | ||||
|     ] | ||||
|   ); | ||||
|  | ||||
| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint