Improved CSS and design system

This commit is contained in:
DrMint 2022-10-29 16:22:49 +02:00
parent cffe26a29a
commit fe24a77d6e
64 changed files with 1750 additions and 501 deletions

View File

@ -34,13 +34,13 @@
- Support for Arbitrary React Components and Component Props! - Support for Arbitrary React Components and Component Props!
- Autogenerated multi-level table of content and anchor links for the different headers - Autogenerated multi-level table of content and anchor links for the different headers
- Styling: [Tailwind CSS](https://tailwindcss.com/) - Styling: [Tailwind CSS](https://tailwindcss.com/)
- Manually added support for scrollbar styling to Tailwind CSS
- Support for [Material Icons](https://fonts.google.com/icons) - Support for [Material Icons](https://fonts.google.com/icons)
- Support for creating any arbitrary theming mode by swapping CSS variables - Support for creating any arbitrary theming mode by swapping CSS variables
- Support for Container Queries (media queries at the element level) - 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. - The website has a three-column layout, which turns into one-column + 2 toggleable side-menus if the screen is too narrow.
- Show out the Design System Showcase [here](https://accords-library.com/dev/showcase/design-system)
- State Management: [React Context](https://reactjs.org/docs/context.html) - State Management: [React Context](https://reactjs.org/docs/context.html)
- Persistent app state using LocalStorage - Persistent app state using LocalStorage and SessionStorage
- Accessibility - Accessibility
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable) - Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
- Keyboard hotkeys using [react-hotkeys-hook](https://www.npmjs.com/package/react-hotkeys-hook) - Keyboard hotkeys using [react-hotkeys-hook](https://www.npmjs.com/package/react-hotkeys-hook)
@ -54,7 +54,7 @@
- Furthermore, the user can temporary select another language then the one that was automatically selected - Furthermore, the user can temporary select another language then the one that was automatically selected
- SSG + ISR (Static Site Generation + Incremental Static Regeneration): - SSG + ISR (Static Site Generation + Incremental Static Regeneration):
- The website is built before running in production - The website is built before running in production
- Performances are great, and possibility to deploy the app using a CDN - Performances are great, and possibility to deploy the app on a CDN
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted - On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
- SEO - SEO
- Good defaults for the metadata and OpenGraph properties - Good defaults for the metadata and OpenGraph properties

View File

@ -8,6 +8,7 @@ const locales = ["en", "es", "fr", "pt-br", "ja"];
module.exports = { module.exports = {
swcMinify: true, swcMinify: true,
reactStrictMode: true, reactStrictMode: true,
poweredByHeader: false,
i18n: { i18n: {
locales: locales, locales: locales,
defaultLocale: "en", defaultLocale: "en",

View File

@ -168,8 +168,7 @@ export const AppLayout = ({
id={Ids.SubPanel} id={Ids.SubPanel}
className={cJoin( className={cJoin(
`texture-paper-dots z-20 overflow-y-scroll border-r border-dark/50 `texture-paper-dots z-20 overflow-y-scroll border-r border-dark/50
bg-light transition-transform duration-300 [scrollbar-width:none] bg-light transition-transform duration-300 scrollbar-none`,
webkit-scrollbar:w-0`,
cIf( cIf(
is1ColumnLayout, is1ColumnLayout,
"justify-self-end border-r-0 [grid-area:content]", "justify-self-end border-r-0 [grid-area:content]",
@ -187,7 +186,7 @@ export const AppLayout = ({
<div <div
className={cJoin( className={cJoin(
`texture-paper-dots z-30 overflow-y-scroll border-r border-dark/50 `texture-paper-dots z-30 overflow-y-scroll border-r border-dark/50
bg-light transition-transform duration-300 [scrollbar-width:none] webkit-scrollbar:w-0`, bg-light transition-transform duration-300 scrollbar-none`,
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"), cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"), cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
cIf(!mainPanelOpen && is1ColumnLayout, "-translate-x-full") cIf(!mainPanelOpen && is1ColumnLayout, "-translate-x-full")

View File

@ -1,9 +1,10 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { Link } from "components/Inputs/Link";
import { DatePickerFragment } from "graphql/generated"; import { DatePickerFragment } from "graphql/generated";
import { cIf, cJoin } from "helpers/className";
import { TranslatedProps } from "types/TranslatedProps"; import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { DownPressable } from "components/Containers/DownPressable";
import { isDefined } from "helpers/others";
import { cIf, cJoin } from "helpers/className";
/* /*
* *
@ -14,25 +15,32 @@ interface Props {
date: DatePickerFragment; date: DatePickerFragment;
title: string; title: string;
url: string; url: string;
isActive?: boolean; active?: boolean;
disabled?: boolean;
} }
const ChroniclePreview = ({ date, url, title, isActive }: Props): JSX.Element => ( export const ChroniclePreview = ({ date, url, title, active, disabled }: Props): JSX.Element => (
<Link <DownPressable
className="flex w-full gap-4 py-4 px-5"
href={url} href={url}
className={cJoin( active={active}
`flex w-full cursor-pointer gap-4 rounded-2xl py-4 px-5 text-left align-top outline outline-2 border
-outline-offset-2 outline-mid transition-all hover:bg-mid hover:shadow-inner-sm disabled={disabled}>
hover:shadow-shade hover:outline-transparent hover:active:shadow-inner {isDefined(date.year) && (
hover:active:shadow-shade`,
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade outline-transparent")
)}>
<div className="text-right"> <div className="text-right">
<p>{date.year}</p> <p>{date.year}</p>
<p className="text-sm text-dark">{prettyMonthDay(date.month, date.day)}</p> <p className="text-sm text-dark">{prettyMonthDay(date.month, date.day)}</p>
</div> </div>
<p className="text-lg leading-tight">{title}</p> )}
</Link>
<p
className={cJoin(
"text-lg leading-tight",
cIf(isDefined(date.year), "text-left", "w-full text-center")
)}>
{title}
</p>
</DownPressable>
); );
/* /*

View File

@ -54,7 +54,7 @@ const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element
] as const).map((content, index) => ( ] as const).map((content, index) => (
<TranslatedChroniclePreview <TranslatedChroniclePreview
key={index} key={index}
isActive={chronicle.attributes.slug === currentSlug} active={chronicle.attributes.slug === currentSlug}
date={chronicle.attributes.date_start} date={chronicle.attributes.date_start}
translations={filterHasAttributes(content.attributes.translations, [ translations={filterHasAttributes(content.attributes.translations, [
"language.data.attributes.code", "language.data.attributes.code",
@ -80,7 +80,7 @@ const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element
: chronicle.attributes.translations.length > 0 && ( : chronicle.attributes.translations.length > 0 && (
<TranslatedChroniclePreview <TranslatedChroniclePreview
date={chronicle.attributes.date_start} date={chronicle.attributes.date_start}
isActive={chronicle.attributes.slug === currentSlug} active={chronicle.attributes.slug === currentSlug}
translations={filterHasAttributes(chronicle.attributes.translations, [ translations={filterHasAttributes(chronicle.attributes.translations, [
"language.data.attributes.code", "language.data.attributes.code",
"title", "title",

View File

@ -239,8 +239,7 @@ export const Terminal = ({
)}> )}>
<div <div
ref={terminalWindowRef} ref={terminalWindowRef}
className="h-full overflow-scroll scroll-auto p-6 className="h-full overflow-scroll scroll-auto p-6 scrollbar-none">
[scrollbar-width:none] webkit-scrollbar:w-0">
{previousLines.map((previousLine, index) => ( {previousLines.map((previousLine, index) => (
<p key={index} className="whitespace-pre-line font-realmono"> <p key={index} className="whitespace-pre-line font-realmono">
{previousLine} {previousLine}
@ -311,7 +310,7 @@ export const Terminal = ({
<span <span
className={cJoin( className={cJoin(
"whitespace-pre font-realmono", "whitespace-pre font-realmono",
cIf(isTextAreaFocused, "animation-carret border-b-2 border-black") cIf(isTextAreaFocused, "animate-carret border-b-2 border-black")
)}> )}>
{line[carretPosition] ?? " "} {line[carretPosition] ?? " "}
</span> </span>

View File

@ -0,0 +1,61 @@
import { MouseEventHandler, useState } from "react";
import { cJoin, cIf } from "helpers/className";
import { Link } from "components/Inputs/Link";
/*
*
* COMPONENT
*/
interface Props {
border?: boolean;
active?: boolean;
disabled?: boolean;
href: string;
children: React.ReactNode;
className?: string;
onFocusChanged?: (isFocused: boolean) => void;
onClick?: MouseEventHandler<HTMLDivElement>;
}
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const DownPressable = ({
href,
border = false,
active = false,
disabled = false,
children,
className,
onFocusChanged,
onClick,
}: Props): JSX.Element => {
const [isFocused, setFocused] = useState(false);
return (
<Link
href={href}
onClick={onClick}
onFocusChanged={(focus) => {
setFocused(focus);
onFocusChanged?.(focus);
}}
className={cJoin(
`rounded-2xl p-4 transition-all`,
cIf(border, "outline outline-2 -outline-offset-2 outline-mid"),
cIf(active, "!bg-mid shadow-inner-sm outline-transparent shadow-shade"),
cIf(
disabled,
"cursor-not-allowed select-none opacity-50 grayscale",
cJoin(
"cursor-pointer hover:bg-mid hover:shadow-inner-sm hover:shadow-shade",
cIf(isFocused, "!shadow-inner !shadow-shade"),
cIf(border, "hover:outline-transparent")
)
),
className
)}
disabled={disabled}>
{children}
</Link>
);
};

View File

@ -0,0 +1,42 @@
import { useState } from "react";
import { Link } from "components/Inputs/Link";
import { cIf, cJoin } from "helpers/className";
interface Props {
children: React.ReactNode;
href: string;
className?: string;
noBackground?: boolean;
disabled?: boolean;
}
export const UpPressable = ({
children,
href,
className,
disabled = false,
noBackground = false,
}: Props): JSX.Element => {
const [isFocused, setFocused] = useState(false);
return (
<Link
href={href}
onFocusChanged={setFocused}
className={cJoin(
`overflow-hidden rounded-md drop-shadow-lg transition-all duration-300 shadow-shade`,
cIf(!noBackground, "bg-highlight"),
cIf(
disabled,
"cursor-not-allowed opacity-50 grayscale",
cJoin(
"cursor-pointer hover:scale-102 hover:drop-shadow-xl",
cIf(isFocused, "hover:scale-105 hover:drop-shadow-2xl hover:duration-100")
)
),
className
)}
disabled={disabled}>
{children}
</Link>
);
};

View File

@ -0,0 +1,38 @@
import { useCallback } from "react";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedProps } from "types/TranslatedProps";
import { UpPressable } from "components/Containers/UpPressable";
import { cIf, cJoin } from "helpers/className";
interface PreviewFolderProps {
href: string;
title?: string | null;
disabled?: boolean;
}
export const PreviewFolder = ({ href, title, disabled }: PreviewFolderProps): JSX.Element => (
<UpPressable href={href} disabled={disabled}>
<div
className={cJoin(
`flex w-full cursor-pointer flex-row place-content-center place-items-center gap-4
p-6`,
cIf(disabled, "pointer-events-none touch-none select-none")
)}>
{title && <p className="text-center font-headers text-lg font-bold leading-none">{title}</p>}
</div>
</UpPressable>
);
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const TranslatedPreviewFolder = ({
translations,
fallback,
...otherProps
}: TranslatedProps<PreviewFolderProps, "title">): JSX.Element => {
const [selectedTranslation] = useSmartLanguage({
items: translations,
languageExtractor: useCallback((item: { language: string }): string => item.language, []),
});
return <PreviewFolder title={selectedTranslation?.title ?? fallback.title} {...otherProps} />;
};

View File

@ -46,28 +46,32 @@ export const Button = ({
size = "normal", size = "normal",
}: Props): JSX.Element => ( }: Props): JSX.Element => (
<ConditionalWrapper <ConditionalWrapper
isWrapping={isDefinedAndNotEmpty(href)} isWrapping={isDefinedAndNotEmpty(href) && !disabled}
wrapperProps={{ href: href ?? "", alwaysNewTab }} wrapperProps={{ href: href ?? "", alwaysNewTab }}
wrapper={LinkWrapper}> wrapper={LinkWrapper}>
<div className="relative"> <div className="relative">
<div <div
draggable={draggable} draggable={draggable}
id={id} id={id}
onClick={onClick} onClick={(event) => !disabled && onClick?.(event)}
onMouseUp={onMouseUp} onMouseUp={onMouseUp}
onFocus={(event) => event.target.blur()} onFocus={(event) => event.target.blur()}
className={cJoin( className={cJoin(
`group grid cursor-pointer select-none grid-flow-col place-content-center `group grid cursor-pointer select-none grid-flow-col place-content-center
place-items-center gap-2 rounded-full border border-dark py-3 px-4 place-items-center gap-2 rounded-full border border-dark py-3 px-4
leading-none text-dark transition-all`, leading-none text-dark transition-all`,
cIf(
active,
"!border-black bg-black !text-light drop-shadow-black-lg",
`hover:bg-dark hover:text-light hover:drop-shadow-shade-lg active:hover:!border-black
active:hover:bg-black active:hover:!text-light active:hover:drop-shadow-black-lg`
),
cIf(size === "small", "px-3 py-1 text-xs"), cIf(size === "small", "px-3 py-1 text-xs"),
cIf(disabled, "cursor-not-allowed"), cIf(active, "!border-black bg-black !text-light drop-shadow-lg shadow-black"),
cIf(
disabled,
"cursor-not-allowed opacity-50 grayscale",
cIf(
!active,
`shadow-shade hover:bg-dark hover:text-light hover:drop-shadow-lg
active:hover:!border-black active:hover:bg-black active:hover:!text-light
active:hover:drop-shadow-lg active:hover:shadow-black`
)
),
className className
)}> )}>
{isDefined(badgeNumber) && ( {isDefined(badgeNumber) && (

View File

@ -1,5 +1,5 @@
import router from "next/router"; import router from "next/router";
import { MouseEventHandler, useState } from "react"; import { PointerEventHandler, useState } from "react";
import { isDefined } from "helpers/others"; import { isDefined } from "helpers/others";
interface Props { interface Props {
@ -8,29 +8,41 @@ interface Props {
allowNewTab?: boolean; allowNewTab?: boolean;
alwaysNewTab?: boolean; alwaysNewTab?: boolean;
children: React.ReactNode; children: React.ReactNode;
onClick?: MouseEventHandler<HTMLDivElement>; onClick?: PointerEventHandler<HTMLDivElement>;
onFocusChanged?: (isFocused: boolean) => void;
disabled?: boolean;
} }
export const Link = ({ export const Link = ({
href, href,
allowNewTab = true, allowNewTab = true,
alwaysNewTab = false, alwaysNewTab = false,
disabled = false,
children, children,
className, className,
onClick, onClick,
onFocusChanged,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const [isValidClick, setIsValidClick] = useState(false); const [isValidClick, setIsValidClick] = useState(false);
return ( return (
<div <div
className={className} className={className}
onMouseLeave={() => setIsValidClick(false)} onPointerLeave={() => {
onContextMenu={(event) => event.preventDefault()} setIsValidClick(false);
onMouseDown={(event) => { onFocusChanged?.(false);
event.preventDefault();
setIsValidClick(true);
}} }}
onMouseUp={(event) => { onContextMenu={(event) => event.preventDefault()}
onPointerDown={(event) => {
if (!disabled) {
event.preventDefault();
onFocusChanged?.(true);
setIsValidClick(true);
}
}}
onPointerUp={(event) => {
onFocusChanged?.(false);
if (!disabled) {
if (isDefined(onClick)) { if (isDefined(onClick)) {
onClick(event); onClick(event);
} else if (isValidClick && href) { } else if (isValidClick && href) {
@ -48,6 +60,7 @@ export const Link = ({
} }
} }
} }
}
}}> }}>
{children} {children}
</div> </div>

View File

@ -58,7 +58,7 @@ export const OrderableList = ({ onChange, items, insertLabels }: Props): JSX.Ele
}} }}
className="grid cursor-grab select-none grid-cols-[auto_1fr] place-content-center gap-2 className="grid cursor-grab select-none grid-cols-[auto_1fr] place-content-center gap-2
rounded-full border border-dark bg-light px-1 py-2 pr-4 text-dark transition-all rounded-full border border-dark bg-light px-1 py-2 pr-4 text-dark transition-all
hover:bg-dark hover:text-light hover:drop-shadow-shade-lg" hover:shadow-shade hover:bg-dark hover:text-light hover:shadow-lg"
draggable> draggable>
<div className="grid grid-rows-[.8em_.8em] place-items-center"> <div className="grid grid-rows-[.8em_.8em] place-items-center">
{index > 0 && ( {index > 0 && (

View File

@ -15,17 +15,34 @@ interface Props {
allowEmpty?: boolean; allowEmpty?: boolean;
className?: string; className?: string;
onChange: (value: number) => void; onChange: (value: number) => void;
disabled?: boolean;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const Select = ({ className, value, options, allowEmpty, onChange }: Props): JSX.Element => { export const Select = ({
className,
value,
options,
allowEmpty,
disabled = false,
onChange,
}: Props): JSX.Element => {
const { value: isOpened, setFalse: setClosed, toggle: toggleOpened } = useBoolean(false); const { value: isOpened, setFalse: setClosed, toggle: toggleOpened } = useBoolean(false);
const tryToggling = useCallback(() => { const tryToggling = useCallback(() => {
if (disabled) return;
const optionCount = options.length + (value === -1 ? 1 : 0); const optionCount = options.length + (value === -1 ? 1 : 0);
if (optionCount > 1) toggleOpened(); if (optionCount > 1) toggleOpened();
}, [options.length, value, toggleOpened]); }, [disabled, options.length, value, toggleOpened]);
const onSelectionChanged = useCallback(
(newIndex: number) => {
setClosed();
onChange(newIndex);
},
[onChange, setClosed]
);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
useOnClickOutside(ref, setClosed); useOnClickOutside(ref, setClosed);
@ -35,27 +52,29 @@ export const Select = ({ className, value, options, allowEmpty, onChange }: Prop
ref={ref} ref={ref}
className={cJoin( className={cJoin(
"relative text-center transition-filter", "relative text-center transition-filter",
cIf(isOpened, "z-10 drop-shadow-shade-lg"), cIf(isOpened, "z-10 drop-shadow-lg shadow-shade"),
className className
)}> )}>
<div <div
className={cJoin( className={cJoin(
`grid cursor-pointer grid-flow-col grid-cols-[1fr_auto_auto] place-items-center `grid cursor-pointer select-none grid-flow-col grid-cols-[1fr_auto_auto]
rounded-[1em] bg-light p-1 outline outline-1 -outline-offset-1 outline-mid place-items-center rounded-3xl p-1 outline outline-1 -outline-offset-1
transition-all hover:bg-mid hover:outline-transparent`, outline-mid`,
cIf(isOpened, "rounded-b-none bg-highlight outline-transparent") cIf(isOpened, "rounded-b-none bg-highlight outline-transparent"),
cIf(
disabled,
"cursor-not-allowed text-dark opacity-50 outline-dark/60 grayscale",
"transition-all hover:bg-mid hover:outline-transparent"
)
)}> )}>
<p onClick={tryToggling} className="w-full"> <p onClick={tryToggling} className="w-full px-4 py-1">
{value === -1 ? "—" : options[value]} {value === -1 ? "—" : options[value]}
</p> </p>
{value >= 0 && allowEmpty && ( {value >= 0 && allowEmpty && (
<Ico <Ico
icon={Icon.Close} icon={Icon.Close}
className="!text-xs" className="!text-xs"
onClick={() => { onClick={() => !disabled && onSelectionChanged(-1)}
setClosed();
onChange(-1);
}}
/> />
)} )}
<Ico onClick={tryToggling} icon={isOpened ? Icon.ArrowDropUp : Icon.ArrowDropDown} /> <Ico onClick={tryToggling} icon={isOpened ? Icon.ArrowDropUp : Icon.ArrowDropDown} />
@ -70,10 +89,7 @@ export const Select = ({ className, value, options, allowEmpty, onChange }: Prop
cIf(isOpened, "bg-highlight", "bg-light") cIf(isOpened, "bg-highlight", "bg-light")
)} )}
id={option} id={option}
onClick={() => { onClick={() => onSelectionChanged(index)}>
setClosed();
onChange(index);
}}>
{option} {option}
</div> </div>
)} )}

View File

@ -22,24 +22,21 @@ export const Switch = ({ value, onClick, className, disabled = false }: Props):
className={cJoin( className={cJoin(
`relative grid h-6 w-12 content-center rounded-full border-mid outline `relative grid h-6 w-12 content-center rounded-full border-mid outline
outline-1 -outline-offset-1 outline-mid transition-colors`, outline-1 -outline-offset-1 outline-mid transition-colors`,
cIf(disabled, "cursor-not-allowed", "cursor-pointer"), cIf(value, "border-none bg-mid shadow-inner-sm shadow-shade outline-transparent"),
cIf( cIf(disabled, "cursor-not-allowed opacity-50 grayscale", "cursor-pointer"),
value, cIf(disabled, cIf(value, "bg-dark/40 outline-transparent", "outline-dark/60")),
"border-none bg-mid shadow-inner-sm shadow-shade outline-transparent",
"bg-light"
),
className className
)} )}
onClick={() => { onClick={() => {
if (!disabled) onClick(); if (!disabled) onClick();
}} }}
onPointerDown={() => setIsFocused(true)} onPointerDown={() => !disabled && setIsFocused(true)}
onPointerOut={() => setIsFocused(false)} onPointerOut={() => setIsFocused(false)}
onPointerLeave={() => setIsFocused(false)} onPointerLeave={() => setIsFocused(false)}
onPointerUp={() => setIsFocused(false)}> onPointerUp={() => setIsFocused(false)}>
<div <div
className={cJoin( className={cJoin(
"ml-1 h-4 w-4 rounded-full bg-dark transition-transform", "ml-1 h-4 w-4 rounded-full bg-dark transition-transform touch-none pointer-events-none",
cIf(value, "translate-x-6"), cIf(value, "translate-x-6"),
cIf(isFocused, cIf(value, "translate-x-5", "translate-x-1")) cIf(isFocused, cIf(value, "translate-x-5", "translate-x-1"))
)} )}

View File

@ -1,5 +1,5 @@
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { isDefinedAndNotEmpty } from "helpers/others"; import { isDefinedAndNotEmpty } from "helpers/others";
/* /*
@ -13,6 +13,7 @@ interface Props {
className?: string; className?: string;
name?: string; name?: string;
placeholder?: string; placeholder?: string;
disabled?: boolean;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
@ -23,6 +24,7 @@ export const TextInput = ({
className, className,
name, name,
placeholder, placeholder,
disabled = false,
}: Props): JSX.Element => ( }: Props): JSX.Element => (
<div className={cJoin("relative", className)}> <div className={cJoin("relative", className)}>
<input <input
@ -30,6 +32,7 @@ export const TextInput = ({
type="text" type="text"
name={name} name={name}
value={value} value={value}
disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
onChange={(event) => { onChange={(event) => {
onChange(event.target.value); onChange(event.target.value);
@ -38,11 +41,9 @@ export const TextInput = ({
{isDefinedAndNotEmpty(value) && ( {isDefinedAndNotEmpty(value) && (
<div className="absolute right-4 top-0 bottom-0 grid place-items-center"> <div className="absolute right-4 top-0 bottom-0 grid place-items-center">
<Ico <Ico
className="cursor-pointer !text-xs" className={cJoin("!text-xs", cIf(disabled, "opacity-30 grayscale", "cursor-pointer"))}
icon={Icon.Close} icon={Icon.Close}
onClick={() => { onClick={() => !disabled && onChange("")}
onChange("");
}}
/> />
</div> </div>
)} )}

View File

@ -8,18 +8,13 @@ import { isDefinedAndNotEmpty } from "helpers/others";
interface Props { interface Props {
label: string | null | undefined; label: string | null | undefined;
disabled?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const WithLabel = ({ label, children, disabled }: Props): JSX.Element => ( export const WithLabel = ({ label, children }: Props): JSX.Element => (
<div <div className="flex flex-row place-content-between place-items-center gap-2">
className={cJoin(
"flex flex-row place-content-between place-items-center gap-2",
cIf(disabled, "text-dark brightness-150 contrast-75 grayscale")
)}>
{isDefinedAndNotEmpty(label) && ( {isDefinedAndNotEmpty(label) && (
<p className={cJoin("text-left", cIf(label.length < 10, "flex-shrink-0"))}>{label}:</p> <p className={cJoin("text-left", cIf(label.length < 10, "flex-shrink-0"))}>{label}:</p>
)} )}

View File

@ -10,15 +10,16 @@ import { Ids } from "types/ids";
import { UploadImageFragment } from "graphql/generated"; import { UploadImageFragment } from "graphql/generated";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { isDefined } from "helpers/others"; import { isDefined } from "helpers/others";
import { useContainerQueries } from "contexts/ContainerQueriesContext";
interface Props { interface Props {
onCloseRequest: () => void; onCloseRequest: () => void;
isVisible: boolean; isVisible: boolean;
image?: UploadImageFragment | string; image?: UploadImageFragment | string;
isNextImageAvailable?: boolean; isNextImageAvailable: boolean;
isPreviousImageAvailable?: boolean; isPreviousImageAvailable: boolean;
onPressNext?: () => void; onPressNext: () => void;
onPressPrevious?: () => void; onPressPrevious: () => void;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
@ -37,18 +38,15 @@ export const LightBox = ({
Ids.LightBox Ids.LightBox
); );
useHotkeys( useHotkeys("left", () => onPressPrevious(), { enabled: isVisible && isPreviousImageAvailable }, [
"left", onPressPrevious,
() => onPressPrevious?.(), ]);
{ enabled: isVisible && isPreviousImageAvailable },
[onPressPrevious]
);
useHotkeys("f", () => requestFullscreen(), { enabled: isVisible && !isFullscreen }, [ useHotkeys("f", () => requestFullscreen(), { enabled: isVisible && !isFullscreen }, [
requestFullscreen, requestFullscreen,
]); ]);
useHotkeys("right", () => onPressNext?.(), { enabled: isVisible && isNextImageAvailable }, [ useHotkeys("right", () => onPressNext(), { enabled: isVisible && isNextImageAvailable }, [
onPressNext, onPressNext,
]); ]);
@ -69,7 +67,7 @@ export const LightBox = ({
/> />
<div <div
className={cJoin( className={cJoin(
"absolute inset-8 grid transition-transform", "absolute inset-0 grid transition-transform",
cIf(isVisible, "scale-100", "scale-0") cIf(isVisible, "scale-100", "scale-0")
)}> )}>
<TransformWrapper <TransformWrapper
@ -87,46 +85,32 @@ export const LightBox = ({
}}> }}>
{isDefined(src) && ( {isDefined(src) && (
<Img <Img
className={`drop-shadow-shade-2xl-shade h-[calc(100vh-4rem)] w-full className={`shadow-shade drop-shadow-2xl h-[calc(100vh-4rem)] w-full
object-contain`} object-contain`}
src={src} src={src}
quality={ImageQuality.Large} quality={ImageQuality.Large}
/> />
)} )}
</TransformComponent> </TransformComponent>
<ControlButtons
{isPreviousImageAvailable && ( isNextImageAvailable={isNextImageAvailable}
<div isPreviousImageAvailable={isPreviousImageAvailable}
className={`absolute top-1/2 left-0 grid gap-4 rounded-[2rem] p-4 isFullscreen={isFullscreen}
backdrop-blur-lg`}> onCloseRequest={() => {
<Button icon={Icon.NavigateBefore} onClick={onPressPrevious} />
</div>
)}
{isNextImageAvailable && (
<div
className={`absolute top-1/2 right-0 grid gap-4 rounded-[2rem] p-4
backdrop-blur-lg`}>
<Button icon={Icon.NavigateNext} onClick={onPressNext} />{" "}
</div>
)}
<div
className={`absolute top-0 right-0 grid gap-4 rounded-[2rem] p-4
backdrop-blur-lg`}>
<Button
onClick={() => {
resetTransform(); resetTransform();
exitFullscreen(); exitFullscreen();
onCloseRequest(); onCloseRequest();
}} }}
icon={Icon.Close} onPressPrevious={() => {
resetTransform();
onPressPrevious();
}}
onPressNext={() => {
resetTransform();
onPressNext();
}}
toggleFullscreen={toggleFullscreen}
/> />
<Button
icon={isFullscreen ? Icon.FullscreenExit : Icon.Fullscreen}
onClick={toggleFullscreen}
/>
</div>
</> </>
)} )}
</TransformWrapper> </TransformWrapper>
@ -134,3 +118,83 @@ export const LightBox = ({
</div> </div>
); );
}; };
interface ControlButtonsProps {
isPreviousImageAvailable: boolean;
isNextImageAvailable: boolean;
isFullscreen: boolean;
onPressPrevious?: () => void;
onPressNext?: () => void;
onCloseRequest: () => void;
toggleFullscreen: () => void;
}
const ControlButtons = ({
isFullscreen,
isPreviousImageAvailable,
isNextImageAvailable,
onPressPrevious,
onPressNext,
onCloseRequest,
toggleFullscreen,
}: ControlButtonsProps): JSX.Element => {
const { is1ColumnLayout } = useContainerQueries();
const PreviousButton = () => (
<Button
icon={Icon.NavigateBefore}
onClick={onPressPrevious}
disabled={!isPreviousImageAvailable}
/>
);
const NextButton = () => (
<Button icon={Icon.NavigateNext} onClick={onPressNext} disabled={!isNextImageAvailable} />
);
const FullscreenButton = () => (
<Button
icon={isFullscreen ? Icon.FullscreenExit : Icon.Fullscreen}
onClick={toggleFullscreen}
/>
);
const CloseButton = () => <Button onClick={onCloseRequest} icon={Icon.Close} />;
return is1ColumnLayout ? (
<>
<div className="absolute bottom-2 left-0 right-0 grid place-content-center">
<div className="grid grid-flow-col gap-4 rounded-4xl p-4 backdrop-blur-lg">
<PreviousButton />
<FullscreenButton />
<NextButton />
</div>
</div>
<div className="absolute top-2 right-2 grid gap-4 rounded-4xl p-4 backdrop-blur-lg">
<CloseButton />
</div>
</>
) : (
<>
{isPreviousImageAvailable && (
<div
className={`absolute top-1/2 left-8 grid gap-4 rounded-4xl p-4
backdrop-blur-lg`}>
<PreviousButton />
</div>
)}
{isNextImageAvailable && (
<div
className={`absolute top-1/2 right-8 grid gap-4 rounded-4xl p-4
backdrop-blur-lg`}>
<NextButton />
</div>
)}
<div
className={`absolute top-4 right-8 grid gap-4 rounded-4xl p-4
backdrop-blur-lg`}>
<CloseButton />
<FullscreenButton />
</div>
</>
);
};

View File

@ -4,7 +4,7 @@ import React, { Fragment, useMemo } from "react";
import ReactDOMServer from "react-dom/server"; import ReactDOMServer from "react-dom/server";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { slugify } from "helpers/formatters"; import { slugify } from "helpers/formatters";
import { getAssetURL, ImageQuality } from "helpers/img"; import { getAssetURL, ImageQuality } from "helpers/img";
@ -204,7 +204,7 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
: compProps.src : compProps.src
} }
quality={ImageQuality.Medium} quality={ImageQuality.Medium}
className="drop-shadow-shade-lg" className="drop-shadow-lg shadow-shade"
/> />
</div> </div>
), ),

View File

@ -1,12 +1,12 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { MouseEventHandler, useCallback, useMemo } from "react"; import { MouseEventHandler, useCallback, useMemo, useState } from "react";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { cJoin, cIf } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { isDefinedAndNotEmpty } from "helpers/others"; import { isDefinedAndNotEmpty } from "helpers/others";
import { Link } from "components/Inputs/Link";
import { TranslatedProps } from "types/TranslatedProps"; import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { DownPressable } from "components/Containers/DownPressable";
/* /*
* *
@ -21,6 +21,7 @@ interface Props {
border?: boolean; border?: boolean;
reduced?: boolean; reduced?: boolean;
active?: boolean; active?: boolean;
disabled?: boolean;
onClick?: MouseEventHandler<HTMLDivElement>; onClick?: MouseEventHandler<HTMLDivElement>;
} }
@ -34,6 +35,7 @@ export const NavOption = ({
border = false, border = false,
reduced = false, reduced = false,
active = false, active = false,
disabled = false,
onClick, onClick,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const router = useRouter(); const router = useRouter();
@ -41,6 +43,7 @@ export const NavOption = ({
() => active || router.asPath.startsWith(url), () => active || router.asPath.startsWith(url),
[active, router.asPath, url] [active, router.asPath, url]
); );
const [isFocused, setFocused] = useState(false);
return ( return (
<ToolTip <ToolTip
@ -52,27 +55,28 @@ export const NavOption = ({
} }
placement="right" placement="right"
className="text-left" className="text-left"
disabled={!reduced}> disabled={!reduced || disabled}>
<Link <DownPressable
href={url}
onClick={onClick}
className={cJoin( className={cJoin(
`relative grid w-full cursor-pointer auto-cols-fr grid-flow-col grid-cols-[auto] "grid w-full auto-cols-fr grid-flow-col grid-cols-[auto] justify-center gap-x-5",
justify-center gap-x-5 rounded-2xl p-4 transition-all hover:bg-mid hover:shadow-inner-sm cIf(icon, "text-left", "text-center")
hover:shadow-shade hover:active:shadow-inner hover:active:shadow-shade`, )}
cIf(icon, "text-left", "text-center"), href={url}
cIf(border, "outline outline-2 -outline-offset-2 outline-mid hover:outline-transparent"), border={border}
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade") onClick={onClick}
)}> active={isActive}
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive} />} disabled={disabled}
onFocusChanged={setFocused}>
{icon && (
<Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive || isFocused} />
)}
{!reduced && ( {!reduced && (
<div> <div>
<h3 className="text-2xl">{title}</h3> <h3 className="text-2xl">{title}</h3>
{isDefinedAndNotEmpty(subtitle) && <p className="col-start-2">{subtitle}</p>} {isDefinedAndNotEmpty(subtitle) && <p className="col-start-2">{subtitle}</p>}
</div> </div>
)} )}
</Link> </DownPressable>
</ToolTip> </ToolTip>
); );
}; };

View File

@ -2,7 +2,7 @@ import { useMemo } from "react";
import UAParser from "ua-parser-js"; import UAParser from "ua-parser-js";
import { useIsClient, useSessionStorage } from "usehooks-ts"; import { useIsClient, useSessionStorage } from "usehooks-ts";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { Popup } from "components/Popup"; import { Popup } from "components/Containers/Popup";
import { sendAnalytics } from "helpers/analytics"; import { sendAnalytics } from "helpers/analytics";
export const SafariPopup = (): JSX.Element => { export const SafariPopup = (): JSX.Element => {

View File

@ -6,7 +6,7 @@ import { ButtonGroup } from "components/Inputs/ButtonGroup";
import { OrderableList } from "components/Inputs/OrderableList"; import { OrderableList } from "components/Inputs/OrderableList";
import { Select } from "components/Inputs/Select"; import { Select } from "components/Inputs/Select";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";
import { Popup } from "components/Popup"; import { Popup } from "components/Containers/Popup";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import { useLocalData } from "contexts/LocalDataContext"; import { useLocalData } from "contexts/LocalDataContext";
import { useUserSettings } from "contexts/UserSettingsContext"; import { useUserSettings } from "contexts/UserSettingsContext";

View File

@ -4,8 +4,8 @@ import { Chip } from "./Chip";
import { HorizontalLine } from "./HorizontalLine"; import { HorizontalLine } from "./HorizontalLine";
import { Markdawn, TableOfContents } from "./Markdown/Markdawn"; import { Markdawn, TableOfContents } from "./Markdown/Markdawn";
import { ReturnButton } from "./PanelComponents/ReturnButton"; import { ReturnButton } from "./PanelComponents/ReturnButton";
import { ContentPanel } from "./Panels/ContentPanel"; import { ContentPanel } from "./Containers/ContentPanel";
import { SubPanel } from "./Panels/SubPanel"; import { SubPanel } from "./Containers/SubPanel";
import { RecorderChip } from "./RecorderChip"; import { RecorderChip } from "./RecorderChip";
import { ThumbnailHeader } from "./ThumbnailHeader"; import { ThumbnailHeader } from "./ThumbnailHeader";
import { ToolTip } from "./ToolTip"; import { ToolTip } from "./ToolTip";

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import { Chip } from "./Chip"; import { Chip } from "./Chip";
import { Ico, Icon } from "./Ico"; import { Ico, Icon } from "./Ico";
import { Img } from "./Img"; import { Img } from "./Img";
import { Link } from "./Inputs/Link"; import { UpPressable } from "./Containers/UpPressable";
import { DatePickerFragment, PricePickerFragment, UploadImageFragment } from "graphql/generated"; import { DatePickerFragment, PricePickerFragment, UploadImageFragment } from "graphql/generated";
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { prettyDate, prettyDuration, prettyPrice, prettyShortenNumber } from "helpers/formatters"; import { prettyDate, prettyDuration, prettyPrice, prettyShortenNumber } from "helpers/formatters";
@ -47,6 +47,7 @@ interface Props {
duration: number; duration: number;
} }
| { __typename: "anotherHoverlayName" }; | { __typename: "anotherHoverlayName" };
disabled?: boolean;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
@ -67,6 +68,7 @@ export const PreviewCard = ({
metadata, metadata,
hoverlay, hoverlay,
infoAppend, infoAppend,
disabled = false,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { currency } = useUserSettings(); const { currency } = useUserSettings();
const { currencies } = useLocalData(); const { currencies } = useLocalData();
@ -110,10 +112,8 @@ export const PreviewCard = ({
); );
return ( return (
<Link <UpPressable className="grid items-end text-left" href={href} noBackground disabled={disabled}>
href={href} <div className={cJoin("group", cIf(disabled, "pointer-events-none touch-none select-none"))}>
className="group grid cursor-pointer items-end text-left transition-transform
drop-shadow-shade-xl hover:scale-[1.02]">
{thumbnail ? ( {thumbnail ? (
<div <div
className="relative" className="relative"
@ -135,11 +135,13 @@ export const PreviewCard = ({
{hoverlay && hoverlay.__typename === "Video" && ( {hoverlay && hoverlay.__typename === "Video" && (
<> <>
<div <div
className="absolute inset-0 grid place-content-center bg-shade bg-opacity-0 className="group absolute inset-0 grid place-content-center bg-shade bg-opacity-0
text-light transition-colors drop-shadow-shade-lg group-hover:bg-opacity-50"> text-light transition-colors
hover:bg-opacity-50">
<Ico <Ico
icon={Icon.PlayCircleOutline} icon={Icon.PlayCircleOutline}
className="!text-6xl opacity-0 transition-opacity group-hover:opacity-100" className="!text-6xl text-black opacity-0 drop-shadow-lg transition-opacity
shadow-shade group-hover:opacity-100"
/> />
</div> </div>
<div <div
@ -154,7 +156,7 @@ export const PreviewCard = ({
<div <div
style={{ aspectRatio: thumbnailAspectRatio }} style={{ aspectRatio: thumbnailAspectRatio }}
className={cJoin( className={cJoin(
"relative w-full bg-light", "relative w-full bg-highlight",
cIf(keepInfoVisible, "rounded-t-md", "rounded-md notHoverable:rounded-b-none") cIf(keepInfoVisible, "rounded-t-md", "rounded-md notHoverable:rounded-b-none")
)} )}
/> />
@ -164,15 +166,18 @@ export const PreviewCard = ({
"z-20 grid gap-2 p-4 transition-opacity linearbg-obi", "z-20 grid gap-2 p-4 transition-opacity linearbg-obi",
cIf( cIf(
!keepInfoVisible && isHoverable, !keepInfoVisible && isHoverable,
`-inset-x-0.5 bottom-2 opacity-0 [border-radius:10%_10%_10%_10%_/_1%_1%_3%_3%] `-inset-x-0.5 bottom-2 opacity-0 shadow-shade
group-hover:opacity-100 hoverable:absolute hoverable:drop-shadow-shade-lg [border-radius:10%_10%_10%_10%_/_1%_1%_3%_3%]
group-hover:opacity-100 hoverable:absolute hoverable:drop-shadow-lg
notHoverable:rounded-b-md notHoverable:opacity-100`, notHoverable:rounded-b-md notHoverable:opacity-100`,
"[border-radius:0%_0%_10%_10%_/_0%_0%_3%_3%]" "[border-radius:0%_0%_10%_10%_/_0%_0%_3%_3%]"
) )
)}> )}>
{metadata?.position === "Top" && metadataJSX} {metadata?.position === "Top" && metadataJSX}
{topChips && topChips.length > 0 && ( {topChips && topChips.length > 0 && (
<div className="grid grid-flow-col place-content-start gap-1 overflow-hidden"> <div
className="grid grid-flow-col place-content-start gap-1 overflow-x-scroll
scrollbar-none">
{topChips.map((text, index) => ( {topChips.map((text, index) => (
<Chip key={index} text={text} /> <Chip key={index} text={text} />
))} ))}
@ -189,7 +194,7 @@ export const PreviewCard = ({
{bottomChips && bottomChips.length > 0 && ( {bottomChips && bottomChips.length > 0 && (
<div <div
className="grid grid-flow-col place-content-start gap-1 overflow-x-scroll className="grid grid-flow-col place-content-start gap-1 overflow-x-scroll
[scrollbar-width:none] webkit-scrollbar:h-0"> scrollbar-none">
{bottomChips.map((text, index) => ( {bottomChips.map((text, index) => (
<Chip key={index} className="text-sm" text={text} /> <Chip key={index} className="text-sm" text={text} />
))} ))}
@ -200,7 +205,8 @@ export const PreviewCard = ({
{infoAppend} {infoAppend}
</div> </div>
</Link> </div>
</UpPressable>
); );
}; };

View File

@ -1,11 +1,13 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { Chip } from "./Chip"; import { Chip } from "./Chip";
import { Img } from "./Img"; import { Img } from "./Img";
import { Link } from "./Inputs/Link"; import { UpPressable } from "./Containers/UpPressable";
import { UploadImageFragment } from "graphql/generated"; import { UploadImageFragment } from "graphql/generated";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { TranslatedProps } from "types/TranslatedProps"; import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { cIf, cJoin } from "helpers/className";
import { isDefined } from "helpers/others";
/* /*
* *
@ -14,41 +16,44 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
interface Props { interface Props {
thumbnail?: UploadImageFragment | string | null | undefined; thumbnail?: UploadImageFragment | string | null | undefined;
thumbnailAspectRatio?: string;
href: string; href: string;
pre_title?: string | null | undefined; pre_title?: string | null | undefined;
title: string | null | undefined; title: string | null | undefined;
subtitle?: string | null | undefined; subtitle?: string | null | undefined;
topChips?: string[]; topChips?: string[];
bottomChips?: string[]; bottomChips?: string[];
disabled?: boolean;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
const PreviewLine = ({ export const PreviewLine = ({
href, href,
thumbnail, thumbnail,
pre_title, pre_title,
title, title,
subtitle, subtitle,
topChips, topChips,
disabled,
bottomChips, bottomChips,
thumbnailAspectRatio,
}: Props): JSX.Element => ( }: Props): JSX.Element => (
<Link <UpPressable href={href} disabled={disabled}>
href={href} <div
className="flex h-36 w-full cursor-pointer flex-row place-items-center gap-4 overflow-hidden className={cJoin(
rounded-md bg-light pr-4 transition-transform drop-shadow-shade-xl hover:scale-[1.02]"> "grid w-full grid-flow-col place-items-center gap-4",
{thumbnail ? ( cIf(disabled, "pointer-events-none touch-none select-none")
<div className="aspect-[3/2] h-full"> )}>
{thumbnail && (
<div className="h-full w-full">
<Img className="h-full object-cover" src={thumbnail} quality={ImageQuality.Medium} /> <Img className="h-full object-cover" src={thumbnail} quality={ImageQuality.Medium} />
</div> </div>
) : (
<div style={{ aspectRatio: thumbnailAspectRatio }} />
)} )}
<div className="grid gap-2">
<div className={cJoin("grid gap-2 py-4", cIf(isDefined(thumbnail), "pr-3", "px-6"))}>
{topChips && topChips.length > 0 && ( {topChips && topChips.length > 0 && (
<div className="grid grid-flow-col place-content-start gap-1 overflow-hidden"> <div
className="grid grid-flow-col place-content-start gap-1 overflow-scroll
scrollbar-none">
{topChips.map((text, index) => ( {topChips.map((text, index) => (
<Chip key={index} text={text} /> <Chip key={index} text={text} />
))} ))}
@ -60,14 +65,17 @@ const PreviewLine = ({
{subtitle && <p className="leading-none">{subtitle}</p>} {subtitle && <p className="leading-none">{subtitle}</p>}
</div> </div>
{bottomChips && bottomChips.length > 0 && ( {bottomChips && bottomChips.length > 0 && (
<div className="grid grid-flow-col place-content-start gap-1 overflow-hidden"> <div
className="grid grid-flow-col place-content-start gap-1 overflow-scroll
scrollbar-none">
{bottomChips.map((text, index) => ( {bottomChips.map((text, index) => (
<Chip key={index} className="text-sm" text={text} /> <Chip key={index} className="text-sm" text={text} />
))} ))}
</div> </div>
)} )}
</div> </div>
</Link> </div>
</UpPressable>
); );
/* /*

View File

@ -9,6 +9,7 @@ import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { Ids } from "types/ids"; import { Ids } from "types/ids";
import { useLocalData } from "contexts/LocalDataContext"; import { useLocalData } from "contexts/LocalDataContext";
import { useContainerQueries } from "contexts/ContainerQueriesContext"; import { useContainerQueries } from "contexts/ContainerQueriesContext";
import { useHotkeys } from "react-hotkeys-hook";
interface Group<T> { interface Group<T> {
name: string; name: string;
@ -163,6 +164,11 @@ export const SmartList = <T,>({
return memo; return memo;
}, [groups, paginationItemPerPage]); }, [groups, paginationItemPerPage]);
useHotkeys("left", () => setPage((current) => current - 1), { enabled: page > 0 });
useHotkeys("right", () => setPage((current) => current + 1), {
enabled: page < pages.length - 1,
});
return ( return (
<> <>
{pages.length > 1 && paginationSelectorTop && ( {pages.length > 1 && paginationSelectorTop && (

View File

@ -1,6 +1,6 @@
import { Chip } from "components/Chip"; import { Chip } from "components/Chip";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { Markdawn } from "components/Markdown/Markdawn"; import { Markdawn } from "components/Markdown/Markdawn";
import { GetContentTextQuery, UploadImageFragment } from "graphql/generated"; import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters"; import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
@ -48,7 +48,7 @@ export const ThumbnailHeader = ({
return ( return (
<> <>
<div className="mb-12 grid place-items-center gap-12"> <div className="mb-12 grid place-items-center gap-12">
<div className="drop-shadow-shade-lg"> <div className="shadow-shade drop-shadow-lg">
{thumbnail ? ( {thumbnail ? (
<Img <Img
className="cursor-pointer rounded-xl" className="cursor-pointer rounded-xl"

View File

@ -2,7 +2,9 @@ export const cIf = (
condition: boolean | string | null | undefined, condition: boolean | string | null | undefined,
ifTrueCss: string, ifTrueCss: string,
ifFalseCss?: string ifFalseCss?: string
): string => (condition ? ifTrueCss : ifFalseCss ?? ""); ): string => removeWhitespace(condition ? ifTrueCss : ifFalseCss ?? "");
export const cJoin = (...args: (string | undefined)[]): string => export const cJoin = (...args: (string | undefined)[]): string =>
args.filter((elem) => elem).join(" "); removeWhitespace(args.filter((elem) => elem).join(" "));
const removeWhitespace = (string: string): string => string.replaceAll(/\s+/gu, " ");

View File

@ -1,7 +1,7 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Containers/ContentPanel";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";
import { Img } from "components/Img"; import { Img } from "components/Img";
@ -20,7 +20,10 @@ const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
<AppLayout <AppLayout
contentPanel={ contentPanel={
<ContentPanel> <ContentPanel>
<Img src={"/gameover_cards.webp"} className="animate-zoom-in drop-shadow-shade-lg" /> <Img
src={"/gameover_cards.webp"}
className="animate-zoom-in drop-shadow-lg shadow-shade"
/>
<div className="mt-8 grid place-items-center gap-6"> <div className="mt-8 grid place-items-center gap-6">
<h2>{langui.page_not_found}</h2> <h2>{langui.page_not_found}</h2>
<ReturnButton href="/" title="Home" /> <ReturnButton href="/" title="Home" />

View File

@ -1,7 +1,7 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Containers/ContentPanel";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";
import { Img } from "components/Img"; import { Img } from "components/Img";
@ -20,7 +20,10 @@ const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
<AppLayout <AppLayout
contentPanel={ contentPanel={
<ContentPanel> <ContentPanel>
<Img src={"/gameover_cards.webp"} className="animate-zoom-in drop-shadow-shade-lg" /> <Img
src={"/gameover_cards.webp"}
className="animate-zoom-in drop-shadow-lg shadow-shade"
/>
<div className="mt-8 grid place-items-center gap-6"> <div className="mt-8 grid place-items-center gap-6">
<h2>{langui.page_not_found}</h2> <h2>{langui.page_not_found}</h2>
<ReturnButton href="/" title="Home" /> <ReturnButton href="/" title="Home" />

View File

@ -11,8 +11,6 @@ import type { AppProps } from "next/app";
import Script from "next/script"; import Script from "next/script";
import { AppContextProvider } from "contexts/AppLayoutContext"; import { AppContextProvider } from "contexts/AppLayoutContext";
import "styles/animations.css";
import "styles/custom-classes.css";
import "styles/debug.css"; import "styles/debug.css";
import "styles/formatted.css"; import "styles/formatted.css";
import "styles/others.css"; import "styles/others.css";

View File

@ -1,6 +1,6 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useState } from "react"; import { useState } from "react";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { PostPage } from "components/PostPage"; import { PostPage } from "components/PostPage";
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps"; import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";

View File

@ -3,7 +3,7 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";

View File

@ -3,7 +3,7 @@ import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";

View File

@ -5,8 +5,8 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
import { GetVideoChannelQuery } from "graphql/generated"; import { GetVideoChannelQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";

View File

@ -9,8 +9,8 @@ import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel"; import { WithLabel } from "components/Inputs/WithLabel";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
import { GetVideosPreviewQuery } from "graphql/generated"; import { GetVideosPreviewQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";

View File

@ -5,11 +5,11 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import { GetVideoQuery } from "graphql/generated"; import { GetVideoQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
@ -83,7 +83,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
/> />
<div className="grid place-items-center gap-12"> <div className="grid place-items-center gap-12">
<div id="video" className="w-full overflow-hidden rounded-xl shadow-lg shadow-shade"> <div id="video" className="w-full overflow-hidden rounded-xl shadow-xl shadow-shade/80">
{video.gone ? ( {video.gone ? (
<video className="w-full" src={getVideoFile(video.uid)} controls /> <video className="w-full" src={getVideoFile(video.uid)} controls />
) : ( ) : (

View File

@ -5,9 +5,9 @@ import { isDefined, filterHasAttributes } from "helpers/others";
import { ChronicleWithTranslations } from "types/types"; import { ChronicleWithTranslations } from "types/types";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Containers/ContentPanel";
import { Markdawn } from "components/Markdown/Markdawn"; import { Markdawn } from "components/Markdown/Markdawn";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { ThumbnailHeader } from "components/ThumbnailHeader"; import { ThumbnailHeader } from "components/ThumbnailHeader";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { GetChroniclesChaptersQuery } from "graphql/generated"; import { GetChroniclesChaptersQuery } from "graphql/generated";

View File

@ -2,7 +2,7 @@ import { GetStaticProps } from "next";
import { useMemo } from "react"; import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { GetChroniclesChaptersQuery } from "graphql/generated"; import { GetChroniclesChaptersQuery } from "graphql/generated";

View File

@ -7,8 +7,8 @@ import { HorizontalLine } from "components/HorizontalLine";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn"; import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
import { TranslatedReturnButton } from "components/PanelComponents/ReturnButton"; import { TranslatedReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
import { RecorderChip } from "components/RecorderChip"; import { RecorderChip } from "components/RecorderChip";
import { ThumbnailHeader } from "components/ThumbnailHeader"; import { ThumbnailHeader } from "components/ThumbnailHeader";
@ -315,7 +315,6 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
title: prettySlug(previousContent.attributes.slug), title: prettySlug(previousContent.attributes.slug),
}} }}
thumbnail={previousContent.attributes.thumbnail?.data?.attributes} thumbnail={previousContent.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2"
topChips={ topChips={
isContentPanelAtLeast2xl && previousContent.attributes.type?.data?.attributes isContentPanelAtLeast2xl && previousContent.attributes.type?.data?.attributes
? [ ? [
@ -359,7 +358,6 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
}))} }))}
fallback={{ title: nextContent.attributes.slug }} fallback={{ title: nextContent.attributes.slug }}
thumbnail={nextContent.attributes.thumbnail?.data?.attributes} thumbnail={nextContent.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2"
topChips={ topChips={
isContentPanelAtLeast2xl && nextContent.attributes.type?.data?.attributes isContentPanelAtLeast2xl && nextContent.attributes.type?.data?.attributes
? [ ? [

View File

@ -6,8 +6,8 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Select } from "components/Inputs/Select"; import { Select } from "components/Inputs/Select";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { prettyInlineTitle, prettySlug } from "helpers/formatters"; import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { TextInput } from "components/Inputs/TextInput"; import { TextInput } from "components/Inputs/TextInput";

View File

@ -1,8 +1,8 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { useCallback, useMemo } from "react"; import { useMemo } from "react";
import naturalCompare from "string-natural-compare"; import naturalCompare from "string-natural-compare";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes } from "helpers/others"; import { filterHasAttributes } from "helpers/others";
@ -12,17 +12,15 @@ import { prettySlug } from "helpers/formatters";
import { SmartList } from "components/SmartList"; import { SmartList } from "components/SmartList";
import { Ico, Icon } from "components/Ico"; import { Ico, Icon } from "components/Ico";
import { Button, TranslatedButton } from "components/Inputs/Button"; import { Button, TranslatedButton } from "components/Inputs/Button";
import { Link } from "components/Inputs/Link";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { cJoin, cIf } from "helpers/className"; import { cJoin, cIf } from "helpers/className";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";
import { useLocalData } from "contexts/LocalDataContext"; import { useLocalData } from "contexts/LocalDataContext";
import { useContainerQueries } from "contexts/ContainerQueriesContext"; import { useContainerQueries } from "contexts/ContainerQueriesContext";
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
/* /*
* *
@ -274,36 +272,6 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
* PRIVATE COMPONENTS * PRIVATE COMPONENTS
*/ */
interface PreviewFolderProps {
href: string;
title: string | null | undefined;
}
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 transition-transform drop-shadow-shade-xl hover:scale-[1.02]">
{title && <p className="text-center font-headers text-lg font-bold leading-none">{title}</p>}
</Link>
);
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
const TranslatedPreviewFolder = ({
translations,
fallback,
...otherProps
}: TranslatedProps<PreviewFolderProps, "title">): JSX.Element => {
const [selectedTranslation] = useSmartLanguage({
items: translations,
languageExtractor: useCallback((item: { language: string }): string => item.language, []),
});
return <PreviewFolder title={selectedTranslation?.title ?? fallback.title} {...otherProps} />;
};
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
const NoContentNorFolderMessage = () => { const NoContentNorFolderMessage = () => {
const { langui } = useLocalData(); const { langui } = useLocalData();
return ( return (

View File

@ -3,7 +3,7 @@ import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip"; import { Chip } from "components/Chip";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { DevGetContentsQuery } from "graphql/generated"; import { DevGetContentsQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";

View File

@ -3,7 +3,7 @@ import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip"; import { Chip } from "components/Chip";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { import {
DevGetLibraryItemsQuery, DevGetLibraryItemsQuery,

View File

@ -4,8 +4,8 @@ import TurndownService from "turndown";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn"; import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { Popup } from "components/Popup"; import { Popup } from "components/Containers/Popup";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";

View File

@ -0,0 +1,966 @@
/* eslint-disable id-denylist */
import { GetStaticProps } from "next";
import { ReactNode, useState } from "react";
import Slider from "rc-slider";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { getLangui } from "graphql/fetchLocalData";
import { getOpenGraph } from "helpers/openGraph";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { Button } from "components/Inputs/Button";
import { Icon } from "components/Ico";
import { cJoin } from "helpers/className";
import { HorizontalLine } from "components/HorizontalLine";
import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput";
import { Select } from "components/Inputs/Select";
import { WithLabel } from "components/Inputs/WithLabel";
import { NavOption } from "components/PanelComponents/NavOption";
import { ButtonGroup } from "components/Inputs/ButtonGroup";
import { PreviewCard } from "components/PreviewCard";
import { PreviewLine } from "components/PreviewLine";
import { ChroniclePreview } from "components/Chronicles/ChroniclePreview";
import { PreviewFolder } from "components/Contents/PreviewFolder";
/*
*
* PAGE
*/
interface Props extends AppLayoutRequired {}
const DesignSystem = (props: Props): JSX.Element => {
const [switchState, setSwitchState] = useState(false);
const [selectState, setSelectState] = useState(0);
const [sliderState, setSliderState] = useState(5);
const [textInputState, setTextInputState] = useState("");
const [textAreaState, setTextAreaState] = useState("");
const [buttonGroupState, setButtonGroupState] = useState(0);
const contentPanel = (
<ContentPanel
className="grid place-items-center text-center"
width={ContentPanelWidthSizes.Full}>
<h1 className="mb-8 text-4xl">Design System</h1>
<h2 className="mb-4 text-3xl">Colors</h2>
<WhiteSection className="grid grid-cols-[repeat(7,auto)] place-items-center gap-4">
<p />
<p>Highlight</p>
<p>Light</p>
<p>Mid</p>
<p>Dark</p>
<p>Shade</p>
<p>Black</p>
<p>Light theme</p>
<ColorSquare className="bg-highlight" />
<ColorSquare className="bg-light" />
<ColorSquare className="bg-mid" />
<ColorSquare className="bg-dark" />
<ColorSquare className="bg-shade" />
<ColorSquare className="bg-black" />
<p>Dark theme</p>
<ColorSquare className="bg-highlight set-theme-dark" />
<ColorSquare className="bg-light set-theme-dark" />
<ColorSquare className="bg-mid set-theme-dark" />
<ColorSquare className="bg-dark set-theme-dark" />
<ColorSquare className="bg-shade set-theme-dark" />
<ColorSquare className="bg-black set-theme-dark" />
</WhiteSection>
<h2 className="mb-4 text-3xl">Fonts</h2>
<WhiteSection className="grid grid-cols-[repeat(5,auto)] place-items-start gap-y-2 gap-x-12">
<p />
<p className="font-headers text-xl text-black/50">Vollkorn</p>
<p className="font-body text-xl text-black/50">Zen Maru Gothic</p>
<p className="font-mono text-xl text-black/50">Share Tech Mono</p>
<p className="font-openDyslexic text-xl text-black/50">Open Dyslexic</p>
<p className="text-3xl text-black/30">3XL</p>
<p className="font-headers text-3xl">Header H3XL</p>
<p />
<p />
<p className="font-openDyslexic text-3xl">Dyslexia D3XL</p>
<p className="text-2xl text-black/30">2XL</p>
<p className="font-headers text-2xl">Header H2XL</p>
<p />
<p />
<p className="font-openDyslexic text-2xl">Dyslexia D2XL</p>
<p className="text-xl text-black/30">XL</p>
<p className="font-headers text-xl">Header HXL</p>
<p className="font-body text-xl">Body BXL</p>
<p className="font-mono text-xl">Mono MXL</p>
<p className="font-openDyslexic text-xl">Dyslexia DXL</p>
<p className="text-lg text-black/30">LG</p>
<p className="font-headers text-lg">Header HLG</p>
<p className="font-body text-lg">Body BLG</p>
<p className="font-mono text-lg">Mono MLG</p>
<p className="font-openDyslexic text-lg">Dyslexia DLG</p>
<p className="text-base text-black/30">B</p>
<p />
<p className="font-body text-base">Body BB</p>
<p className="font-mono text-base">Mono MB</p>
<p className="font-openDyslexic text-base">Dyslexia DB</p>
<p className="text-sm text-black/30">SM</p>
<p />
<p className="font-body text-sm">Body BSM</p>
<p />
<p className="font-openDyslexic text-sm">Dyslexia DSM</p>
<p className="text-xs text-black/30">XS</p>
<p />
<p />
<p />
<p className="font-openDyslexic text-xs">Dyslexia DXS</p>
</WhiteSection>
<h2 className="mb-4 text-3xl">Elevations</h2>
<TwoThemedSection
className="grid grid-cols-[repeat(7,auto)] place-content-center gap-4
text-left">
<ShadowSquare className="bg-light shadow-inner shadow-shade" text="IN" />
<ShadowSquare className="bg-light shadow-inner-sm shadow-shade" text="IN/SM" />
<ShadowSquare className="bg-light shadow-sm shadow-shade" text="SM" />
<ShadowSquare className="bg-light shadow-md shadow-shade" text="MD" />
<ShadowSquare className="bg-light shadow-lg shadow-shade" text="LG" />
<ShadowSquare className="bg-light shadow-xl shadow-shade" text="XL" />
<ShadowSquare className="bg-light shadow-2xl shadow-shade" text="2XL" />
<p className="mt-6">Drop shadow</p>
<p />
<ShadowSquare className="bg-light drop-shadow-sm shadow-shade" text="SM" />
<ShadowSquare className="bg-light drop-shadow-md shadow-shade" text="MD" />
<ShadowSquare className="bg-light drop-shadow-lg shadow-shade" text="LG" />
<ShadowSquare className="bg-light drop-shadow-xl shadow-shade" text="XL" />
<ShadowSquare className="bg-light drop-shadow-2xl shadow-shade" text="2XL" />
<p className="mt-6">Black</p>
<p />
<ShadowSquare className="bg-black text-light shadow-sm shadow-black" text="SM" />
<ShadowSquare className="bg-black text-light shadow-md shadow-black" text="MD" />
<ShadowSquare className="bg-black text-light shadow-lg shadow-black" text="LG" />
<ShadowSquare className="bg-black text-light shadow-xl shadow-black" text="XL" />
<ShadowSquare className="bg-black text-light shadow-2xl shadow-black" text="2XL" />
<p className="mt-6">
Drop shadow
<br />
black
</p>
<p />
<ShadowSquare className="bg-black text-light drop-shadow-sm shadow-black" text="SM" />
<ShadowSquare className="bg-black text-light drop-shadow-md shadow-black" text="MD" />
<ShadowSquare className="bg-black text-light drop-shadow-lg shadow-black" text="LG" />
<ShadowSquare className="bg-black text-light drop-shadow-xl shadow-black" text="XL" />
<ShadowSquare className="bg-black text-light drop-shadow-2xl shadow-black" text="2XL" />
</TwoThemedSection>
<h2 className="mb-4 text-3xl">Buttons</h2>
<TwoThemedSection className="grid gap-4">
<h3 className="text-xl">Normal sized</h3>
<div className="grid grid-cols-[repeat(4,auto)] place-content-center gap-4">
<p />
<p>Icon</p>
<p>Text</p>
<p>Icon + Text</p>
<p className="self-center justify-self-start">Normal</p>
<Button icon={Icon.Check} />
<Button text="Label" />
<Button icon={Icon.NavigateBefore} text="Label" />
<p className="self-center justify-self-start">Active</p>
<Button icon={Icon.Camera} active />
<Button text="Label" active />
<Button icon={Icon.NavigateBefore} text="Label" active />
<p className="self-center justify-self-start">Disabled</p>
<Button icon={Icon.Air} disabled />
<Button text="Label" disabled />
<Button icon={Icon.NavigateBefore} text="Label" disabled />
<p className="self-center justify-self-start">Badge</p>
<Button icon={Icon.Snooze} badgeNumber={5} />
<Button text="Label" badgeNumber={12} />
<Button icon={Icon.NavigateBefore} text="Label" badgeNumber={201} />
</div>
<HorizontalLine />
<h3 className="text-xl">Small sized</h3>
<div className="grid grid-cols-[repeat(4,auto)] place-content-center gap-4">
<p className="self-center justify-self-start">Normal</p>
<Button icon={Icon.Check} size={"small"} />
<Button text="Label" size={"small"} />
<Button icon={Icon.NavigateBefore} text="Label" size={"small"} />
<p className="self-center justify-self-start">Active</p>
<Button icon={Icon.Camera} active size={"small"} />
<Button text="Label" active size={"small"} />
<Button icon={Icon.NavigateBefore} text="Label" active size={"small"} />
<p className="self-center justify-self-start">Disabled</p>
<Button icon={Icon.Air} disabled size={"small"} />
<Button text="Label" disabled size={"small"} />
<Button icon={Icon.NavigateBefore} text="Label" disabled size={"small"} />
<p className="self-center justify-self-start">Badge</p>
<Button icon={Icon.Snooze} badgeNumber={5} size={"small"} />
<Button text="Label" badgeNumber={12} size={"small"} />
<Button icon={Icon.NavigateBefore} text="Label" badgeNumber={201} size={"small"} />
</div>
<HorizontalLine />
<h3 className="text-xl">Groups</h3>
<div className="grid place-items-center gap-4">
<ButtonGroup buttonsProps={[{ icon: Icon.CallEnd }, { icon: Icon.ZoomInMap }]} />
<ButtonGroup
buttonsProps={[
{ icon: Icon.CarCrash },
{ icon: Icon.TimeToLeave },
{ icon: Icon.LeakAdd },
]}
/>
<ButtonGroup
buttonsProps={[
{ icon: Icon.CarCrash },
{ icon: Icon.TimeToLeave, text: "Label", active: true },
{ text: "Another Label" },
{ icon: Icon.Cable },
]}
/>
<ButtonGroup
buttonsProps={[
{
text: "Try me!",
active: buttonGroupState === 0,
onClick: () => setButtonGroupState(0),
},
{
icon: Icon.AdUnits,
text: "Label",
active: buttonGroupState === 1,
onClick: () => setButtonGroupState(1),
},
{
text: "Yet another label",
active: buttonGroupState === 2,
onClick: () => setButtonGroupState(2),
},
{
icon: Icon.Security,
active: buttonGroupState === 3,
onClick: () => setButtonGroupState(3),
},
]}
/>
</div>
</TwoThemedSection>
<h2 className="mb-4 text-3xl">Inputs</h2>
<TwoThemedSection className="grid place-content-center gap-4">
<h3 className="text-xl">Switches</h3>
<WithLabel label="Off">
<Switch value={false} onClick={() => null} />
</WithLabel>
<WithLabel label="On">
<Switch value={true} onClick={() => null} />
</WithLabel>
<WithLabel label="Disabled (Off)">
<Switch value={false} onClick={() => null} disabled />
</WithLabel>
<WithLabel label="Disabled (On)">
<Switch value={true} onClick={() => null} disabled />
</WithLabel>
<WithLabel label={`Try me! (${switchState ? "On" : "Off"})`}>
<Switch value={switchState} onClick={() => setSwitchState((current) => !current)} />
</WithLabel>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Selects</h3>
<WithLabel label="Empty">
<Select
value={-1}
options={["Option 1", "Option 2", "Option 3", "Option 4"]}
onChange={() => null}
/>
</WithLabel>
<WithLabel label="Filled">
<Select
value={0}
options={["Option 1", "Option 2", "Option 3", "Option 4"]}
onChange={() => null}
/>
</WithLabel>
<WithLabel label="Filled + allow empty">
<Select
value={0}
options={["Option 1", "Option 2", "Option 3", "Option 4"]}
onChange={() => null}
allowEmpty
/>
</WithLabel>
<WithLabel label="Disabled">
<Select
value={0}
options={["Option 1", "Option 2", "Option 3", "Option 4"]}
onChange={() => null}
allowEmpty
disabled
/>
</WithLabel>
<WithLabel label="Try me!">
<Select
value={selectState}
options={["Option 1", "Option 2", "Option 3", "Option 4"]}
onChange={(index) => setSelectState(index)}
allowEmpty
/>
</WithLabel>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Text inputs</h3>
<WithLabel label="Empty">
<TextInput value="" onChange={() => null} />
</WithLabel>
<WithLabel label="Placeholder">
<TextInput value="" placeholder="Placeholder..." onChange={() => null} />
</WithLabel>
<WithLabel label="Filled">
<TextInput value="Value" onChange={() => null} />
</WithLabel>
<WithLabel label="Disabled">
<TextInput value="Value" onChange={() => null} disabled />
</WithLabel>
<WithLabel label="Try me!">
<TextInput
value={textInputState}
onChange={setTextInputState}
placeholder={"Placeholder..."}
/>
</WithLabel>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Text area</h3>
<WithLabel label="Empty">
<textarea value="" name="test" title="aria" />
</WithLabel>
<WithLabel label="Placeholder">
<textarea value="" placeholder="Placeholder..." />
</WithLabel>
<WithLabel label="Filled">
<textarea
value="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio.
Et consequatur dolorem omnis cupiditate."
placeholder="Placeholder..."
/>
</WithLabel>
<WithLabel label="Not resizable">
<textarea
className="resize-none"
value="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio.
Et consequatur dolorem omnis cupiditate."
placeholder="Placeholder..."
/>
</WithLabel>
<WithLabel label="Disabled">
<textarea
value="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio.
Et consequatur dolorem omnis cupiditate."
placeholder="Placeholder..."
disabled
/>
</WithLabel>
<WithLabel label="Try me!">
<textarea
value={textAreaState}
onChange={(event) => setTextAreaState(event.target.value)}
placeholder="Placeholder..."
/>
</WithLabel>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Slider</h3>
<WithLabel label="Normal">
<Slider value={5} />
</WithLabel>
<WithLabel label="Disabled">
<Slider value={5} disabled />
</WithLabel>
<WithLabel label="Try me!">
<Slider
value={sliderState}
max={100}
onChange={(event) => {
let value = 0;
if (Array.isArray(event)) {
value = event[0];
} else {
value = event;
}
setSliderState(() => value);
}}
/>
</WithLabel>
</TwoThemedSection>
<h2 className="mb-4 text-3xl">Down-Pressables</h2>
<TwoThemedSection className="grid gap-4">
<h3 className="mb-2 text-xl">Navigation Options</h3>
<div className="grid grid-cols-[repeat(6,auto)] place-items-center gap-4">
<p />
<p>Title</p>
<p>
Title
<br />+ Icon
</p>
<p>
Title
<br />+ Subtitle
</p>
<p>
Title
<br />+ Subtitle
<br />+ Icon
</p>
<p>Reduced</p>
<p>Normal</p>
<NavOption title="Title" url="#" />
<NavOption icon={Icon.Home} title="Title" url="#" />
<NavOption title="Title" subtitle="This is a subtitle" url="#" />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.CalendarMonth}
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.AccountBalance}
reduced
/>
<p>Border</p>
<NavOption title="Title" url="#" border />
<NavOption icon={Icon.TravelExplore} title="Title" url="#" border />
<NavOption title="Title" subtitle="This is a subtitle" url="#" border />
<NavOption title="Title" subtitle="This is a subtitle" url="#" icon={Icon.Help} border />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.TableRestaurant}
border
reduced
/>
<p>Active</p>
<NavOption title="Title" url="#" active />
<NavOption icon={Icon.Hail} title="Title" url="#" active />
<NavOption title="Title" subtitle="This is a subtitle" url="#" active />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Grading}
active
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Timer}
active
reduced
/>
<p>
Active
<br />+ Border
</p>
<NavOption title="Title" url="#" active />
<NavOption icon={Icon.Upcoming} title="Title" url="#" active border />
<NavOption title="Title" subtitle="This is a subtitle" url="#" active border />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Gamepad}
active
border
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Scale}
active
border
reduced
/>
<p>Disabled</p>
<NavOption title="Title" url="#" disabled />
<NavOption icon={Icon.Lan} title="Title" url="#" disabled />
<NavOption title="Title" subtitle="This is a subtitle" url="#" disabled />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.AlignHorizontalRight}
disabled
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.YoutubeSearchedFor}
reduced
disabled
/>
<p>
Disabled
<br />+ Border
</p>
<NavOption title="Title" url="#" border disabled />
<NavOption icon={Icon.Sanitizer} title="Title" url="#" border disabled />
<NavOption title="Title" subtitle="This is a subtitle" url="#" border disabled />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Pages}
border
disabled
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Synagogue}
border
reduced
disabled
/>
<p>
Disabled
<br />+ Active
</p>
<NavOption title="Title" url="#" active disabled />
<NavOption icon={Icon.Stairs} title="Title" url="#" active disabled />
<NavOption title="Title" subtitle="This is a subtitle" url="#" active disabled />
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Park}
active
disabled
/>
<NavOption
title="Title"
subtitle="This is a subtitle"
url="#"
icon={Icon.Password}
active
reduced
disabled
/>
</div>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Chronology Previews</h3>
<div className="grid grid-cols-[repeat(5,auto)] place-items-center gap-4">
<p />
<p>Title</p>
<p>Year</p>
<p>
Year
<br />+ Month
</p>
<p>
Year
<br />+ Month
<br />+ Day
</p>
<p>Normal</p>
<ChroniclePreview date={{}} title="Title" url="#" />
<ChroniclePreview date={{ year: 1970 }} title="Title" url="#" />
<ChroniclePreview date={{ year: 1970, month: 1 }} title="Title" url="#" />
<ChroniclePreview date={{ year: 1970, month: 1, day: 1 }} title="Title" url="#" />
<p>Active</p>
<ChroniclePreview date={{}} title="Title" url="#" active />
<ChroniclePreview date={{ year: 1970 }} title="Title" url="#" active />
<ChroniclePreview date={{ year: 1970, month: 1 }} title="Title" url="#" active />
<ChroniclePreview date={{ year: 1970, month: 1, day: 1 }} title="Title" url="#" active />
<p>Disabled</p>
<ChroniclePreview date={{}} title="Title" url="#" disabled />
<ChroniclePreview date={{ year: 1970 }} title="Title" url="#" disabled />
<ChroniclePreview date={{ year: 1970, month: 1 }} title="Title" url="#" disabled />
<ChroniclePreview
date={{ year: 1970, month: 1, day: 1 }}
title="Title"
url="#"
disabled
/>
<p>
Disabled
<br />
Active
</p>
<ChroniclePreview date={{}} title="Title" url="#" active disabled />
<ChroniclePreview date={{ year: 1970 }} title="Title" url="#" active disabled />
<ChroniclePreview date={{ year: 1970, month: 1 }} title="Title" url="#" active disabled />
<ChroniclePreview
date={{ year: 1970, month: 1, day: 1 }}
title="Title"
url="#"
active
disabled
/>
</div>
</TwoThemedSection>
<h2 className="mb-4 text-3xl">Up-Pressables</h2>
<TwoThemedSection className="grid gap-4">
<h3 className="-mt-6 mb-2 text-xl">Preview Cards</h3>
<div className="grid grid-cols-[repeat(2,auto)] place-items-center gap-4">
<PreviewCard
title="This one only has a title"
subtitle="And a subtitle"
href="#"
keepInfoVisible
/>
<PreviewCard
title="This one only has a title/subtitle"
subtitle="And a long description"
description="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio."
href="#"
thumbnail={"/default_og.jpg"}
keepInfoVisible
/>
<PreviewCard
pre_title="Breaking News"
title="This one only displays info"
subtitle="When it's hovered"
href="#"
thumbnail={"/default_og.jpg"}
/>
<PreviewCard
title="This one also has metadata at the top"
subtitle="And a subtitle"
description="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio."
href="#"
thumbnail={"/default_og.jpg"}
metadata={{
price: {
amount: 5.23,
currency: { data: { attributes: { code: "USD", rate_to_usd: 1, symbol: "$" } } },
},
releaseDate: { year: 1970, month: 1, day: 1 },
views: 550669,
position: "Top",
}}
/>
<PreviewCard
title="This one also has metadata at the bottom"
subtitle="And the thumbnail aspect ratio is forced to be 4:3"
href="#"
thumbnail={"/default_og.jpg"}
keepInfoVisible
thumbnailAspectRatio="4/3"
thumbnailForceAspectRatio
metadata={{
price: {
amount: 5.23,
currency: { data: { attributes: { code: "USD", rate_to_usd: 1, symbol: "$" } } },
},
releaseDate: { year: 1970, month: 1, day: 1 },
views: 550669,
position: "Bottom",
}}
/>
<PreviewCard
pre_title="Wow, that's a lot"
title="This one pretty much has everything"
subtitle="No joke, this is a lot of stuff"
href="#"
thumbnail={"/default_og.jpg"}
keepInfoVisible
infoAppend={<Button text="Another custom component" />}
bottomChips={["Bottom chip 1", "Chip 2", "Chip 3", "Chip 4"]}
description="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio."
hoverlay={{ __typename: "Video", duration: 465 }}
topChips={[
"Top chip 1",
"Chip 2",
"Chip 3",
"Chip 4",
"When there are too many, it overflow",
]}
metadata={{
price: {
amount: 5.23,
currency: { data: { attributes: { code: "USD", rate_to_usd: 1, symbol: "$" } } },
},
releaseDate: { year: 1970, month: 1, day: 1 },
views: 550669,
position: "Bottom",
}}
/>
<PreviewCard
title="This one is disabled"
subtitle="And a subtitle"
href="#"
thumbnail={"/default_og.jpg"}
keepInfoVisible
metadata={{
price: {
amount: 5.23,
currency: { data: { attributes: { code: "USD", rate_to_usd: 1, symbol: "$" } } },
},
releaseDate: { year: 1970, month: 1, day: 1 },
views: 550669,
position: "Bottom",
}}
disabled
/>
<PreviewCard
pre_title="Wow, that's a lot"
title="This one pretty much has everything"
subtitle="And it's disabled"
href="#"
thumbnail={"/default_og.jpg"}
keepInfoVisible
infoAppend={<Button text="Another custom component" />}
bottomChips={["Bottom chip 1", "Chip 2", "Chip 3", "Chip 4"]}
description="Eveniet occaecati qui dicta explicabo dolor.
Ipsum quam dolorum dolores.
Neque dolor nihil neque tempora.
Mollitia voluptates iste qui et temporibus eum omnis.
Itaque atque architecto maiores qui et optio."
hoverlay={{ __typename: "Video", duration: 465 }}
topChips={[
"Top chip 1",
"Chip 2",
"Chip 3",
"Chip 4",
"When there are too many, it overflow",
]}
metadata={{
price: {
amount: 5.23,
currency: { data: { attributes: { code: "USD", rate_to_usd: 1, symbol: "$" } } },
},
releaseDate: { year: 1970, month: 1, day: 1 },
views: 550669,
position: "Bottom",
}}
disabled
/>
</div>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Preview Line</h3>
<div className="grid grid-cols-[repeat(2,auto)] place-items-center gap-4">
<PreviewLine
href="#"
pre_title="Breaking News"
title="Accord's Library is live"
subtitle="I know, big deal, this is subtitle"
/>
<PreviewLine
href="#"
pre_title="Breaking News"
title="Accord's Library is live"
subtitle="I know, big deal, this is subtitle"
thumbnail={"/default_og.jpg"}
/>
<PreviewLine
href="#"
pre_title="Breaking News"
title="Accord's Library is live"
subtitle="I know, big deal, this is subtitle"
thumbnail={"/default_og.jpg"}
topChips={["Top chip 1", "Chip 2", "Chip 3", "Chip 4"]}
/>
<PreviewLine
href="#"
pre_title="Breaking News"
title="This one has everything"
subtitle="I know, big deal, this is subtitle"
thumbnail={"/default_og.jpg"}
topChips={["Top chip 1", "Chip 2", "Chip 3", "Chip 4"]}
bottomChips={["Bottom chip 1", "Chip 2", "Chip 3", "Chip 4"]}
/>
<PreviewLine
href="#"
title="Just a title"
thumbnail={"/default_og.jpg"}
topChips={["Top chip 1", "Chip 2", "Chip 3", "Chip 4"]}
bottomChips={["Bottom chip 1", "Chip 2", "Chip 3", "Chip 4"]}
/>
<PreviewLine
href="#"
title="Disabled"
thumbnail={"/default_og.jpg"}
topChips={["Top chip 1", "Chip 2", "Chip 3", "Chip 4"]}
bottomChips={["Bottom chip 1", "Chip 2", "Chip 3", "Chip 4"]}
disabled
/>
</div>
<HorizontalLine />
<h3 className="-mt-6 mb-2 text-xl">Folder Card</h3>
<div className="grid grid-cols-[repeat(2,auto)] place-items-center gap-4">
<PreviewFolder href="#" title="Title" />
<PreviewFolder href="#" title="A longer title, I guess" />
<PreviewFolder href="#" title="Disabled" disabled />
<PreviewFolder href="#" title="Disabled, with a longer title" disabled />
</div>
</TwoThemedSection>
</ContentPanel>
);
return <AppLayout {...props} contentPanel={contentPanel} />;
};
export default DesignSystem;
/*
*
* NEXT DATA FETCHING
*/
export const getStaticProps: GetStaticProps = (context) => {
const langui = getLangui(context.locale);
const props: Props = {
openGraph: getOpenGraph(langui, "Design System"),
};
return {
props: props,
};
};
/*
*
* PRIVATE COMPONENTS
*/
interface ThemedSectionProps {
className?: string;
children?: ReactNode;
}
const TwoThemedSection = ({ children, className }: ThemedSectionProps) => (
<div className="mb-12 grid grid-flow-col drop-shadow-lg shadow-shade">
<LightThemeSection className={cJoin("rounded-l-xl text-black", className)}>
{children}
</LightThemeSection>
<DarkThemeSection className={cJoin("rounded-r-xl text-black", className)}>
{children}
</DarkThemeSection>
</div>
);
const DarkThemeSection = ({ className, children }: ThemedSectionProps) => (
<div className={cJoin("bg-light py-10 px-14 set-theme-dark", className)}>{children}</div>
);
const LightThemeSection = ({ className, children }: ThemedSectionProps) => (
<div className={cJoin("bg-light py-10 px-14 set-theme-light", className)}>{children}</div>
);
const WhiteSection = ({ className, children }: ThemedSectionProps) => (
<div
className={cJoin(
`mb-12 rounded-xl bg-[white] py-10 px-14 text-black drop-shadow-lg shadow-shade
set-theme-light`,
className
)}>
{children}
</div>
);
interface ColorSquareProps {
className?: string;
}
const ColorSquare = ({ className }: ColorSquareProps) => (
<div className={cJoin("h-24 w-24 rounded-lg shadow-inner-sm shadow-shade", className)} />
);
interface ShadowSquareProps {
className?: string;
text: string;
}
const ShadowSquare = ({ className, text }: ShadowSquareProps) => (
<div className={cJoin("mb-12 grid h-20 w-20 place-content-center rounded-lg", className)}>
{text}
</div>
);

View File

@ -3,7 +3,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { ButtonGroup } from "components/Inputs/ButtonGroup"; import { ButtonGroup } from "components/Inputs/ButtonGroup";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";

View File

@ -7,12 +7,12 @@ import { Chip } from "components/Chip";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard } from "components/PreviewCard"; import { PreviewCard } from "components/PreviewCard";
import { import {
Enum_Componentmetadatabooks_Binding_Type, Enum_Componentmetadatabooks_Binding_Type,
@ -167,7 +167,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
<div className="grid place-items-center gap-12"> <div className="grid place-items-center gap-12">
<div <div
className={cJoin( className={cJoin(
"relative h-[50vh] w-full cursor-pointer drop-shadow-shade-xl", "relative h-[50vh] w-full cursor-pointer drop-shadow-xl shadow-shade",
cIf(isContentPanelAtLeast3xl, "mb-16", "h-[60vh]") cIf(isContentPanelAtLeast3xl, "mb-16", "h-[60vh]")
)}> )}>
{item.thumbnail?.data?.attributes ? ( {item.thumbnail?.data?.attributes ? (
@ -248,7 +248,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
<Fragment key={galleryItem.id}> <Fragment key={galleryItem.id}>
<div <div
className="relative aspect-square cursor-pointer className="relative aspect-square cursor-pointer
transition-transform hover:scale-[1.02]" transition-transform hover:scale-102"
onClick={() => { onClick={() => {
showLightBox( showLightBox(
filterHasAttributes(item.gallery?.data, ["attributes"] as const).map( filterHasAttributes(item.gallery?.data, ["attributes"] as const).map(
@ -258,8 +258,8 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
); );
}}> }}>
<Img <Img
className="h-full w-full rounded-lg className="h-full w-full rounded-lg bg-light object-cover shadow-md
bg-light object-cover drop-shadow-shade-md" shadow-shade/30 transition-shadow hover:shadow-lg hover:shadow-shade/50"
src={galleryItem.attributes} src={galleryItem.attributes}
/> />
</div> </div>

View File

@ -20,12 +20,12 @@ import {
} from "helpers/others"; } from "helpers/others";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { getAssetFilename, ImageQuality } from "helpers/img"; import { getAssetFilename, ImageQuality } from "helpers/img";
import { cIf, cJoin } from "helpers/className"; import { cIf, cJoin } from "helpers/className";
import { clamp, isInteger } from "helpers/numbers"; import { clamp, isInteger } from "helpers/numbers";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { Ids } from "types/ids"; import { Ids } from "types/ids";
@ -1045,8 +1045,8 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{pages.map((page, index) => ( {pages.map((page, index) => (
<div <div
key={page.id} key={page.id}
className="cursor-pointer transition-transform className="cursor-pointer drop-shadow-lg
drop-shadow-shade-lg hover:scale-[1.02]" transition-transform shadow-shade hover:scale-102"
onClick={() => { onClick={() => {
onClickOnImage(index); onClickOnImage(index);
}}> }}>

View File

@ -6,8 +6,8 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Select } from "components/Inputs/Select"; import { Select } from "components/Inputs/Select";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { GetLibraryItemsPreviewQuery } from "graphql/generated"; import { GetLibraryItemsPreviewQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { prettyInlineTitle, prettyItemSubType } from "helpers/formatters"; import { prettyInlineTitle, prettyItemSubType } from "helpers/formatters";

View File

@ -1,7 +1,7 @@
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getOpenGraph } from "helpers/openGraph"; import { getOpenGraph } from "helpers/openGraph";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui } from "graphql/fetchLocalData";

View File

@ -4,8 +4,8 @@ import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { GetPostsPreviewQuery } from "graphql/generated"; import { GetPostsPreviewQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";

View File

@ -6,8 +6,8 @@ import { Chip } from "components/Chip";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import DefinitionCard from "components/Wiki/DefinitionCard"; import DefinitionCard from "components/Wiki/DefinitionCard";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others"; import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";

View File

@ -2,10 +2,10 @@ import { GetStaticProps } from "next";
import { Fragment, useCallback, useMemo } from "react"; import { Fragment, useCallback, useMemo } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { InsetBox } from "components/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { ReturnButton } from "components/PanelComponents/ReturnButton"; import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel } from "components/Panels/ContentPanel"; import { ContentPanel } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { import {
Enum_Componenttranslationschronologyitem_Status, Enum_Componenttranslationschronologyitem_Status,
GetChronologyItemsQuery, GetChronologyItemsQuery,

View File

@ -4,11 +4,11 @@ import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { NavOption } from "components/PanelComponents/NavOption"; import { NavOption } from "components/PanelComponents/NavOption";
import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Panels/SubPanel"; import { SubPanel } from "components/Containers/SubPanel";
import { Icon } from "components/Ico"; import { Icon } from "components/Ico";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { GetWikiPageQuery, GetWikiPagesPreviewsQuery } from "graphql/generated"; import { GetWikiPageQuery, GetWikiPagesPreviewsQuery } from "graphql/generated";
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Button } from "components/Inputs/Button"; import { Button } from "components/Inputs/Button";
import { Switch } from "components/Inputs/Switch"; import { Switch } from "components/Inputs/Switch";

View File

@ -1,32 +0,0 @@
.animation-carret {
animation-name: blink;
animation-duration: 1s;
animation-timing-function: step-end;
animation-iteration-count: infinite;
}
.animate-zoom-in {
animation-name: zoom-in;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: 1;
}
@keyframes zoom-in {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes blink {
from,
to {
border-bottom-style: solid;
}
50% {
border-bottom-style: none;
}
}

View File

@ -1,4 +0,0 @@
.texture-paper-dots {
@apply bg-[length:10cm] bg-local [background-image:var(--theme-texture-dots)]
[background-blend-mode:var(--theme-texture-dots-blend)];
}

View File

@ -7,7 +7,7 @@
} }
* { * {
@apply box-border scroll-m-[40vh] scroll-smooth ![-webkit-tap-highlight-color:transparent]; @apply box-border scroll-m-[40vh] scroll-smooth scrollbar-thin ![-webkit-tap-highlight-color:transparent];
} }
h1, h1,
@ -32,31 +32,12 @@ mark {
@apply bg-mid px-2; @apply bg-mid px-2;
} }
/* SCROLLBARS STYLING */
* {
@apply [scrollbar-color:theme(colors.dark/1)_transparent] [scrollbar-width:thin];
}
*::-webkit-scrollbar {
@apply w-3;
}
*::-webkit-scrollbar-track {
@apply bg-light;
}
*::-webkit-scrollbar-thumb {
@apply rounded-full border-2 border-solid border-light bg-dark;
}
/* INPUT */ /* INPUT */
input, input,
textarea { textarea {
@apply rounded-full bg-light p-2 text-center text-dark outline outline-1 -outline-offset-1 @apply rounded-full bg-[transparent] p-2 text-center text-dark outline outline-1 -outline-offset-1
outline-mid transition-all placeholder:text-dark placeholder:opacity-60 hover:bg-mid outline-mid transition-all placeholder:text-dark placeholder:opacity-60;
hover:outline-transparent;
} }
input::placeholder { input::placeholder {
@ -65,7 +46,7 @@ input::placeholder {
input:focus-visible, input:focus-visible,
textarea:focus-within { textarea:focus-within {
@apply bg-mid shadow-inner-sm shadow-shade outline-none; @apply bg-mid shadow-inner-sm outline-none shadow-shade;
} }
textarea { textarea {
@ -74,7 +55,21 @@ textarea {
input[type="submit"] { input[type="submit"] {
@apply grid cursor-pointer place-content-center place-items-center rounded-full border @apply grid cursor-pointer place-content-center place-items-center rounded-full border
border-dark px-4 pt-[0.4rem] pb-[0.5rem] text-dark outline-none transition-all hover:bg-dark border-dark px-4 pt-[0.4rem] pb-[0.5rem] text-dark outline-none transition-all shadow-shade
hover:text-light hover:drop-shadow-shade-lg active:border-black active:bg-black hover:bg-dark hover:text-light hover:drop-shadow-lg active:border-black active:bg-black
active:text-light active:drop-shadow-black-lg; active:text-light active:drop-shadow-lg active:shadow-black;
}
input:enabled,
textarea:enabled {
@apply hover:bg-mid hover:outline-transparent;
}
input:disabled,
textarea:disabled {
@apply cursor-not-allowed opacity-50 outline-dark/60 grayscale;
}
textarea {
@apply scrollbar-none;
} }

View File

@ -6,7 +6,7 @@
} }
.tippy-box { .tippy-box {
@apply relative rounded-lg bg-light transition-[transform,visibility,opacity] @apply relative rounded-lg bg-light transition-[transform,visibility,opacity]
drop-shadow-shade-xl; shadow-shade shadow-xl;
} }
.tippy-box[data-placement^="top"] > .tippy-arrow { .tippy-box[data-placement^="top"] > .tippy-arrow {
@apply bottom-0; @apply bottom-0;

View File

@ -15,6 +15,7 @@ module.exports = {
dark: "rgb(var(--theme-color-dark) / <alpha-value>)", dark: "rgb(var(--theme-color-dark) / <alpha-value>)",
shade: "rgb(var(--theme-color-shade) / <alpha-value>)", shade: "rgb(var(--theme-color-shade) / <alpha-value>)",
black: "rgb(var(--theme-color-black) / <alpha-value>)", black: "rgb(var(--theme-color-black) / <alpha-value>)",
transparent: "transparent",
}, },
fontFamily: { fontFamily: {
body: "var(--theme-font-body)", body: "var(--theme-font-body)",
@ -51,22 +52,147 @@ module.exports = {
1: "0.15rem", 1: "0.15rem",
2: "0.17rem", 2: "0.17rem",
}, },
extend: { borderRadius: {
boxShadow: { none: "0",
"inner-sm": "inset 0 1px 4px -2px", sm: "0.125rem",
DEFAULT: "0.25rem",
md: "0.375rem",
lg: "0.5rem",
xl: "0.75rem",
"2xl": "1rem",
"3xl": "1.25rem",
"4xl": "2rem",
full: "9999rem",
}, },
boxShadow: {
sm: "0 1px 2px 0",
DEFAULT: ["0 1px 3px 0", "0 1px 2px -1px"],
md: ["0 4px 6px -1px", "0 1px 4px -2px"],
lg: ["0 10px 15px -3px", "0 0 6px -4px"],
xl: ["0 20px 25px -5px", "0 0 10px -6px"],
"2xl": ["0 25px 50px -5px", "0 15px 20px -5px"],
inner: "inset 0 2px 4px 0",
"inner-sm": "inset 0 1px 4px -2px",
none: "0 0 #0000",
},
dropShadow: {
none: "0 0 var(--tw-raw-shadow-color)",
sm: "0 2px 1px rgb(var(--tw-raw-shadow-color) / 0.5)",
DEFAULT: [
"0 1px 2px rgb(var(--tw-raw-shadow-color) / 0.1)",
"0 1px 1px rgb(var(--tw-raw-shadow-color) / 0.6)",
],
md: [
"0 4px 3px rgb(var(--tw-raw-shadow-color) / 0.4)",
"0 2px 2px rgb(var(--tw-raw-shadow-color) / 0.6)",
],
lg: [
"0 10px 8px rgb(var(--tw-raw-shadow-color) / 0.5)",
"0 4px 4px rgb(var(--tw-raw-shadow-color) / 0.7)",
],
xl: [
"0 20px 13px rgb(var(--tw-raw-shadow-color) / 0.4)",
"0 8px 8px rgb(var(--tw-raw-shadow-color) / 0.7)",
],
"2xl": [
"0 15px 16px rgb(var(--tw-raw-shadow-color) / 0.7)",
"0 10px 8px rgb(var(--tw-raw-shadow-color) / 0.6)",
"0 0px 2px rgb(var(--tw-raw-shadow-color) / 0.2)",
],
},
extend: {
transitionProperty: { transitionProperty: {
height: "height, max-height, min-height", height: "height, max-height, min-height",
filter: "filter, backdrop-filter", filter: "filter, backdrop-filter",
colors: colors:
"color, background-color, border-color, text-decoration-color, fill, stroke, outline-color", "color, background-color, border-color, text-decoration-color, fill, stroke, outline-color",
}, },
outlineColor: { scale: {
transparent: "transparent", 102: "1.02",
}, },
}, },
}, },
plugins: [ plugins: [
/* Add support for coloring drop shadows */
plugin(function ({ matchUtilities, theme }) {
matchUtilities(
{
shadow: (value) => ({
"--tw-raw-shadow-color": value.slice(4, value.length - 17),
}),
},
{ values: theme("boxShadowColor") }
);
}),
/* Add support for scrollbar styling */
plugin(({ addUtilities, theme }) => {
addUtilities({
".scrollbar-none": {
"scrollbar-width": "none",
"&::-webkit-scrollbar": {
display: "none",
},
},
".scrollbar-thin": {
scrollbarWidth: "thin",
scrollbarColor: `rgb(var(--theme-color-dark)) transparent`,
"&::-webkit-scrollbar": {
width: theme("width.3"),
height: theme("width.3"),
},
"&::-webkit-scrollbar-track": {
background: "rgb(var(--theme-color-light))",
},
"&::-webkit-scrollbar-thumb": {
background: "rgb(var(--theme-color-dark))",
borderRadius: theme("borderRadius.full"),
borderWidth: theme("borderWidth.2"),
borderColor: "rgb(var(--theme-color-light))",
borderStyle: "solid",
},
},
});
}),
/* Add custom animations */
plugin(({ addComponents }) => {
addComponents({
".animate-carret": {
animationName: "blink",
animationDuration: "1s",
animationTimingFunction: "step-end",
animationIterationCount: "infinite",
},
".animate-zoom-in": {
animationName: "zoom-in",
animationDuration: "2s",
animationTimingFunction: "ease-in-out",
animationIterationCount: "1",
},
"@keyframes blink": {
from: {
borderBottomStyle: "solid",
},
"50%": {
borderBottomStyle: "none",
},
to: {
borderBottomStyle: "solid",
},
},
"@keyframes zoom-in": {
from: {
transform: "scale(0)",
},
to: {
transform: "scale(1)",
},
},
});
}),
/* CSS colors setters */
plugin(({ addUtilities }) => { plugin(({ addUtilities }) => {
addUtilities({ addUtilities({
".set-theme-light": { ".set-theme-light": {
@ -92,6 +218,7 @@ module.exports = {
}); });
}), }),
/* CSS fonts setters */
plugin(({ addUtilities }) => { plugin(({ addUtilities }) => {
addUtilities({ addUtilities({
".set-theme-font-standard": { ".set-theme-font-standard": {
@ -107,75 +234,38 @@ module.exports = {
}); });
}), }),
plugin(({ addVariant, e }) => { /* Linear background colors presets */
addVariant("webkit-scrollbar", ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`webkit-scrollbar${separator}${className}`)}::-webkit-scrollbar`;
});
});
}),
// Colored Dropshadow
plugin(({ addUtilities }) => {
addUtilities({
".drop-shadow-shade-md": {
filter: `
drop-shadow(0 4px 3px rgb(var(--theme-color-shade) / 0.15))
drop-shadow(0 2px 2px rgb(var(--theme-color-shade) / 0.2))`,
},
".drop-shadow-shade-lg": {
filter: `
drop-shadow(0 10px 8px rgb(var(--theme-color-shade) / 0.2))
drop-shadow(0 4px 3px rgb(var(--theme-color-shade) / 0.4))`,
},
".drop-shadow-shade-xl": {
filter: `
drop-shadow(0 20px 13px rgb(var(--theme-color-shade) / 0.25))
drop-shadow(0 8px 5px rgb(var(--theme-color-shade) / 0.7))`,
},
".drop-shadow-shade-2xl": {
filter: `drop-shadow(0 25px 25px rgb(var(--theme-color-shade) / 0.8))`,
},
".drop-shadow-black-md": {
filter: `
drop-shadow(0 4px 3px rgb(var(--theme-color-black) / 0.15))
drop-shadow(0 2px 2px rgb(var(--theme-color-black) / 0.2))`,
},
".drop-shadow-black-lg": {
filter: `
drop-shadow(0 10px 8px rgb(var(--theme-color-black) / 0.2))
drop-shadow(0 4px 3px rgb(var(--theme-color-black) / 0.4))`,
},
".drop-shadow-black-xl": {
filter: `
drop-shadow(0 20px 13px rgb(var(--theme-color-black) / 0.25))
drop-shadow(0 8px 5px rgb(var(--theme-color-black) / 0.7))`,
},
".drop-shadow-black-2xl": {
filter: `drop-shadow(0 25px 25px rgb(var(--theme-color-black) / 0.8))`,
},
});
}),
plugin(({ addUtilities }) => { plugin(({ addUtilities }) => {
addUtilities({ addUtilities({
".linearbg-obi": { ".linearbg-obi": {
background: `linear-gradient( background: `linear-gradient(
to right, to right,
rgb(var(--theme-color-mid)), rgb(var(--theme-color-mid)),
rgb(var(--theme-color-light)) 3%, rgb(var(--theme-color-highlight)) 3%,
rgb(var(--theme-color-light)) 97%, rgb(var(--theme-color-highlight)) 97%,
rgb(var(--theme-color-mid)) rgb(var(--theme-color-mid))
)`, )`,
}, },
}); });
}), }),
/* Add support for break-wrods CSS attribute */
plugin(({ addUtilities }) => { plugin(({ addUtilities }) => {
addUtilities({ addUtilities({
".break-words": { ".break-words": {
"word-break": "break-word", wordBreak: "break-word",
},
});
}),
/* Custom background texture */
plugin(({ addUtilities }) => {
addUtilities({
".texture-paper-dots": {
backgroundSize: "10cm",
backgroundAttachment: "local",
backgroundImage: "var(--theme-texture-dots)",
backgroundBlendMode: "var(--theme-texture-dots-blend)",
}, },
}); });
}), }),

View File

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ES6",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"importHelpers": true, "importHelpers": true,
"allowJs": true, "allowJs": true,