Now using expression function style + some sections
This commit is contained in:
parent
8b80ec4ca3
commit
be1a32181e
|
@ -41,7 +41,7 @@ module.exports = {
|
|||
eqeqeq: "error",
|
||||
"func-name-matching": "warn",
|
||||
"func-names": "warn",
|
||||
"func-style": ["warn", "declaration"],
|
||||
"func-style": ["warn", "expression"],
|
||||
"grouped-accessor-pairs": "warn",
|
||||
"guard-for-in": "warn",
|
||||
"id-denylist": ["error", "data", "err", "e", "cb", "callback", "i"],
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# CONTRIBUTING
|
||||
|
||||
## Styling choices
|
||||
|
||||
### Pages
|
||||
|
||||
```tsx
|
||||
import ...
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const MY_CONSTANT = "value"
|
||||
const DEFAULT_FILTERS_STATE = {}
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {}
|
||||
|
||||
const PageName = () => {}
|
||||
export default PageName;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {}
|
||||
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Component1Interface {}
|
||||
const Component1 = () => {}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
interface Component2Interface {}
|
||||
const Component2 = () => {}
|
||||
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
```tsx
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const MY_CONSTANT = "value";
|
||||
const DEFAULT_FILTERS_STATE = {};
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface ComponentProps {}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Component = () => {};
|
||||
```
|
|
@ -27,6 +27,19 @@ import { ContentPlaceholder } from "./PanelComponents/ContentPlaceholder";
|
|||
import { MainPanel } from "./Panels/MainPanel";
|
||||
import { Popup } from "./Popup";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const SENSIBILITY_SWIPE = 1.1;
|
||||
const TITLE_PREFIX = "Accord’s Library";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
subPanel?: React.ReactNode;
|
||||
subPanelIcon?: Icon;
|
||||
|
@ -38,11 +51,9 @@ interface Props extends AppStaticProps {
|
|||
contentPanelScroolbar?: boolean;
|
||||
}
|
||||
|
||||
const SENSIBILITY_SWIPE = 1.1;
|
||||
const TITLE_PREFIX = "Accord’s Library";
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export function AppLayout(props: Props): JSX.Element {
|
||||
const {
|
||||
export const AppLayout = ({
|
||||
langui,
|
||||
currencies,
|
||||
languages,
|
||||
|
@ -54,8 +65,7 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
description,
|
||||
subPanelIcon = Icon.Tune,
|
||||
contentPanelScroolbar = true,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
const {
|
||||
configPanelOpen,
|
||||
currency,
|
||||
|
@ -517,4 +527,4 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Chip(props: Props): JSX.Element {
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Chip = ({ className, children }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin(
|
||||
`grid place-content-center place-items-center whitespace-nowrap rounded-full
|
||||
border-[1px] px-1.5 pb-[0.14rem] text-xs opacity-70
|
||||
transition-[color,_opacity,_border-color] hover:opacity-100`,
|
||||
props.className
|
||||
className
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function HorizontalLine(props: Props): JSX.Element {
|
||||
const { className } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const HorizontalLine = ({ className }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin(
|
||||
"my-8 h-0 w-full border-t-[3px] border-dotted border-black",
|
||||
|
@ -14,4 +19,3 @@ export function HorizontalLine(props: Props): JSX.Element {
|
|||
)}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
import { MouseEventHandler } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
onClick?: MouseEventHandler<HTMLSpanElement> | undefined;
|
||||
icon: Icon;
|
||||
}
|
||||
|
||||
export function Ico(props: Props): JSX.Element {
|
||||
const { onClick, icon, className } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Ico = ({ onClick, icon, className }: Props): JSX.Element => (
|
||||
<span
|
||||
onClick={onClick}
|
||||
className={cJoin(
|
||||
|
@ -21,7 +25,11 @@ export function Ico(props: Props): JSX.Element {
|
|||
{icon}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* ╭─────────╮
|
||||
* ─────────────────────────────────────────╯ OTHER ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
export enum Icon {
|
||||
Onek = "1k",
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { getAssetURL, getImgSizesByQuality, ImageQuality } from "helpers/img";
|
||||
|
||||
import { ImageProps } from "next/image";
|
||||
import { MouseEventHandler } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
image?: UploadImageFragment | string;
|
||||
|
@ -12,15 +16,15 @@ interface Props {
|
|||
onClick?: MouseEventHandler<HTMLImageElement>;
|
||||
}
|
||||
|
||||
export function Img(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Img = ({
|
||||
className,
|
||||
image,
|
||||
quality = ImageQuality.Small,
|
||||
alt,
|
||||
onClick,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
if (typeof image === "string") {
|
||||
return (
|
||||
<img className={className} src={image} alt={alt ?? ""} loading="lazy" />
|
||||
|
@ -40,4 +44,4 @@ export function Img(props: Props): JSX.Element {
|
|||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { MouseEventHandler } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
id?: string;
|
||||
className?: string;
|
||||
|
@ -20,8 +24,9 @@ interface Props {
|
|||
badgeNumber?: number;
|
||||
}
|
||||
|
||||
export function Button(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Button = ({
|
||||
draggable,
|
||||
id,
|
||||
onClick,
|
||||
|
@ -33,8 +38,7 @@ export function Button(props: Props): JSX.Element {
|
|||
href,
|
||||
locale,
|
||||
badgeNumber,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
|
@ -87,17 +91,19 @@ export function Button(props: Props): JSX.Element {
|
|||
</div>
|
||||
</ConditionalWrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface LinkWrapperProps {
|
||||
href?: string;
|
||||
}
|
||||
|
||||
function LinkWrapper(props: LinkWrapperProps & Wrapper) {
|
||||
const { children, href } = props;
|
||||
return (
|
||||
const LinkWrapper = ({ children, href }: LinkWrapperProps & Wrapper) => (
|
||||
<a href={href} target="_blank" rel="noreferrer">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,11 @@ import { ConditionalWrapper, Wrapper } from "helpers/component";
|
|||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { Button } from "./Button";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
buttonsProps: (Parameters<typeof Button>[0] & {
|
||||
|
@ -11,10 +16,12 @@ interface Props {
|
|||
})[];
|
||||
}
|
||||
|
||||
export function ButtonGroup(props: Props): JSX.Element {
|
||||
const { buttonsProps, className } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
export const ButtonGroup = ({
|
||||
buttonsProps,
|
||||
className,
|
||||
}: Props): JSX.Element => (
|
||||
<div className={cJoin("grid grid-flow-col", className)}>
|
||||
{buttonsProps.map((buttonProps, index) => (
|
||||
<ConditionalWrapper
|
||||
|
@ -37,17 +44,18 @@ export function ButtonGroup(props: Props): JSX.Element {
|
|||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface ToolTipWrapperProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
function ToolTipWrapper(props: ToolTipWrapperProps & Wrapper) {
|
||||
const { text, children } = props;
|
||||
return (
|
||||
const ToolTipWrapper = ({ text, children }: ToolTipWrapperProps & Wrapper) => (
|
||||
<ToolTip content={text}>
|
||||
<>{children}</>
|
||||
</ToolTip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,15 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { cJoin } from "helpers/className";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { iterateMap } from "helpers/others";
|
||||
|
||||
import { Fragment } from "react";
|
||||
import { ToolTip } from "../ToolTip";
|
||||
import { Button } from "./Button";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
languages: AppStaticProps["languages"];
|
||||
|
@ -16,10 +20,15 @@ interface Props {
|
|||
onLanguageChanged: (index: number) => void;
|
||||
}
|
||||
|
||||
export function LanguageSwitcher(props: Props): JSX.Element {
|
||||
const { locales, className, localesIndex, onLanguageChanged } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
export const LanguageSwitcher = ({
|
||||
className,
|
||||
locales,
|
||||
localesIndex,
|
||||
languages,
|
||||
onLanguageChanged,
|
||||
}: Props): JSX.Element => (
|
||||
<ToolTip
|
||||
content={
|
||||
<div className={cJoin("flex flex-col gap-2", className)}>
|
||||
|
@ -28,7 +37,7 @@ export function LanguageSwitcher(props: Props): JSX.Element {
|
|||
<Button
|
||||
active={value === localesIndex}
|
||||
onClick={() => onLanguageChanged(value)}
|
||||
text={prettyLanguage(locale, props.languages)}
|
||||
text={prettyLanguage(locale, languages)}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
|
@ -41,4 +50,3 @@ export function LanguageSwitcher(props: Props): JSX.Element {
|
|||
/>
|
||||
</ToolTip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { isDefinedAndNotEmpty, iterateMap, mapMoveEntry } from "helpers/others";
|
||||
|
||||
import { Fragment, useCallback, useState } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
items: Map<string, string>;
|
||||
|
@ -10,9 +14,14 @@ interface Props {
|
|||
onChange?: (items: Map<string, string>) => void;
|
||||
}
|
||||
|
||||
export function OrderableList(props: Props): JSX.Element {
|
||||
const { onChange } = props;
|
||||
const [items, setItems] = useState<Map<string, string>>(props.items);
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const OrderableList = ({
|
||||
onChange,
|
||||
items: propsItems,
|
||||
insertLabels,
|
||||
}: Props): JSX.Element => {
|
||||
const [items, setItems] = useState<Map<string, string>>(propsItems);
|
||||
|
||||
const updateOrder = useCallback(
|
||||
(sourceIndex: number, targetIndex: number) => {
|
||||
|
@ -27,9 +36,8 @@ export function OrderableList(props: Props): JSX.Element {
|
|||
<div className="grid gap-2">
|
||||
{iterateMap(items, (key, value, index) => (
|
||||
<Fragment key={key}>
|
||||
{props.insertLabels &&
|
||||
isDefinedAndNotEmpty(props.insertLabels.get(index)) && (
|
||||
<p>{props.insertLabels.get(index)}</p>
|
||||
{insertLabels && isDefinedAndNotEmpty(insertLabels.get(index)) && (
|
||||
<p>{insertLabels.get(index)}</p>
|
||||
)}
|
||||
<div
|
||||
onDragStart={(event) => {
|
||||
|
@ -89,4 +97,4 @@ export function OrderableList(props: Props): JSX.Element {
|
|||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Icon } from "components/Ico";
|
||||
import { cJoin } from "helpers/className";
|
||||
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import { Button } from "./Button";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
maxPage: number;
|
||||
|
@ -11,20 +15,21 @@ interface Props {
|
|||
setPage: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export function PageSelector(props: Props): JSX.Element {
|
||||
const { page, setPage, maxPage, className } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
export const PageSelector = ({
|
||||
page,
|
||||
setPage,
|
||||
maxPage,
|
||||
className,
|
||||
}: Props): JSX.Element => (
|
||||
<div className={cJoin("flex flex-row place-content-center", className)}>
|
||||
<Button
|
||||
onClick={() => setPage((current) => (page > 0 ? current - 1 : current))}
|
||||
className="rounded-r-none"
|
||||
icon={Icon.NavigateBefore}
|
||||
/>
|
||||
<Button
|
||||
className="rounded-none border-x-0"
|
||||
text={(page + 1).toString()}
|
||||
/>
|
||||
<Button className="rounded-none border-x-0" text={(page + 1).toString()} />
|
||||
<Button
|
||||
onClick={() =>
|
||||
setPage((current) => (page < maxPage ? page + 1 : current))
|
||||
|
@ -34,4 +39,3 @@ export function PageSelector(props: Props): JSX.Element {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
import { useToggle } from "hooks/useToggle";
|
||||
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
setState: Dispatch<SetStateAction<number>>;
|
||||
state: number;
|
||||
|
@ -13,8 +17,15 @@ interface Props {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
export function Select(props: Props): JSX.Element {
|
||||
const { className, state, options, allowEmpty, setState } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Select = ({
|
||||
className,
|
||||
state,
|
||||
options,
|
||||
allowEmpty,
|
||||
setState,
|
||||
}: Props): JSX.Element => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const toggleOpened = useToggle(setOpened);
|
||||
|
||||
|
@ -80,4 +91,4 @@ export function Select(props: Props): JSX.Element {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
import { useToggle } from "hooks/useToggle";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
setState: Dispatch<SetStateAction<boolean>>;
|
||||
state: boolean;
|
||||
|
@ -10,8 +14,14 @@ interface Props {
|
|||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function Switch(props: Props): JSX.Element {
|
||||
const { state, setState, className, disabled = false } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Switch = ({
|
||||
state,
|
||||
setState,
|
||||
className,
|
||||
disabled = false,
|
||||
}: Props): JSX.Element => {
|
||||
const toggleState = useToggle(setState);
|
||||
return (
|
||||
<div
|
||||
|
@ -41,4 +51,4 @@ export function Switch(props: Props): JSX.Element {
|
|||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
state: string | undefined;
|
||||
setState:
|
||||
|
@ -14,10 +18,15 @@ interface Props {
|
|||
placeholder?: string;
|
||||
}
|
||||
|
||||
export function TextInput(props: Props): JSX.Element {
|
||||
const { state, setState, className, name, placeholder } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
export const TextInput = ({
|
||||
state,
|
||||
setState,
|
||||
className,
|
||||
name,
|
||||
placeholder,
|
||||
}: Props): JSX.Element => (
|
||||
<div className={cJoin("relative", className)}>
|
||||
<input
|
||||
className="w-full"
|
||||
|
@ -42,4 +51,3 @@ export function TextInput(props: Props): JSX.Element {
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
label: string | null | undefined;
|
||||
input: JSX.Element;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function WithLabel(props: Props): JSX.Element {
|
||||
const { label, input, disabled } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const WithLabel = ({ label, input, disabled }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin(
|
||||
"flex flex-row place-content-between place-items-center gap-2",
|
||||
|
@ -18,10 +23,7 @@ export function WithLabel(props: Props): JSX.Element {
|
|||
>
|
||||
{isDefinedAndNotEmpty(label) && (
|
||||
<p
|
||||
className={cJoin(
|
||||
"text-left",
|
||||
cIf(label.length < 10, "flex-shrink-0")
|
||||
)}
|
||||
className={cJoin("text-left", cIf(label.length < 10, "flex-shrink-0"))}
|
||||
>
|
||||
{label}:
|
||||
</p>
|
||||
|
@ -29,4 +31,3 @@ export function WithLabel(props: Props): JSX.Element {
|
|||
{input}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export function InsetBox(props: Props): JSX.Element {
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const InsetBox = ({ id, className, children }: Props): JSX.Element => (
|
||||
<div
|
||||
id={props.id}
|
||||
id={id}
|
||||
className={cJoin(
|
||||
"w-full rounded-xl bg-mid p-8 shadow-inner-sm shadow-shade",
|
||||
props.className
|
||||
className
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,20 +5,27 @@ import { useAppLayout } from "contexts/AppLayoutContext";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { LibraryItemUserStatus } from "helpers/types";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
id: string | null | undefined;
|
||||
displayCTAs: boolean;
|
||||
id: string;
|
||||
expand?: boolean;
|
||||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function PreviewCardCTAs(props: Props): JSX.Element {
|
||||
const { id, displayCTAs, expand = false, langui } = props;
|
||||
const appLayout = useAppLayout();
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const PreviewCardCTAs = ({
|
||||
id,
|
||||
expand = false,
|
||||
langui,
|
||||
}: Props): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
return (
|
||||
<>
|
||||
{displayCTAs && id && (
|
||||
<div
|
||||
className={`flex flex-row place-content-center place-items-center ${
|
||||
expand ? "gap-4" : "gap-2"
|
||||
|
@ -35,9 +42,7 @@ export function PreviewCardCTAs(props: Props): JSX.Element {
|
|||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
appLayout.setLibraryItemUserStatus((current) => {
|
||||
const newLibraryItemUserStatus = current
|
||||
? { ...current }
|
||||
: {};
|
||||
const newLibraryItemUserStatus = current ? { ...current } : {};
|
||||
newLibraryItemUserStatus[id] =
|
||||
newLibraryItemUserStatus[id] === LibraryItemUserStatus.Want
|
||||
? LibraryItemUserStatus.None
|
||||
|
@ -58,9 +63,7 @@ export function PreviewCardCTAs(props: Props): JSX.Element {
|
|||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
appLayout.setLibraryItemUserStatus((current) => {
|
||||
const newLibraryItemUserStatus = current
|
||||
? { ...current }
|
||||
: {};
|
||||
const newLibraryItemUserStatus = current ? { ...current } : {};
|
||||
newLibraryItemUserStatus[id] =
|
||||
newLibraryItemUserStatus[id] === LibraryItemUserStatus.Have
|
||||
? LibraryItemUserStatus.None
|
||||
|
@ -71,7 +74,6 @@ export function PreviewCardCTAs(props: Props): JSX.Element {
|
|||
/>
|
||||
</ToolTip>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,10 +13,14 @@ import {
|
|||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
} from "helpers/others";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Fragment, useCallback, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
openLightBox: (images: string[], index?: number) => void;
|
||||
scanSet: NonNullable<
|
||||
|
@ -45,10 +49,17 @@ interface Props {
|
|||
>["content"];
|
||||
}
|
||||
|
||||
export function ScanSet(props: Props): JSX.Element {
|
||||
const { openLightBox, scanSet, slug, title, languages, langui, content } =
|
||||
props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ScanSet = ({
|
||||
openLightBox,
|
||||
scanSet,
|
||||
slug,
|
||||
title,
|
||||
languages,
|
||||
langui,
|
||||
content,
|
||||
}: Props): JSX.Element => {
|
||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] =
|
||||
useSmartLanguage({
|
||||
items: scanSet,
|
||||
|
@ -227,4 +238,4 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,10 +9,14 @@ import {
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Fragment, useCallback, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
openLightBox: (images: string[], index?: number) => void;
|
||||
images: NonNullable<
|
||||
|
@ -26,9 +30,14 @@ interface Props {
|
|||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function ScanSetCover(props: Props): JSX.Element {
|
||||
const { openLightBox, images, languages, langui } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ScanSetCover = ({
|
||||
openLightBox,
|
||||
images,
|
||||
languages,
|
||||
langui,
|
||||
}: Props): JSX.Element => {
|
||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] =
|
||||
useSmartLanguage({
|
||||
items: images,
|
||||
|
@ -176,4 +185,4 @@ export function ScanSetCover(props: Props): JSX.Element {
|
|||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,6 +6,18 @@ import { Button } from "./Inputs/Button";
|
|||
import { Popup } from "./Popup";
|
||||
import { Icon } from "components/Ico";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const SENSIBILITY_SWIPE = 0.5;
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
setState:
|
||||
| Dispatch<SetStateAction<boolean | undefined>>
|
||||
|
@ -16,11 +28,15 @@ interface Props {
|
|||
setIndex: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const SENSIBILITY_SWIPE = 0.5;
|
||||
|
||||
export function LightBox(props: Props): JSX.Element {
|
||||
const { state, setState, images, index, setIndex } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const LightBox = ({
|
||||
state,
|
||||
setState,
|
||||
images,
|
||||
index,
|
||||
setIndex,
|
||||
}: Props): JSX.Element => {
|
||||
const handlePrevious = useCallback(() => {
|
||||
if (index > 0) setIndex(index - 1);
|
||||
}, [index, setIndex]);
|
||||
|
@ -83,4 +99,4 @@ export function LightBox(props: Props): JSX.Element {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,24 +4,33 @@ import { Img } from "components/Img";
|
|||
import { InsetBox } from "components/InsetBox";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import Markdown from "markdown-to-jsx";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useMemo } from "react";
|
||||
import React, { Fragment, useMemo } from "react";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
|
||||
interface Props {
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface MarkdawnProps {
|
||||
className?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function Markdawn(props: Props): JSX.Element {
|
||||
const { className, text: rawText } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Markdawn = ({
|
||||
className,
|
||||
text: rawText,
|
||||
}: MarkdawnProps): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
const router = useRouter();
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
|
@ -299,10 +308,94 @@ export function Markdawn(props: Props): JSX.Element {
|
|||
);
|
||||
}
|
||||
return <></>;
|
||||
};
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
interface TableOfContentsProps {
|
||||
text: string;
|
||||
title?: string;
|
||||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
function HeaderToolTip(props: { id: string }) {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const TableOfContents = ({
|
||||
text,
|
||||
title,
|
||||
langui,
|
||||
}: TableOfContentsProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const toc = useMemo(
|
||||
() => getTocFromMarkdawn(preprocessMarkDawn(text), title),
|
||||
[text, title]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-xl">{langui.table_of_contents}</h3>
|
||||
<div className="max-w-[14.5rem] text-left">
|
||||
<p className="relative my-2 overflow-x-hidden text-ellipsis whitespace-nowrap text-left">
|
||||
<a onClick={async () => router.replace(`#${toc.slug}`)}>
|
||||
{<abbr title={toc.title}>{toc.title}</abbr>}
|
||||
</a>
|
||||
</p>
|
||||
<TocLevel tocchildren={toc.children} parentNumbering="" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface TocInterface {
|
||||
title: string;
|
||||
slug: string;
|
||||
children: TocInterface[];
|
||||
}
|
||||
|
||||
interface LevelProps {
|
||||
tocchildren: TocInterface[];
|
||||
parentNumbering: string;
|
||||
}
|
||||
|
||||
const TocLevel = ({
|
||||
tocchildren,
|
||||
parentNumbering,
|
||||
}: LevelProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<ol className="pl-4 text-left">
|
||||
{tocchildren.map((child, childIndex) => (
|
||||
<Fragment key={child.slug}>
|
||||
<li className="my-2 w-full overflow-x-hidden text-ellipsis whitespace-nowrap">
|
||||
<span className="text-dark">{`${parentNumbering}${
|
||||
childIndex + 1
|
||||
}.`}</span>{" "}
|
||||
<a onClick={async () => router.replace(`#${child.slug}`)}>
|
||||
{<abbr title={child.title}>{child.title}</abbr>}
|
||||
</a>
|
||||
</li>
|
||||
<TocLevel
|
||||
tocchildren={child.children}
|
||||
parentNumbering={`${parentNumbering}${childIndex + 1}.`}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
const HeaderToolTip = (props: { id: string }): JSX.Element => (
|
||||
<ToolTip
|
||||
content={"Copy anchor link"}
|
||||
trigger="mouseenter"
|
||||
|
@ -323,9 +416,13 @@ function HeaderToolTip(props: { id: string }) {
|
|||
</ToolTip>
|
||||
</ToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
function typographicRules(text: string): string {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
const typographicRules = (text: string): string => {
|
||||
let newText = text;
|
||||
newText = newText.replace(/--/gu, "—");
|
||||
/*
|
||||
|
@ -336,9 +433,20 @@ function typographicRules(text: string): string {
|
|||
* newText = newText.replace(/'/gu, "’");
|
||||
*/
|
||||
return newText;
|
||||
};
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
enum HeaderLevels {
|
||||
H1 = 1,
|
||||
H2 = 2,
|
||||
H3 = 3,
|
||||
H4 = 4,
|
||||
H5 = 5,
|
||||
H6 = 6,
|
||||
}
|
||||
|
||||
export function preprocessMarkDawn(text: string): string {
|
||||
const preprocessMarkDawn = (text: string): string => {
|
||||
if (!text) return "";
|
||||
|
||||
let preprocessed = typographicRules(text);
|
||||
|
@ -383,22 +491,15 @@ export function preprocessMarkDawn(text: string): string {
|
|||
.join("\n");
|
||||
|
||||
return preprocessed;
|
||||
}
|
||||
};
|
||||
|
||||
enum HeaderLevels {
|
||||
H1 = 1,
|
||||
H2 = 2,
|
||||
H3 = 3,
|
||||
H4 = 4,
|
||||
H5 = 5,
|
||||
H6 = 6,
|
||||
}
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
function markdawnHeadersParser(
|
||||
const markdawnHeadersParser = (
|
||||
headerLevel: HeaderLevels,
|
||||
line: string,
|
||||
visitedSlugs: string[]
|
||||
): string {
|
||||
): string => {
|
||||
const lineText = line.slice(headerLevel + 1);
|
||||
const slug = slugify(lineText);
|
||||
let newSlug = slug;
|
||||
|
@ -409,4 +510,102 @@ function markdawnHeadersParser(
|
|||
}
|
||||
visitedSlugs.push(newSlug);
|
||||
return `<h${headerLevel} id="${newSlug}">${lineText}</h${headerLevel}>`;
|
||||
};
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||
const toc: TocInterface = {
|
||||
title: title ?? "Return to top",
|
||||
slug: slugify(title),
|
||||
children: [],
|
||||
};
|
||||
let h2 = -1;
|
||||
let h3 = -1;
|
||||
let h4 = -1;
|
||||
let h5 = -1;
|
||||
let scenebreak = 0;
|
||||
let scenebreakIndex = 0;
|
||||
|
||||
const getTitle = (line: string): string =>
|
||||
line.slice(line.indexOf(`">`) + 2, line.indexOf("</"));
|
||||
|
||||
const getSlug = (line: string): string =>
|
||||
line.slice(line.indexOf(`id="`) + 4, line.indexOf(`">`));
|
||||
|
||||
text.split("\n").map((line) => {
|
||||
if (line.startsWith("<h1 id=")) {
|
||||
toc.title = getTitle(line);
|
||||
toc.slug = getSlug(line);
|
||||
} else if (line.startsWith("<h2 id=")) {
|
||||
toc.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h2 += 1;
|
||||
h3 = -1;
|
||||
h4 = -1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h2 >= 0 && line.startsWith("<h3 id=")) {
|
||||
toc.children[h2].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h3 += 1;
|
||||
h4 = -1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h3 >= 0 && line.startsWith("<h4 id=")) {
|
||||
toc.children[h2].children[h3].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h4 += 1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h4 >= 0 && line.startsWith("<h5 id=")) {
|
||||
toc.children[h2].children[h3].children[h4].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h5 += 1;
|
||||
scenebreak = 0;
|
||||
} else if (h5 >= 0 && line.startsWith("<h6 id=")) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
} else if (line.startsWith(`<SceneBreak`)) {
|
||||
scenebreak += 1;
|
||||
scenebreakIndex += 1;
|
||||
|
||||
const child = {
|
||||
title: `Scene break ${scenebreak}`,
|
||||
slug: slugify(`scene-break-${scenebreakIndex}`),
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (h5 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push(
|
||||
child
|
||||
);
|
||||
} else if (h4 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children.push(child);
|
||||
} else if (h3 >= 0) {
|
||||
toc.children[h2].children[h3].children.push(child);
|
||||
} else if (h2 >= 0) {
|
||||
toc.children[h2].children.push(child);
|
||||
} else {
|
||||
toc.children.push(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return toc;
|
||||
};
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
import { slugify } from "helpers/formatters";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { Fragment, useMemo } from "react";
|
||||
import { preprocessMarkDawn } from "./Markdawn";
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function TOC(props: Props): JSX.Element {
|
||||
const { text, title } = props;
|
||||
const router = useRouter();
|
||||
const toc = useMemo(
|
||||
() => getTocFromMarkdawn(preprocessMarkDawn(text), title),
|
||||
[text, title]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* TODO: add to LANGUI */}
|
||||
<h3 className="text-xl">Table of content</h3>
|
||||
<div className="max-w-[14.5rem] text-left">
|
||||
<p className="relative my-2 overflow-x-hidden text-ellipsis whitespace-nowrap text-left">
|
||||
<a onClick={async () => router.replace(`#${toc.slug}`)}>
|
||||
{<abbr title={toc.title}>{toc.title}</abbr>}
|
||||
</a>
|
||||
</p>
|
||||
<TOCLevel tocchildren={toc.children} parentNumbering="" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface LevelProps {
|
||||
tocchildren: TOCInterface[];
|
||||
parentNumbering: string;
|
||||
}
|
||||
|
||||
function TOCLevel(props: LevelProps): JSX.Element {
|
||||
const router = useRouter();
|
||||
const { tocchildren, parentNumbering } = props;
|
||||
return (
|
||||
<ol className="pl-4 text-left">
|
||||
{tocchildren.map((child, childIndex) => (
|
||||
<Fragment key={child.slug}>
|
||||
<li className="my-2 w-full overflow-x-hidden text-ellipsis whitespace-nowrap">
|
||||
<span className="text-dark">{`${parentNumbering}${
|
||||
childIndex + 1
|
||||
}.`}</span>{" "}
|
||||
<a onClick={async () => router.replace(`#${child.slug}`)}>
|
||||
{<abbr title={child.title}>{child.title}</abbr>}
|
||||
</a>
|
||||
</li>
|
||||
<TOCLevel
|
||||
tocchildren={child.children}
|
||||
parentNumbering={`${parentNumbering}${childIndex + 1}.`}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
|
||||
interface TOCInterface {
|
||||
title: string;
|
||||
slug: string;
|
||||
children: TOCInterface[];
|
||||
}
|
||||
|
||||
function getTocFromMarkdawn(text: string, title?: string): TOCInterface {
|
||||
const toc: TOCInterface = {
|
||||
title: title ?? "Return to top",
|
||||
slug: slugify(title),
|
||||
children: [],
|
||||
};
|
||||
let h2 = -1;
|
||||
let h3 = -1;
|
||||
let h4 = -1;
|
||||
let h5 = -1;
|
||||
let scenebreak = 0;
|
||||
let scenebreakIndex = 0;
|
||||
|
||||
function getTitle(line: string): string {
|
||||
return line.slice(line.indexOf(`">`) + 2, line.indexOf("</"));
|
||||
}
|
||||
|
||||
function getSlug(line: string): string {
|
||||
return line.slice(line.indexOf(`id="`) + 4, line.indexOf(`">`));
|
||||
}
|
||||
|
||||
text.split("\n").map((line) => {
|
||||
if (line.startsWith("<h1 id=")) {
|
||||
toc.title = getTitle(line);
|
||||
toc.slug = getSlug(line);
|
||||
} else if (line.startsWith("<h2 id=")) {
|
||||
toc.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h2 += 1;
|
||||
h3 = -1;
|
||||
h4 = -1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h2 >= 0 && line.startsWith("<h3 id=")) {
|
||||
toc.children[h2].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h3 += 1;
|
||||
h4 = -1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h3 >= 0 && line.startsWith("<h4 id=")) {
|
||||
toc.children[h2].children[h3].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h4 += 1;
|
||||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h4 >= 0 && line.startsWith("<h5 id=")) {
|
||||
toc.children[h2].children[h3].children[h4].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
h5 += 1;
|
||||
scenebreak = 0;
|
||||
} else if (h5 >= 0 && line.startsWith("<h6 id=")) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
});
|
||||
} else if (line.startsWith(`<SceneBreak`)) {
|
||||
scenebreak += 1;
|
||||
scenebreakIndex += 1;
|
||||
|
||||
const child = {
|
||||
title: `Scene break ${scenebreak}`,
|
||||
slug: slugify(`scene-break-${scenebreakIndex}`),
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (h5 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push(
|
||||
child
|
||||
);
|
||||
} else if (h4 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children.push(child);
|
||||
} else if (h3 >= 0) {
|
||||
toc.children[h2].children[h3].children.push(child);
|
||||
} else if (h2 >= 0) {
|
||||
toc.children[h2].children.push(child);
|
||||
} else {
|
||||
toc.children.push(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return toc;
|
||||
}
|
|
@ -2,14 +2,19 @@ import { Ico, Icon } from "components/Ico";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefined } from "helpers/others";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
message: string;
|
||||
icon?: Icon;
|
||||
}
|
||||
|
||||
export function ContentPlaceholder(props: Props): JSX.Element {
|
||||
const { message, icon } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ContentPlaceholder = ({ message, icon }: Props): JSX.Element => (
|
||||
<div className="grid h-full place-content-center">
|
||||
<div
|
||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||
|
@ -17,14 +22,10 @@ export function ContentPlaceholder(props: Props): JSX.Element {
|
|||
>
|
||||
{isDefined(icon) && <Ico icon={icon} className="!text-[300%]" />}
|
||||
<p
|
||||
className={cJoin(
|
||||
"w-64 text-2xl",
|
||||
cIf(!isDefined(icon), "text-center")
|
||||
)}
|
||||
className={cJoin("w-64 text-2xl", cIf(!isDefined(icon), "text-center"))}
|
||||
>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,14 @@ import { Ico, Icon } from "components/Ico";
|
|||
import { ToolTip } from "components/ToolTip";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { MouseEventHandler, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
icon?: Icon;
|
||||
|
@ -16,8 +20,9 @@ interface Props {
|
|||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export function NavOption(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const NavOption = ({
|
||||
url,
|
||||
icon,
|
||||
title,
|
||||
|
@ -25,7 +30,7 @@ export function NavOption(props: Props): JSX.Element {
|
|||
border = false,
|
||||
reduced = false,
|
||||
onClick,
|
||||
} = props;
|
||||
}: Props): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const isActive = useMemo(
|
||||
() => router.asPath.startsWith(url),
|
||||
|
@ -82,4 +87,4 @@ export function NavOption(props: Props): JSX.Element {
|
|||
</div>
|
||||
</ToolTip>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,15 +2,24 @@ import { HorizontalLine } from "components/HorizontalLine";
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
icon?: Icon;
|
||||
title: string | null | undefined;
|
||||
description?: string | null | undefined;
|
||||
}
|
||||
|
||||
export function PanelHeader(props: Props): JSX.Element {
|
||||
const { icon, description, title } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const PanelHeader = ({
|
||||
icon,
|
||||
description,
|
||||
title,
|
||||
}: Props): JSX.Element => (
|
||||
<>
|
||||
<div className="grid w-full place-items-center">
|
||||
{icon && <Ico icon={icon} className="mb-3 !text-4xl" />}
|
||||
|
@ -20,4 +29,3 @@ export function PanelHeader(props: Props): JSX.Element {
|
|||
<HorizontalLine />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@ import { useAppLayout } from "contexts/AppLayoutContext";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
href: string;
|
||||
title: string | null | undefined;
|
||||
|
@ -20,8 +25,16 @@ export enum ReturnButtonType {
|
|||
Both = "both",
|
||||
}
|
||||
|
||||
export function ReturnButton(props: Props): JSX.Element {
|
||||
const { href, title, langui, displayOn, horizontalLine, className } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ReturnButton = ({
|
||||
href,
|
||||
title,
|
||||
langui,
|
||||
displayOn,
|
||||
horizontalLine,
|
||||
className,
|
||||
}: Props): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
|
||||
return (
|
||||
|
@ -44,4 +57,4 @@ export function ReturnButton(props: Props): JSX.Element {
|
|||
{horizontalLine === true && <HorizontalLine />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
width?: ContentPanelWidthSizes;
|
||||
|
@ -12,10 +17,13 @@ export enum ContentPanelWidthSizes {
|
|||
Full = "full",
|
||||
}
|
||||
|
||||
export function ContentPanel(props: Props): JSX.Element {
|
||||
const { width = ContentPanelWidthSizes.Default, children, className } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
export const ContentPanel = ({
|
||||
width = ContentPanelWidthSizes.Default,
|
||||
children,
|
||||
className,
|
||||
}: Props): JSX.Element => (
|
||||
<div className="grid h-full">
|
||||
<main
|
||||
className={cJoin(
|
||||
|
@ -32,4 +40,3 @@ export function ContentPanel(props: Props): JSX.Element {
|
|||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,12 +12,18 @@ import { Icon } from "components/Ico";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function MainPanel(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const MainPanel = ({ langui }: Props): JSX.Element => {
|
||||
const isDesktop = useMediaDesktop();
|
||||
const {
|
||||
mainPanelReduced = false,
|
||||
|
@ -239,4 +245,4 @@ export function MainPanel(props: Props): JSX.Element {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function SubPanel(props: Props): JSX.Element {
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const SubPanel = ({ children }: Props): JSX.Element => (
|
||||
<div className="grid gap-y-2 px-6 pt-10 pb-20 text-center desktop:py-8 desktop:px-10">
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
import { Dispatch, SetStateAction, useEffect } from "react";
|
||||
import Hotkeys from "react-hot-keys";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
setState:
|
||||
| Dispatch<SetStateAction<boolean | undefined>>
|
||||
|
@ -15,16 +19,16 @@ interface Props {
|
|||
padding?: boolean;
|
||||
}
|
||||
|
||||
export function Popup(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Popup = ({
|
||||
setState,
|
||||
state,
|
||||
children,
|
||||
fillViewport,
|
||||
hideBackground = false,
|
||||
padding = true,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
const { setMenuGestures } = useAppLayout();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -77,4 +81,4 @@ export function Popup(props: Props): JSX.Element {
|
|||
</div>
|
||||
</Hotkeys>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,8 +8,7 @@ import { Fragment, useCallback, useMemo } from "react";
|
|||
import { AppLayout } from "./AppLayout";
|
||||
import { Chip } from "./Chip";
|
||||
import { HorizontalLine } from "./HorizontalLine";
|
||||
import { Markdawn } from "./Markdown/Markdawn";
|
||||
import { TOC } from "./Markdown/TOC";
|
||||
import { Markdawn, TableOfContents } from "./Markdown/Markdawn";
|
||||
import { ReturnButton, ReturnButtonType } from "./PanelComponents/ReturnButton";
|
||||
import { ContentPanel } from "./Panels/ContentPanel";
|
||||
import { SubPanel } from "./Panels/SubPanel";
|
||||
|
@ -17,6 +16,11 @@ import { RecorderChip } from "./RecorderChip";
|
|||
import { ThumbnailHeader } from "./ThumbnailHeader";
|
||||
import { ToolTip } from "./ToolTip";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
post: PostWithTranslations;
|
||||
langui: AppStaticProps["langui"];
|
||||
|
@ -33,8 +37,9 @@ interface Props {
|
|||
appendBody?: JSX.Element;
|
||||
}
|
||||
|
||||
export function PostPage(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const PostPage = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
|
@ -46,9 +51,9 @@ export function PostPage(props: Props): JSX.Element {
|
|||
displayLanguageSwitcher,
|
||||
appendBody,
|
||||
prependBody,
|
||||
} = props;
|
||||
const displayTitle = props.displayTitle ?? true;
|
||||
|
||||
displayTitle = true,
|
||||
currencies,
|
||||
}: Props): JSX.Element => {
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
||||
useSmartLanguage({
|
||||
items: post.translations,
|
||||
|
@ -124,7 +129,9 @@ export function PostPage(props: Props): JSX.Element {
|
|||
</>
|
||||
)}
|
||||
|
||||
{displayToc && <TOC text={body} title={title} />}
|
||||
{displayToc && (
|
||||
<TableOfContents text={body} title={title} langui={langui} />
|
||||
)}
|
||||
</SubPanel>
|
||||
) : undefined,
|
||||
[
|
||||
|
@ -216,7 +223,9 @@ export function PostPage(props: Props): JSX.Element {
|
|||
contentPanel={contentPanel}
|
||||
subPanel={subPanel}
|
||||
thumbnail={thumbnail ?? undefined}
|
||||
{...props}
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,14 +14,18 @@ import {
|
|||
prettySlug,
|
||||
} from "helpers/formatters";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import Link from "next/link";
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Chip } from "./Chip";
|
||||
import { Ico, Icon } from "./Ico";
|
||||
import { Img } from "./Img";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
thumbnail?: UploadImageFragment | string | null | undefined;
|
||||
thumbnailAspectRatio?: string;
|
||||
|
@ -53,8 +57,9 @@ interface Props {
|
|||
| { __typename: "anotherHoverlayName" };
|
||||
}
|
||||
|
||||
export function PreviewCard(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const PreviewCard = ({
|
||||
href,
|
||||
thumbnail,
|
||||
thumbnailAspectRatio = "4/3",
|
||||
|
@ -71,11 +76,11 @@ export function PreviewCard(props: Props): JSX.Element {
|
|||
metadata,
|
||||
hoverlay,
|
||||
infoAppend,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
|
||||
const metadataJSX =
|
||||
const metadataJSX = useMemo(
|
||||
() =>
|
||||
metadata && (metadata.release_date || metadata.price) ? (
|
||||
<div className="flex w-full flex-row flex-wrap gap-x-3">
|
||||
{metadata.release_date && (
|
||||
|
@ -121,6 +126,8 @@ export function PreviewCard(props: Props): JSX.Element {
|
|||
</div>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
[appLayout.currency, metadata]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -241,12 +248,13 @@ export function PreviewCard(props: Props): JSX.Element {
|
|||
)}
|
||||
<div
|
||||
className={cJoin(
|
||||
"z-20 grid gap-2 p-4 transition-opacity linearbg-obi",
|
||||
"grid gap-2 p-4 transition-opacity linearbg-obi",
|
||||
cIf(
|
||||
!keepInfoVisible,
|
||||
`-inset-x-0.5 bottom-2 opacity-0 group-hover:opacity-100 hoverable:absolute
|
||||
hoverable:drop-shadow-shade-lg notHoverable:rounded-b-md
|
||||
notHoverable:opacity-100`
|
||||
`-inset-x-0.5 bottom-2 opacity-0 [border-radius:10%_10%_10%_10%_/_1%_1%_3%_3%]
|
||||
group-hover:opacity-100 hoverable:absolute hoverable:drop-shadow-shade-lg
|
||||
notHoverable:rounded-b-md notHoverable:opacity-100`,
|
||||
"[border-radius:0%_0%_10%_10%_/_0%_0%_3%_3%]"
|
||||
)
|
||||
)}
|
||||
>
|
||||
|
@ -287,7 +295,9 @@ export function PreviewCard(props: Props): JSX.Element {
|
|||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
interface TranslatedProps
|
||||
extends Omit<Props, "description" | "pre_title" | "subtitle" | "title"> {
|
||||
|
@ -302,13 +312,14 @@ interface TranslatedProps
|
|||
languages: AppStaticProps["languages"];
|
||||
}
|
||||
|
||||
export function TranslatedPreviewCard(props: TranslatedProps): JSX.Element {
|
||||
const {
|
||||
translations = [{ title: props.slug, language: "default" }],
|
||||
slug,
|
||||
languages,
|
||||
} = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const TranslatedPreviewCard = ({
|
||||
slug,
|
||||
translations = [{ title: slug, language: "default" }],
|
||||
languages,
|
||||
...otherProps
|
||||
}: TranslatedProps): JSX.Element => {
|
||||
const [selectedTranslation] = useSmartLanguage({
|
||||
items: translations,
|
||||
languages: languages,
|
||||
|
@ -324,7 +335,7 @@ export function TranslatedPreviewCard(props: TranslatedProps): JSX.Element {
|
|||
title={selectedTranslation?.title ?? prettySlug(slug)}
|
||||
subtitle={selectedTranslation?.subtitle}
|
||||
description={selectedTranslation?.description}
|
||||
{...props}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,13 +2,17 @@ import { UploadImageFragment } from "graphql/generated";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import Link from "next/link";
|
||||
import { useCallback } from "react";
|
||||
import { Chip } from "./Chip";
|
||||
import { Img } from "./Img";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
thumbnail?: UploadImageFragment | string | null | undefined;
|
||||
thumbnailAspectRatio?: string;
|
||||
|
@ -20,8 +24,9 @@ interface Props {
|
|||
bottomChips?: string[];
|
||||
}
|
||||
|
||||
function PreviewLine(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const PreviewLine = ({
|
||||
href,
|
||||
thumbnail,
|
||||
pre_title,
|
||||
|
@ -30,9 +35,7 @@ function PreviewLine(props: Props): JSX.Element {
|
|||
topChips,
|
||||
bottomChips,
|
||||
thumbnailAspectRatio,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
}: Props): JSX.Element => (
|
||||
<Link href={href} passHref>
|
||||
<div
|
||||
className="flex h-36 w-full cursor-pointer flex-row place-items-center gap-4 overflow-hidden
|
||||
|
@ -73,7 +76,8 @@ function PreviewLine(props: Props): JSX.Element {
|
|||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
interface TranslatedProps
|
||||
extends Omit<Props, "pre_title" | "subtitle" | "title"> {
|
||||
|
@ -88,13 +92,14 @@ interface TranslatedProps
|
|||
languages: AppStaticProps["languages"];
|
||||
}
|
||||
|
||||
export function TranslatedPreviewLine(props: TranslatedProps): JSX.Element {
|
||||
const {
|
||||
translations = [{ title: props.slug, language: "default" }],
|
||||
slug,
|
||||
languages,
|
||||
} = props;
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const TranslatedPreviewLine = ({
|
||||
slug,
|
||||
translations = [{ title: slug, language: "default" }],
|
||||
languages,
|
||||
...otherProps
|
||||
}: TranslatedProps): JSX.Element => {
|
||||
const [selectedTranslation] = useSmartLanguage({
|
||||
items: translations,
|
||||
languages: languages,
|
||||
|
@ -109,7 +114,7 @@ export function TranslatedPreviewLine(props: TranslatedProps): JSX.Element {
|
|||
pre_title={selectedTranslation?.pre_title}
|
||||
title={selectedTranslation?.title ?? prettySlug(slug)}
|
||||
subtitle={selectedTranslation?.subtitle}
|
||||
{...props}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,21 +3,25 @@ import { RecorderChipFragment } from "graphql/generated";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { Fragment } from "react";
|
||||
import { Img } from "./Img";
|
||||
import { Markdawn } from "./Markdown/Markdawn";
|
||||
import { ToolTip } from "./ToolTip";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
recorder: RecorderChipFragment;
|
||||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function RecorderChip(props: Props): JSX.Element {
|
||||
const { recorder, langui } = props;
|
||||
return (
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const RecorderChip = ({ recorder, langui }: Props): JSX.Element => (
|
||||
<ToolTip
|
||||
content={
|
||||
<div className="grid gap-8 p-2 py-5 text-left">
|
||||
|
@ -63,4 +67,3 @@ export function RecorderChip(props: Props): JSX.Element {
|
|||
</Chip>
|
||||
</ToolTip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,13 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { prettyinlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
pre_title?: string | null | undefined;
|
||||
title: string | null | undefined;
|
||||
|
@ -26,8 +30,9 @@ interface Props {
|
|||
languageSwitcher?: JSX.Element;
|
||||
}
|
||||
|
||||
export function ThumbnailHeader(props: Props): JSX.Element {
|
||||
const {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ThumbnailHeader = ({
|
||||
langui,
|
||||
pre_title,
|
||||
title,
|
||||
|
@ -37,8 +42,7 @@ export function ThumbnailHeader(props: Props): JSX.Element {
|
|||
categories,
|
||||
description,
|
||||
languageSwitcher,
|
||||
} = props;
|
||||
|
||||
}: Props): JSX.Element => {
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
|
||||
return (
|
||||
|
@ -105,4 +109,4 @@ export function ThumbnailHeader(props: Props): JSX.Element {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,20 +2,30 @@ import Tippy, { TippyProps } from "@tippyjs/react";
|
|||
import { cJoin } from "helpers/className";
|
||||
import "tippy.js/animations/scale-subtle.css";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends TippyProps {}
|
||||
|
||||
export function ToolTip(props: Props): JSX.Element {
|
||||
const newProps: Props = {
|
||||
className: cJoin("text-sm", props.className),
|
||||
delay: [150, 0],
|
||||
interactive: true,
|
||||
animation: "scale-subtle",
|
||||
...props,
|
||||
};
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
return (
|
||||
<Tippy {...newProps}>
|
||||
<div>{props.children}</div>
|
||||
export const ToolTip = ({
|
||||
className,
|
||||
delay = [150, 0],
|
||||
interactive = true,
|
||||
animation = "scale-subtle",
|
||||
children,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => (
|
||||
<Tippy
|
||||
className={cJoin("text-sm", className)}
|
||||
delay={delay}
|
||||
interactive={interactive}
|
||||
animation={animation}
|
||||
{...otherProps}
|
||||
>
|
||||
<div>{children}</div>
|
||||
</Tippy>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,36 +20,35 @@ interface Props {
|
|||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function ChronologyItemComponent(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
|
||||
if (props.item.attributes) {
|
||||
export const ChronologyItemComponent = ({
|
||||
langui,
|
||||
item,
|
||||
displayYear,
|
||||
}: Props): JSX.Element => {
|
||||
if (item.attributes) {
|
||||
return (
|
||||
<div
|
||||
className="grid grid-cols-[4em] grid-rows-[auto_1fr] place-content-start
|
||||
rounded-2xl py-4 px-8 target:my-4 target:bg-mid target:py-8"
|
||||
id={generateAnchor(
|
||||
props.item.attributes.year,
|
||||
props.item.attributes.month,
|
||||
props.item.attributes.day
|
||||
item.attributes.year,
|
||||
item.attributes.month,
|
||||
item.attributes.day
|
||||
)}
|
||||
>
|
||||
{props.displayYear && (
|
||||
{displayYear && (
|
||||
<p className="mt-[-.2em] text-lg font-bold">
|
||||
{generateYear(
|
||||
props.item.attributes.displayed_date,
|
||||
props.item.attributes.year
|
||||
)}
|
||||
{generateYear(item.attributes.displayed_date, item.attributes.year)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="col-start-1 text-sm text-dark">
|
||||
{generateDate(props.item.attributes.month, props.item.attributes.day)}
|
||||
{generateDate(item.attributes.month, item.attributes.day)}
|
||||
</p>
|
||||
|
||||
<div className="col-start-2 row-span-2 row-start-1 grid gap-4">
|
||||
{props.item.attributes.events &&
|
||||
filterHasAttributes(props.item.attributes.events, [
|
||||
{item.attributes.events &&
|
||||
filterHasAttributes(item.attributes.events, [
|
||||
"id",
|
||||
"translations",
|
||||
]).map((event) => (
|
||||
|
@ -123,31 +122,29 @@ export function ChronologyItemComponent(props: Props): JSX.Element {
|
|||
}
|
||||
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
function generateAnchor(
|
||||
const generateAnchor = (
|
||||
year: number | undefined,
|
||||
month: number | null | undefined,
|
||||
day: number | null | undefined
|
||||
): string {
|
||||
): string => {
|
||||
let result = "";
|
||||
if (year) result += year;
|
||||
if (month) result += `- ${month.toString().padStart(2, "0")}`;
|
||||
if (day) result += `- ${day.toString().padStart(2, "0")}`;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function generateYear(
|
||||
const generateYear = (
|
||||
displayed_date: string | null | undefined,
|
||||
year: number | undefined
|
||||
): string {
|
||||
return displayed_date ?? year?.toString() ?? "";
|
||||
}
|
||||
): string => displayed_date ?? year?.toString() ?? "";
|
||||
|
||||
function generateDate(
|
||||
const generateDate = (
|
||||
month: number | null | undefined,
|
||||
day: number | null | undefined
|
||||
): string {
|
||||
): string => {
|
||||
const lut = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
|
@ -172,4 +169,4 @@ function generateDate(
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,15 +10,16 @@ interface Props {
|
|||
langui: AppStaticProps["langui"];
|
||||
}
|
||||
|
||||
export function ChronologyYearComponent(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
|
||||
return (
|
||||
export const ChronologyYearComponent = ({
|
||||
langui,
|
||||
year,
|
||||
items,
|
||||
}: Props): JSX.Element => (
|
||||
<div
|
||||
className="rounded-2xl target:my-4 target:bg-mid target:py-4"
|
||||
id={props.items.length > 1 ? props.year.toString() : undefined}
|
||||
id={items.length > 1 ? year.toString() : undefined}
|
||||
>
|
||||
{props.items.map((item, index) => (
|
||||
{items.map((item, index) => (
|
||||
<ChronologyItemComponent
|
||||
key={index}
|
||||
item={item}
|
||||
|
@ -28,4 +29,3 @@ export function ChronologyYearComponent(props: Props): JSX.Element {
|
|||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,13 @@ interface Props {
|
|||
index: number;
|
||||
}
|
||||
|
||||
export default function DefinitionCard(props: Props): JSX.Element {
|
||||
const { source, translations = [], languages, langui, index } = props;
|
||||
|
||||
const DefinitionCard = ({
|
||||
source,
|
||||
translations = [],
|
||||
languages,
|
||||
langui,
|
||||
index,
|
||||
}: Props): JSX.Element => {
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
||||
useSmartLanguage({
|
||||
items: translations,
|
||||
|
@ -33,8 +37,7 @@ export default function DefinitionCard(props: Props): JSX.Element {
|
|||
return (
|
||||
<>
|
||||
<div className="flex place-items-center gap-2">
|
||||
{/* TODO: Langui */}
|
||||
<p className="font-headers text-lg">{`Definition ${index}`}</p>
|
||||
<p className="font-headers text-lg">{`${langui.definition} ${index}`}</p>
|
||||
{selectedTranslation?.status && (
|
||||
<ToolTip
|
||||
content={getStatusDescription(selectedTranslation.status, langui)}
|
||||
|
@ -52,4 +55,5 @@ export default function DefinitionCard(props: Props): JSX.Element {
|
|||
<p>{selectedTranslation?.definition}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default DefinitionCard;
|
||||
|
|
|
@ -106,15 +106,13 @@ const initialState: RequiredNonNullable<AppLayoutState> = {
|
|||
|
||||
const AppContext = React.createContext<AppLayoutState>(initialState);
|
||||
|
||||
export function useAppLayout(): AppLayoutState {
|
||||
return useContext(AppContext);
|
||||
}
|
||||
export const useAppLayout = (): AppLayoutState => useContext(AppContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AppContextProvider(props: Props): JSX.Element {
|
||||
export const AppContextProvider = (props: Props): JSX.Element => {
|
||||
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage(
|
||||
"subPanelOpen",
|
||||
initialState.subPanelOpen
|
||||
|
@ -176,43 +174,43 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
initialState.libraryItemUserStatus
|
||||
);
|
||||
|
||||
function toggleSubPanelOpen() {
|
||||
const toggleSubPanelOpen = () => {
|
||||
setSubPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleConfigPanelOpen() {
|
||||
const toggleConfigPanelOpen = () => {
|
||||
setConfigPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleSearchPanelOpen() {
|
||||
const toggleSearchPanelOpen = () => {
|
||||
setSearchPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleMainPanelReduced() {
|
||||
const toggleMainPanelReduced = () => {
|
||||
setMainPanelReduced((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleMainPanelOpen() {
|
||||
const toggleMainPanelOpen = () => {
|
||||
setMainPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleDarkMode() {
|
||||
const toggleDarkMode = () => {
|
||||
setDarkMode((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
function toggleMenuGestures() {
|
||||
const toggleMenuGestures = () => {
|
||||
setMenuGestures((current) => !current);
|
||||
}
|
||||
};
|
||||
|
||||
function toggleSelectedThemeMode() {
|
||||
const toggleSelectedThemeMode = () => {
|
||||
setSelectedThemeMode((current) =>
|
||||
isDefined(current) ? !current : current
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function toggleDyslexic() {
|
||||
const toggleDyslexic = () => {
|
||||
setDyslexic((current) => (isDefined(current) ? !current : current));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AppContext.Provider
|
||||
|
@ -259,4 +257,4 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
{props.children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,9 +17,9 @@ export type AppStaticProps = {
|
|||
languages: NonNullable<GetLanguagesQuery["languages"]>["data"];
|
||||
};
|
||||
|
||||
export async function getAppStaticProps(
|
||||
export const getAppStaticProps = async (
|
||||
context: GetStaticPropsContext
|
||||
): Promise<AppStaticProps> {
|
||||
): Promise<AppStaticProps> => {
|
||||
const sdk = getReadySdk();
|
||||
const languages = (await sdk.getLanguages()).languages;
|
||||
|
||||
|
@ -53,4 +53,4 @@ export async function getAppStaticProps(
|
|||
};
|
||||
|
||||
return appStaticProps;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PostWithTranslations } from "helpers/types";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps, GetStaticPropsContext } from "next";
|
||||
import { AppStaticProps, getAppStaticProps } from "./getAppStaticProps";
|
||||
import { getReadySdk } from "./sdk";
|
||||
|
||||
|
@ -7,12 +7,8 @@ export interface PostStaticProps extends AppStaticProps {
|
|||
post: PostWithTranslations;
|
||||
}
|
||||
|
||||
export function getPostStaticProps(
|
||||
slug: string
|
||||
): (
|
||||
context: GetStaticPropsContext
|
||||
) => Promise<{ notFound: boolean } | { props: PostStaticProps }> {
|
||||
return async (context: GetStaticPropsContext) => {
|
||||
export const getPostStaticProps = (slug: string): GetStaticProps => {
|
||||
return async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const post = await sdk.getPost({
|
||||
slug: slug,
|
||||
|
@ -29,4 +25,4 @@ export function getPostStaticProps(
|
|||
}
|
||||
return { notFound: true };
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -154,6 +154,11 @@ query getWebsiteInterface($language_code: String) {
|
|||
only_display_items_i_want
|
||||
only_display_unmarked_items
|
||||
display_all_items
|
||||
table_of_contents
|
||||
definition
|
||||
no_results_message
|
||||
all
|
||||
special_pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { GraphQLClient } from "graphql-request";
|
||||
import { getSdk } from "graphql/generated";
|
||||
|
||||
export function getReadySdk() {
|
||||
export const getReadySdk = () => {
|
||||
const client = new GraphQLClient(process.env.URL_GRAPHQL ?? "", {
|
||||
headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` },
|
||||
});
|
||||
return getSdk(client);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
export function cIf(
|
||||
export const cIf = (
|
||||
condition: boolean | null | undefined | string,
|
||||
ifTrueCss: string,
|
||||
ifFalseCss?: string
|
||||
) {
|
||||
) => {
|
||||
return condition ? ifTrueCss : ifFalseCss ?? "";
|
||||
}
|
||||
};
|
||||
|
||||
export function cJoin(...args: (string | undefined)[]): string {
|
||||
export const cJoin = (...args: (string | undefined)[]): string => {
|
||||
return args.filter((elem) => elem).join(" ");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,16 +2,21 @@ export interface Wrapper {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function ConditionalWrapper<T>(props: {
|
||||
interface Props<T> {
|
||||
isWrapping: boolean;
|
||||
children: React.ReactNode;
|
||||
wrapper: (wrapperProps: T & Wrapper) => JSX.Element;
|
||||
wrapperProps: T;
|
||||
}): JSX.Element {
|
||||
const { isWrapping, children, wrapper: Wrapper, wrapperProps } = props;
|
||||
return isWrapping ? (
|
||||
}
|
||||
|
||||
export const ConditionalWrapper = <T,>({
|
||||
isWrapping,
|
||||
children,
|
||||
wrapper: Wrapper,
|
||||
wrapperProps,
|
||||
}: Props<T>): JSX.Element =>
|
||||
isWrapping ? (
|
||||
<Wrapper {...wrapperProps}>{children}</Wrapper>
|
||||
) : (
|
||||
<>{children}</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,9 +10,12 @@ interface Description {
|
|||
categories?: Content["categories"];
|
||||
}
|
||||
|
||||
export function getDescription(props: Description): string {
|
||||
const { langui, description: text, type, categories } = props;
|
||||
|
||||
export const getDescription = ({
|
||||
langui,
|
||||
description: text,
|
||||
type,
|
||||
categories,
|
||||
}: Description): string => {
|
||||
let description = "";
|
||||
|
||||
// TEXT
|
||||
|
@ -44,15 +47,15 @@ export function getDescription(props: Description): string {
|
|||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
};
|
||||
|
||||
function prettyMarkdown(markdown: string): string {
|
||||
const prettyMarkdown = (markdown: string): string => {
|
||||
return markdown.replace(/[*]/gu, "").replace(/[_]/gu, "");
|
||||
}
|
||||
};
|
||||
|
||||
function prettyChip(items: (string | undefined)[]): string {
|
||||
const prettyChip = (items: (string | undefined)[]): string => {
|
||||
return items
|
||||
.filter((item) => isDefined(item))
|
||||
.map((item) => `(${item})`)
|
||||
.join(" ");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
|||
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
||||
import { convertPrice } from "./numbers";
|
||||
|
||||
export function prettyDate(datePicker: DatePickerFragment): string {
|
||||
export const prettyDate = (datePicker: DatePickerFragment): string => {
|
||||
let result = "";
|
||||
if (datePicker.year) result += datePicker.year.toString();
|
||||
if (datePicker.month)
|
||||
|
@ -10,13 +10,13 @@ export function prettyDate(datePicker: DatePickerFragment): string {
|
|||
if (datePicker.day)
|
||||
result += `/${datePicker.day.toString().padStart(2, "0")}`;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyPrice(
|
||||
export const prettyPrice = (
|
||||
pricePicker: PricePickerFragment,
|
||||
currencies: AppStaticProps["currencies"],
|
||||
targetCurrencyCode?: string
|
||||
): string {
|
||||
): string => {
|
||||
if (!targetCurrencyCode) return "";
|
||||
let result = "";
|
||||
currencies.map((currency) => {
|
||||
|
@ -31,9 +31,9 @@ export function prettyPrice(
|
|||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function prettySlug(slug?: string, parentSlug?: string): string {
|
||||
export const prettySlug = (slug?: string, parentSlug?: string): string => {
|
||||
if (slug) {
|
||||
if (parentSlug && slug.startsWith(parentSlug))
|
||||
slug = slug.substring(parentSlug.length + 1);
|
||||
|
@ -42,24 +42,24 @@ export function prettySlug(slug?: string, parentSlug?: string): string {
|
|||
return capitalizeString(slug);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyinlineTitle(
|
||||
export const prettyinlineTitle = (
|
||||
pretitle: string | undefined | null,
|
||||
title: string | undefined | null,
|
||||
subtitle: string | undefined | null
|
||||
): string {
|
||||
): string => {
|
||||
let result = "";
|
||||
if (pretitle) result += `${pretitle}: `;
|
||||
result += title;
|
||||
if (subtitle) result += ` - ${subtitle}`;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyItemType(
|
||||
export const prettyItemType = (
|
||||
metadata: any,
|
||||
langui: AppStaticProps["langui"]
|
||||
): string | undefined | null {
|
||||
): string | undefined | null => {
|
||||
switch (metadata.__typename) {
|
||||
case "ComponentMetadataAudio":
|
||||
return langui.audio;
|
||||
|
@ -76,9 +76,9 @@ export function prettyItemType(
|
|||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyItemSubType(
|
||||
export const prettyItemSubType = (
|
||||
metadata:
|
||||
| {
|
||||
__typename: "ComponentMetadataAudio";
|
||||
|
@ -156,7 +156,7 @@ export function prettyItemSubType(
|
|||
}
|
||||
| { __typename: "Error" }
|
||||
| null
|
||||
): string {
|
||||
): string => {
|
||||
if (metadata) {
|
||||
switch (metadata.__typename) {
|
||||
case "ComponentMetadataAudio":
|
||||
|
@ -195,9 +195,9 @@ export function prettyItemSubType(
|
|||
}
|
||||
return "";
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyShortenNumber(number: number): string {
|
||||
export const prettyShortenNumber = (number: number): string => {
|
||||
if (number > 1000000) {
|
||||
return number.toLocaleString(undefined, {
|
||||
maximumSignificantDigits: 3,
|
||||
|
@ -210,9 +210,9 @@ export function prettyShortenNumber(number: number): string {
|
|||
);
|
||||
}
|
||||
return number.toLocaleString();
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyDuration(seconds: number): string {
|
||||
export const prettyDuration = (seconds: number): string => {
|
||||
let hours = 0;
|
||||
let minutes = 0;
|
||||
while (seconds > 60) {
|
||||
|
@ -228,36 +228,36 @@ export function prettyDuration(seconds: number): string {
|
|||
result += minutes.toString().padStart(2, "0") + ":";
|
||||
result += seconds.toString().padStart(2, "0");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyLanguage(
|
||||
export const prettyLanguage = (
|
||||
code: string,
|
||||
languages: AppStaticProps["languages"]
|
||||
): string {
|
||||
): string => {
|
||||
let result = code;
|
||||
languages.forEach((language) => {
|
||||
if (language?.attributes?.code === code)
|
||||
result = language.attributes.localized_name;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function prettyURL(url: string): string {
|
||||
export const prettyURL = (url: string): string => {
|
||||
let domain = new URL(url);
|
||||
return domain.hostname.replace("www.", "");
|
||||
}
|
||||
};
|
||||
|
||||
function capitalizeString(string: string): string {
|
||||
function capitalizeWord(word: string): string {
|
||||
const capitalizeString = (string: string): string => {
|
||||
const capitalizeWord = (word: string): string => {
|
||||
return word.charAt(0).toUpperCase() + word.substring(1);
|
||||
}
|
||||
};
|
||||
|
||||
let words = string.split(" ");
|
||||
words = words.map((word) => capitalizeWord(word));
|
||||
return words.join(" ");
|
||||
}
|
||||
};
|
||||
|
||||
export function slugify(string: string | undefined): string {
|
||||
export const slugify = (string: string | undefined): string => {
|
||||
if (!string) {
|
||||
return "";
|
||||
}
|
||||
|
@ -275,4 +275,4 @@ export function slugify(string: string | undefined): string {
|
|||
.replace(/[^a-z0-9- ]/gu, "")
|
||||
.trim()
|
||||
.replace(/ /gu, "-");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ interface OgImage {
|
|||
alt: string;
|
||||
}
|
||||
|
||||
export function getAssetFilename(path: string): string {
|
||||
export const getAssetFilename = (path: string): string => {
|
||||
let result = path.split("/");
|
||||
result = result[result.length - 1].split(".");
|
||||
result = result
|
||||
|
@ -22,9 +22,9 @@ export function getAssetFilename(path: string): string {
|
|||
.join(".")
|
||||
.split("_");
|
||||
return result[0];
|
||||
}
|
||||
};
|
||||
|
||||
export function getAssetURL(url: string, quality: ImageQuality): string {
|
||||
export const getAssetURL = (url: string, quality: ImageQuality): string => {
|
||||
let newUrl = url;
|
||||
newUrl = newUrl.replace(/^\/uploads/u, `/${quality}`);
|
||||
newUrl = newUrl.replace(/.jpg$/u, ".webp");
|
||||
|
@ -32,26 +32,26 @@ export function getAssetURL(url: string, quality: ImageQuality): string {
|
|||
newUrl = newUrl.replace(/.png$/u, ".webp");
|
||||
if (quality === ImageQuality.Og) newUrl = newUrl.replace(/.webp$/u, ".jpg");
|
||||
return process.env.NEXT_PUBLIC_URL_IMG + newUrl;
|
||||
}
|
||||
};
|
||||
|
||||
function getImgSizesByMaxSize(
|
||||
const getImgSizesByMaxSize = (
|
||||
width: number,
|
||||
height: number,
|
||||
maxSize: number
|
||||
): { width: number; height: number } {
|
||||
): { width: number; height: number } => {
|
||||
if (width > height) {
|
||||
if (width < maxSize) return { width: width, height: height };
|
||||
return { width: maxSize, height: (height / width) * maxSize };
|
||||
}
|
||||
if (height < maxSize) return { width: width, height: height };
|
||||
return { width: (width / height) * maxSize, height: maxSize };
|
||||
}
|
||||
};
|
||||
|
||||
export function getImgSizesByQuality(
|
||||
export const getImgSizesByQuality = (
|
||||
width: number,
|
||||
height: number,
|
||||
quality: ImageQuality
|
||||
): { width: number; height: number } {
|
||||
): { width: number; height: number } => {
|
||||
switch (quality) {
|
||||
case ImageQuality.Og:
|
||||
return getImgSizesByMaxSize(width, height, 512);
|
||||
|
@ -64,12 +64,12 @@ export function getImgSizesByQuality(
|
|||
default:
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function getOgImage(
|
||||
export const getOgImage = (
|
||||
quality: ImageQuality,
|
||||
image: UploadImageFragment
|
||||
): OgImage {
|
||||
): OgImage => {
|
||||
const imgSize = getImgSizesByQuality(
|
||||
image.width ?? 0,
|
||||
image.height ?? 0,
|
||||
|
@ -81,4 +81,4 @@ export function getOgImage(
|
|||
height: imgSize.height,
|
||||
alt: image.alternativeText || "",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,11 +10,11 @@ import LibraryPage from "../pages/library/index";
|
|||
type Items = Parameters<typeof LibraryPage>[0]["items"];
|
||||
type GroupLibraryItems = Map<string, Items>;
|
||||
|
||||
export function getGroups(
|
||||
export const getGroups = (
|
||||
langui: AppStaticProps["langui"],
|
||||
groupByType: number,
|
||||
items: Items
|
||||
): GroupLibraryItems {
|
||||
): GroupLibraryItems => {
|
||||
const groups: GroupLibraryItems = new Map();
|
||||
|
||||
switch (groupByType) {
|
||||
|
@ -145,9 +145,9 @@ export function getGroups(
|
|||
}
|
||||
}
|
||||
return mapRemoveEmptyValues(groups);
|
||||
}
|
||||
};
|
||||
|
||||
export function filterItems(
|
||||
export const filterItems = (
|
||||
appLayout: AppLayoutState,
|
||||
items: Items,
|
||||
searchName: string,
|
||||
|
@ -155,7 +155,7 @@ export function filterItems(
|
|||
showPrimaryItems: boolean,
|
||||
showSecondaryItems: boolean,
|
||||
filterUserStatus: LibraryItemUserStatus | undefined
|
||||
): Items {
|
||||
): Items => {
|
||||
return items.filter((item) => {
|
||||
if (!showSubitems && !item.attributes?.root_item) return false;
|
||||
if (showSubitems && isUntangibleGroupItem(item.attributes?.metadata?.[0])) {
|
||||
|
@ -194,23 +194,23 @@ export function filterItems(
|
|||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Properly type this shit
|
||||
export function isUntangibleGroupItem(metadata: any) {
|
||||
export const isUntangibleGroupItem = (metadata: any) => {
|
||||
return (
|
||||
metadata &&
|
||||
metadata.__typename === "ComponentMetadataGroup" &&
|
||||
(metadata.subtype?.data?.attributes?.slug === "variant-set" ||
|
||||
metadata.subtype?.data?.attributes?.slug === "relation-set")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export function sortBy(
|
||||
export const sortBy = (
|
||||
orderByType: number,
|
||||
items: Items,
|
||||
currencies: AppStaticProps["currencies"]
|
||||
) {
|
||||
) => {
|
||||
switch (orderByType) {
|
||||
case 0:
|
||||
return items.sort((a, b) => {
|
||||
|
@ -249,4 +249,4 @@ export function sortBy(
|
|||
default:
|
||||
return items;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { GetCurrenciesQuery, PricePickerFragment } from "graphql/generated";
|
||||
|
||||
export function convertPrice(
|
||||
export const convertPrice = (
|
||||
pricePicker: PricePickerFragment,
|
||||
targetCurrency: NonNullable<GetCurrenciesQuery["currencies"]>["data"][number]
|
||||
): number {
|
||||
): number => {
|
||||
if (
|
||||
pricePicker.amount &&
|
||||
pricePicker.currency?.data?.attributes &&
|
||||
|
@ -14,17 +14,17 @@ export function convertPrice(
|
|||
targetCurrency.attributes.rate_to_usd
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
export function convertMmToInch(mm: number | null | undefined): string {
|
||||
export const convertMmToInch = (mm: number | null | undefined): string => {
|
||||
return mm ? (mm * 0.03937008).toPrecision(3) : "";
|
||||
}
|
||||
};
|
||||
|
||||
export function randomInt(min: number, max: number) {
|
||||
export const randomInt = (min: number, max: number) => {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
||||
};
|
||||
|
||||
export function isInteger(value: string): boolean {
|
||||
export const isInteger = (value: string): boolean => {
|
||||
// eslint-disable-next-line require-unicode-regexp
|
||||
return /^[+-]?[0-9]+$/.test(value);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ type SortContentProps =
|
|||
>["data"][number]["attributes"]
|
||||
>["contents"];
|
||||
|
||||
export function sortContent(contents: SortContentProps) {
|
||||
export const sortContent = (contents: SortContentProps) => {
|
||||
contents?.data.sort((a, b) => {
|
||||
if (
|
||||
a.attributes?.range[0]?.__typename === "ComponentRangePageRange" &&
|
||||
|
@ -31,12 +31,12 @@ export function sortContent(contents: SortContentProps) {
|
|||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getStatusDescription(
|
||||
export const getStatusDescription = (
|
||||
status: string,
|
||||
langui: AppStaticProps["langui"]
|
||||
): string | null | undefined {
|
||||
): string | null | undefined => {
|
||||
switch (status) {
|
||||
case Enum_Componentsetstextset_Status.Incomplete:
|
||||
return langui.status_incomplete;
|
||||
|
@ -53,45 +53,44 @@ export function getStatusDescription(
|
|||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function isDefined<T>(t: T): t is NonNullable<T> {
|
||||
return t !== null && t !== undefined;
|
||||
}
|
||||
export const isDefined = <T>(t: T): t is NonNullable<T> =>
|
||||
t !== null && t !== undefined;
|
||||
|
||||
export function isUndefined<T>(t: T | undefined | null): t is undefined | null {
|
||||
return t === null || t === undefined;
|
||||
}
|
||||
export const isUndefined = <T>(
|
||||
t: T | undefined | null
|
||||
): t is undefined | null => t === null || t === undefined;
|
||||
|
||||
export function isDefinedAndNotEmpty(
|
||||
export const isDefinedAndNotEmpty = (
|
||||
string: string | undefined | null
|
||||
): string is string {
|
||||
return isDefined(string) && string.length > 0;
|
||||
}
|
||||
): string is string => isDefined(string) && string.length > 0;
|
||||
|
||||
export function filterDefined<T>(t: T[] | undefined | null): NonNullable<T>[] {
|
||||
if (isUndefined(t)) return [];
|
||||
return t.filter((item) => isDefined(item)) as NonNullable<T>[];
|
||||
}
|
||||
export const filterDefined = <T>(t: T[] | undefined | null): NonNullable<T>[] =>
|
||||
isUndefined(t)
|
||||
? []
|
||||
: (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
||||
|
||||
export function filterHasAttributes<T, P extends keyof NonNullable<T>>(
|
||||
export const filterHasAttributes = <T, P extends keyof NonNullable<T>>(
|
||||
t: T[] | undefined | null,
|
||||
attributes?: P[]
|
||||
): SelectiveRequiredNonNullable<NonNullable<T>, P>[] {
|
||||
if (isUndefined(t)) return [];
|
||||
return t.filter((item) => {
|
||||
): SelectiveRequiredNonNullable<NonNullable<T>, P>[] =>
|
||||
isUndefined(t)
|
||||
? []
|
||||
: (t.filter((item) => {
|
||||
if (isDefined(item)) {
|
||||
const attributesToCheck = attributes ?? (Object.keys(item) as P[]);
|
||||
return attributesToCheck.every((attribute) => isDefined(item[attribute]));
|
||||
return attributesToCheck.every((attribute) =>
|
||||
isDefined(item[attribute])
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}) as unknown as SelectiveRequiredNonNullable<NonNullable<T>, P>[];
|
||||
}
|
||||
}) as unknown as SelectiveRequiredNonNullable<NonNullable<T>, P>[]);
|
||||
|
||||
export function iterateMap<K, V, U>(
|
||||
export const iterateMap = <K, V, U>(
|
||||
map: Map<K, V>,
|
||||
callbackfn: (key: K, value: V, index: number) => U
|
||||
): U[] {
|
||||
): U[] => {
|
||||
const result: U[] = [];
|
||||
let index = 0;
|
||||
for (const [key, value] of map.entries()) {
|
||||
|
@ -99,21 +98,18 @@ export function iterateMap<K, V, U>(
|
|||
index += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export function mapMoveEntry<K, V>(
|
||||
export const mapMoveEntry = <K, V>(
|
||||
map: Map<K, V>,
|
||||
sourceIndex: number,
|
||||
targetIndex: number
|
||||
) {
|
||||
return new Map(arrayMove([...map], sourceIndex, targetIndex));
|
||||
}
|
||||
) => new Map(arrayMove([...map], sourceIndex, targetIndex));
|
||||
|
||||
function arrayMove<T>(arr: T[], sourceIndex: number, targetIndex: number) {
|
||||
const arrayMove = <T>(arr: T[], sourceIndex: number, targetIndex: number) => {
|
||||
arr.splice(targetIndex, 0, arr.splice(sourceIndex, 1)[0]);
|
||||
return arr;
|
||||
}
|
||||
};
|
||||
|
||||
export function mapRemoveEmptyValues<K, V>(groups: Map<K, V[]>): Map<K, V[]> {
|
||||
return new Map([...groups].filter(([_, items]) => items.length > 0));
|
||||
}
|
||||
export const mapRemoveEmptyValues = <K, V>(groups: Map<K, V[]>): Map<K, V[]> =>
|
||||
new Map([...groups].filter(([_, items]) => items.length > 0));
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
export function getVideoThumbnailURL(uid: string): string {
|
||||
return `${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.webp`;
|
||||
}
|
||||
export const getVideoThumbnailURL = (uid: string): string =>
|
||||
`${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.webp`;
|
||||
|
||||
export function getVideoFile(uid: string): string {
|
||||
return `${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.mp4`;
|
||||
}
|
||||
export const getVideoFile = (uid: string): string =>
|
||||
`${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.mp4`;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect } from "react";
|
|||
import { usePrefersDarkMode } from "./useMediaQuery";
|
||||
import { useStateWithLocalStorage } from "./useStateWithLocalStorage";
|
||||
|
||||
export function useDarkMode(
|
||||
export const useDarkMode = (
|
||||
key: string,
|
||||
initialValue: boolean | undefined
|
||||
): [
|
||||
|
@ -10,11 +10,9 @@ export function useDarkMode(
|
|||
boolean | undefined,
|
||||
React.Dispatch<React.SetStateAction<boolean | undefined>>,
|
||||
React.Dispatch<React.SetStateAction<boolean | undefined>>
|
||||
] {
|
||||
] => {
|
||||
const [darkMode, setDarkMode] = useStateWithLocalStorage(key, initialValue);
|
||||
|
||||
const prefersDarkMode = usePrefersDarkMode();
|
||||
|
||||
const [selectedThemeMode, setSelectedThemeMode] = useStateWithLocalStorage(
|
||||
"selectedThemeMode",
|
||||
false
|
||||
|
@ -25,4 +23,4 @@ export function useDarkMode(
|
|||
}, [selectedThemeMode, prefersDarkMode, setDarkMode]);
|
||||
|
||||
return [darkMode, selectedThemeMode, setDarkMode, setSelectedThemeMode];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { LightBox } from "components/LightBox";
|
||||
import { useState } from "react";
|
||||
|
||||
export function useLightBox(): [
|
||||
export const useLightBox = (): [
|
||||
(images: string[], index?: number) => void,
|
||||
() => JSX.Element
|
||||
] {
|
||||
] => {
|
||||
const [lightboxOpen, setLightboxOpen] = useState(false);
|
||||
const [lightboxImages, setLightboxImages] = useState([""]);
|
||||
const [lightboxIndex, setLightboxIndex] = useState(0);
|
||||
|
@ -25,4 +25,4 @@ export function useLightBox(): [
|
|||
/>
|
||||
),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { breaks } from "../../design.config";
|
||||
|
||||
function useMediaQuery(query: string): boolean {
|
||||
function getMatches(query: string): boolean {
|
||||
const useMediaQuery = (query: string): boolean => {
|
||||
const getMatches = (query: string): boolean => {
|
||||
// Prevents SSR issues
|
||||
if (typeof window !== "undefined") {
|
||||
return window.matchMedia(query).matches;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const [matches, setMatches] = useState<boolean>(getMatches(query));
|
||||
|
||||
useEffect(() => {
|
||||
function handleChange() {
|
||||
const handleChange = () => {
|
||||
setMatches(getMatches(query));
|
||||
}
|
||||
};
|
||||
|
||||
const matchMedia = window.matchMedia(query);
|
||||
|
||||
|
@ -31,25 +31,16 @@ function useMediaQuery(query: string): boolean {
|
|||
}, [query]);
|
||||
|
||||
return matches;
|
||||
}
|
||||
};
|
||||
|
||||
// ts-unused-exports:disable-next-line
|
||||
export function useMediaThin() {
|
||||
return useMediaQuery(breaks.thin.raw);
|
||||
}
|
||||
export const useMediaThin = () => useMediaQuery(breaks.thin.raw);
|
||||
|
||||
export function useMediaMobile() {
|
||||
return useMediaQuery(breaks.mobile.raw);
|
||||
}
|
||||
export const useMediaMobile = () => useMediaQuery(breaks.mobile.raw);
|
||||
|
||||
export function useMediaDesktop() {
|
||||
return useMediaQuery(breaks.desktop.raw);
|
||||
}
|
||||
export const useMediaDesktop = () => useMediaQuery(breaks.desktop.raw);
|
||||
|
||||
export function useMediaHoverable() {
|
||||
return useMediaQuery("(hover: hover)");
|
||||
}
|
||||
export const useMediaHoverable = () => useMediaQuery("(hover: hover)");
|
||||
|
||||
export function usePrefersDarkMode() {
|
||||
return useMediaQuery("(prefers-color-scheme: dark)");
|
||||
}
|
||||
export const usePrefersDarkMode = () =>
|
||||
useMediaQuery("(prefers-color-scheme: dark)");
|
||||
|
|
|
@ -5,8 +5,8 @@ export enum AnchorIds {
|
|||
}
|
||||
|
||||
// Scroll to top of element "id" when "deps" update.
|
||||
export function useScrollTopOnChange(id: AnchorIds, deps: DependencyList) {
|
||||
export const useScrollTopOnChange = (id: AnchorIds, deps: DependencyList) => {
|
||||
useEffect(() => {
|
||||
document.querySelector(`#${id}`)?.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}, deps);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,31 +13,28 @@ interface Props<T> {
|
|||
transform?: (item: NonNullable<T>) => NonNullable<T>;
|
||||
}
|
||||
|
||||
function getPreferredLanguage(
|
||||
const getPreferredLanguage = (
|
||||
preferredLanguages: (string | undefined)[],
|
||||
availableLanguages: Map<string, number>
|
||||
): number | undefined {
|
||||
): number | undefined => {
|
||||
for (const locale of preferredLanguages) {
|
||||
if (isDefined(locale) && availableLanguages.has(locale)) {
|
||||
return availableLanguages.get(locale);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export function useSmartLanguage<T>(
|
||||
props: Props<T>
|
||||
): [
|
||||
T | undefined,
|
||||
typeof LanguageSwitcher,
|
||||
Parameters<typeof LanguageSwitcher>[0]
|
||||
] {
|
||||
const {
|
||||
export const useSmartLanguage = <T>({
|
||||
items,
|
||||
languageExtractor,
|
||||
languages,
|
||||
transform = (item) => item,
|
||||
} = props;
|
||||
}: Props<T>): [
|
||||
T | undefined,
|
||||
typeof LanguageSwitcher,
|
||||
Parameters<typeof LanguageSwitcher>[0]
|
||||
] => {
|
||||
const { preferredLanguages } = useAppLayout();
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -81,4 +78,4 @@ export function useSmartLanguage<T>(
|
|||
};
|
||||
|
||||
return [selectedTranslation, LanguageSwitcher, languageSwitcherProps];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { filterDefined, isDefined } from "helpers/others";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
interface Props<T> {
|
||||
items: T[];
|
||||
languages: AppStaticProps["languages"];
|
||||
languageExtractor: (item: NonNullable<T>) => string | undefined;
|
||||
transform?: (item: NonNullable<T>) => NonNullable<T>;
|
||||
}
|
||||
|
||||
function getPreferredLanguage(
|
||||
preferredLanguages: (string | undefined)[],
|
||||
availableLanguages: Map<string, number>
|
||||
): number | undefined {
|
||||
for (const locale of preferredLanguages) {
|
||||
if (isDefined(locale) && availableLanguages.has(locale)) {
|
||||
return availableLanguages.get(locale);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function useSmartLanguage<T>(
|
||||
props: Props<T>
|
||||
): [
|
||||
T | undefined,
|
||||
typeof LanguageSwitcher,
|
||||
Parameters<typeof LanguageSwitcher>[0]
|
||||
] {
|
||||
const {
|
||||
items,
|
||||
languageExtractor,
|
||||
languages,
|
||||
transform = (item) => item,
|
||||
} = props;
|
||||
const { preferredLanguages } = useAppLayout();
|
||||
const router = useRouter();
|
||||
|
||||
const availableLocales = useMemo(() => {
|
||||
const memo = new Map<string, number>();
|
||||
filterDefined(items).map((elem, index) => {
|
||||
const result = languageExtractor(elem);
|
||||
if (isDefined(result)) memo.set(result, index);
|
||||
});
|
||||
return memo;
|
||||
}, [items, languageExtractor]);
|
||||
|
||||
const [selectedTranslationIndex, setSelectedTranslationIndex] = useState<
|
||||
number | undefined
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedTranslationIndex(
|
||||
getPreferredLanguage(
|
||||
preferredLanguages ?? [router.locale],
|
||||
availableLocales
|
||||
)
|
||||
);
|
||||
}, [preferredLanguages, availableLocales, router.locale]);
|
||||
|
||||
const selectedTranslation = useMemo(() => {
|
||||
if (isDefined(selectedTranslationIndex)) {
|
||||
const item = items[selectedTranslationIndex];
|
||||
if (isDefined(item)) {
|
||||
return transform(item);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}, [items, selectedTranslationIndex, transform]);
|
||||
|
||||
const languageSwitcherProps = {
|
||||
languages: languages,
|
||||
locales: availableLocales,
|
||||
localesIndex: selectedTranslationIndex,
|
||||
onLanguageChanged: setSelectedTranslationIndex,
|
||||
};
|
||||
|
||||
return [selectedTranslation, LanguageSwitcher, languageSwitcherProps];
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import { isDefined } from "helpers/others";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useStateWithLocalStorage<T>(
|
||||
export const useStateWithLocalStorage = <T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>] {
|
||||
): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>] => {
|
||||
const [value, setValue] = useState<T | undefined>(undefined);
|
||||
const [, setFromLocaleStorage] = useState<boolean>(false);
|
||||
|
||||
|
@ -28,4 +28,4 @@ export function useStateWithLocalStorage<T>(
|
|||
}, [value, key]);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Dispatch, SetStateAction, useCallback } from "react";
|
||||
|
||||
export function useToggle(setState: Dispatch<SetStateAction<boolean>>) {
|
||||
return useCallback(() => {
|
||||
export const useToggle = (setState: Dispatch<SetStateAction<boolean>>) =>
|
||||
useCallback(() => {
|
||||
setState((current) => !current);
|
||||
}, []);
|
||||
}
|
||||
|
|
|
@ -5,16 +5,19 @@ import {
|
|||
} from "components/PanelComponents/ReturnButton";
|
||||
import { ContentPanel } from "components/Panels/ContentPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { GetStaticProps } from "next";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useMemo } from "react";
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function FourOhFour(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const contentPanel = useMemo(
|
||||
() => (
|
||||
const FourOhFour = ({ langui, ...otherProps }: Props): JSX.Element => (
|
||||
<AppLayout
|
||||
navTitle="404"
|
||||
contentPanel={
|
||||
<ContentPanel>
|
||||
<h1>404 - {langui.page_not_found}</h1>
|
||||
<ReturnButton
|
||||
|
@ -24,19 +27,23 @@ export default function FourOhFour(props: Props): JSX.Element {
|
|||
displayOn={ReturnButtonType.Both}
|
||||
/>
|
||||
</ContentPanel>
|
||||
),
|
||||
[langui]
|
||||
);
|
||||
return <AppLayout navTitle="404" contentPanel={contentPanel} {...props} />;
|
||||
}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
export default FourOhFour;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,16 +5,19 @@ import {
|
|||
} from "components/PanelComponents/ReturnButton";
|
||||
import { ContentPanel } from "components/Panels/ContentPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { GetStaticProps } from "next";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useMemo } from "react";
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function FiveHundred(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const contentPanel = useMemo(
|
||||
() => (
|
||||
const FiveHundred = ({ langui, ...otherProps }: Props): JSX.Element => (
|
||||
<AppLayout
|
||||
navTitle="500"
|
||||
contentPanel={
|
||||
<ContentPanel>
|
||||
<h1>500 - Internal Server Error</h1>
|
||||
<ReturnButton
|
||||
|
@ -24,19 +27,23 @@ export default function FiveHundred(props: Props): JSX.Element {
|
|||
displayOn={ReturnButtonType.Both}
|
||||
/>
|
||||
</ContentPanel>
|
||||
),
|
||||
[langui]
|
||||
);
|
||||
return <AppLayout navTitle="500" contentPanel={contentPanel} {...props} />;
|
||||
}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
export default FiveHundred;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,10 +9,9 @@ import { AppContextProvider } from "contexts/AppLayoutContext";
|
|||
import type { AppProps } from "next/app";
|
||||
import "tailwind.css";
|
||||
|
||||
export default function AccordsLibraryApp(props: AppProps): JSX.Element {
|
||||
return (
|
||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
||||
<AppContextProvider>
|
||||
<props.Component {...props.pageProps} />
|
||||
</AppContextProvider>
|
||||
);
|
||||
}
|
||||
export default AccordsLibraryApp;
|
||||
|
|
|
@ -4,9 +4,17 @@ import {
|
|||
PostStaticProps,
|
||||
} from "graphql/getPostStaticProps";
|
||||
|
||||
export default function AccordsHandbook(props: PostStaticProps): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
return (
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const AccordsHandbook = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
currencies,
|
||||
}: PostStaticProps): JSX.Element => (
|
||||
<PostPage
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
|
@ -18,6 +26,11 @@ export default function AccordsHandbook(props: PostStaticProps): JSX.Element {
|
|||
displayLanguageSwitcher
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default AccordsHandbook;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps = getPostStaticProps("accords-handbook");
|
||||
|
|
|
@ -11,9 +11,17 @@ import { useRouter } from "next/router";
|
|||
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function AboutUs(props: PostStaticProps): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const AboutUs = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
currencies,
|
||||
}: PostStaticProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const [formResponse, setFormResponse] = useState("");
|
||||
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">(
|
||||
|
@ -181,6 +189,12 @@ export default function AboutUs(props: PostStaticProps): JSX.Element {
|
|||
displayLanguageSwitcher
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default AboutUs;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps = getPostStaticProps("contact");
|
||||
|
|
|
@ -4,16 +4,19 @@ import { NavOption } from "components/PanelComponents/NavOption";
|
|||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { GetStaticProps } from "next";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useMemo } from "react";
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function AboutUs(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
const AboutUs = ({ langui, ...otherProps }: Props): JSX.Element => (
|
||||
<AppLayout
|
||||
navTitle={langui.about_us}
|
||||
subPanel={
|
||||
<SubPanel>
|
||||
<PanelHeader
|
||||
icon={Icon.Info}
|
||||
|
@ -33,21 +36,23 @@ export default function AboutUs(props: Props): JSX.Element {
|
|||
/>
|
||||
<NavOption title={langui.contact_us} url="/about-us/contact" border />
|
||||
</SubPanel>
|
||||
),
|
||||
[langui]
|
||||
);
|
||||
return (
|
||||
<AppLayout navTitle={langui.about_us} subPanel={subPanel} {...props} />
|
||||
);
|
||||
}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
export default AboutUs;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,9 +4,17 @@ import {
|
|||
PostStaticProps,
|
||||
} from "graphql/getPostStaticProps";
|
||||
|
||||
export default function Legality(props: PostStaticProps): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
return (
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const Legality = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
currencies,
|
||||
}: PostStaticProps): JSX.Element => (
|
||||
<PostPage
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
|
@ -18,6 +26,11 @@ export default function Legality(props: PostStaticProps): JSX.Element {
|
|||
displayLanguageSwitcher
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default Legality;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps = getPostStaticProps("legality");
|
||||
|
|
|
@ -4,9 +4,17 @@ import {
|
|||
PostStaticProps,
|
||||
} from "graphql/getPostStaticProps";
|
||||
|
||||
export default function SharingPolicy(props: PostStaticProps): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
return (
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const SharingPolicy = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
currencies,
|
||||
}: PostStaticProps): JSX.Element => (
|
||||
<PostPage
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
|
@ -18,6 +26,11 @@ export default function SharingPolicy(props: PostStaticProps): JSX.Element {
|
|||
displayLanguageSwitcher
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default SharingPolicy;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps = getPostStaticProps("sharing-policy");
|
||||
|
|
|
@ -14,10 +14,10 @@ export interface RequestMailProps {
|
|||
formName: string;
|
||||
}
|
||||
|
||||
export default async function Mail(
|
||||
const Mail = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<ResponseMailProps>
|
||||
) {
|
||||
) => {
|
||||
if (req.method === "POST") {
|
||||
const body = req.body as RequestMailProps;
|
||||
|
||||
|
@ -48,4 +48,5 @@ export default async function Mail(
|
|||
}
|
||||
|
||||
res.status(200).json({ code: "OKAY" });
|
||||
}
|
||||
};
|
||||
export default Mail;
|
||||
|
|
|
@ -81,10 +81,10 @@ type ResponseMailProps = {
|
|||
revalidated: boolean;
|
||||
};
|
||||
|
||||
export default async function Revalidate(
|
||||
const Revalidate = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<ResponseMailProps>
|
||||
) {
|
||||
) => {
|
||||
const body = req.body as RequestProps;
|
||||
const { serverRuntimeConfig } = getConfig();
|
||||
|
||||
|
@ -217,4 +217,5 @@ export default async function Revalidate(
|
|||
.status(500)
|
||||
.send({ message: "Error revalidating", revalidated: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
export default Revalidate;
|
||||
|
|
|
@ -4,14 +4,18 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
|||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Icon } from "components/Ico";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function Archives(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const Archives = ({ langui, ...otherProps }: Props): JSX.Element => {
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
|
@ -26,17 +30,26 @@ export default function Archives(props: Props): JSX.Element {
|
|||
[langui]
|
||||
);
|
||||
return (
|
||||
<AppLayout navTitle={langui.archives} subPanel={subPanel} {...props} />
|
||||
<AppLayout
|
||||
navTitle={langui.archives}
|
||||
subPanel={subPanel}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Archives;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,25 +15,25 @@ import { GetVideoChannelQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { getVideoThumbnailURL } from "helpers/videos";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { Fragment, useState, useMemo } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
channel: NonNullable<
|
||||
GetVideoChannelQuery["videoChannels"]
|
||||
>["data"][number]["attributes"];
|
||||
}
|
||||
|
||||
export default function Channel(props: Props): JSX.Element {
|
||||
const { langui, channel } = props;
|
||||
const Channel = ({ langui, channel, ...otherProps }: Props): JSX.Element => {
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
|
||||
const hoverable = useMediaHoverable();
|
||||
|
||||
|
@ -115,14 +115,19 @@ export default function Channel(props: Props): JSX.Element {
|
|||
navTitle={langui.archives}
|
||||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Channel;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const channel = await sdk.getVideoChannel({
|
||||
channel:
|
||||
|
@ -138,11 +143,11 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const channels = await sdk.getVideoChannelsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -159,4 +164,4 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,18 +21,29 @@ import { prettyDate } from "helpers/formatters";
|
|||
import { filterHasAttributes } from "helpers/others";
|
||||
import { getVideoThumbnailURL } from "helpers/videos";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const ITEM_PER_PAGE = 50;
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
videos: NonNullable<GetVideosPreviewQuery["videos"]>["data"];
|
||||
}
|
||||
|
||||
const ITEM_PER_PAGE = 50;
|
||||
|
||||
export default function Videos(props: Props): JSX.Element {
|
||||
const { langui, videos } = props;
|
||||
const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useMediaHoverable();
|
||||
const [page, setPage] = useState(0);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
|
||||
|
||||
const paginatedVideos = useMemo(() => {
|
||||
const memo = [];
|
||||
|
@ -44,9 +55,6 @@ export default function Videos(props: Props): JSX.Element {
|
|||
return memo;
|
||||
}, [videos]);
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
|
@ -130,14 +138,19 @@ export default function Videos(props: Props): JSX.Element {
|
|||
navTitle={langui.archives}
|
||||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Videos;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const videos = await sdk.getVideosPreview();
|
||||
if (!videos.videos) return { notFound: true };
|
||||
|
@ -159,4 +172,4 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,21 +21,21 @@ import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
|||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { getVideoFile } from "helpers/videos";
|
||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
video: NonNullable<
|
||||
NonNullable<GetVideoQuery["videos"]>["data"][number]["attributes"]
|
||||
>;
|
||||
}
|
||||
|
||||
export default function Video(props: Props): JSX.Element {
|
||||
const { langui, video } = props;
|
||||
const Video = ({ langui, video, ...otherProps }: Props): JSX.Element => {
|
||||
const isMobile = useMediaMobile();
|
||||
const appLayout = useAppLayout();
|
||||
const subPanel = useMemo(
|
||||
|
@ -201,14 +201,19 @@ export default function Video(props: Props): JSX.Element {
|
|||
navTitle={langui.archives}
|
||||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Video;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const videos = await sdk.getVideo({
|
||||
uid:
|
||||
|
@ -224,11 +229,11 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const videos = await sdk.getVideosSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -242,4 +247,4 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,14 +3,18 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
|||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Icon } from "components/Ico";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function Chronicles(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const Chronicles = ({ langui, ...otherProps }: Props): JSX.Element => {
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
|
@ -25,17 +29,26 @@ export default function Chronicles(props: Props): JSX.Element {
|
|||
);
|
||||
|
||||
return (
|
||||
<AppLayout navTitle={langui.chronicles} subPanel={subPanel} {...props} />
|
||||
<AppLayout
|
||||
navTitle={langui.chronicles}
|
||||
subPanel={subPanel}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Chronicles;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,8 +2,7 @@ import { AppLayout } from "components/AppLayout";
|
|||
import { Chip } from "components/Chip";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
|
||||
import { Markdawn } from "components/Markdown/Markdawn";
|
||||
import { TOC } from "components/Markdown/TOC";
|
||||
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
|
||||
import {
|
||||
ReturnButton,
|
||||
ReturnButtonType,
|
||||
|
@ -35,27 +34,25 @@ import { ContentWithTranslations } from "helpers/types";
|
|||
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { Fragment, useCallback, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
content: ContentWithTranslations;
|
||||
}
|
||||
|
||||
type Group = NonNullable<
|
||||
NonNullable<
|
||||
NonNullable<
|
||||
NonNullable<ContentWithTranslations["group"]>["data"]
|
||||
>["attributes"]
|
||||
>["contents"]
|
||||
>["data"];
|
||||
|
||||
export default function Content(props: Props): JSX.Element {
|
||||
const { langui, content, languages, currencies } = props;
|
||||
const Content = ({
|
||||
langui,
|
||||
content,
|
||||
languages,
|
||||
currencies,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const isMobile = useMediaMobile();
|
||||
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
||||
|
@ -253,15 +250,14 @@ export default function Content(props: Props): JSX.Element {
|
|||
position: "Bottom",
|
||||
}}
|
||||
infoAppend={
|
||||
<PreviewCardCTAs
|
||||
id={libraryItem.id}
|
||||
displayCTAs={
|
||||
!isUntangibleGroupItem(
|
||||
libraryItem.attributes.metadata?.[0]
|
||||
)
|
||||
}
|
||||
) && (
|
||||
<PreviewCardCTAs
|
||||
id={libraryItem.id}
|
||||
langui={langui}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
@ -277,13 +273,14 @@ export default function Content(props: Props): JSX.Element {
|
|||
{selectedTranslation?.text_set?.text && (
|
||||
<>
|
||||
<HorizontalLine />
|
||||
<TOC
|
||||
<TableOfContents
|
||||
text={selectedTranslation.text_set.text}
|
||||
title={prettyinlineTitle(
|
||||
selectedTranslation.pre_title,
|
||||
selectedTranslation.title,
|
||||
selectedTranslation.subtitle
|
||||
)}
|
||||
langui={langui}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -460,14 +457,21 @@ export default function Content(props: Props): JSX.Element {
|
|||
thumbnail={content.thumbnail?.data?.attributes ?? undefined}
|
||||
contentPanel={contentPanel}
|
||||
subPanel={subPanel}
|
||||
{...props}
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Content;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const slug = context.params?.slug ? context.params.slug.toString() : "";
|
||||
const content = await sdk.getContentText({
|
||||
|
@ -485,11 +489,11 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const contents = await sdk.getContentsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -505,9 +509,22 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function getPreviousContent(group: Group, currentSlug: string) {
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
type Group = NonNullable<
|
||||
NonNullable<
|
||||
NonNullable<
|
||||
NonNullable<ContentWithTranslations["group"]>["data"]
|
||||
>["attributes"]
|
||||
>["contents"]
|
||||
>["data"];
|
||||
|
||||
const getPreviousContent = (group: Group, currentSlug: string) => {
|
||||
for (let index = 0; index < group.length; index += 1) {
|
||||
const content = group[index];
|
||||
if (content.attributes?.slug === currentSlug && index > 0) {
|
||||
|
@ -515,9 +532,11 @@ function getPreviousContent(group: Group, currentSlug: string) {
|
|||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
function getNextContent(group: Group, currentSlug: string) {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const getNextContent = (group: Group, currentSlug: string) => {
|
||||
for (let index = 0; index < group.length; index += 1) {
|
||||
const content = group[index];
|
||||
if (content.attributes?.slug === currentSlug && index < group.length - 1) {
|
||||
|
@ -525,4 +544,4 @@ function getNextContent(group: Group, currentSlug: string) {
|
|||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,12 +9,10 @@ import {
|
|||
} from "components/Panels/ContentPanel";
|
||||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useState, useMemo } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
|
@ -29,33 +27,47 @@ import {
|
|||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
import { GetContentsQuery } from "graphql/generated";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
contents: NonNullable<GetContentsQuery["contents"]>["data"];
|
||||
}
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
type GroupContentItems = Map<string, Props["contents"]>;
|
||||
|
||||
const defaultFiltersState = {
|
||||
const DEFAULT_FILTERS_STATE = {
|
||||
groupingMethod: -1,
|
||||
keepInfoVisible: false,
|
||||
combineRelatedContent: true,
|
||||
searchName: "",
|
||||
};
|
||||
|
||||
export default function Contents(props: Props): JSX.Element {
|
||||
const { langui, contents, languages } = props;
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
contents: NonNullable<GetContentsQuery["contents"]>["data"];
|
||||
}
|
||||
|
||||
const Contents = ({
|
||||
langui,
|
||||
contents,
|
||||
languages,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const hoverable = useMediaHoverable();
|
||||
|
||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||
defaultFiltersState.groupingMethod
|
||||
DEFAULT_FILTERS_STATE.groupingMethod
|
||||
);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
defaultFiltersState.keepInfoVisible
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
);
|
||||
const [combineRelatedContent, setCombineRelatedContent] = useState(
|
||||
defaultFiltersState.combineRelatedContent
|
||||
DEFAULT_FILTERS_STATE.combineRelatedContent
|
||||
);
|
||||
const [searchName, setSearchName] = useState(
|
||||
DEFAULT_FILTERS_STATE.searchName
|
||||
);
|
||||
const [searchName, setSearchName] = useState(defaultFiltersState.searchName);
|
||||
|
||||
const effectiveCombineRelatedContent = useMemo(
|
||||
() => (searchName.length > 1 ? false : combineRelatedContent),
|
||||
|
@ -126,10 +138,12 @@ export default function Contents(props: Props): JSX.Element {
|
|||
text={langui.reset_all_filters}
|
||||
icon={Icon.Replay}
|
||||
onClick={() => {
|
||||
setSearchName(defaultFiltersState.searchName);
|
||||
setGroupingMethod(defaultFiltersState.groupingMethod);
|
||||
setKeepInfoVisible(defaultFiltersState.keepInfoVisible);
|
||||
setCombineRelatedContent(defaultFiltersState.combineRelatedContent);
|
||||
setSearchName(DEFAULT_FILTERS_STATE.searchName);
|
||||
setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
|
||||
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||
setCombineRelatedContent(
|
||||
DEFAULT_FILTERS_STATE.combineRelatedContent
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</SubPanel>
|
||||
|
@ -147,12 +161,9 @@ export default function Contents(props: Props): JSX.Element {
|
|||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
{/* TODO: Add to langui */}
|
||||
{groups.size === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
message={langui.no_results_message ?? "No results"}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
|
@ -259,14 +270,20 @@ export default function Contents(props: Props): JSX.Element {
|
|||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
subPanelIcon={Icon.Search}
|
||||
{...props}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Contents;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const contents = await sdk.getContents({
|
||||
language_code: context.locale ?? "en",
|
||||
|
@ -285,13 +302,20 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export function getGroups(
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
type GroupContentItems = Map<string, Props["contents"]>;
|
||||
|
||||
export const getGroups = (
|
||||
langui: AppStaticProps["langui"],
|
||||
groupByType: number,
|
||||
items: Props["contents"]
|
||||
): GroupContentItems {
|
||||
): GroupContentItems => {
|
||||
const groups: GroupContentItems = new Map();
|
||||
|
||||
switch (groupByType) {
|
||||
|
@ -347,14 +371,16 @@ export function getGroups(
|
|||
}
|
||||
}
|
||||
return mapRemoveEmptyValues(groups);
|
||||
}
|
||||
};
|
||||
|
||||
export function filterContents(
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const filterContents = (
|
||||
contents: Props["contents"],
|
||||
combineRelatedContent: boolean,
|
||||
searchName: string
|
||||
): Props["contents"] {
|
||||
return contents.filter((content) => {
|
||||
): Props["contents"] =>
|
||||
contents.filter((content) => {
|
||||
if (
|
||||
combineRelatedContent &&
|
||||
content.attributes?.group?.data?.attributes?.combine === true &&
|
||||
|
@ -381,4 +407,3 @@ export function filterContents(
|
|||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,16 +10,19 @@ import { DevGetContentsQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
contents: DevGetContentsQuery;
|
||||
}
|
||||
|
||||
export default function CheckupContents(props: Props): JSX.Element {
|
||||
const { contents } = props;
|
||||
const CheckupContents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
||||
const testReport = testingContent(contents);
|
||||
|
||||
const contentPanel = useMemo(
|
||||
|
@ -82,13 +85,21 @@ export default function CheckupContents(props: Props): JSX.Element {
|
|||
);
|
||||
|
||||
return (
|
||||
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
|
||||
<AppLayout
|
||||
navTitle={"Checkup"}
|
||||
contentPanel={contentPanel}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default CheckupContents;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const contents = await sdk.devGetContents();
|
||||
const props: Props = {
|
||||
|
@ -98,7 +109,12 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
type Report = {
|
||||
title: string;
|
||||
|
@ -116,7 +132,7 @@ type ReportLine = {
|
|||
frontendUrl: string;
|
||||
};
|
||||
|
||||
function testingContent(contents: Props["contents"]): Report {
|
||||
const testingContent = (contents: Props["contents"]): Report => {
|
||||
const report: Report = {
|
||||
title: "Contents",
|
||||
lines: [],
|
||||
|
@ -438,4 +454,4 @@ function testingContent(contents: Props["contents"]): Report {
|
|||
}
|
||||
});
|
||||
return report;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,15 +13,22 @@ import {
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
libraryItems: DevGetLibraryItemsQuery;
|
||||
}
|
||||
|
||||
export default function CheckupLibraryItems(props: Props): JSX.Element {
|
||||
const { libraryItems } = props;
|
||||
const CheckupLibraryItems = ({
|
||||
libraryItems,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const testReport = testingLibraryItem(libraryItems);
|
||||
|
||||
const contentPanel = useMemo(
|
||||
|
@ -84,13 +91,21 @@ export default function CheckupLibraryItems(props: Props): JSX.Element {
|
|||
);
|
||||
|
||||
return (
|
||||
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
|
||||
<AppLayout
|
||||
navTitle={"Checkup"}
|
||||
contentPanel={contentPanel}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default CheckupLibraryItems;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const libraryItems = await sdk.devGetLibraryItems();
|
||||
const props: Props = {
|
||||
|
@ -100,7 +115,12 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
type Report = {
|
||||
title: string;
|
||||
|
@ -118,7 +138,7 @@ type ReportLine = {
|
|||
frontendUrl: string;
|
||||
};
|
||||
|
||||
function testingLibraryItem(libraryItems: Props["libraryItems"]): Report {
|
||||
const testingLibraryItem = (libraryItems: Props["libraryItems"]): Report => {
|
||||
const report: Report = {
|
||||
title: "Contents",
|
||||
lines: [],
|
||||
|
@ -757,4 +777,4 @@ function testingLibraryItem(libraryItems: Props["libraryItems"]): Report {
|
|||
});
|
||||
|
||||
return report;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AppLayout } from "components/AppLayout";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { Markdawn } from "components/Markdown/Markdawn";
|
||||
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
|
||||
import {
|
||||
ContentPanel,
|
||||
ContentPanelWidthSizes,
|
||||
|
@ -8,16 +8,19 @@ import {
|
|||
import { Popup } from "components/Popup";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import TurndownService from "turndown";
|
||||
import { Icon } from "components/Ico";
|
||||
import { TOC } from "components/Markdown/TOC";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
export default function Editor(props: Props): JSX.Element {
|
||||
const Editor = ({ langui, ...otherProps }: Props): JSX.Element => {
|
||||
const handleInput = useCallback((text: string) => {
|
||||
setMarkdown(text);
|
||||
}, []);
|
||||
|
@ -444,7 +447,7 @@ export default function Editor(props: Props): JSX.Element {
|
|||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<TOC text={markdown} />
|
||||
<TableOfContents text={markdown} langui={langui} />
|
||||
</div>
|
||||
</ContentPanel>
|
||||
),
|
||||
|
@ -453,6 +456,7 @@ export default function Editor(props: Props): JSX.Element {
|
|||
converterOpened,
|
||||
handleInput,
|
||||
insert,
|
||||
langui,
|
||||
markdown,
|
||||
preline,
|
||||
toggleWrap,
|
||||
|
@ -464,18 +468,23 @@ export default function Editor(props: Props): JSX.Element {
|
|||
<AppLayout
|
||||
navTitle="Markdawn Editor"
|
||||
contentPanel={contentPanel}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Editor;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { AppLayout } from "components/AppLayout";
|
||||
import { Icon } from "components/Ico";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||
import {
|
||||
|
@ -8,42 +7,43 @@ import {
|
|||
} from "components/Panels/ContentPanel";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const SIZE_MULTIPLIER = 1000;
|
||||
|
||||
function replaceSelection(
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
|
||||
const replaceSelection = (
|
||||
text: string,
|
||||
selectionStart: number,
|
||||
selectionEnd: number,
|
||||
newSelectedText: string
|
||||
) {
|
||||
return (
|
||||
) =>
|
||||
text.substring(0, selectionStart) +
|
||||
newSelectedText +
|
||||
text.substring(selectionEnd)
|
||||
);
|
||||
}
|
||||
text.substring(selectionEnd);
|
||||
|
||||
function swapChar(char: string, swaps: string[]): string {
|
||||
const swapChar = (char: string, swaps: string[]): string => {
|
||||
for (let index = 0; index < swaps.length; index++) {
|
||||
if (char === swaps[index]) {
|
||||
console.log(
|
||||
"found it",
|
||||
char,
|
||||
" returning",
|
||||
swaps[(index + 1) % swaps.length]
|
||||
);
|
||||
return swaps[(index + 1) % swaps.length];
|
||||
}
|
||||
}
|
||||
return char;
|
||||
}
|
||||
};
|
||||
|
||||
export default function Transcript(props: Props): JSX.Element {
|
||||
const Transcript = (props: Props): JSX.Element => {
|
||||
const [text, setText] = useState("");
|
||||
const [fontSize, setFontSize] = useState(1);
|
||||
const [xOffset, setXOffset] = useState(0);
|
||||
|
@ -67,6 +67,114 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const convertFullWidth = useCallback(() => {
|
||||
if (textAreaRef.current) {
|
||||
textAreaRef.current.value = textAreaRef.current.value
|
||||
// Numbers
|
||||
.replaceAll("0", "0")
|
||||
.replaceAll("1", "1")
|
||||
.replaceAll("2", "2")
|
||||
.replaceAll("3", "3")
|
||||
.replaceAll("4", "4")
|
||||
.replaceAll("5", "5")
|
||||
.replaceAll("6", "6")
|
||||
.replaceAll("7", "7")
|
||||
.replaceAll("8", "8")
|
||||
.replaceAll("9", "9")
|
||||
// Uppercase letters
|
||||
.replaceAll("A", "A")
|
||||
.replaceAll("B", "B")
|
||||
.replaceAll("C", "C")
|
||||
.replaceAll("D", "D")
|
||||
.replaceAll("E", "E")
|
||||
.replaceAll("F", "F")
|
||||
.replaceAll("G", "G")
|
||||
.replaceAll("H", "H")
|
||||
.replaceAll("I", "I")
|
||||
.replaceAll("J", "J")
|
||||
.replaceAll("K", "K")
|
||||
.replaceAll("L", "L")
|
||||
.replaceAll("M", "M")
|
||||
.replaceAll("N", "N")
|
||||
.replaceAll("O", "O")
|
||||
.replaceAll("P", "P")
|
||||
.replaceAll("Q", "Q")
|
||||
.replaceAll("R", "R")
|
||||
.replaceAll("S", "S")
|
||||
.replaceAll("T", "T")
|
||||
.replaceAll("U", "U")
|
||||
.replaceAll("V", "V")
|
||||
.replaceAll("W", "W")
|
||||
.replaceAll("X", "X")
|
||||
.replaceAll("Y", "Y")
|
||||
.replaceAll("Z", "Z")
|
||||
// Lowercase letters
|
||||
.replaceAll("a", "a")
|
||||
.replaceAll("b", "b")
|
||||
.replaceAll("c", "c")
|
||||
.replaceAll("d", "d")
|
||||
.replaceAll("e", "e")
|
||||
.replaceAll("f", "f")
|
||||
.replaceAll("g", "g")
|
||||
.replaceAll("h", "h")
|
||||
.replaceAll("i", "i")
|
||||
.replaceAll("j", "j")
|
||||
.replaceAll("k", "k")
|
||||
.replaceAll("l", "l")
|
||||
.replaceAll("m", "m")
|
||||
.replaceAll("n", "n")
|
||||
.replaceAll("o", "o")
|
||||
.replaceAll("p", "p")
|
||||
.replaceAll("q", "q")
|
||||
.replaceAll("r", "r")
|
||||
.replaceAll("s", "s")
|
||||
.replaceAll("t", "t")
|
||||
.replaceAll("u", "u")
|
||||
.replaceAll("v", "v")
|
||||
.replaceAll("w", "w")
|
||||
.replaceAll("x", "x")
|
||||
.replaceAll("y", "y")
|
||||
.replaceAll("z", "z")
|
||||
// Others
|
||||
.replaceAll(" ", " ")
|
||||
.replaceAll(",", ",")
|
||||
.replaceAll(".", ".")
|
||||
.replaceAll(":", ":")
|
||||
.replaceAll(";", ";")
|
||||
.replaceAll("!", "!")
|
||||
.replaceAll("?", "?")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'")
|
||||
.replaceAll("`", "`")
|
||||
.replaceAll("^", "^")
|
||||
.replaceAll("~", "~")
|
||||
.replaceAll("_", "_")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("@", "@")
|
||||
.replaceAll("#", "#")
|
||||
.replaceAll("%", "%")
|
||||
.replaceAll("+", "+")
|
||||
.replaceAll("-", "-")
|
||||
.replaceAll("*", "*")
|
||||
.replaceAll("=", "=")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll("(", "(")
|
||||
.replaceAll(")", ")")
|
||||
.replaceAll("[", "[")
|
||||
.replaceAll("]", "]")
|
||||
.replaceAll("{", "{")
|
||||
.replaceAll("}", "}")
|
||||
.replaceAll("|", "|")
|
||||
.replaceAll("$", "$")
|
||||
.replaceAll("£", "£")
|
||||
.replaceAll("¢", "¢")
|
||||
.replaceAll("₩", "₩")
|
||||
.replaceAll("¥", "¥");
|
||||
updateDisplayedText();
|
||||
}
|
||||
}, [updateDisplayedText]);
|
||||
|
||||
const convertPunctuation = useCallback(() => {
|
||||
if (textAreaRef.current) {
|
||||
textAreaRef.current.value = textAreaRef.current.value
|
||||
|
@ -76,7 +184,9 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
.replaceAll(".", "。")
|
||||
.replaceAll(",", "、")
|
||||
.replaceAll("?", "?")
|
||||
.replaceAll("!", "!");
|
||||
.replaceAll("!", "!")
|
||||
.replaceAll(":", ":")
|
||||
.replaceAll("~", "~");
|
||||
updateDisplayedText();
|
||||
}
|
||||
}, [updateDisplayedText]);
|
||||
|
@ -320,14 +430,19 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
}
|
||||
></input>
|
||||
</div>
|
||||
<ToolTip content="Automatically convert punctuations">
|
||||
<Button icon={Icon.QuestionMark} onClick={convertPunctuation} />
|
||||
<ToolTip content="Automatically convert Western punctuations to Japanese ones.">
|
||||
<Button text=". ⟹ 。" onClick={convertPunctuation} />
|
||||
</ToolTip>
|
||||
<Button text={"か ⟺ が"} onClick={toggleDakuten} />
|
||||
<Button text={"つ ⟺ っ"} onClick={toggleSmallForm} />
|
||||
<Button text={"。"} onClick={() => insert("。")} />
|
||||
<Button text={"?"} onClick={() => insert("?")} />
|
||||
<Button text={"!"} onClick={() => insert("!")} />
|
||||
<ToolTip content="Swap a kana for one of its variant (different diacritics).">
|
||||
<Button text="か ⟺ が" onClick={toggleDakuten} />
|
||||
</ToolTip>
|
||||
<ToolTip content="Toggle a kana's small form">
|
||||
<Button text="つ ⟺ っ" onClick={toggleSmallForm} />
|
||||
</ToolTip>
|
||||
<ToolTip content="Convert standard characters to their full width variant.">
|
||||
<Button text="123 ⟹ 123" onClick={convertFullWidth} />
|
||||
</ToolTip>
|
||||
|
||||
<ToolTip
|
||||
content={
|
||||
<div className="grid gap-2">
|
||||
|
@ -351,8 +466,8 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "(", onClick: () => insert("(") },
|
||||
{ text: ")", onClick: () => insert(")") },
|
||||
{ text: "〖", onClick: () => insert("〖") },
|
||||
{ text: "〗", onClick: () => insert("〗") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
|
@ -361,6 +476,18 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
{ text: "〟", onClick: () => insert("〟") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "(", onClick: () => insert("(") },
|
||||
{ text: ")", onClick: () => insert(")") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "⦅", onClick: () => insert("⦅") },
|
||||
{ text: "⦆", onClick: () => insert("⦆") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "〈", onClick: () => insert("〈") },
|
||||
|
@ -373,19 +500,57 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
{ text: "》", onClick: () => insert("》") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "{", onClick: () => insert("{") },
|
||||
{ text: "}", onClick: () => insert("}") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "[", onClick: () => insert("[") },
|
||||
{ text: "]", onClick: () => insert("]") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "〔", onClick: () => insert("〔") },
|
||||
{ text: "〕", onClick: () => insert("〕") },
|
||||
]}
|
||||
/>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{ text: "〘", onClick: () => insert("〘") },
|
||||
{ text: "〙", onClick: () => insert("〙") },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button text={"Quotations"} />
|
||||
</ToolTip>
|
||||
|
||||
<ToolTip
|
||||
content={
|
||||
<div className="grid gap-2">
|
||||
<Button text={"。"} onClick={() => insert("。")} />
|
||||
<Button text={"?"} onClick={() => insert("?")} />
|
||||
<Button text={"!"} onClick={() => insert("!")} />
|
||||
<Button text={"⋯"} onClick={() => insert("⋯")} />
|
||||
<Button text={"※"} onClick={() => insert("※")} />
|
||||
<Button text={"♪"} onClick={() => insert("♪")} />
|
||||
<Button text={"・"} onClick={() => insert("・")} />
|
||||
<Button text={"〇"} onClick={() => insert("〇")} />
|
||||
<Button text={'" "'} onClick={() => insert(" ")} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button text="Insert" />
|
||||
</ToolTip>
|
||||
</div>
|
||||
</ContentPanel>
|
||||
),
|
||||
[
|
||||
convertFullWidth,
|
||||
convertPunctuation,
|
||||
fontSize,
|
||||
insert,
|
||||
|
@ -407,15 +572,19 @@ export default function Transcript(props: Props): JSX.Element {
|
|||
contentPanelScroolbar={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Transcript;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,9 +4,17 @@ import {
|
|||
PostStaticProps,
|
||||
} from "graphql/getPostStaticProps";
|
||||
|
||||
export default function Home(props: PostStaticProps): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
return (
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const Home = ({
|
||||
post,
|
||||
langui,
|
||||
languages,
|
||||
currencies,
|
||||
}: PostStaticProps): JSX.Element => (
|
||||
<PostPage
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
|
@ -28,6 +36,12 @@ export default function Home(props: PostStaticProps): JSX.Element {
|
|||
displayLanguageSwitcher
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps = getPostStaticProps("home");
|
||||
|
|
|
@ -42,14 +42,9 @@ import {
|
|||
isDefinedAndNotEmpty,
|
||||
sortContent,
|
||||
} from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { Fragment, useCallback, useMemo, useState } from "react";
|
||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
|
@ -58,6 +53,12 @@ import { useToggle } from "hooks/useToggle";
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { getDescription } from "helpers/description";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
item: NonNullable<
|
||||
|
@ -70,8 +71,14 @@ interface Props extends AppStaticProps {
|
|||
>["data"][number]["id"];
|
||||
}
|
||||
|
||||
export default function LibrarySlug(props: Props): JSX.Element {
|
||||
const { item, itemId, langui, currencies, languages } = props;
|
||||
const LibrarySlug = ({
|
||||
item,
|
||||
itemId,
|
||||
langui,
|
||||
currencies,
|
||||
languages,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
const hoverable = useMediaHoverable();
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
|
@ -192,12 +199,11 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<PreviewCardCTAs
|
||||
id={itemId}
|
||||
displayCTAs={!isUntangibleGroupItem(item.metadata?.[0])}
|
||||
langui={langui}
|
||||
expand
|
||||
/>
|
||||
{!isUntangibleGroupItem(item.metadata?.[0]) &&
|
||||
isDefinedAndNotEmpty(itemId) && (
|
||||
<PreviewCardCTAs id={itemId} langui={langui} expand />
|
||||
)}
|
||||
|
||||
{item.descriptions?.[0] && (
|
||||
<p className="text-justify">
|
||||
{item.descriptions[0].description}
|
||||
|
@ -479,15 +485,9 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
position: "Bottom",
|
||||
}}
|
||||
infoAppend={
|
||||
<PreviewCardCTAs
|
||||
id={subitem.id}
|
||||
langui={langui}
|
||||
displayCTAs={
|
||||
!isUntangibleGroupItem(
|
||||
subitem.attributes.metadata?.[0]
|
||||
)
|
||||
}
|
||||
/>
|
||||
) && <PreviewCardCTAs id={subitem.id} langui={langui} />
|
||||
}
|
||||
/>
|
||||
</Fragment>
|
||||
|
@ -598,15 +598,25 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
contentPanel={contentPanel}
|
||||
subPanel={subPanel}
|
||||
thumbnail={item.thumbnail?.data?.attributes ?? undefined}
|
||||
description={item.descriptions?.[0]?.description ?? undefined}
|
||||
{...props}
|
||||
description={getDescription({
|
||||
langui,
|
||||
description: item.descriptions?.[0]?.description,
|
||||
})}
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default LibrarySlug;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const item = await sdk.getLibraryItem({
|
||||
slug:
|
||||
|
@ -625,11 +635,11 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const libraryItems = await sdk.getLibraryItemsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -638,12 +648,16 @@ export async function getStaticPaths(
|
|||
paths.push({ params: { slug: item.attributes.slug }, locale: local })
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface ContentLineProps {
|
||||
content?: {
|
||||
|
@ -665,8 +679,7 @@ interface ContentLineProps {
|
|||
hasScanSet: boolean;
|
||||
}
|
||||
|
||||
export function ContentLine(props: ContentLineProps): JSX.Element {
|
||||
const {
|
||||
const ContentLine = ({
|
||||
rangeStart,
|
||||
content,
|
||||
langui,
|
||||
|
@ -674,8 +687,7 @@ export function ContentLine(props: ContentLineProps): JSX.Element {
|
|||
hasScanSet,
|
||||
slug,
|
||||
parentSlug,
|
||||
} = props;
|
||||
|
||||
}: ContentLineProps): JSX.Element => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const toggleOpened = useToggle(setOpened);
|
||||
|
||||
|
@ -753,6 +765,4 @@ export function ContentLine(props: ContentLineProps): JSX.Element {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,13 +18,14 @@ import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
|||
import { filterHasAttributes, isDefined, sortContent } from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { Fragment, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
item: NonNullable<
|
||||
NonNullable<
|
||||
|
@ -36,8 +37,12 @@ interface Props extends AppStaticProps {
|
|||
>;
|
||||
}
|
||||
|
||||
export default function LibrarySlug(props: Props): JSX.Element {
|
||||
const { item, langui, languages } = props;
|
||||
const LibrarySlug = ({
|
||||
item,
|
||||
langui,
|
||||
languages,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
sortContent(item.contents);
|
||||
|
||||
|
@ -129,14 +134,20 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
contentPanel={contentPanel}
|
||||
subPanel={subPanel}
|
||||
thumbnail={item.thumbnail?.data?.attributes ?? undefined}
|
||||
{...props}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default LibrarySlug;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const item = await sdk.getLibraryItemScans({
|
||||
slug:
|
||||
|
@ -155,11 +166,11 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const libraryItems = await sdk.getLibraryItemsSlugs({});
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -173,4 +184,4 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyItemSubType } from "helpers/formatters";
|
||||
import { LibraryItemUserStatus } from "helpers/types";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useState, useMemo } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
|
@ -38,11 +38,12 @@ import {
|
|||
} from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
items: NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"];
|
||||
}
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const defaultFiltersState = {
|
||||
const DEFAULT_FILTERS_STATE = {
|
||||
searchName: "",
|
||||
showSubitems: false,
|
||||
showPrimaryItems: true,
|
||||
|
@ -53,33 +54,48 @@ const defaultFiltersState = {
|
|||
filterUserStatus: undefined,
|
||||
};
|
||||
|
||||
export default function Library(props: Props): JSX.Element {
|
||||
const { langui, items: libraryItems, currencies } = props;
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
items: NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"];
|
||||
}
|
||||
|
||||
const Library = ({
|
||||
langui,
|
||||
items: libraryItems,
|
||||
currencies,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const appLayout = useAppLayout();
|
||||
const hoverable = useMediaHoverable();
|
||||
|
||||
const [searchName, setSearchName] = useState(defaultFiltersState.searchName);
|
||||
const [searchName, setSearchName] = useState(
|
||||
DEFAULT_FILTERS_STATE.searchName
|
||||
);
|
||||
const [showSubitems, setShowSubitems] = useState<boolean>(
|
||||
defaultFiltersState.showSubitems
|
||||
DEFAULT_FILTERS_STATE.showSubitems
|
||||
);
|
||||
const [showPrimaryItems, setShowPrimaryItems] = useState<boolean>(
|
||||
defaultFiltersState.showPrimaryItems
|
||||
DEFAULT_FILTERS_STATE.showPrimaryItems
|
||||
);
|
||||
const [showSecondaryItems, setShowSecondaryItems] = useState<boolean>(
|
||||
defaultFiltersState.showSecondaryItems
|
||||
DEFAULT_FILTERS_STATE.showSecondaryItems
|
||||
);
|
||||
const [sortingMethod, setSortingMethod] = useState<number>(
|
||||
defaultFiltersState.sortingMethod
|
||||
DEFAULT_FILTERS_STATE.sortingMethod
|
||||
);
|
||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||
defaultFiltersState.groupingMethod
|
||||
DEFAULT_FILTERS_STATE.groupingMethod
|
||||
);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
defaultFiltersState.keepInfoVisible
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
);
|
||||
const [filterUserStatus, setFilterUserStatus] = useState<
|
||||
LibraryItemUserStatus | undefined
|
||||
>(defaultFiltersState.filterUserStatus);
|
||||
>(DEFAULT_FILTERS_STATE.filterUserStatus);
|
||||
|
||||
const filteredItems = useMemo(
|
||||
() =>
|
||||
|
@ -193,7 +209,6 @@ export default function Library(props: Props): JSX.Element {
|
|||
/>
|
||||
)}
|
||||
|
||||
{/* TODO: Add "All" to langui */}
|
||||
<ButtonGroup
|
||||
className="mt-4"
|
||||
buttonsProps={[
|
||||
|
@ -217,7 +232,7 @@ export default function Library(props: Props): JSX.Element {
|
|||
},
|
||||
{
|
||||
tooltip: langui.only_display_unmarked_items,
|
||||
text: "All",
|
||||
text: langui.all,
|
||||
onClick: () => setFilterUserStatus(undefined),
|
||||
active: isUndefined(filterUserStatus),
|
||||
},
|
||||
|
@ -229,14 +244,14 @@ export default function Library(props: Props): JSX.Element {
|
|||
text={langui.reset_all_filters}
|
||||
icon={Icon.Replay}
|
||||
onClick={() => {
|
||||
setSearchName(defaultFiltersState.searchName);
|
||||
setShowSubitems(defaultFiltersState.showSubitems);
|
||||
setShowPrimaryItems(defaultFiltersState.showPrimaryItems);
|
||||
setShowSecondaryItems(defaultFiltersState.showSecondaryItems);
|
||||
setSortingMethod(defaultFiltersState.sortingMethod);
|
||||
setGroupingMethod(defaultFiltersState.groupingMethod);
|
||||
setKeepInfoVisible(defaultFiltersState.keepInfoVisible);
|
||||
setFilterUserStatus(defaultFiltersState.filterUserStatus);
|
||||
setSearchName(DEFAULT_FILTERS_STATE.searchName);
|
||||
setShowSubitems(DEFAULT_FILTERS_STATE.showSubitems);
|
||||
setShowPrimaryItems(DEFAULT_FILTERS_STATE.showPrimaryItems);
|
||||
setShowSecondaryItems(DEFAULT_FILTERS_STATE.showSecondaryItems);
|
||||
setSortingMethod(DEFAULT_FILTERS_STATE.sortingMethod);
|
||||
setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
|
||||
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||
setFilterUserStatus(DEFAULT_FILTERS_STATE.filterUserStatus);
|
||||
}}
|
||||
/>
|
||||
</SubPanel>
|
||||
|
@ -258,12 +273,9 @@ export default function Library(props: Props): JSX.Element {
|
|||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
{/* TODO: Add to langui */}
|
||||
{groups.size === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
message={langui.no_results_message ?? "No results"}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
|
@ -314,13 +326,9 @@ export default function Library(props: Props): JSX.Element {
|
|||
position: "Bottom",
|
||||
}}
|
||||
infoAppend={
|
||||
<PreviewCardCTAs
|
||||
id={item.id}
|
||||
displayCTAs={
|
||||
!isUntangibleGroupItem(item.attributes.metadata?.[0])
|
||||
}
|
||||
langui={langui}
|
||||
/>
|
||||
!isUntangibleGroupItem(item.attributes.metadata?.[0]) && (
|
||||
<PreviewCardCTAs id={item.id} langui={langui} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Fragment>
|
||||
|
@ -339,14 +347,20 @@ export default function Library(props: Props): JSX.Element {
|
|||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
subPanelIcon={Icon.Search}
|
||||
{...props}
|
||||
currencies={currencies}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Library;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const items = await sdk.getLibraryItemsPreview({
|
||||
language_code: context.locale ?? "en",
|
||||
|
@ -359,4 +373,4 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,15 +3,19 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
|||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Icon } from "components/Ico";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {}
|
||||
export default function Merch(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
const Merch = ({ langui, ...otherProps }: Props): JSX.Element => (
|
||||
<AppLayout
|
||||
navTitle={langui.merch}
|
||||
subPanel={
|
||||
<SubPanel>
|
||||
<PanelHeader
|
||||
icon={Icon.Store}
|
||||
|
@ -19,20 +23,23 @@ export default function Merch(props: Props): JSX.Element {
|
|||
description={langui.merch_description}
|
||||
/>
|
||||
</SubPanel>
|
||||
),
|
||||
[langui]
|
||||
);
|
||||
|
||||
return <AppLayout navTitle={langui.merch} subPanel={subPanel} {...props} />;
|
||||
}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
export default Merch;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,45 +6,43 @@ import {
|
|||
} from "graphql/getPostStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps, PostStaticProps {}
|
||||
|
||||
export default function LibrarySlug(props: Props): JSX.Element {
|
||||
const { post, langui, languages, currencies } = props;
|
||||
return (
|
||||
const LibrarySlug = (props: Props): JSX.Element => (
|
||||
<PostPage
|
||||
currencies={currencies}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
post={post}
|
||||
returnHref="/news"
|
||||
returnTitle={langui.news}
|
||||
returnTitle={props.langui.news}
|
||||
displayCredits
|
||||
displayThumbnailHeader
|
||||
displayToc
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default LibrarySlug;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const slug =
|
||||
context.params && isDefined(context.params.slug)
|
||||
? context.params.slug.toString()
|
||||
: "";
|
||||
return await getPostStaticProps(slug)(context);
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const posts = await sdk.getPostsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -58,4 +56,4 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyDate, prettySlug } from "helpers/formatters";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
|
@ -21,24 +21,32 @@ import { Button } from "components/Inputs/Button";
|
|||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
|
||||
}
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const defaultFiltersState = {
|
||||
const DEFAULT_FILTERS_STATE = {
|
||||
searchName: "",
|
||||
keepInfoVisible: true,
|
||||
};
|
||||
|
||||
export default function News(props: Props): JSX.Element {
|
||||
const { langui } = props;
|
||||
const posts = sortPosts(props.posts);
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
|
||||
}
|
||||
|
||||
const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useMediaHoverable();
|
||||
|
||||
const [searchName, setSearchName] = useState(defaultFiltersState.searchName);
|
||||
|
||||
const [searchName, setSearchName] = useState(
|
||||
DEFAULT_FILTERS_STATE.searchName
|
||||
);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
defaultFiltersState.keepInfoVisible
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
);
|
||||
|
||||
const filteredItems = useMemo(
|
||||
|
@ -76,8 +84,8 @@ export default function News(props: Props): JSX.Element {
|
|||
text={langui.reset_all_filters}
|
||||
icon={Icon.Replay}
|
||||
onClick={() => {
|
||||
setSearchName(defaultFiltersState.searchName);
|
||||
setKeepInfoVisible(defaultFiltersState.keepInfoVisible);
|
||||
setSearchName(DEFAULT_FILTERS_STATE.searchName);
|
||||
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||
}}
|
||||
/>
|
||||
</SubPanel>
|
||||
|
@ -127,14 +135,19 @@ export default function News(props: Props): JSX.Element {
|
|||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
subPanelIcon={Icon.Search}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default News;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const posts = await sdk.getPostsPreview({
|
||||
language_code: context.locale ?? "en",
|
||||
|
@ -142,25 +155,31 @@ export async function getStaticProps(
|
|||
if (!posts.posts) return { notFound: true };
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
posts: posts.posts.data,
|
||||
posts: sortPosts(posts.posts.data),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function sortPosts(posts: Props["posts"]): Props["posts"] {
|
||||
return posts
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
const sortPosts = (posts: Props["posts"]): Props["posts"] =>
|
||||
posts
|
||||
.sort((a, b) => {
|
||||
const dateA = a.attributes?.date ? prettyDate(a.attributes.date) : "9999";
|
||||
const dateB = b.attributes?.date ? prettyDate(b.attributes.date) : "9999";
|
||||
return dateA.localeCompare(dateB);
|
||||
})
|
||||
.reverse();
|
||||
}
|
||||
|
||||
function filterItems(posts: Props["posts"], searchName: string) {
|
||||
return posts.filter((post) => {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const filterItems = (posts: Props["posts"], searchName: string) =>
|
||||
posts.filter((post) => {
|
||||
if (searchName.length > 1) {
|
||||
if (
|
||||
post.attributes?.translations?.[0]?.title
|
||||
|
@ -173,4 +192,3 @@ function filterItems(posts: Props["posts"], searchName: string) {
|
|||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,20 +21,19 @@ import {
|
|||
} from "helpers/others";
|
||||
import { WikiPageWithTranslations } from "helpers/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
GetStaticPathsResult,
|
||||
GetStaticPropsContext,
|
||||
} from "next";
|
||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
page: WikiPageWithTranslations;
|
||||
}
|
||||
|
||||
export default function WikiPage(props: Props): JSX.Element {
|
||||
const { page, langui, languages } = props;
|
||||
|
||||
const WikiPage = ({
|
||||
page,
|
||||
langui,
|
||||
languages,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
||||
useSmartLanguage({
|
||||
items: page.translations,
|
||||
|
@ -143,14 +142,15 @@ export default function WikiPage(props: Props): JSX.Element {
|
|||
navTitle={langui.news}
|
||||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
{...props}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default WikiPage;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const slug =
|
||||
context.params && isDefined(context.params.slug)
|
||||
|
@ -169,11 +169,9 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function getStaticPaths(
|
||||
context: GetStaticPathsContext
|
||||
): Promise<GetStaticPathsResult> {
|
||||
export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const contents = await sdk.getWikiPagesSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
|
@ -189,4 +187,4 @@ export async function getStaticPaths(
|
|||
paths,
|
||||
fallback: "blocking",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,9 +13,14 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Fragment, useMemo } from "react";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
chronologyItems: NonNullable<
|
||||
GetChronologyItemsQuery["chronologyItems"]
|
||||
|
@ -23,9 +28,12 @@ interface Props extends AppStaticProps {
|
|||
chronologyEras: NonNullable<GetErasQuery["chronologyEras"]>["data"];
|
||||
}
|
||||
|
||||
export default function Chronology(props: Props): JSX.Element {
|
||||
const { chronologyItems, chronologyEras, langui } = props;
|
||||
|
||||
const Chronology = ({
|
||||
chronologyItems,
|
||||
chronologyEras,
|
||||
langui,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
// Group by year the Chronology items
|
||||
const chronologyItemYearGroups = useMemo(() => {
|
||||
const memo: Props["chronologyItems"][number][][][] = [];
|
||||
|
@ -141,14 +149,19 @@ export default function Chronology(props: Props): JSX.Element {
|
|||
navTitle={langui.chronology}
|
||||
contentPanel={contentPanel}
|
||||
subPanel={subPanel}
|
||||
{...props}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Chronology;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const chronologyItems = await sdk.getChronologyItems({
|
||||
language_code: context.locale ?? "en",
|
||||
|
@ -166,4 +179,4 @@ export async function getStaticProps(
|
|||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
|||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { GetStaticProps } from "next";
|
||||
import { Icon } from "components/Ico";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { GetWikiPagesPreviewsQuery } from "graphql/generated";
|
||||
|
@ -23,23 +23,38 @@ import { useMediaHoverable } from "hooks/useMediaQuery";
|
|||
import { filterHasAttributes } from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
pages: NonNullable<GetWikiPagesPreviewsQuery["wikiPages"]>["data"];
|
||||
}
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const defaultFiltersState = {
|
||||
const DEFAULT_FILTERS_STATE = {
|
||||
searchName: "",
|
||||
keepInfoVisible: true,
|
||||
};
|
||||
|
||||
export default function Wiki(props: Props): JSX.Element {
|
||||
const { langui, languages } = props;
|
||||
const pages = sortPages(props.pages);
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
pages: NonNullable<GetWikiPagesPreviewsQuery["wikiPages"]>["data"];
|
||||
}
|
||||
|
||||
const Wiki = ({
|
||||
langui,
|
||||
languages,
|
||||
pages,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const hoverable = useMediaHoverable();
|
||||
|
||||
const [searchName, setSearchName] = useState(defaultFiltersState.searchName);
|
||||
const [searchName, setSearchName] = useState(
|
||||
DEFAULT_FILTERS_STATE.searchName
|
||||
);
|
||||
const [keepInfoVisible, setKeepInfoVisible] = useState(
|
||||
defaultFiltersState.keepInfoVisible
|
||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||
);
|
||||
|
||||
const filteredPages = useMemo(
|
||||
|
@ -77,14 +92,13 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
text={langui.reset_all_filters}
|
||||
icon={Icon.Replay}
|
||||
onClick={() => {
|
||||
setSearchName(defaultFiltersState.searchName);
|
||||
setKeepInfoVisible(defaultFiltersState.keepInfoVisible);
|
||||
setSearchName(DEFAULT_FILTERS_STATE.searchName);
|
||||
setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||
}}
|
||||
/>
|
||||
<HorizontalLine />
|
||||
|
||||
{/* TODO: Langui */}
|
||||
<p className="mb-4 font-headers text-xl">Special Pages</p>
|
||||
<p className="mb-4 font-headers text-xl">{langui.special_pages}</p>
|
||||
|
||||
<NavOption title={langui.chronology} url="/wiki/chronology" border />
|
||||
</SubPanel>
|
||||
|
@ -99,12 +113,9 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
className="grid grid-cols-2 items-end gap-8
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))] mobile:gap-4"
|
||||
>
|
||||
{/* TODO: Add to langui */}
|
||||
{filteredPages.length === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
message={langui.no_results_message ?? "No results"}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
|
@ -137,7 +148,7 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
</div>
|
||||
</ContentPanel>
|
||||
),
|
||||
[filteredPages, keepInfoVisible, languages]
|
||||
[filteredPages, keepInfoVisible, languages, langui]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -146,36 +157,48 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
subPanel={subPanel}
|
||||
contentPanel={contentPanel}
|
||||
subPanelIcon={Icon.Search}
|
||||
{...props}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Wiki;
|
||||
|
||||
export async function getStaticProps(
|
||||
context: GetStaticPropsContext
|
||||
): Promise<{ notFound: boolean } | { props: Props }> {
|
||||
/*
|
||||
* ╭──────────────────────╮
|
||||
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
||||
*/
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const pages = await sdk.getWikiPagesPreviews({});
|
||||
if (!pages.wikiPages?.data) return { notFound: true };
|
||||
const props: Props = {
|
||||
...(await getAppStaticProps(context)),
|
||||
pages: pages.wikiPages.data,
|
||||
pages: sortPages(pages.wikiPages.data),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function sortPages(pages: Props["pages"]): Props["pages"] {
|
||||
return pages.sort((a, b) => {
|
||||
/*
|
||||
* ╭───────────────────╮
|
||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||
*/
|
||||
|
||||
const sortPages = (pages: Props["pages"]): Props["pages"] =>
|
||||
pages.sort((a, b) => {
|
||||
const slugA = a.attributes?.slug ?? "";
|
||||
const slugB = b.attributes?.slug ?? "";
|
||||
return slugA.localeCompare(slugB);
|
||||
});
|
||||
}
|
||||
|
||||
function filterPages(posts: Props["pages"], searchName: string) {
|
||||
return posts.filter((post) => {
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const filterPages = (posts: Props["pages"], searchName: string) =>
|
||||
posts.filter((post) => {
|
||||
if (searchName.length > 1) {
|
||||
if (
|
||||
post.attributes?.translations?.[0]?.title
|
||||
|
@ -188,4 +211,3 @@ function filterPages(posts: Props["pages"], searchName: string) {
|
|||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
const plugin = require("tailwindcss/plugin");
|
||||
const { breaks, colors, fonts, fontFamilies } = require("./design.config.js");
|
||||
|
||||
function rgb(color) {
|
||||
return [color.r, color.g, color.b].join(" ");
|
||||
}
|
||||
const rgb = (color) => [color.r, color.g, color.b].join(" ");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
|
@ -38,7 +36,7 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
plugins: [
|
||||
plugin(function ({ addUtilities }) {
|
||||
plugin(({ addUtilities }) => {
|
||||
addUtilities({
|
||||
".set-theme-light": {
|
||||
"--theme-color-highlight": rgb(colors.light.hightlight),
|
||||
|
@ -63,7 +61,7 @@ module.exports = {
|
|||
});
|
||||
}),
|
||||
|
||||
plugin(function ({ addUtilities }) {
|
||||
plugin(({ addUtilities }) => {
|
||||
addUtilities({
|
||||
".set-theme-font-standard": {
|
||||
"--theme-font-body": fontFamilies.standard.body,
|
||||
|
@ -78,7 +76,7 @@ module.exports = {
|
|||
});
|
||||
}),
|
||||
|
||||
plugin(function ({ addVariant, e }) {
|
||||
plugin(({ addVariant, e }) => {
|
||||
addVariant("webkit-scrollbar", ({ modifySelectors, separator }) => {
|
||||
modifySelectors(({ className }) => {
|
||||
return `.${e(
|
||||
|
@ -89,7 +87,7 @@ module.exports = {
|
|||
}),
|
||||
|
||||
// Colored Dropshadow
|
||||
plugin(function ({ addUtilities }) {
|
||||
plugin(({ addUtilities }) => {
|
||||
addUtilities({
|
||||
".drop-shadow-shade-md": {
|
||||
filter: `
|
||||
|
@ -131,7 +129,7 @@ module.exports = {
|
|||
});
|
||||
}),
|
||||
|
||||
plugin(function ({ addUtilities }) {
|
||||
plugin(({ addUtilities }) => {
|
||||
addUtilities({
|
||||
".linearbg-obi": {
|
||||
background: `linear-gradient(
|
||||
|
@ -145,7 +143,7 @@ module.exports = {
|
|||
});
|
||||
}),
|
||||
|
||||
plugin(function ({ addUtilities }) {
|
||||
plugin(({ addUtilities }) => {
|
||||
addUtilities({
|
||||
".break-words": {
|
||||
"word-break": "break-word",
|
||||
|
|
Loading…
Reference in New Issue