Support for responsive images
This commit is contained in:
parent
bb86a5238b
commit
d25e6ba711
8
TODO.md
8
TODO.md
|
@ -1,16 +1,18 @@
|
|||
# Accord's Library v3.0
|
||||
|
||||
## Ongoing
|
||||
|
||||
- [Analytics] Add analytics
|
||||
|
||||
## Short term
|
||||
|
||||
- Number of audio players seems limited (on Chrome and Firefox)
|
||||
- Automatically generate different sizes of images
|
||||
- [RichTextContent] Handle relationship
|
||||
- [RichTextContent] Add autolink block support
|
||||
|
||||
## Mid term
|
||||
|
||||
- Save cookies for longer than just the session
|
||||
- [Folders] Support for nameless section
|
||||
- [Timeline] Error if collectible not published?
|
||||
- [Timeline] display source language
|
||||
- [Timeline] Add details button in footer with credits + last updated / created
|
||||
|
@ -18,10 +20,10 @@
|
|||
- [Videos] Display platform info + channel page
|
||||
- [JSLess] Provide JS-less alternative for timeline card footers
|
||||
- [JSLess] Provide JS-less alternative for parent pages
|
||||
- [Analytics] Add analytics
|
||||
|
||||
## Long term
|
||||
|
||||
- [Folders] Support for nameless section
|
||||
- [Scripts] Can't run the scripts using node (ts-node?)
|
||||
- [Scans] Adapt size of obi based on cover/dustjacket
|
||||
- [Scans] Order of cover/dustjacket/obi should be based on the book's page order.
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"@fontsource-variable/murecho": "^5.0.19",
|
||||
"@fontsource-variable/vollkorn": "^5.0.21",
|
||||
"accept-language": "^3.0.18",
|
||||
"astro": "4.8.7",
|
||||
"astro": "4.9.0",
|
||||
"astro-icon": "^1.1.0",
|
||||
"node-cache": "^5.1.2",
|
||||
"tippy.js": "^6.3.7",
|
||||
|
@ -3166,9 +3166,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "4.8.7",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-4.8.7.tgz",
|
||||
"integrity": "sha512-bTtv6zv94+U5cQdwy89LWTnocEfJ2Tfy6vpYYSUthfj12HtOpk0ykGW7YBe9a69NHqPwivSOvRjXOxGKigL9qA==",
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-4.9.0.tgz",
|
||||
"integrity": "sha512-beb3go5Oh5QDjns7YVxG1r40Flt/cuXB+YFTnVRqbh2NuMpYRoXZqIT+ZNaorleEfrLjLFXfsn1AAKVP+XZ2KA==",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.8.0",
|
||||
"@astrojs/internal-helpers": "0.4.0",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"script:download-currencies": "npm run scripts/download-currencies.ts",
|
||||
"script:download-wording-keys": "npm run scripts/download-wording-keys.ts",
|
||||
"prettier": "prettier --write --list-different --plugin=prettier-plugin-astro .",
|
||||
"precommit": "npm run script:download-wording-keys && npm run script:download-payload-sdk && npm run prettier && npm run astro check"
|
||||
"precommit": "npm run script:download-payload-sdk && npm run script:download-wording-keys && npm run prettier && npm run astro check"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=10.0.0",
|
||||
|
@ -24,7 +24,7 @@
|
|||
"@fontsource-variable/murecho": "^5.0.19",
|
||||
"@fontsource-variable/vollkorn": "^5.0.21",
|
||||
"accept-language": "^3.0.18",
|
||||
"astro": "4.8.7",
|
||||
"astro": "4.9.0",
|
||||
"astro-icon": "^1.1.0",
|
||||
"node-cache": "^5.1.2",
|
||||
"tippy.js": "^6.3.7",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 668 KiB |
Binary file not shown.
Before Width: | Height: | Size: 86 KiB |
Binary file not shown.
Before Width: | Height: | Size: 455 KiB |
|
@ -1,14 +1,20 @@
|
|||
---
|
||||
import type { PayloadImage } from "src/shared/payload/payload-sdk";
|
||||
import type {
|
||||
EndpointImage,
|
||||
EndpointMediaThumbnail,
|
||||
EndpointScanImage,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { getRandomId } from "src/utils/random";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
img: Pick<PayloadImage, "url" | "width" | "height">;
|
||||
img: EndpointImage | EndpointMediaThumbnail | EndpointScanImage;
|
||||
}
|
||||
|
||||
const {
|
||||
img: { url, width, height },
|
||||
img: { url, width, height, sizes },
|
||||
} = Astro.props;
|
||||
|
||||
const uniqueId = getRandomId();
|
||||
|
||||
const style = `
|
||||
|
@ -22,8 +28,24 @@ const style = `
|
|||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<img id={uniqueId} src={url} class="when-no-print when-js" />
|
||||
<img id={uniqueId} src={url} class="when-no-print when-no-js" />
|
||||
<img
|
||||
id={uniqueId}
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes="100vw"
|
||||
width={width}
|
||||
height={height}
|
||||
class="when-no-print when-js"
|
||||
/>
|
||||
<img
|
||||
id={uniqueId}
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes="100vw"
|
||||
width={width}
|
||||
height={height}
|
||||
class="when-no-print when-no-js"
|
||||
/>
|
||||
|
||||
{/* ------------------------------------------- CSS -------------------------------------------- */}
|
||||
|
||||
|
|
|
@ -5,14 +5,20 @@ import "@fontsource-variable/vollkorn";
|
|||
import "@fontsource-variable/murecho";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import AppLayoutSpinner from "./AppLayoutSpinner.astro";
|
||||
import type { EndpointAudio, EndpointVideo, PayloadImage } from "src/shared/payload/payload-sdk";
|
||||
import type {
|
||||
EndpointAudio,
|
||||
EndpointImage,
|
||||
EndpointMediaThumbnail,
|
||||
EndpointVideo,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { cache } from "src/utils/payload";
|
||||
|
||||
interface Props {
|
||||
openGraph?:
|
||||
| {
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
thumbnail?: PayloadImage | undefined;
|
||||
thumbnail?: EndpointImage | EndpointMediaThumbnail | undefined;
|
||||
audio?: EndpointAudio | undefined;
|
||||
video?: EndpointVideo | undefined;
|
||||
}
|
||||
|
@ -23,19 +29,10 @@ const { currentLocale } = Astro.locals;
|
|||
const { t } = await getI18n(currentLocale);
|
||||
|
||||
const { openGraph = {} } = Astro.props;
|
||||
const {
|
||||
description = t("global.meta.description"),
|
||||
thumbnail = {
|
||||
filename: "default_og.jpg",
|
||||
filesize: 0,
|
||||
height: 630,
|
||||
width: 1200,
|
||||
mimeType: "image/jpeg",
|
||||
url: "/img/default_og.jpg",
|
||||
},
|
||||
audio,
|
||||
video,
|
||||
} = openGraph;
|
||||
const { description = t("global.meta.description"), audio, video } = openGraph;
|
||||
|
||||
const thumbnail =
|
||||
openGraph.thumbnail?.openGraph ?? openGraph.thumbnail ?? cache.config.defaultOpenGraphImage;
|
||||
|
||||
const title = openGraph.title
|
||||
? `${openGraph.title} – ${t("global.siteName")}`
|
||||
|
@ -76,8 +73,6 @@ const { currentTheme } = Astro.locals;
|
|||
<meta name="twitter:site" content="@AccordsLibrary" />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:image" content={thumbnail.url} />
|
||||
|
||||
<meta property="og:type" content={video ? "video.movie" : audio ? "music.song" : "website"} />
|
||||
<meta property="og:locale" content={currentLocale} />
|
||||
|
@ -86,11 +81,20 @@ const { currentTheme } = Astro.locals;
|
|||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
|
||||
<meta property="og:image" content={thumbnail.url} />
|
||||
<meta property="og:image:secure_url" content={thumbnail.url} />
|
||||
<meta property="og:image:width" content={thumbnail.width.toString()} />
|
||||
<meta property="og:image:height" content={thumbnail.height.toString()} />
|
||||
<meta property="og:image:type" content={thumbnail.mimeType} />
|
||||
{
|
||||
thumbnail && (
|
||||
<>
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:image" content={thumbnail.url} />
|
||||
|
||||
<meta property="og:image" content={thumbnail.url} />
|
||||
<meta property="og:image:secure_url" content={thumbnail.url} />
|
||||
<meta property="og:image:width" content={thumbnail.width.toString()} />
|
||||
<meta property="og:image:height" content={thumbnail.height.toString()} />
|
||||
<meta property="og:image:type" content={thumbnail.mimeType} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
audio && (
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
import Button from "components/Button.astro";
|
||||
import {
|
||||
type EndpointCredit,
|
||||
type PayloadImage,
|
||||
type EndpointImage,
|
||||
type EndpointMediaThumbnail,
|
||||
type EndpointScanImage,
|
||||
type RichTextContent,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import Credits from "./Credits.astro";
|
||||
|
@ -11,11 +13,12 @@ import AppLayoutTitle from "./AppLayout/components/AppLayoutTitle.astro";
|
|||
import type { ComponentProps } from "astro/types";
|
||||
import AppLayoutDescription from "./AppLayout/components/AppLayoutDescription.astro";
|
||||
import Attributes from "./Attributes.astro";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
previousImageHref?: string | undefined;
|
||||
nextImageHref?: string | undefined;
|
||||
image: PayloadImage;
|
||||
image: EndpointImage | EndpointScanImage | EndpointMediaThumbnail;
|
||||
pretitle?: string | undefined;
|
||||
title: string;
|
||||
subtitle?: string | undefined;
|
||||
|
@ -29,7 +32,7 @@ interface Props {
|
|||
const {
|
||||
nextImageHref,
|
||||
previousImageHref,
|
||||
image: { url, width, height },
|
||||
image: { url, width, height, sizes },
|
||||
attributes = [],
|
||||
metaAttributes = [],
|
||||
credits = [],
|
||||
|
@ -41,24 +44,45 @@ const {
|
|||
} = Astro.props;
|
||||
|
||||
const smallTitle = !subtitle && !pretitle;
|
||||
|
||||
const hasNavigation = previousImageHref || nextImageHref;
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<div id="container">
|
||||
<div id="image-viewer" class:list={{ "with-buttons": previousImageHref || nextImageHref }}>
|
||||
<a
|
||||
class:list={{ hidden: !previousImageHref }}
|
||||
href={previousImageHref}
|
||||
data-astro-history="replace">
|
||||
<Button icon="material-symbols:chevron-left" />
|
||||
<div id="image-viewer" class:list={{ "with-buttons": hasNavigation }}>
|
||||
{
|
||||
hasNavigation && (
|
||||
<a
|
||||
class:list={{ hidden: !previousImageHref }}
|
||||
href={previousImageHref}
|
||||
data-astro-history="replace">
|
||||
<Button icon="material-symbols:chevron-left" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
<a href={url} target="_blank">
|
||||
<img
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes={`(max-aspect-ratio: ${width / 0.9}/${height / 0.7}) 90vw, ${(width / height) * 70}vh`}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a href={url} target="_blank"><img src={url} width={width} height={height} /></a>
|
||||
|
||||
<a class:list={{ hidden: !nextImageHref }} href={nextImageHref} data-astro-history="replace">
|
||||
<Button icon="material-symbols:chevron-right" />
|
||||
</a>
|
||||
{
|
||||
hasNavigation && (
|
||||
<a
|
||||
class:list={{ hidden: !nextImageHref }}
|
||||
href={nextImageHref}
|
||||
data-astro-history="replace">
|
||||
<Button icon="material-symbols:chevron-right" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -128,11 +152,11 @@ const smallTitle = !subtitle && !pretitle;
|
|||
align-items: center;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
& > h1 {
|
||||
max-width: 35em;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: 35em;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
---
|
||||
import type { PayloadImage } from "src/shared/payload/payload-sdk";
|
||||
import type {
|
||||
EndpointImage,
|
||||
EndpointMediaThumbnail,
|
||||
EndpointScanImage,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import Card from "components/Card.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
import type { ComponentProps } from "astro/types";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import InlineAttributes from "components/InlineAttributes.astro";
|
||||
import { sizesToSrcset, sizesForGridLayout } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
thumbnail?: PayloadImage | undefined;
|
||||
thumbnail?: EndpointImage | EndpointMediaThumbnail | EndpointScanImage | undefined;
|
||||
pretitle?: string | undefined;
|
||||
title: string;
|
||||
subtitle?: string | undefined;
|
||||
|
@ -41,7 +46,13 @@ const {
|
|||
<div id="card">
|
||||
{
|
||||
thumbnail && (
|
||||
<img src={thumbnail.url} width={thumbnail.width} height={thumbnail.height} alt="" />
|
||||
<img
|
||||
src={thumbnail.url}
|
||||
srcset={sizesToSrcset(thumbnail.sizes)}
|
||||
sizes={sizesForGridLayout(250, 1.15)}
|
||||
width={thumbnail.width}
|
||||
height={thumbnail.height}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import OpenMediaPageButton from "./OpenMediaPageButton.astro";
|
|||
import { getI18n } from "src/i18n/i18n";
|
||||
import HeaderTitle from "components/HeaderTitle.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
node: RichTextUploadImageNode;
|
||||
|
@ -13,7 +14,7 @@ interface Props {
|
|||
|
||||
const {
|
||||
node: {
|
||||
value: { id, url, width, height, translations },
|
||||
value: { id, url, width, height, translations, sizes },
|
||||
},
|
||||
context,
|
||||
} = Astro.props;
|
||||
|
@ -36,7 +37,15 @@ const mediaPage = getLocalizedUrl(`/images/${id}`);
|
|||
</HeaderTitle>
|
||||
)
|
||||
}
|
||||
<a href={mediaPage}><img src={url} width={width} height={height} /></a>
|
||||
<a href={mediaPage}>
|
||||
<img
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes={`(max-width: 550px) 90vw, 550px`}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</a>
|
||||
<OpenMediaPageButton url={mediaPage} />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
---
|
||||
import type { EndpointImage } from "src/shared/payload/payload-sdk";
|
||||
import { sizesForGridLayout, sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
img?: { light: string; dark: string } | undefined;
|
||||
img?: { light: EndpointImage; dark: EndpointImage } | undefined;
|
||||
name: string;
|
||||
href: string;
|
||||
}
|
||||
|
@ -14,8 +17,26 @@ const { img, name, href } = Astro.props;
|
|||
{
|
||||
img ? (
|
||||
<>
|
||||
<img src={img.light} class="when-light-theme" alt={name} title={name} />
|
||||
<img src={img.dark} class="when-dark-theme" alt={name} title={name} />
|
||||
<img
|
||||
src={img.light.url}
|
||||
srcset={sizesToSrcset(img.light.sizes)}
|
||||
sizes={sizesForGridLayout(250, 1.15)}
|
||||
width={img.light.width}
|
||||
height={img.light.height}
|
||||
class="when-light-theme"
|
||||
alt={name}
|
||||
title={name}
|
||||
/>
|
||||
<img
|
||||
src={img.dark.url}
|
||||
srcset={sizesToSrcset(img.dark.sizes)}
|
||||
sizes={sizesForGridLayout(250, 1.15)}
|
||||
width={img.dark.width}
|
||||
height={img.dark.height}
|
||||
class="when-dark-theme"
|
||||
alt={name}
|
||||
title={name}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
|
|
|
@ -9,12 +9,10 @@ const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.curren
|
|||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
{
|
||||
cache.config.homeFolders.map(({ slug, translations, darkThumbnail, lightThumbnail }) => (
|
||||
cache.config.home.folders.map(({ slug, translations, darkThumbnail, lightThumbnail }) => (
|
||||
<CategoryCard
|
||||
img={
|
||||
darkThumbnail && lightThumbnail
|
||||
? { dark: darkThumbnail.url, light: lightThumbnail.url }
|
||||
: undefined
|
||||
darkThumbnail && lightThumbnail ? { dark: darkThumbnail, light: lightThumbnail } : undefined
|
||||
}
|
||||
name={getLocalizedMatch(translations).name}
|
||||
href={getLocalizedUrl(`/folders/${slug}`)}
|
||||
|
|
|
@ -12,6 +12,7 @@ import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescr
|
|||
import Attributes from "components/Attributes.astro";
|
||||
import type { Attribute } from "src/utils/attributes";
|
||||
import { payload } from "src/utils/payload";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
export const partial = true;
|
||||
|
||||
|
@ -71,6 +72,8 @@ if (updatedBy) {
|
|||
<img
|
||||
id="thumbnail"
|
||||
src={thumbnail.url}
|
||||
srcset={sizesToSrcset(thumbnail.sizes)}
|
||||
sizes={`(max-width: 550px) 90vw, 550px`}
|
||||
width={thumbnail.width}
|
||||
height={thumbnail.height}
|
||||
/>
|
||||
|
|
|
@ -11,8 +11,8 @@ import { payload } from "src/utils/payload";
|
|||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
const audio = await fetchOr404(() => payload.getAudioByID(id!));
|
||||
const id = Astro.params.id!;
|
||||
const audio = await fetchOr404(() => payload.getAudioByID(id));
|
||||
if (audio instanceof Response) {
|
||||
return audio;
|
||||
}
|
||||
|
@ -72,10 +72,10 @@ const metaAttributes = [
|
|||
|
||||
<AppLayout
|
||||
openGraph={{
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
description: description && formatRichTextToString(description),
|
||||
thumbnail,
|
||||
audio,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
}}>
|
||||
<div id="container">
|
||||
<AudioPlayer audio={audio} />
|
||||
|
|
|
@ -1,18 +1,39 @@
|
|||
---
|
||||
import type {
|
||||
EndpointImage,
|
||||
EndpointMediaThumbnail,
|
||||
EndpointScanImage,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
image: string;
|
||||
image: EndpointImage | EndpointScanImage | EndpointMediaThumbnail;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const { image, title, subtitle, href } = Astro.props;
|
||||
const {
|
||||
image: { url, width, height, sizes },
|
||||
title,
|
||||
subtitle,
|
||||
href,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<a href={href}>
|
||||
<img src={image} />
|
||||
<img
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes=`
|
||||
(max-width: 400px) 90vw,
|
||||
(max-width: 850px) 50vw,
|
||||
200px`
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
|
||||
<div class="high-contrast-text">
|
||||
<p class="title">{title}</p>
|
||||
|
|
|
@ -55,9 +55,9 @@ const metaAttributes = [
|
|||
|
||||
<AppLayout
|
||||
openGraph={{
|
||||
thumbnail: image,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
description: description && formatRichTextToString(description),
|
||||
thumbnail: image,
|
||||
}}
|
||||
parentPages={parentPages}>
|
||||
<Lightbox
|
||||
|
|
|
@ -6,6 +6,7 @@ import { getI18n } from "src/i18n/i18n";
|
|||
import { payload } from "src/utils/payload";
|
||||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
const slug = Astro.params.slug!;
|
||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
||||
|
@ -38,9 +39,17 @@ const translation = getLocalizedMatch(translations);
|
|||
|
||||
<div>
|
||||
{
|
||||
images.map((image, index) => (
|
||||
images.map(({ url, width, height, sizes }, index) => (
|
||||
<a href={getLocalizedUrl(`/collectibles/${slug}/gallery/${index}`)}>
|
||||
<img src={image.url} />
|
||||
<img
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes={`
|
||||
(max-width: ${(width / height) * 320}px) 90vw,
|
||||
${(width / height) * 320}px`}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
|
@ -70,7 +79,7 @@ const translation = getLocalizedMatch(translations);
|
|||
}
|
||||
|
||||
& > img {
|
||||
max-height: 20em;
|
||||
max-height: 320px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
width: auto;
|
||||
|
|
|
@ -18,13 +18,14 @@ import { convert } from "src/utils/currencies";
|
|||
import Attributes from "components/Attributes.astro";
|
||||
import type { Attribute } from "src/utils/attributes";
|
||||
import { payload } from "src/utils/payload";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const slug = Astro.params.slug!;
|
||||
const { getLocalizedMatch, getLocalizedUrl, t, formatDate, formatPrice } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const collectible = await fetchOr404(() => payload.getCollectible(slug!));
|
||||
const collectible = await fetchOr404(() => payload.getCollectible(slug));
|
||||
if (collectible instanceof Response) {
|
||||
return collectible;
|
||||
}
|
||||
|
@ -158,6 +159,8 @@ if (price) {
|
|||
<img
|
||||
id="thumbnail"
|
||||
src={thumbnail.url}
|
||||
srcset={sizesToSrcset(thumbnail.sizes)}
|
||||
sizes={`(max-width: 550px) 90vw, 550px`}
|
||||
width={thumbnail.width}
|
||||
height={thumbnail.height}
|
||||
/>
|
||||
|
@ -169,7 +172,7 @@ if (price) {
|
|||
{
|
||||
gallery && (
|
||||
<ImageTile
|
||||
image={gallery.thumbnail.url}
|
||||
image={gallery.thumbnail}
|
||||
title={t("collectibles.gallery.title")}
|
||||
subtitle={t("collectibles.gallery.subtitle", { count: gallery.count })}
|
||||
href={getLocalizedUrl(`/collectibles/${slug}/gallery`)}
|
||||
|
@ -180,7 +183,7 @@ if (price) {
|
|||
{
|
||||
scans && (
|
||||
<ImageTile
|
||||
image={scans.thumbnail.url}
|
||||
image={scans.thumbnail}
|
||||
title={t("collectibles.scans.title")}
|
||||
subtitle={t("collectibles.scans.subtitle", { count: scans.count })}
|
||||
href={getLocalizedUrl(`/collectibles/${slug}/scans`)}
|
||||
|
|
|
@ -17,7 +17,7 @@ if (scanPage instanceof Response) {
|
|||
return scanPage;
|
||||
}
|
||||
|
||||
const { parentPages, previousIndex, nextIndex, image, translations } = scanPage;
|
||||
const { parentPages, previousIndex, nextIndex, image, translations, thumbnail } = scanPage;
|
||||
const translation = getLocalizedMatch(translations);
|
||||
---
|
||||
|
||||
|
@ -27,7 +27,7 @@ const translation = getLocalizedMatch(translations);
|
|||
openGraph={{
|
||||
title: `${formatInlineTitle(translation)} (${index})`,
|
||||
description: translation.description && formatRichTextToString(translation.description),
|
||||
thumbnail: image,
|
||||
thumbnail,
|
||||
}}
|
||||
parentPages={parentPages}>
|
||||
<Lightbox
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import type { EndpointScanImage } from "src/shared/payload/payload-sdk";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
interface Props {
|
||||
scan: EndpointScanImage;
|
||||
|
@ -8,7 +9,7 @@ interface Props {
|
|||
}
|
||||
|
||||
const {
|
||||
scan: { url, index, width, height },
|
||||
scan: { url, index, width, height, sizes },
|
||||
collectibleSlug,
|
||||
} = Astro.props;
|
||||
|
||||
|
@ -18,7 +19,15 @@ const { getLocalizedUrl, formatScanIndexShort } = await getI18n(Astro.locals.cur
|
|||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<a href={getLocalizedUrl(`/collectibles/${collectibleSlug}/scans/${index}`)}>
|
||||
<img width={width} height={height} src={url} alt={index} />
|
||||
<img
|
||||
src={url}
|
||||
srcset={sizesToSrcset(sizes)}
|
||||
sizes={`
|
||||
(max-width: ${(width / height) * 320}px) 90vw,
|
||||
${(width / height) * 320}px`}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
<p>{formatScanIndexShort(index)}</p>
|
||||
</a>
|
||||
|
||||
|
@ -39,10 +48,11 @@ const { getLocalizedUrl, formatScanIndexShort } = await getI18n(Astro.locals.cur
|
|||
}
|
||||
|
||||
& > img {
|
||||
max-height: 20em;
|
||||
max-height: 320px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
width: auto;
|
||||
aspect-ratio: auto 21/29.7;
|
||||
box-shadow: 0 5px 20px -10px var(--color-shadow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro
|
|||
import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro";
|
||||
import { payload } from "src/utils/payload";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const slug = Astro.params.slug!;
|
||||
|
||||
const folder = await fetchOr404(() => payload.getFolder(slug!));
|
||||
const folder = await fetchOr404(() => payload.getFolder(slug));
|
||||
if (folder instanceof Response) {
|
||||
return folder;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import { payload } from "src/utils/payload";
|
|||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
const image = await fetchOr404(() => payload.getImageByID(id!));
|
||||
const id = Astro.params.id!;
|
||||
const image = await fetchOr404(() => payload.getImageByID(id));
|
||||
if (image instanceof Response) {
|
||||
return image;
|
||||
}
|
||||
|
@ -50,9 +50,9 @@ const metaAttributes = [
|
|||
|
||||
<AppLayout
|
||||
openGraph={{
|
||||
thumbnail: image,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
description: description && formatRichTextToString(description),
|
||||
thumbnail: image,
|
||||
}}>
|
||||
<Lightbox
|
||||
image={image}
|
||||
|
|
|
@ -16,11 +16,7 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
|||
|
||||
<AppLayout
|
||||
openGraph={{ title: t("home.title") }}
|
||||
backgroundImage={{
|
||||
url: "/img/background-image.webp",
|
||||
height: 2279,
|
||||
width: 1920,
|
||||
}}
|
||||
backgroundImage={cache.config.home.backgroundImage}
|
||||
hideFooterLinks
|
||||
hideHomeButton>
|
||||
<div id="title">
|
||||
|
|
|
@ -6,15 +6,17 @@ import { payload } from "src/utils/payload";
|
|||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const slug = Astro.params.slug!;
|
||||
|
||||
const page = await fetchOr404(() => payload.getPage(slug!));
|
||||
const page = await fetchOr404(() => payload.getPage(slug));
|
||||
if (page instanceof Response) {
|
||||
return page;
|
||||
}
|
||||
|
||||
const { parentPages, thumbnail, translations, backgroundImage } = page;
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const meta = getLocalizedMatch(page.translations);
|
||||
const meta = getLocalizedMatch(translations);
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
@ -23,9 +25,9 @@ const meta = getLocalizedMatch(page.translations);
|
|||
openGraph={{
|
||||
title: formatInlineTitle(meta),
|
||||
description: meta.summary && formatRichTextToString(meta.summary),
|
||||
thumbnail: page.thumbnail,
|
||||
thumbnail: thumbnail,
|
||||
}}
|
||||
parentPages={page.parentPages}
|
||||
backgroundImage={page.backgroundImage ?? page.thumbnail}>
|
||||
<Page slug={page.slug} lang={Astro.locals.currentLocale} page={page} />
|
||||
parentPages={parentPages}
|
||||
backgroundImage={backgroundImage ?? thumbnail}>
|
||||
<Page slug={slug} lang={Astro.locals.currentLocale} page={page} />
|
||||
</AppLayout>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { payload } from "src/utils/payload";
|
|||
import type { Attribute } from "src/utils/attributes";
|
||||
import { formatLocale } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const recorder = await fetchOr404(() => payload.getRecorderByID(id));
|
||||
|
@ -46,7 +47,13 @@ if (languages.length > 0) {
|
|||
{
|
||||
avatar && (
|
||||
<Fragment slot="header-aside">
|
||||
<img src={avatar.url} width={avatar.width} height={avatar.height} />
|
||||
<img
|
||||
src={avatar.url}
|
||||
srcset={sizesToSrcset(avatar.sizes)}
|
||||
sizes={`(max-width: 550px) 90vw, 550px`}
|
||||
width={avatar.width}
|
||||
height={avatar.height}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import AppLayout from "components/AppLayout/AppLayout.astro";
|
|||
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||
import Card from "components/Card.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import AppLayoutBackgroundImg from "components/AppLayout/components/AppLayoutBackgroundImg.astro";
|
||||
import { cache } from "src/utils/payload";
|
||||
import type { WordingKey } from "src/i18n/wordings-keys";
|
||||
import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro";
|
||||
|
@ -18,14 +17,7 @@ const { getLocalizedUrl, t, formatTimelineDate } = await getI18n(Astro.locals.cu
|
|||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<AppLayout>
|
||||
<AppLayoutBackgroundImg
|
||||
img={{
|
||||
url: "/img/timeline-background.webp",
|
||||
width: 2478,
|
||||
height: 4110,
|
||||
}}
|
||||
/>
|
||||
<AppLayout backgroundImage={cache.config.timeline.backgroundImage}>
|
||||
<AppLayoutTitle title={t("timeline.title")} />
|
||||
<AppLayoutDescription description={t("timeline.description")} />
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import { payload } from "src/utils/payload";
|
|||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
const video = await fetchOr404(() => payload.getVideoByID(id!));
|
||||
const id = Astro.params.id!;
|
||||
const video = await fetchOr404(() => payload.getVideoByID(id));
|
||||
if (video instanceof Response) {
|
||||
return video;
|
||||
}
|
||||
|
@ -71,10 +71,10 @@ const metaAttributes = [
|
|||
|
||||
<AppLayout
|
||||
openGraph={{
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
description: description && formatRichTextToString(description),
|
||||
thumbnail,
|
||||
video,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
}}>
|
||||
<div id="container">
|
||||
<VideoPlayer video={video} />
|
||||
|
|
|
@ -55,7 +55,6 @@ export interface Page {
|
|||
slug: string;
|
||||
thumbnail?: string | Image | null;
|
||||
backgroundImage?: string | Image | null;
|
||||
tags?: (string | Tag)[] | null;
|
||||
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -133,7 +132,6 @@ export interface Image {
|
|||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
tags?: (string | Tag)[] | null;
|
||||
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
|
||||
credits?: Credits;
|
||||
updatedAt: string;
|
||||
|
@ -161,6 +159,62 @@ export interface Image {
|
|||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"200w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"320w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"480w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"800w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"1280w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"1920w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"2560w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
@ -171,22 +225,6 @@ export interface Language {
|
|||
id: string;
|
||||
name: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "tags".
|
||||
*/
|
||||
export interface Tag {
|
||||
id: string;
|
||||
slug: string;
|
||||
page?: (string | null) | Page;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
name: string;
|
||||
id?: string | null;
|
||||
}[];
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "TagsBlock".
|
||||
|
@ -215,6 +253,22 @@ export interface Attribute {
|
|||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "tags".
|
||||
*/
|
||||
export interface Tag {
|
||||
id: string;
|
||||
slug: string;
|
||||
page?: (string | null) | Page;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
name: string;
|
||||
id?: string | null;
|
||||
}[];
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "NumberBlock".
|
||||
|
@ -373,7 +427,6 @@ export interface Collectible {
|
|||
thumbnail?: string | Image | null;
|
||||
nature: "Physical" | "Digital";
|
||||
languages?: (string | Language)[] | null;
|
||||
tags?: (string | Tag)[] | null;
|
||||
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -576,7 +629,31 @@ export interface Scan {
|
|||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
og?: {
|
||||
"200w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"320w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"480w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"800w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
|
@ -638,7 +715,6 @@ export interface Audio {
|
|||
} | null;
|
||||
id?: string | null;
|
||||
}[];
|
||||
tags?: (string | Tag)[] | null;
|
||||
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
|
||||
credits?: Credits;
|
||||
updatedAt: string;
|
||||
|
@ -681,6 +757,62 @@ export interface MediaThumbnail {
|
|||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"200w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"320w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"480w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"800w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"1280w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"1920w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
"2560w"?: {
|
||||
url?: string | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
filename?: string | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
@ -714,7 +846,6 @@ export interface Video {
|
|||
subfile?: string | VideoSubtitle | null;
|
||||
id?: string | null;
|
||||
}[];
|
||||
tags?: (string | Tag)[] | null;
|
||||
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
|
||||
credits?: Credits;
|
||||
platformEnabled?: boolean | null;
|
||||
|
@ -928,6 +1059,9 @@ export interface PayloadMigration {
|
|||
*/
|
||||
export interface WebsiteConfig {
|
||||
id: string;
|
||||
homeBackgroundImage: string | Image;
|
||||
timelineBackgroundImage: string | Image;
|
||||
defaultOpenGraphImage: string | Image;
|
||||
homeFolders?:
|
||||
| {
|
||||
lightThumbnail?: string | Image | null;
|
||||
|
@ -1393,11 +1527,15 @@ export type EndpointFolder = {
|
|||
};
|
||||
|
||||
export type EndpointWebsiteConfig = {
|
||||
homeFolders: (EndpointFolder & {
|
||||
lightThumbnail?: EndpointImage;
|
||||
darkThumbnail?: EndpointImage;
|
||||
})[];
|
||||
home: {
|
||||
backgroundImage?: EndpointImage;
|
||||
folders: (EndpointFolder & {
|
||||
lightThumbnail?: EndpointImage;
|
||||
darkThumbnail?: EndpointImage;
|
||||
})[];
|
||||
};
|
||||
timeline: {
|
||||
backgroundImage?: EndpointImage;
|
||||
breaks: number[];
|
||||
eventCount: number;
|
||||
eras: {
|
||||
|
@ -1406,6 +1544,7 @@ export type EndpointWebsiteConfig = {
|
|||
name: string;
|
||||
}[];
|
||||
};
|
||||
defaultOpenGraphImage?: EndpointImage;
|
||||
};
|
||||
|
||||
export type EndpointRecorder = {
|
||||
|
@ -1516,7 +1655,7 @@ export type EndpointCollectible = {
|
|||
backgroundImage?: EndpointImage;
|
||||
nature: CollectibleNature;
|
||||
gallery?: { count: number; thumbnail: EndpointImage };
|
||||
scans?: { count: number; thumbnail: PayloadImage };
|
||||
scans?: { count: number; thumbnail: EndpointScanImage };
|
||||
urls: { url: string; label: string }[];
|
||||
price?: {
|
||||
amount: number;
|
||||
|
@ -1681,6 +1820,7 @@ export type EndpointCollectibleScanPage = {
|
|||
|
||||
export type EndpointScanImage = PayloadImage & {
|
||||
index: string;
|
||||
sizes: PayloadImage[];
|
||||
};
|
||||
|
||||
export type TableOfContentEntry = {
|
||||
|
@ -1748,15 +1888,17 @@ export type EndpointMedia = {
|
|||
export type EndpointImage = EndpointMedia & {
|
||||
width: number;
|
||||
height: number;
|
||||
sizes: PayloadImage[];
|
||||
openGraph?: PayloadImage;
|
||||
};
|
||||
|
||||
export type EndpointAudio = EndpointMedia & {
|
||||
thumbnail?: PayloadImage;
|
||||
thumbnail?: EndpointMediaThumbnail;
|
||||
duration: number;
|
||||
};
|
||||
|
||||
export type EndpointVideo = EndpointMedia & {
|
||||
thumbnail?: PayloadImage;
|
||||
thumbnail?: EndpointMediaThumbnail;
|
||||
subtitles: {
|
||||
language: string;
|
||||
url: string;
|
||||
|
@ -1776,6 +1918,11 @@ export type EndpointVideo = EndpointMedia & {
|
|||
duration: number;
|
||||
};
|
||||
|
||||
export type EndpointMediaThumbnail = PayloadImage & {
|
||||
sizes: PayloadImage[];
|
||||
openGraph?: PayloadImage;
|
||||
};
|
||||
|
||||
export type PayloadMedia = {
|
||||
url: string;
|
||||
mimeType: string;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import type { PayloadImage } from "src/shared/payload/payload-sdk";
|
||||
|
||||
export const sizesToSrcset = (sizes: PayloadImage[]): string =>
|
||||
sizes.map(({ url, width }) => `${encodeURI(url)} ${width}w`).join(", ");
|
||||
|
||||
export const sizesForGridLayout = (targetSize: number, marginMultiplier: number) => `
|
||||
(max-width: ${targetSize * 2 * marginMultiplier}px) ${100 / 1}vw,
|
||||
(max-width: ${targetSize * 3 * marginMultiplier}px) ${100 / 2}vw,
|
||||
(max-width: ${targetSize * 4 * marginMultiplier}px) ${100 / 3}vw,
|
||||
${targetSize}px`;
|
Loading…
Reference in New Issue