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