Use of cJoin and cIf
This commit is contained in:
		
							parent
							
								
									1510366bc8
								
							
						
					
					
						commit
						c076ec06ad
					
				| @ -2,6 +2,7 @@ import { Button } from "components/Inputs/Button"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { UploadImageFragment } from "graphql/generated"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { prettyLanguage, prettySlug } from "helpers/formatters"; | ||||
| import { getOgImage, ImageQuality, OgImage } from "helpers/img"; | ||||
| // import { getClient, Indexes, search, SearchResult } from "helpers/search";
 | ||||
| @ -181,19 +182,23 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`${ | ||||
|         appLayout.darkMode ? "set-theme-dark" : "set-theme-light" | ||||
|       } ${ | ||||
|         appLayout.dyslexic | ||||
|           ? "set-theme-font-dyslexic" | ||||
|           : "set-theme-font-standard" | ||||
|       }`}
 | ||||
|       className={cJoin( | ||||
|         cIf(appLayout.darkMode, "set-theme-dark", "set-theme-light"), | ||||
|         cIf( | ||||
|           appLayout.dyslexic, | ||||
|           "set-theme-font-dyslexic", | ||||
|           "set-theme-font-standard" | ||||
|         ) | ||||
|       )} | ||||
|     > | ||||
|       <div | ||||
|         {...handlers} | ||||
|         className={`fixed inset-0 m-0 grid touch-pan-y bg-light p-0 text-black
 | ||||
|         [grid-template-areas:'main_sub_content'] ${gridCol} mobile:grid-cols-[1fr] | ||||
|         mobile:grid-rows-[1fr_5rem] mobile:[grid-template-areas:'content''navbar']`}
 | ||||
|         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 | ||||
|         )} | ||||
|       > | ||||
|         <Head> | ||||
|           <title>{metaTitle}</title> | ||||
| @ -222,21 +227,25 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|         {/* Background when navbar is opened */} | ||||
|         <div | ||||
|           className={`absolute inset-0 transition-[backdrop-filter]
 | ||||
|           duration-500 [grid-area:content] mobile:z-10 ${ | ||||
|             (appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile | ||||
|               ? "[backdrop-filter:blur(2px)]" | ||||
|               : "pointer-events-none touch-none " | ||||
|           }`}
 | ||||
|           className={cJoin( | ||||
|             `absolute inset-0 transition-[backdrop-filter] duration-500 [grid-area:content]
 | ||||
|             mobile:z-10`,
 | ||||
|             cIf( | ||||
|               (appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile, | ||||
|               "[backdrop-filter:blur(2px)]", | ||||
|               "pointer-events-none touch-none" | ||||
|             ) | ||||
|           )} | ||||
|         > | ||||
|           <div | ||||
|             className={`absolute inset-0 bg-shade transition-opacity duration-500 
 | ||||
|         ${turnSubIntoContent ? "" : ""} | ||||
|         ${ | ||||
|           (appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile | ||||
|             ? "opacity-60" | ||||
|             : "opacity-0" | ||||
|         }`}
 | ||||
|             className={cJoin( | ||||
|               "absolute inset-0 bg-shade transition-opacity duration-500", | ||||
|               cIf( | ||||
|                 (appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile, | ||||
|                 "opacity-60", | ||||
|                 "opacity-0" | ||||
|               ) | ||||
|             )} | ||||
|             onClick={() => { | ||||
|               appLayout.setMainPanelOpen(false); | ||||
|               appLayout.setSubPanelOpen(false); | ||||
| @ -255,7 +264,7 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
|             <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" | ||||
|                 border-2 border-dotted border-dark p-8 text-dark opacity-40" | ||||
|               > | ||||
|                 <p className="text-4xl">❮</p> | ||||
|                 <p className="w-64 text-2xl">{langui.select_option_sidebar}</p> | ||||
| @ -267,16 +276,18 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
|         {/* Sub panel */} | ||||
|         {subPanel && ( | ||||
|           <div | ||||
|             className={`texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
 | ||||
|             border-black 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" | ||||
|               : !appLayout.subPanelOpen && "mobile:translate-x-[100vw]" | ||||
|           }`}
 | ||||
|             className={cJoin( | ||||
|               `texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
 | ||||
|               border-black 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" | ||||
|                 : appLayout.subPanelOpen | ||||
|                 ? "" | ||||
|                 : "mobile:translate-x-[100vw]" | ||||
|             )} | ||||
|           > | ||||
|             {subPanel} | ||||
|           </div> | ||||
| @ -284,12 +295,13 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|         {/* Main panel */} | ||||
|         <div | ||||
|           className={`texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
 | ||||
|           border-black 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] ${ | ||||
|             appLayout.mainPanelOpen ? "" : "mobile:-translate-x-full" | ||||
|           }`}
 | ||||
|           className={cJoin( | ||||
|             `texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
 | ||||
|             border-black 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(!appLayout.mainPanelOpen, "mobile:-translate-x-full") | ||||
|           )} | ||||
|         > | ||||
|           <MainPanel langui={langui} /> | ||||
|         </div> | ||||
| @ -301,25 +313,28 @@ export function AppLayout(props: Immutable<Props>): JSX.Element { | ||||
|         > | ||||
|           <Ico | ||||
|             icon={appLayout.mainPanelOpen ? Icon.Close : Icon.Menu} | ||||
|             className="mt-[.1em] cursor-pointer" | ||||
|             className="mt-[.1em] cursor-pointer !text-2xl" | ||||
|             onClick={() => { | ||||
|               appLayout.setMainPanelOpen(!appLayout.mainPanelOpen); | ||||
|               appLayout.setSubPanelOpen(false); | ||||
|             }} | ||||
|           /> | ||||
|           <p | ||||
|             className={`overflow-hidden text-center font-headers font-black ${ | ||||
|               ogTitle && ogTitle.length > 30 | ||||
|                 ? "max-h-14 text-xl" | ||||
|                 : "max-h-16 text-2xl" | ||||
|             }`}
 | ||||
|             className={cJoin( | ||||
|               "overflow-hidden text-center font-headers font-black", | ||||
|               cIf( | ||||
|                 ogTitle && ogTitle.length > 30, | ||||
|                 "max-h-14 text-xl", | ||||
|                 "max-h-16 text-2xl" | ||||
|               ) | ||||
|             )} | ||||
|           > | ||||
|             {ogTitle} | ||||
|           </p> | ||||
|           {subPanel && !turnSubIntoContent && ( | ||||
|             <Ico | ||||
|               icon={appLayout.subPanelOpen ? Icon.Close : subPanelIcon} | ||||
|               className="mt-[.1em] cursor-pointer" | ||||
|               className="mt-[.1em] cursor-pointer !text-2xl" | ||||
|               onClick={() => { | ||||
|                 appLayout.setSubPanelOpen(!appLayout.subPanelOpen); | ||||
|                 appLayout.setMainPanelOpen(false); | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -8,9 +9,12 @@ interface Props { | ||||
| export function Chip(props: Immutable<Props>): JSX.Element { | ||||
|   return ( | ||||
|     <div | ||||
|       className={`grid place-content-center place-items-center whitespace-nowrap rounded-full
 | ||||
|       border-[1px] px-1.5 pb-[0.14rem] text-xs opacity-70 | ||||
|       transition-[color,_opacity,_border-color] hover:opacity-100 ${props.className}`}
 | ||||
|       className={cJoin( | ||||
|         `grid place-content-center place-items-center whitespace-nowrap rounded-full
 | ||||
|         border-[1px] px-1.5 pb-[0.14rem] text-xs opacity-70 | ||||
|         transition-[color,_opacity,_border-color] hover:opacity-100`,
 | ||||
|         props.className | ||||
|       )} | ||||
|     > | ||||
|       {props.children} | ||||
|     </div> | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -5,9 +6,13 @@ interface Props { | ||||
| } | ||||
| 
 | ||||
| export function HorizontalLine(props: Immutable<Props>): JSX.Element { | ||||
|   const { className } = props; | ||||
|   return ( | ||||
|     <div | ||||
|       className={`my-8 h-0 w-full border-t-[3px] border-dotted border-black ${props.className}`} | ||||
|       className={cJoin( | ||||
|         "my-8 h-0 w-full border-t-[3px] border-dotted border-black", | ||||
|         className | ||||
|       )} | ||||
|     ></div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { MouseEventHandler } from "react"; | ||||
| 
 | ||||
| @ -10,7 +11,13 @@ interface Props { | ||||
| export function Ico(props: Immutable<Props>): JSX.Element { | ||||
|   const { onClick, icon, className } = props; | ||||
|   return ( | ||||
|     <span onClick={onClick} className={`material-icons ${className}`}> | ||||
|     <span | ||||
|       onClick={onClick} | ||||
|       className={cJoin( | ||||
|         "material-icons [font-size:inherit] [line-height:inherit]", | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       {icon} | ||||
|     </span> | ||||
|   ); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Ico, Icon } from "components/Ico"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { MouseEventHandler } from "react"; | ||||
| @ -38,14 +39,18 @@ export function Button(props: Immutable<Props>): JSX.Element { | ||||
|       draggable={draggable} | ||||
|       id={id} | ||||
|       onClick={onClick} | ||||
|       className={`component-button group grid select-none grid-flow-col place-content-center
 | ||||
|       place-items-center gap-2 rounded-full border-[1px] border-dark px-4 pt-[0.4rem] pb-[0.5rem] | ||||
|       text-dark transition-all ${ | ||||
|         active | ||||
|           ? "!border-black bg-black text-light drop-shadow-black-lg" | ||||
|           : `cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg
 | ||||
|       className={cJoin( | ||||
|         `component-button group grid select-none grid-flow-col place-content-center
 | ||||
|         place-items-center gap-2 rounded-full border-[1px] border-dark py-3 px-4 leading-none | ||||
|         text-dark transition-all`,
 | ||||
|         cIf( | ||||
|           active, | ||||
|           "!border-black bg-black text-light drop-shadow-black-lg", | ||||
|           `cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg
 | ||||
|           active:border-black active:bg-black active:text-light active:drop-shadow-black-lg` | ||||
|       } ${className}`}
 | ||||
|         ), | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       {badgeNumber && ( | ||||
|         <div | ||||
| @ -55,7 +60,9 @@ export function Button(props: Immutable<Props>): JSX.Element { | ||||
|           <p className="-translate-y-[0.05em]">{badgeNumber}</p> | ||||
|         </div> | ||||
|       )} | ||||
|       {icon && <Ico className="translate-y-[0.04em] !text-base" icon={icon} />} | ||||
|       {icon && ( | ||||
|         <Ico className="[font-size:150%] [line-height:0.66]" icon={icon} /> | ||||
|       )} | ||||
|       {text && <p className="-translate-y-[0.05em] text-center">{text}</p>} | ||||
|     </div> | ||||
|   ); | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { useEffect, useRef } from "react"; | ||||
| 
 | ||||
| @ -31,7 +32,7 @@ export function ButtonGroup(props: Immutable<Props>): JSX.Element { | ||||
|   }, [children]); | ||||
| 
 | ||||
|   return ( | ||||
|     <div ref={ref} className={`grid grid-flow-col ${className}`}> | ||||
|     <div ref={ref} className={cJoin("grid grid-flow-col", className)}> | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { Icon } from "components/Ico"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { prettyLanguage } from "helpers/formatters"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, Fragment, SetStateAction } from "react"; | ||||
| @ -20,7 +21,7 @@ export function LanguageSwitcher(props: Immutable<Props>): JSX.Element { | ||||
|   return ( | ||||
|     <ToolTip | ||||
|       content={ | ||||
|         <div className={`flex flex-col gap-2 ${className}`}> | ||||
|         <div className={cJoin("flex flex-col gap-2", className)}> | ||||
|           {[...locales].map(([locale, value], index) => ( | ||||
|             <Fragment key={index}> | ||||
|               {locale && ( | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Icon } from "components/Ico"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, SetStateAction } from "react"; | ||||
| import { Button } from "./Button"; | ||||
| @ -11,10 +12,10 @@ interface Props { | ||||
| } | ||||
| 
 | ||||
| export function PageSelector(props: Immutable<Props>): JSX.Element { | ||||
|   const { page, setPage, maxPage } = props; | ||||
|   const { page, setPage, maxPage, className } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={`flex flex-row place-content-center ${props.className}`}> | ||||
|     <div className={cJoin("flex flex-row place-content-center", className)}> | ||||
|       <Button | ||||
|         onClick={() => { | ||||
|           if (page > 0) setPage(page - 1); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Ico, Icon } from "components/Ico"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, Fragment, SetStateAction, useState } from "react"; | ||||
| 
 | ||||
| @ -17,16 +18,19 @@ export function Select(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`relative text-center transition-[filter] ${ | ||||
|         opened && "z-10 drop-shadow-shade-lg" | ||||
|       } ${className}`}
 | ||||
|       className={cJoin( | ||||
|         "relative text-center transition-[filter]", | ||||
|         cIf(opened, "z-10 drop-shadow-shade-lg"), | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       <div | ||||
|         className={`grid cursor-pointer grid-flow-col grid-cols-[1fr_auto_auto] place-items-center
 | ||||
|         rounded-[1em] bg-light p-1 outline outline-2 outline-offset-[-2px] outline-mid | ||||
|         transition-all hover:bg-mid hover:outline-[transparent] ${ | ||||
|           opened && "rounded-b-none bg-highlight outline-[transparent]" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           `grid cursor-pointer grid-flow-col grid-cols-[1fr_auto_auto] place-items-center
 | ||||
|           rounded-[1em] bg-light p-1 outline outline-2 outline-offset-[-2px] outline-mid | ||||
|           transition-all hover:bg-mid hover:outline-[transparent]`,
 | ||||
|           cIf(opened, "rounded-b-none bg-highlight outline-[transparent]") | ||||
|         )} | ||||
|       > | ||||
|         <p onClick={() => setOpened(!opened)} className="w-full"> | ||||
|           {state === -1 ? "—" : options[state]} | ||||
| @ -44,18 +48,19 @@ export function Select(props: Immutable<Props>): JSX.Element { | ||||
|         /> | ||||
|       </div> | ||||
|       <div | ||||
|         className={`left-0 right-0 rounded-b-[1em] ${ | ||||
|           opened ? "absolute" : "hidden" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           "left-0 right-0 rounded-b-[1em]", | ||||
|           cIf(opened, "absolute", "hidden") | ||||
|         )} | ||||
|       > | ||||
|         {options.map((option, index) => ( | ||||
|           <Fragment key={index}> | ||||
|             {index !== state && ( | ||||
|               <div | ||||
|                 className={` ${ | ||||
|                   opened ? "bg-highlight" : "bg-light" | ||||
|                 } cursor-pointer p-1 | ||||
|                 transition-colors last-of-type:rounded-b-[1em] hover:bg-mid`}
 | ||||
|                 className={cJoin( | ||||
|                   "cursor-pointer p-1 transition-colors last-of-type:rounded-b-[1em] hover:bg-mid", | ||||
|                   cIf(opened, "bg-highlight", "bg-light") | ||||
|                 )} | ||||
|                 id={option} | ||||
|                 onClick={() => { | ||||
|                   setOpened(false); | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, SetStateAction } from "react"; | ||||
| 
 | ||||
| @ -12,23 +13,29 @@ export function Switch(props: Immutable<Props>): JSX.Element { | ||||
|   const { state, setState, className, disabled } = props; | ||||
|   return ( | ||||
|     <div | ||||
|       className={`relative grid h-6 w-12 rounded-full border-2
 | ||||
|       border-mid transition-colors ${ | ||||
|         disabled ? "cursor-not-allowed" : "cursor-pointer" | ||||
|       } ${className} ${ | ||||
|         state ? "border-none bg-mid shadow-inner-sm shadow-shade" : "bg-light" | ||||
|       }`}
 | ||||
|       className={cJoin( | ||||
|         "relative grid h-6 w-12 rounded-full border-2 border-mid transition-colors", | ||||
|         cIf(disabled, "cursor-not-allowed", "cursor-pointer"), | ||||
|         cIf( | ||||
|           state, | ||||
|           "border-none bg-mid shadow-inner-sm shadow-shade", | ||||
|           "bg-light" | ||||
|         ), | ||||
|         className | ||||
|       )} | ||||
|       onClick={() => { | ||||
|         if (!disabled) setState(!state); | ||||
|       }} | ||||
|     > | ||||
|       <div | ||||
|         className={`absolute ${ | ||||
|           state ? "top-[2px] bottom-[2px] left-[2px]" : "top-0 bottom-0 left-0" | ||||
|         } | ||||
|         aspect-square rounded-full bg-dark transition-transform ${ | ||||
|           state && "translate-x-[120%]" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           "absolute aspect-square rounded-full bg-dark transition-transform", | ||||
|           cIf( | ||||
|             state, | ||||
|             "top-[2px] bottom-[2px] left-[2px] translate-x-[120%]", | ||||
|             "top-0 bottom-0 left-0" | ||||
|           ) | ||||
|         )} | ||||
|       ></div> | ||||
|     </div> | ||||
|   ); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { Ico, Icon } from "components/Ico"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, SetStateAction } from "react"; | ||||
| 
 | ||||
| @ -16,7 +17,7 @@ export function TextInput(props: Immutable<Props>): JSX.Element { | ||||
|   const { state, setState, className, name, placeholder } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={`relative ${className}`}> | ||||
|     <div className={cJoin("relative", className)}> | ||||
|       <input | ||||
|         className="w-full" | ||||
|         type="text" | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -10,12 +11,18 @@ export function WithLabel(props: Immutable<Props>): JSX.Element { | ||||
|   const { label, input, disabled } = props; | ||||
|   return ( | ||||
|     <div | ||||
|       className={`flex flex-row place-content-between place-items-center gap-2 ${ | ||||
|         disabled ? "text-dark brightness-150 contrast-75 grayscale" : "" | ||||
|       }`}
 | ||||
|       className={cJoin( | ||||
|         "flex flex-row place-content-between place-items-center gap-2", | ||||
|         cIf(disabled, "text-dark brightness-150 contrast-75 grayscale") | ||||
|       )} | ||||
|     > | ||||
|       {label && ( | ||||
|         <p className={`text-left ${label.length < 10 ? "flex-shrink-0" : ""}`}> | ||||
|         <p | ||||
|           className={cJoin( | ||||
|             "text-left", | ||||
|             cIf(label.length < 10, "flex-shrink-0") | ||||
|           )} | ||||
|         > | ||||
|           {label}: | ||||
|         </p> | ||||
|       )} | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -10,7 +11,10 @@ export function InsetBox(props: Immutable<Props>): JSX.Element { | ||||
|   return ( | ||||
|     <div | ||||
|       id={props.id} | ||||
|       className={`w-full rounded-xl bg-mid p-8 shadow-inner-sm shadow-shade ${props.className}`} | ||||
|       className={cJoin( | ||||
|         "w-full rounded-xl bg-mid p-8 shadow-inner-sm shadow-shade", | ||||
|         props.className | ||||
|       )} | ||||
|     > | ||||
|       {props.children} | ||||
|     </div> | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { Img } from "components/Img"; | ||||
| import { InsetBox } from "components/InsetBox"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { slugify } from "helpers/formatters"; | ||||
| import { getAssetURL, ImageQuality } from "helpers/img"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| @ -32,7 +33,7 @@ export function Markdawn(props: Immutable<Props>): JSX.Element { | ||||
|       <> | ||||
|         <LightBox /> | ||||
|         <Markdown | ||||
|           className={`formatted ${props.className}`} | ||||
|           className={cJoin("formatted", props.className)} | ||||
|           options={{ | ||||
|             slugify: slugify, | ||||
|             overrides: { | ||||
|  | ||||
| @ -19,7 +19,7 @@ export function TOC(props: Immutable<Props>): JSX.Element { | ||||
|       <h3 className="text-xl">Table of content</h3> | ||||
|       <div className="max-w-[14.5rem] text-left"> | ||||
|         <p className="relative my-2 overflow-x-hidden text-ellipsis whitespace-nowrap text-left"> | ||||
|           <a className="" onClick={async () => router.replace(`#${toc.slug}`)}> | ||||
|           <a onClick={async () => router.replace(`#${toc.slug}`)}> | ||||
|             {<abbr title={toc.title}>{toc.title}</abbr>} | ||||
|           </a> | ||||
|         </p> | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { Ico, Icon } from "components/Ico"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| import { cJoin, cIf } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { MouseEventHandler } from "react"; | ||||
| @ -17,16 +18,6 @@ interface Props { | ||||
| export function NavOption(props: Immutable<Props>): JSX.Element { | ||||
|   const router = useRouter(); | ||||
|   const isActive = router.asPath.startsWith(props.url); | ||||
|   const divActive = "bg-mid shadow-inner-sm shadow-shade"; | ||||
| 
 | ||||
|   const border = | ||||
|     "outline outline-mid outline-2 outline-offset-[-2px] hover:outline-[transparent]"; | ||||
| 
 | ||||
|   const divCommon = `gap-x-5 w-full rounded-2xl cursor-pointer p-4 hover:bg-mid
 | ||||
|   hover:shadow-inner-sm hover:shadow-shade hover:active:shadow-inner | ||||
|   hover:active:shadow-shade transition-all ${props.border ? border : ""} ${ | ||||
|     isActive ? divActive : "" | ||||
|   }`;
 | ||||
| 
 | ||||
|   return ( | ||||
|     <ToolTip | ||||
| @ -51,11 +42,21 @@ export function NavOption(props: Immutable<Props>): JSX.Element { | ||||
|             } | ||||
|           } | ||||
|         }} | ||||
|         className={`relative grid auto-cols-fr grid-flow-col grid-cols-[auto] justify-center ${ | ||||
|           props.icon ? "text-left" : "text-center" | ||||
|         } ${divCommon}`}
 | ||||
|         className={cJoin( | ||||
|           `relative grid w-full cursor-pointer auto-cols-fr grid-flow-col grid-cols-[auto]
 | ||||
|           justify-center gap-x-5 rounded-2xl p-4 transition-all hover:bg-mid hover:shadow-inner-sm | ||||
|           hover:shadow-shade hover:active:shadow-inner hover:active:shadow-shade`,
 | ||||
|           cIf(props.icon, "text-left", "text-center"), | ||||
|           cIf( | ||||
|             props.border, | ||||
|             "outline outline-2 outline-offset-[-2px] outline-mid hover:outline-[transparent]" | ||||
|           ), | ||||
|           cIf(isActive, "bg-mid shadow-inner-sm shadow-shade") | ||||
|         )} | ||||
|       > | ||||
|         {props.icon && <Ico icon={props.icon} className="mt-[.1em]" />} | ||||
|         {props.icon && ( | ||||
|           <Ico icon={props.icon} className="mt-[-.1em] !text-2xl" /> | ||||
|         )} | ||||
| 
 | ||||
|         {!props.reduced && ( | ||||
|           <div> | ||||
|  | ||||
| @ -3,6 +3,7 @@ import { Icon } from "components/Ico"; | ||||
| import { Button } from "components/Inputs/Button"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -25,13 +26,14 @@ export function ReturnButton(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`${ | ||||
|       className={cJoin( | ||||
|         props.displayOn === ReturnButtonType.Mobile | ||||
|           ? "desktop:hidden" | ||||
|           : props.displayOn === ReturnButtonType.Desktop | ||||
|           ? "mobile:hidden" | ||||
|           : "" | ||||
|       } ${props.className}`}
 | ||||
|           : "", | ||||
|         props.className | ||||
|       )} | ||||
|     > | ||||
|       <Button | ||||
|         onClick={() => appLayout.setSubPanelOpen(false)} | ||||
|  | ||||
| @ -1,29 +1,33 @@ | ||||
| import { cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| 
 | ||||
| interface Props { | ||||
|   children: React.ReactNode; | ||||
|   autoformat?: boolean; | ||||
|   width?: ContentPanelWidthSizes; | ||||
| } | ||||
| 
 | ||||
| export enum ContentPanelWidthSizes { | ||||
|   Default = "default", | ||||
|   Large = "large", | ||||
|   Full = "full", | ||||
| } | ||||
| 
 | ||||
| export function ContentPanel(props: Immutable<Props>): JSX.Element { | ||||
|   const width = props.width ? props.width : ContentPanelWidthSizes.Default; | ||||
|   const widthCSS = | ||||
|     width === ContentPanelWidthSizes.Default ? "max-w-2xl" : "w-full"; | ||||
|   const { width = ContentPanelWidthSizes.Default, children } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={`grid px-4 pt-10 pb-20 desktop:py-20 desktop:px-10`}> | ||||
|       <main | ||||
|         className={`${ | ||||
|           props.autoformat && "formatted" | ||||
|         } ${widthCSS} place-self-center`}
 | ||||
|         className={cJoin( | ||||
|           "place-self-center", | ||||
|           width === ContentPanelWidthSizes.Default | ||||
|             ? "max-w-2xl" | ||||
|             : width === ContentPanelWidthSizes.Large | ||||
|             ? "max-w-4xl" | ||||
|             : "w-full" | ||||
|         )} | ||||
|       > | ||||
|         {props.children} | ||||
|         {children} | ||||
|       </main> | ||||
|     </div> | ||||
|   ); | ||||
|  | ||||
| @ -9,6 +9,7 @@ import { useMediaDesktop } from "hooks/useMediaQuery"; | ||||
| import Markdown from "markdown-to-jsx"; | ||||
| import Link from "next/link"; | ||||
| import { Icon } from "components/Ico"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| 
 | ||||
| interface Props { | ||||
|   langui: AppStaticProps["langui"]; | ||||
| @ -21,15 +22,17 @@ export function MainPanel(props: Immutable<Props>): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`grid content-start justify-center gap-y-2 p-8 text-center ${ | ||||
|         appLayout.mainPanelReduced && isDesktop && "px-4" | ||||
|       }`}
 | ||||
|       className={cJoin( | ||||
|         "grid content-start justify-center gap-y-2 p-8 text-center", | ||||
|         cIf(appLayout.mainPanelReduced && isDesktop, "px-4") | ||||
|       )} | ||||
|     > | ||||
|       {/* Reduce/expand main menu */} | ||||
|       <div | ||||
|         className={`fixed top-1/2 mobile:hidden ${ | ||||
|           appLayout.mainPanelReduced ? "left-[4.65rem]" : "left-[18.65rem]" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           "fixed top-1/2 mobile:hidden", | ||||
|           cIf(appLayout.mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]") | ||||
|         )} | ||||
|         onClick={() => | ||||
|           appLayout.setMainPanelReduced(!appLayout.mainPanelReduced) | ||||
|         } | ||||
| @ -46,26 +49,28 @@ export function MainPanel(props: Immutable<Props>): JSX.Element { | ||||
|         <div className="grid place-items-center"> | ||||
|           <Link href="/" passHref> | ||||
|             <div | ||||
|               className={`${ | ||||
|                 appLayout.mainPanelReduced && isDesktop ? "w-12" : "w-1/2" | ||||
|               } 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`}
 | ||||
|               className={cJoin( | ||||
|                 `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(appLayout.mainPanelReduced && isDesktop, "w-12", "w-1/2") | ||||
|               )} | ||||
|             ></div> | ||||
|           </Link> | ||||
| 
 | ||||
|           {appLayout.mainPanelReduced && isDesktop ? ( | ||||
|             "" | ||||
|           ) : ( | ||||
|           {(!appLayout.mainPanelReduced || !isDesktop) && ( | ||||
|             <h2 className="text-3xl">Accord’s Library</h2> | ||||
|           )} | ||||
| 
 | ||||
|           <div | ||||
|             className={`flex ${ | ||||
|               appLayout.mainPanelReduced && isDesktop | ||||
|                 ? "flex-col gap-3" | ||||
|                 : "flex-row" | ||||
|             } flex-wrap gap-2`}
 | ||||
|             className={cJoin( | ||||
|               "flex flex-wrap gap-2", | ||||
|               cIf( | ||||
|                 appLayout.mainPanelReduced && isDesktop, | ||||
|                 "flex-col gap-3", | ||||
|                 "flex-row" | ||||
|               ) | ||||
|             )} | ||||
|           > | ||||
|             <ToolTip | ||||
|               content={<h3 className="text-2xl">{langui.open_settings}</h3>} | ||||
| @ -176,9 +181,10 @@ export function MainPanel(props: Immutable<Props>): JSX.Element { | ||||
|       {appLayout.mainPanelReduced && isDesktop ? "" : <HorizontalLine />} | ||||
| 
 | ||||
|       <div | ||||
|         className={`text-center ${ | ||||
|           appLayout.mainPanelReduced && isDesktop ? "hidden" : "" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           "text-center", | ||||
|           cIf(appLayout.mainPanelReduced && isDesktop, "hidden") | ||||
|         )} | ||||
|       > | ||||
|         <p> | ||||
|           {langui.licensing_notice && ( | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Dispatch, SetStateAction, useEffect } from "react"; | ||||
| import Hotkeys from "react-hot-keys"; | ||||
| @ -40,32 +41,37 @@ export function Popup(props: Immutable<Props>): JSX.Element { | ||||
|       }} | ||||
|     > | ||||
|       <div | ||||
|         className={`fixed inset-0 z-50 grid place-content-center
 | ||||
|       transition-[backdrop-filter] duration-500 ${ | ||||
|         state ? "[backdrop-filter:blur(2px)]" : "pointer-events-none touch-none" | ||||
|       }`}
 | ||||
|         className={cJoin( | ||||
|           "fixed inset-0 z-50 grid place-content-center transition-[backdrop-filter] duration-500", | ||||
|           cIf( | ||||
|             state, | ||||
|             "[backdrop-filter:blur(2px)]", | ||||
|             "pointer-events-none touch-none" | ||||
|           ) | ||||
|         )} | ||||
|       > | ||||
|         <div | ||||
|           className={`fixed inset-0 bg-shade transition-all duration-500 ${ | ||||
|             state ? "bg-opacity-50" : "bg-opacity-0" | ||||
|           }`}
 | ||||
|           className={cJoin( | ||||
|             "fixed inset-0 bg-shade transition-all duration-500", | ||||
|             cIf(state, "bg-opacity-50", "bg-opacity-0") | ||||
|           )} | ||||
|           onClick={() => { | ||||
|             setState(false); | ||||
|           }} | ||||
|         /> | ||||
| 
 | ||||
|         <div | ||||
|           className={`${ | ||||
|             padding && "p-10 mobile:p-6" | ||||
|           } grid place-items-center gap-4 transition-transform ${ | ||||
|             state ? "scale-100" : "scale-0" | ||||
|           } ${ | ||||
|             fillViewport | ||||
|               ? "absolute inset-10" | ||||
|               : "relative max-h-[80vh] overflow-y-auto mobile:w-[85vw]" | ||||
|           } ${ | ||||
|             hideBackground ? "" : "rounded-lg bg-light shadow-2xl shadow-shade" | ||||
|           }`}
 | ||||
|           className={cJoin( | ||||
|             "grid place-items-center gap-4 transition-transform", | ||||
|             cIf(padding, "p-10 mobile:p-6"), | ||||
|             cIf(state, "scale-100", "scale-0"), | ||||
|             cIf( | ||||
|               fillViewport, | ||||
|               "absolute inset-10", | ||||
|               "relative max-h-[80vh] overflow-y-auto mobile:w-[85vw]" | ||||
|             ), | ||||
|             cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade") | ||||
|           )} | ||||
|         > | ||||
|           {children} | ||||
|         </div> | ||||
|  | ||||
| @ -5,6 +5,7 @@ import { | ||||
|   UploadImageFragment, | ||||
| } from "graphql/generated"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { | ||||
|   prettyDate, | ||||
|   prettyDuration, | ||||
| @ -130,14 +131,15 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|         {stackNumber > 0 && ( | ||||
|           <> | ||||
|             <div | ||||
|               className={`absolute inset-0 scale-[.85] overflow-hidden bg-light brightness-[0.8]
 | ||||
|               sepia-[0.5] transition-[top_transform] group-hover:-top-8 ${ | ||||
|                 thumbnailRounded && "rounded-md" | ||||
|               }`}
 | ||||
|               className={cJoin( | ||||
|                 `absolute inset-0 scale-[.85] overflow-hidden bg-light brightness-[0.8] sepia-[0.5]
 | ||||
|                  transition-[top_transform] group-hover:-top-8`,
 | ||||
|                 cIf(thumbnailRounded, "rounded-md") | ||||
|               )} | ||||
|             > | ||||
|               {thumbnail && ( | ||||
|                 <Img | ||||
|                   className="opacity-30 " | ||||
|                   className="opacity-30" | ||||
|                   image={thumbnail} | ||||
|                   quality={ImageQuality.Medium} | ||||
|                 /> | ||||
| @ -145,10 +147,11 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|             </div> | ||||
| 
 | ||||
|             <div | ||||
|               className={`absolute inset-0 overflow-hidden bg-light brightness-[0.9] sepia-[0.2]
 | ||||
|               transition-[top_transform] group-hover:-top-4 group-hover:scale-[.94] ${ | ||||
|                 thumbnailRounded && "rounded-md" | ||||
|               }`}
 | ||||
|               className={cJoin( | ||||
|                 `absolute inset-0 overflow-hidden bg-light brightness-[0.9] sepia-[0.2]
 | ||||
|                 transition-[top_transform] group-hover:-top-4 group-hover:scale-[.94]`,
 | ||||
|                 cIf(thumbnailRounded, "rounded-md") | ||||
|               )} | ||||
|             > | ||||
|               {thumbnail && ( | ||||
|                 <Img | ||||
| @ -171,19 +174,24 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|             }} | ||||
|           > | ||||
|             <Img | ||||
|               className={`${ | ||||
|                 thumbnailRounded && | ||||
|                 (keepInfoVisible | ||||
|                   ? "rounded-t-md" | ||||
|                   : "rounded-md notHoverable:rounded-b-none") | ||||
|               } ${thumbnailForceAspectRatio && "h-full w-full object-cover"}`}
 | ||||
|               className={cJoin( | ||||
|                 cIf( | ||||
|                   thumbnailRounded, | ||||
|                   cIf( | ||||
|                     keepInfoVisible, | ||||
|                     "rounded-t-md", | ||||
|                     "rounded-md notHoverable:rounded-b-none" | ||||
|                   ) | ||||
|                 ), | ||||
|                 cIf(thumbnailForceAspectRatio, "h-full w-full object-cover") | ||||
|               )} | ||||
|               image={thumbnail} | ||||
|               quality={ImageQuality.Medium} | ||||
|             /> | ||||
|             {stackNumber > 0 && ( | ||||
|               <div | ||||
|                 className="absolute right-2 top-2 rounded-full bg-black | ||||
|                   bg-opacity-60 px-2 text-light" | ||||
|                 bg-opacity-60 px-2 text-light" | ||||
|               > | ||||
|                 {stackNumber} | ||||
|               </div> | ||||
| @ -200,8 +208,8 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div | ||||
|                   className="absolute right-2 bottom-2 rounded-full bg-black | ||||
|                   bg-opacity-60 px-2 text-light" | ||||
|                   className="absolute right-2 bottom-2 rounded-full bg-black bg-opacity-60 px-2 | ||||
|                   text-light" | ||||
|                 > | ||||
|                   {prettyDuration(hoverlay.duration)} | ||||
|                 </div> | ||||
| @ -211,11 +219,14 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|         ) : ( | ||||
|           <div | ||||
|             style={{ aspectRatio: thumbnailAspectRatio }} | ||||
|             className={`relative w-full bg-light ${ | ||||
|               keepInfoVisible | ||||
|                 ? "rounded-t-md" | ||||
|                 : "rounded-md notHoverable:rounded-b-none" | ||||
|             }`}
 | ||||
|             className={cJoin( | ||||
|               "relative w-full bg-light", | ||||
|               cIf( | ||||
|                 keepInfoVisible, | ||||
|                 "rounded-t-md", | ||||
|                 "rounded-md notHoverable:rounded-b-none" | ||||
|               ) | ||||
|             )} | ||||
|           > | ||||
|             {stackNumber > 0 && ( | ||||
|               <div | ||||
| @ -228,11 +239,14 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element { | ||||
|           </div> | ||||
|         )} | ||||
|         <div | ||||
|           className={`z-20 grid gap-2 p-4 transition-opacity linearbg-obi ${ | ||||
|             !keepInfoVisible && | ||||
|             `-inset-x-0.5 bottom-2 opacity-0 group-hover:opacity-100 hoverable:absolute
 | ||||
|             hoverable:drop-shadow-shade-lg notHoverable:rounded-b-md` | ||||
|           }`}
 | ||||
|           className={cJoin( | ||||
|             "z-20 grid gap-2 p-4 transition-opacity linearbg-obi", | ||||
|             cIf( | ||||
|               !keepInfoVisible, | ||||
|               `-inset-x-0.5 bottom-2 opacity-0 group-hover:opacity-100 hoverable:absolute
 | ||||
|               hoverable:drop-shadow-shade-lg notHoverable:rounded-b-md` | ||||
|             ) | ||||
|           )} | ||||
|         > | ||||
|           {metadata?.position === "Top" && metadataJSX} | ||||
|           {topChips && topChips.length > 0 && ( | ||||
|  | ||||
| @ -34,9 +34,8 @@ export function PreviewLine(props: Immutable<Props>): JSX.Element { | ||||
|   return ( | ||||
|     <Link href={href} passHref> | ||||
|       <div | ||||
|         className="flex h-36 w-full cursor-pointer | ||||
|         flex-row place-items-center gap-4 overflow-hidden rounded-md | ||||
|         bg-light pr-4 transition-transform drop-shadow-shade-xl hover:scale-[1.02]" | ||||
|         className="flex h-36 w-full cursor-pointer flex-row place-items-center gap-4 overflow-hidden | ||||
|         rounded-md bg-light pr-4 transition-transform drop-shadow-shade-xl hover:scale-[1.02]" | ||||
|       > | ||||
|         {thumbnail ? ( | ||||
|           <div className="aspect-[3/2] h-full"> | ||||
|  | ||||
| @ -1,22 +0,0 @@ | ||||
| import { Immutable } from "helpers/types"; | ||||
| import Image from "next/image"; | ||||
| 
 | ||||
| interface Props { | ||||
|   src: string; | ||||
|   alt: string; | ||||
|   className?: string; | ||||
| } | ||||
| 
 | ||||
| export function SVG(props: Immutable<Props>): JSX.Element { | ||||
|   return ( | ||||
|     <div className={props.className}> | ||||
|       <Image | ||||
|         src={props.src} | ||||
|         alt={props.src} | ||||
|         height={1000} | ||||
|         width={1000} | ||||
|         unoptimized | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @ -1,10 +1,12 @@ | ||||
| import Tippy, { TippyProps } from "@tippyjs/react"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| import "tippy.js/animations/scale-subtle.css"; | ||||
| 
 | ||||
| interface Props extends TippyProps {} | ||||
| 
 | ||||
| export function ToolTip(props: Props): JSX.Element { | ||||
|   const newProps: Props = { | ||||
|     className: cJoin("text-[80%]", props.className), | ||||
|     delay: [150, 0], | ||||
|     interactive: true, | ||||
|     animation: "scale-subtle", | ||||
| @ -12,7 +14,7 @@ export function ToolTip(props: Props): JSX.Element { | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <Tippy className={`text-[80%] ${newProps.className}`} {...newProps}> | ||||
|     <Tippy {...newProps}> | ||||
|       <div>{props.children}</div> | ||||
|     </Tippy> | ||||
|   ); | ||||
|  | ||||
							
								
								
									
										50
									
								
								src/components/Wiki/DefinitionCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/components/Wiki/DefinitionCard.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| import { Chip } from "components/Chip"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { getStatusDescription } from "helpers/others"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| 
 | ||||
| interface Props { | ||||
|   source?: string; | ||||
|   translations: | ||||
|     | { | ||||
|         language: string | undefined; | ||||
|         definition: string | null | undefined; | ||||
|         status: string | undefined; | ||||
|       }[] | ||||
|     | undefined; | ||||
|   languages: AppStaticProps["languages"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   index: number; | ||||
| } | ||||
| 
 | ||||
| export default function DefinitionCard(props: Props): JSX.Element { | ||||
|   const { source, translations = [], languages, langui, index } = props; | ||||
| 
 | ||||
|   const [selectedTranslation, LanguageSwitcher] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language, | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <div className="flex place-items-center gap-2"> | ||||
|         {/* TODO: Langui */} | ||||
|         <p className="font-headers text-lg">{`Definition ${index}`}</p> | ||||
|         {selectedTranslation?.status && ( | ||||
|           <ToolTip | ||||
|             content={getStatusDescription(selectedTranslation.status, langui)} | ||||
|             maxWidth={"20rem"} | ||||
|           > | ||||
|             <Chip>{selectedTranslation.status}</Chip> | ||||
|           </ToolTip> | ||||
|         )} | ||||
|         {translations.length > 1 && <LanguageSwitcher />} | ||||
|       </div> | ||||
| 
 | ||||
|       <p className="italic">{`${langui.source}: ${source}`}</p> | ||||
|       <p>{selectedTranslation?.definition}</p> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										11
									
								
								src/helpers/className.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/helpers/className.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| export function cIf( | ||||
|   condition: boolean | null | undefined | string, | ||||
|   ifTrueCss: string, | ||||
|   ifFalseCss?: string | ||||
| ) { | ||||
|   return condition ? ifTrueCss : ifFalseCss ?? ""; | ||||
| } | ||||
| 
 | ||||
| export function cJoin(...args: (string | undefined)[]): string { | ||||
|   return args.filter((elem) => elem).join(" "); | ||||
| } | ||||
| @ -1,4 +1,8 @@ | ||||
| import { GetContentTextQuery, GetPostQuery } from "graphql/generated"; | ||||
| import { | ||||
|   GetContentTextQuery, | ||||
|   GetPostQuery, | ||||
|   GetWikiPageQuery, | ||||
| } from "graphql/generated"; | ||||
| import React from "react"; | ||||
| 
 | ||||
| type Post = NonNullable< | ||||
| @ -17,6 +21,15 @@ export interface ContentWithTranslations extends Omit<Content, "translations"> { | ||||
|   translations: NonNullable<Content["translations"]>; | ||||
| } | ||||
| 
 | ||||
| type WikiPage = NonNullable< | ||||
|   NonNullable<GetWikiPageQuery["wikiPages"]>["data"][number]["attributes"] | ||||
| >; | ||||
| 
 | ||||
| export interface WikiPageWithTranslations | ||||
|   extends Omit<WikiPage, "translations"> { | ||||
|   translations: NonNullable<WikiPage["translations"]>; | ||||
| } | ||||
| 
 | ||||
| type ImmutableBlackList<T> = JSX.Element | React.ReactNode | Function; | ||||
| 
 | ||||
| export type Immutable<T> = { | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { | ||||
|   getPostStaticProps, | ||||
|   PostStaticProps, | ||||
| } from "graphql/getPostStaticProps"; | ||||
| import { cIf, cJoin } from "helpers/className"; | ||||
| import { randomInt } from "helpers/numbers"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { useRouter } from "next/router"; | ||||
| @ -27,10 +28,13 @@ export default function AboutUs( | ||||
|   const contactForm = ( | ||||
|     <div className="flex flex-col gap-8 text-center"> | ||||
|       <form | ||||
|         className={`grid gap-8 ${ | ||||
|           formState !== "stale" && | ||||
|           "pointer-events-none cursor-not-allowed touch-none opacity-60" | ||||
|         }`}
 | ||||
|         className={cJoin( | ||||
|           "grid gap-8", | ||||
|           cIf( | ||||
|             formState !== "stale", | ||||
|             "pointer-events-none cursor-not-allowed touch-none opacity-60" | ||||
|           ) | ||||
|         )} | ||||
|         onSubmit={(event) => { | ||||
|           event.preventDefault(); | ||||
| 
 | ||||
|  | ||||
| @ -64,7 +64,7 @@ export default function Channel(props: Props): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <div className="mb-8"> | ||||
|         <h1 className="text-3xl">{channel?.title}</h1> | ||||
|         <p>{channel?.subscribers.toLocaleString()} subscribers</p> | ||||
|  | ||||
| @ -82,7 +82,7 @@ export default function Videos(props: Props): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <PageSelector | ||||
|         maxPage={Math.floor(videos.length / itemPerPage)} | ||||
|         page={page} | ||||
|  | ||||
| @ -72,7 +72,7 @@ export default function Video(props: Props): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <ReturnButton | ||||
|         href="/library/" | ||||
|         title={langui.library} | ||||
|  | ||||
| @ -425,10 +425,7 @@ export async function getStaticProps( | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
| 
 | ||||
|   if ( | ||||
|     !content.contents || | ||||
|     !content.contents.data[0]?.attributes?.translations | ||||
|   ) { | ||||
|   if (!content.contents?.data[0]?.attributes?.translations) { | ||||
|     return { notFound: true }; | ||||
|   } | ||||
|   const props: Props = { | ||||
|  | ||||
| @ -143,16 +143,15 @@ export default function Contents(props: Immutable<Props>): JSX.Element { | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|       {[...groups].map(([name, items]) => ( | ||||
|         <Fragment key={name}> | ||||
|           {items.length > 0 && ( | ||||
|             <Fragment> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {[...groups].map( | ||||
|         ([name, items], index) => | ||||
|           items.length > 0 && ( | ||||
|             <Fragment key={index}> | ||||
|               {name && ( | ||||
|                 <h2 | ||||
|                   key={`h2${name}`} | ||||
|                   className="flex flex-row place-items-center gap-2 | ||||
|                   pb-2 pt-10 text-2xl first-of-type:pt-0" | ||||
|                   className="flex flex-row place-items-center gap-2 pb-2 pt-10 text-2xl | ||||
|                 first-of-type:pt-0" | ||||
|                 > | ||||
|                   {name} | ||||
|                   <Chip>{`${items.reduce((currentSum, item) => { | ||||
| @ -173,8 +172,8 @@ export default function Contents(props: Immutable<Props>): JSX.Element { | ||||
|                   }`}</Chip>
 | ||||
|                 </h2> | ||||
|               )} | ||||
| 
 | ||||
|               <div | ||||
|                 key={`items${name}`} | ||||
|                 className="grid grid-cols-2 items-end gap-8 | ||||
|                 desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:gap-4" | ||||
|               > | ||||
| @ -226,9 +225,8 @@ export default function Contents(props: Immutable<Props>): JSX.Element { | ||||
|                 ))} | ||||
|               </div> | ||||
|             </Fragment> | ||||
|           )} | ||||
|         </Fragment> | ||||
|       ))} | ||||
|           ) | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
|   return ( | ||||
|  | ||||
| @ -21,7 +21,7 @@ export default function CheckupContents(props: Immutable<Props>): JSX.Element { | ||||
|   const testReport = testingContent(contents); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {<h2 className="text-2xl">{testReport.title}</h2>} | ||||
| 
 | ||||
|       <div className="my-4 grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center gap-2"> | ||||
| @ -37,8 +37,8 @@ export default function CheckupContents(props: Immutable<Props>): JSX.Element { | ||||
|       {testReport.lines.map((line, index) => ( | ||||
|         <div | ||||
|           key={index} | ||||
|           className="mb-2 grid | ||||
|           grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center justify-items-start gap-2" | ||||
|           className="mb-2 grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center | ||||
|           justify-items-start gap-2" | ||||
|         > | ||||
|           <Button | ||||
|             href={line.frontendUrl} | ||||
|  | ||||
| @ -26,7 +26,7 @@ export default function CheckupLibraryItems( | ||||
|   const testReport = testingLibraryItem(libraryItems); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {<h2 className="text-2xl">{testReport.title}</h2>} | ||||
| 
 | ||||
|       <div className="my-4 grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center gap-2"> | ||||
|  | ||||
| @ -146,7 +146,7 @@ export default function Editor(props: Immutable<Props>): JSX.Element { | ||||
|   } | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <Popup setState={setConverterOpened} state={converterOpened}> | ||||
|         <div className="text-center"> | ||||
|           <h2 className="mt-4">Convert HTML to markdown</h2> | ||||
|  | ||||
| @ -16,9 +16,8 @@ export default function Home(props: Immutable<PostStaticProps>): JSX.Element { | ||||
|       prependBody={ | ||||
|         <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]" | ||||
|             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]" | ||||
|           /> | ||||
|           <h1 className="mb-0 text-5xl">Accord’s Library</h1> | ||||
|           <h2 className="-mt-5 text-xl"> | ||||
|  | ||||
| @ -119,7 +119,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <LightBox /> | ||||
| 
 | ||||
|       <ReturnButton | ||||
| @ -444,6 +444,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element { | ||||
|                       subtitle={subitem.attributes.subtitle} | ||||
|                       thumbnail={subitem.attributes.thumbnail?.data?.attributes} | ||||
|                       thumbnailAspectRatio="21/29.7" | ||||
|                       thumbnailRounded={false} | ||||
|                       keepInfoVisible={keepInfoVisible} | ||||
|                       topChips={ | ||||
|                         subitem.attributes.metadata && | ||||
|  | ||||
| @ -69,7 +69,7 @@ export default function LibrarySlug(props: Immutable<Props>): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <LightBox /> | ||||
| 
 | ||||
|       <ReturnButton | ||||
|  | ||||
| @ -250,7 +250,7 @@ export default function Library(props: Immutable<Props>): JSX.Element { | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {[...groups].map(([name, items]) => ( | ||||
|         <Fragment key={name}> | ||||
|           {items.length > 0 && ( | ||||
|  | ||||
| @ -85,7 +85,7 @@ export default function News(props: Immutable<Props>): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <div | ||||
|         className="grid grid-cols-1 items-end gap-8 | ||||
|         desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]" | ||||
|  | ||||
| @ -1,10 +1,21 @@ | ||||
| import { AppLayout } from "components/AppLayout"; | ||||
| import { ContentPanel } from "components/Panels/ContentPanel"; | ||||
| import { Chip } from "components/Chip"; | ||||
| import { HorizontalLine } from "components/HorizontalLine"; | ||||
| import { Img } from "components/Img"; | ||||
| import { | ||||
|   ReturnButton, | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import { | ||||
|   ContentPanel, | ||||
|   ContentPanelWidthSizes, | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import { SubPanel } from "components/Panels/SubPanel"; | ||||
| import { GetWikiPageQuery } from "graphql/generated"; | ||||
| import DefinitionCard from "components/Wiki/DefinitionCard"; | ||||
| import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { Immutable } from "helpers/types"; | ||||
| import { Immutable, WikiPageWithTranslations } from "helpers/types"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
| @ -12,16 +23,89 @@ import { | ||||
| } from "next"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   page: NonNullable< | ||||
|     NonNullable<GetWikiPageQuery["wikiPages"]>["data"][number]["attributes"] | ||||
|   >; | ||||
|   page: WikiPageWithTranslations; | ||||
| } | ||||
| 
 | ||||
| export default function WikiPage(props: Immutable<Props>): JSX.Element { | ||||
|   const { page, langui } = props; | ||||
|   const { page, langui, languages } = props; | ||||
| 
 | ||||
|   const subPanel = <SubPanel>Hello</SubPanel>; | ||||
|   const contentPanel = <ContentPanel>{page.slug}</ContentPanel>; | ||||
|   const [selectedTranslation, LanguageSwitcher] = useSmartLanguage({ | ||||
|     items: page.translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language?.data?.attributes?.code, | ||||
|   }); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href={`/wiki`} | ||||
|         title={langui.wiki} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.Desktop} | ||||
|         horizontalLine | ||||
|       /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|       <ReturnButton | ||||
|         href={`/wiki`} | ||||
|         title={langui.wiki} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.Mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
| 
 | ||||
|       <div className="flex place-content-center gap-4"> | ||||
|         <h1 className="text-center text-3xl">{selectedTranslation?.title}</h1> | ||||
|         <LanguageSwitcher /> | ||||
|       </div> | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       {selectedTranslation && ( | ||||
|         <div className="text-justify"> | ||||
|           <div | ||||
|             className="float-right ml-8 mb-8 w-[25rem] overflow-hidden rounded-lg bg-mid | ||||
|             text-center" | ||||
|           > | ||||
|             {page.thumbnail?.data?.attributes && ( | ||||
|               <Img image={page.thumbnail.data.attributes} /> | ||||
|             )} | ||||
|             <div className="my-4 grid gap-4 p-4"> | ||||
|               <p className="font-headers text-xl">{langui.categories}</p> | ||||
|               <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                 {page.categories?.data.map((category) => ( | ||||
|                   <Chip key={category.id}>{category.attributes?.name}</Chip> | ||||
|                 ))} | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           {selectedTranslation.summary && ( | ||||
|             <div className="mb-6"> | ||||
|               <p className="font-headers text-lg">{langui.summary}</p> | ||||
|               <p>{selectedTranslation.summary}</p> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           {page.definitions?.map((definition, index) => ( | ||||
|             <DefinitionCard | ||||
|               key={index} | ||||
|               source={definition?.source?.data?.attributes?.name} | ||||
|               translations={definition?.translations?.map((translation) => ({ | ||||
|                 language: translation?.language?.data?.attributes?.code, | ||||
|                 definition: translation?.definition, | ||||
|                 status: translation?.status, | ||||
|               }))} | ||||
|               index={index + 1} | ||||
|               languages={languages} | ||||
|               langui={langui} | ||||
|             /> | ||||
|           ))} | ||||
|         </div> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
| @ -42,10 +126,11 @@ export async function getStaticProps( | ||||
|     language_code: context.locale ?? "en", | ||||
|     slug: slug, | ||||
|   }); | ||||
|   if (!page.wikiPages?.data[0].attributes) return { notFound: true }; | ||||
|   if (!page.wikiPages?.data[0].attributes?.translations) | ||||
|     return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     page: page.wikiPages.data[0].attributes, | ||||
|     page: page.wikiPages.data[0].attributes as WikiPageWithTranslations, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -92,7 +92,7 @@ export default function Wiki(props: Immutable<Props>): JSX.Element { | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <div | ||||
|         className="grid grid-cols-2 items-end gap-8 | ||||
|         desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))] mobile:gap-4" | ||||
|  | ||||
| @ -172,6 +172,19 @@ input[type="submit"] { | ||||
|   [background-blend-mode:var(--theme-texture-dots-blend)]; | ||||
| } | ||||
| 
 | ||||
| /* DEBUGGING */ | ||||
| .false { | ||||
|   @apply border-2 border-[red] text-[red] outline-dotted outline-2 outline-[red]; | ||||
| } | ||||
| 
 | ||||
| .undefined { | ||||
|   @apply border-2 border-[green] text-[green] outline-dotted outline-2 outline-[green]; | ||||
| } | ||||
| 
 | ||||
| .null { | ||||
|   @apply border-2 border-[blue] text-[blue] outline-dotted outline-2 outline-[blue]; | ||||
| } | ||||
| 
 | ||||
| /* TIPPY */ | ||||
| 
 | ||||
| .tippy-box[data-animation="fade"][data-state="hidden"] { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint