diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index a5242e0..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,37 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Node.js CI - -on: -# push: -# branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [16.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run lint - - run: npm run build --if-present - env: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - NEXT_PUBLIC_URL_CMS: ${{ secrets.NEXT_PUBLIC_URL_CMS }} - NEXT_PUBLIC_URL_IMG: ${{ secrets.NEXT_PUBLIC_URL_IMG }} - NEXT_PUBLIC_URL_SELF: ${{ secrets.NEXT_PUBLIC_URL_SELF }} - URL_GRAPHQL: ${{ secrets.URL_GRAPHQL }} diff --git a/README.md b/README.md index 0f72637..7912e64 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # Accords-library.com -[![Node.js CI](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml) -[![GitHub](https://img.shields.io/github/license/Accords-Library/accords-library.com?style=flat-square)](https://github.com/Accords-Library/accords-library.com/blob/main/LICENSE) -![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/Accords-Library/accords-library.com?style=flat-square) - ## Technologies #### [Back](https://github.com/Accords-Library/strapi.accords-library.com) diff --git a/package-lock.json b/package-lock.json index e9fc868..a6f4ebf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "next": "^12.1.0", "react": "17.0.2", "react-dom": "17.0.2", + "react-image-lightbox": "^5.1.4", "react-swipeable": "^6.2.0", "turndown": "^7.1.1" }, @@ -1707,6 +1708,11 @@ "node": ">=0.10.0" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2982,7 +2988,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3055,11 +3060,46 @@ "react": "17.0.2" } }, + "node_modules/react-image-lightbox": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz", + "integrity": "sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==", + "dependencies": { + "prop-types": "^15.7.2", + "react-modal": "^3.11.1" + }, + "peerDependencies": { + "react": "16.x || 17.x", + "react-dom": "16.x || 17.x" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz", + "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17" + } }, "node_modules/react-swipeable": { "version": "6.2.0", @@ -3577,6 +3617,14 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4858,6 +4906,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5761,7 +5814,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5805,11 +5857,35 @@ "scheduler": "^0.20.2" } }, + "react-image-lightbox": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz", + "integrity": "sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==", + "requires": { + "prop-types": "^15.7.2", + "react-modal": "^3.11.1" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-modal": { + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz", + "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==", + "requires": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + } }, "react-swipeable": { "version": "6.2.0", @@ -6171,6 +6247,14 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 07b9196..69a7da8 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "next": "^12.1.0", "react": "17.0.2", "react-dom": "17.0.2", + "react-image-lightbox": "^5.1.4", "react-swipeable": "^6.2.0", "turndown": "^7.1.1" }, diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx index 4d43c9d..97fc640 100644 --- a/src/components/AppLayout.tsx +++ b/src/components/AppLayout.tsx @@ -111,6 +111,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { return (
{props.children}
diff --git a/src/components/Content/ThumbnailHeader.tsx b/src/components/Content/ThumbnailHeader.tsx index b9bfd44..89607d0 100644 --- a/src/components/Content/ThumbnailHeader.tsx +++ b/src/components/Content/ThumbnailHeader.tsx @@ -3,95 +3,88 @@ import { GetWebsiteInterfaceQuery, } from "graphql/operations-types"; import { prettyinlineTitle, prettySlug, slugify } from "queries/helpers"; -import Button from "components/Button"; import Img, { ImageQuality } from "components/Img"; import InsetBox from "components/InsetBox"; import Chip from "components/Chip"; export type ThumbnailHeaderProps = { - content: { - slug: GetContentQuery["contents"]["data"][number]["attributes"]["slug"]; - thumbnail: GetContentQuery["contents"]["data"][number]["attributes"]["thumbnail"]; - titles: GetContentQuery["contents"]["data"][number]["attributes"]["titles"]; - type: GetContentQuery["contents"]["data"][number]["attributes"]["type"]; - categories: GetContentQuery["contents"]["data"][number]["attributes"]["categories"]; - }; + pre_title?: string; + title: string; + subtitle?: string; + description?: string; + type?: GetContentQuery["contents"]["data"][number]["attributes"]["type"]; + categories?: GetContentQuery["contents"]["data"][number]["attributes"]["categories"]; + thumbnail?: GetContentQuery["contents"]["data"][number]["attributes"]["thumbnail"]; langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function ThumbnailHeader( props: ThumbnailHeaderProps ): JSX.Element { - const content = props.content; - const langui = props.langui; + const { + langui, + pre_title, + title, + subtitle, + thumbnail, + type, + categories, + description, + } = props; return ( <>
- {content.thumbnail.data ? ( + {thumbnail && thumbnail.data ? ( ) : ( -
+
)}
0 - ? prettyinlineTitle( - content.titles[0].pre_title, - content.titles[0].title, - content.titles[0].subtitle - ) - : prettySlug(content.slug) + prettyinlineTitle(pre_title || "", title, subtitle || "") )} className="grid place-items-center text-center" > - {content.titles.length > 0 ? ( - <> -

{content.titles[0].pre_title}

-

{content.titles[0].title}

-

{content.titles[0].subtitle}

- - ) : ( -

{prettySlug(content.slug)}

- )} +

{pre_title}

+

{title}

+

{subtitle}

- {content.type && ( + {type && type.data && (

{langui.type}

- {content.type.data.attributes.titles.length > 0 - ? content.type.data.attributes.titles[0].title - : prettySlug(content.type.data.attributes.slug)} + {type.data.attributes.titles.length > 0 + ? type.data.attributes.titles[0].title + : prettySlug(type.data.attributes.slug)}
)} - {content.categories.data.length > 0 && ( + {categories && categories.data.length > 0 && (

{langui.categories}

- {content.categories.data.map((category) => ( + {categories.data.map((category) => ( {category.attributes.name} ))}
)}
- {content.titles.length > 0 && content.titles[0].description && ( - {content.titles[0].description} - )} + {description && {description}} ); } diff --git a/src/components/Img.tsx b/src/components/Img.tsx index 8723bbb..24861e5 100644 --- a/src/components/Img.tsx +++ b/src/components/Img.tsx @@ -50,7 +50,7 @@ export function getImgSizesByQuality( type ImgProps = { className?: string; - image: StrapiImage; + image?: StrapiImage; quality?: ImageQuality; alt?: ImageProps["alt"]; layout?: ImageProps["layout"]; @@ -60,42 +60,46 @@ type ImgProps = { }; export default function Img(props: ImgProps): JSX.Element { - const imgSize = getImgSizesByQuality( - props.image.width, - props.image.height, - props.quality ? props.quality : ImageQuality.Small - ); + if (props.image) { + const imgSize = getImgSizesByQuality( + props.image.width, + props.image.height, + props.quality ? props.quality : ImageQuality.Small + ); - if (props.rawImg) { - return ( - // eslint-disable-next-line @next/next/no-img-element - {props.alt - ); + if (props.rawImg) { + return ( + // eslint-disable-next-line @next/next/no-img-element + {props.alt + ); + } else { + return ( + {props.alt + ); + } } else { - return ( - {props.alt - ); + return <>; } } diff --git a/src/components/LightBox.tsx b/src/components/LightBox.tsx new file mode 100644 index 0000000..5def290 --- /dev/null +++ b/src/components/LightBox.tsx @@ -0,0 +1,39 @@ +import { useMediaMobile } from "hooks/useMediaQuery"; +import { Dispatch, SetStateAction } from "react"; +import Lightbox from "react-image-lightbox"; + +export type LightBoxProps = { + setState: + | Dispatch> + | Dispatch>; + state: boolean; + images: string[]; + index: number; + setIndex: Dispatch>; +}; + +export default function LightBox(props: LightBoxProps): JSX.Element { + const { state, setState, images, index, setIndex } = props; + const mobile = useMediaMobile(); + + return ( + <> + {state && ( + document.getElementById("MyAppLayout"), + }} + mainSrc={images[index]} + prevSrc={index > 0 ? images[index - 1] : undefined} + nextSrc={index < images.length ? images[index + 1] : undefined} + onMovePrevRequest={() => setIndex(index - 1)} + onMoveNextRequest={() => setIndex(index + 1)} + imageCaption="" + imageTitle="" + onCloseRequest={() => setState(false)} + imagePadding={mobile ? 0 : 70} + /> + )} + + ); +} diff --git a/src/components/Markdown/Markdawn.tsx b/src/components/Markdown/Markdawn.tsx index 51c71ba..4cb08c1 100644 --- a/src/components/Markdown/Markdawn.tsx +++ b/src/components/Markdown/Markdawn.tsx @@ -1,10 +1,12 @@ import HorizontalLine from "components/HorizontalLine"; +import Img, { getAssetURL, ImageQuality } from "components/Img"; import InsetBox from "components/InsetBox"; +import LightBox from "components/LightBox"; import ToolTip from "components/ToolTip"; import { useAppLayout } from "contexts/AppLayoutContext"; import Markdown from "markdown-to-jsx"; import { slugify } from "queries/helpers"; -import React from "react"; +import React, { useState } from "react"; type ScenBreakProps = { className?: string; @@ -15,177 +17,353 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element { const appLayout = useAppLayout(); const text = preprocessMarkDawn(props.text); + const [lightboxOpen, setLightboxOpen] = useState(false); + const [lightboxImages, setLightboxImages] = useState([""]); + const [lightboxIndex, setLightboxIndex] = useState(0); + if (text) { return ( - { - return ( -
-

+ <> + + { + return ( +
+

+ {props.children} +

+ +
+ ); + }, + }, + h2: { + component: (props: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => { + return ( +
+

+ {props.children} +

+ +
+ ); + }, + }, + h3: { + component: (props: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => { + return ( +
+

+ {props.children} +

+ +
+ ); + }, + }, + h4: { + component: (props: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => { + return ( +
+

+ {props.children} +

+ +
+ ); + }, + }, + h5: { + component: (props: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => { + return ( +
+
+ {props.children} +
+ +
+ ); + }, + }, + h6: { + component: (props: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => { + return ( +
+
+ {props.children} +
+ +
+ ); + }, + }, + Sep: { + component: () => { + return
; + }, + }, + SceneBreak: { + component: (props: { id: string }) => { + return ( +
+ * * * +
+ ); + }, + }, + player: { + component: () => { + return ( + + {appLayout.playerName ? appLayout.playerName : ""} + + ); + }, + }, + Transcript: { + component: (props) => { + return ( +
{props.children} -

- - - { - navigator.clipboard.writeText( - process.env.NEXT_PUBLIC_URL_SELF + - window.location.pathname + - "#" + - props.id - ); - }} - > - link - - - -
- ); + + ); + }, }, - }, - h3: { - component: (props: { - id: string; - style: React.CSSProperties; - children: React.ReactNode; - }) => { - return ( -
-

+ Line: { + component: (props) => { + return ( + <> + + {props.name} + +

{props.children}

+ + ); + }, + }, + InsetBox: { + component: (props) => { + return ( + {props.children} + ); + }, + }, + li: { + component: (props: { children: React.ReactNode }) => { + return ( +
  • 100 + ? "my-4" + : "" + } + > {props.children} -
  • - - - { - navigator.clipboard.writeText( - process.env.NEXT_PUBLIC_URL_SELF + - window.location.pathname + - "#" + - props.id - ); - }} - > - link - - - -
    - ); + + ); + }, + }, + Highlight: { + component: (props: { children: React.ReactNode }) => { + return {props.children}; + }, + }, + footer: { + component: (props: { children: React.ReactNode }) => { + return ( + <> + +
    {props.children}
    + + ); + }, + }, + img: { + component: (props: { + alt: string; + src: string; + width?: number; + height?: number; + caption?: string; + name?: string; + }) => { + return ( +
    { + setLightboxOpen(true); + setLightboxImages([ + props.src.startsWith("/uploads/") + ? getAssetURL(props.src, ImageQuality.Large) + : props.src, + ]); + setLightboxIndex(0); + }} + > + {props.src.startsWith("/uploads/") ? ( +
    + +
    + ) : ( +
    + +
    + )} +
    + ); + }, }, }, - Sep: { - component: () => { - return
    ; - }, - }, - SceneBreak: { - component: (props: { id: string }) => { - return ( -
    - * * * -
    - ); - }, - }, - player: { - component: () => { - return ( - - {appLayout.playerName ? appLayout.playerName : ""} - - ); - }, - }, - Transcript: { - component: (props) => { - return ( -
    - {props.children} -
    - ); - }, - }, - Line: { - component: (props) => { - return ( - <> - - {props.name} - -

    {props.children}

    - - ); - }, - }, - InsetBox: { - component: (props) => { - return {props.children}; - }, - }, - li: { - component: (props: { children: React.ReactNode }) => { - return ( -
  • 100 - ? "my-4" - : "" - } - > - {props.children} -
  • - ); - }, - }, - Highlight: { - component: (props: { children: React.ReactNode }) => { - return {props.children}; - }, - }, - footer: { - component: (props: { children: React.ReactNode }) => { - return ( - <> - -
    {props.children}
    - - ); - }, - }, - }, - }} - > - {text} -
    + }} + > + {text} + + ); } return <>; } +function HeaderToolTip(props: { id: string }) { + return ( + + + { + navigator.clipboard.writeText( + process.env.NEXT_PUBLIC_URL_SELF + + window.location.pathname + + "#" + + props.id + ); + }} + > + link + + + + ); +} + export function preprocessMarkDawn(text: string): string { let scenebreakIndex = 0; + const visitedSlugs: string[] = []; + const result = text.split("\n").map((line) => { if (line === "* * *" || line === "---") { scenebreakIndex++; return ``; } + + if (line.startsWith("# ")) { + return markdawnHeadersParser(headerLevels.h1, line, visitedSlugs); + } + + if (line.startsWith("## ")) { + return markdawnHeadersParser(headerLevels.h2, line, visitedSlugs); + } + + if (line.startsWith("### ")) { + return markdawnHeadersParser(headerLevels.h3, line, visitedSlugs); + } + + if (line.startsWith("#### ")) { + return markdawnHeadersParser(headerLevels.h4, line, visitedSlugs); + } + + if (line.startsWith("##### ")) { + return markdawnHeadersParser(headerLevels.h5, line, visitedSlugs); + } + + if (line.startsWith("###### ")) { + return markdawnHeadersParser(headerLevels.h6, line, visitedSlugs); + } + return line; }); return result.join("\n"); } + +enum headerLevels { + h1 = 1, + h2 = 2, + h3 = 3, + h4 = 4, + h5 = 5, + h6 = 6, +} + +function markdawnHeadersParser( + headerLevel: headerLevels, + line: string, + visitedSlugs: string[] +): string { + const lineText = line.slice(headerLevel + 1); + let slug = slugify(lineText); + let newSlug = slug; + let index = 2; + while (visitedSlugs.includes(newSlug)) { + newSlug = `${slug}-${index}`; + index++; + } + visitedSlugs.push(newSlug); + return `<${headerLevels[headerLevel]} id="${newSlug}">${lineText}`; +} +function getAssetUrl(): React.SetStateAction { + throw new Error("Function not implemented."); +} diff --git a/src/components/Markdown/TOC.tsx b/src/components/Markdown/TOC.tsx index ed2ce6b..8e50a0e 100644 --- a/src/components/Markdown/TOC.tsx +++ b/src/components/Markdown/TOC.tsx @@ -13,44 +13,55 @@ export default function TOC(props: TOCProps): JSX.Element { const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title); return ( -
    + <>

    Table of content

    -
      -
    1. +
    2. - {toc.children.map((h2, h2Index) => ( - <> -
    3. - {`${h2Index + 1}. `} - router.replace(`#${h2.slug}`)}> - {{h2.title}} - -
    4. -
        - {h2.children.map((h3, h3Index) => ( -
      1. - {`${h2Index + 1}.${ - h3Index + 1 - }. `} - router.replace(`#${h3.slug}`)}> - {{h3.title}} - -
      2. - ))} -
      - - ))} -
    -
    +

    + + + + ); +} + +type TOCLevelProps = { + tocchildren: TOC[]; + parentNumbering: string; + router: NextRouter; +}; + +function TOCLevel(props: TOCLevelProps): JSX.Element { + const { tocchildren, parentNumbering, router } = props; + return ( +
      + {tocchildren.map((child, childIndex) => ( + <> +
    1. + {`${parentNumbering}${ + childIndex + 1 + }.`} + router.replace(`#${child.slug}`)}> + {{child.title}} + +
    2. + + + ))} +
    ); } @@ -69,13 +80,23 @@ export function getTocFromMarkdawn(text: string, title?: string): TOC { let h5 = -1; let scenebreak = 0; let scenebreakIndex = 0; + + function getTitle(line: string): string { + return line.slice(line.indexOf(`">`) + 2, line.indexOf("`)); + } + text.split("\n").map((line) => { - if (line.startsWith("# ")) { - toc.slug = slugify(line); - } else if (line.startsWith("## ")) { + if (line.startsWith("

    = 0) { - toc.children[h2].children[h3].children[h4].children[h5].children.push({ - title: `Scene break ${scenebreak}`, - slug: slugify(`scene-break-${scenebreakIndex}`), - children: [], - }); + 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({ - title: `Scene break ${scenebreak}`, - slug: slugify(`scene-break-${scenebreakIndex}`), - children: [], - }); + toc.children[h2].children[h3].children[h4].children.push(child); } else if (h3 >= 0) { - toc.children[h2].children[h3].children.push({ - title: `Scene break ${scenebreak}`, - slug: slugify(`scene-break-${scenebreakIndex}`), - children: [], - }); + toc.children[h2].children[h3].children.push(child); } else if (h2 >= 0) { - toc.children[h2].children.push({ - title: `Scene break ${scenebreak}`, - slug: slugify(`scene-break-${scenebreakIndex}`), - children: [], - }); + toc.children[h2].children.push(child); } else { - toc.children.push({ - title: `Scene break ${scenebreak}`, - slug: slugify(`scene-break-${scenebreakIndex}`), - children: [], - }); + toc.children.push(child); } } }); + return toc; } diff --git a/src/components/News/PostsPreview.tsx b/src/components/News/PostsPreview.tsx new file mode 100644 index 0000000..ee0fdc9 --- /dev/null +++ b/src/components/News/PostsPreview.tsx @@ -0,0 +1,64 @@ +import Link from "next/link"; +import { prettyDate, prettySlug } from "queries/helpers"; +import Chip from "components/Chip"; +import Img, { ImageQuality } from "components/Img"; +import { GetPostsPreviewQuery } from "graphql/operations-types"; + +export type PostPreviewProps = { + post: { + slug: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["slug"]; + thumbnail: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["thumbnail"]; + translations: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["translations"]; + categories: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["categories"]; + date: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["date"]; + }; +}; + +export default function PostPreview(props: PostPreviewProps): JSX.Element { + const post = props.post; + + return ( + +
    + {post.thumbnail.data ? ( + + ) : ( +
    + )} +
    +
    + {post.date && ( +

    + + event + + {prettyDate(post.date)} +

    + )} +
    +
    + {post.translations.length > 0 ? ( + <> +

    {post.translations[0].title}

    +

    {post.translations[0].excerpt}

    + + ) : ( +

    {prettySlug(post.slug)}

    + )} +
    +
    + {post.categories.data.map((category) => ( + + {category.attributes.short} + + ))} +
    +
    +
    + + ); +} diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index c9a30e1..7cd91b5 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -2,12 +2,17 @@ import { Dispatch, SetStateAction } from "react"; import Button from "./Button"; export type PopupProps = { - setState: Dispatch>; + setState: + | Dispatch> + | Dispatch>; state?: boolean; children: React.ReactNode; + fillViewport?: boolean; + hideBackground?: boolean; }; export default function Popup(props: PopupProps): JSX.Element { + return (
    { + if (e.key.match("Escape")) props.setState(false); + }} + tabIndex={0} >