mirror of
https://github.com/UiO-Gaming/uiogaming.no.git
synced 2026-11-05 13:56:47 +01:00
Apply Sanity crop and hotspot to images
Fetch the full image objects instead of only the asset URL so the URL builder can apply the crop and hotspot set in the Studio, crop the Open Graph image around the hotspot, and add sizes to full-width blog images. Closes #27 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f647dd869d
commit
1a18096cf6
4 files changed
+27
-40
No files matched your search
@@ -2,7 +2,7 @@ import Footer from "@/components/page-sections/footer"
|
||||
import { generateSeoMetadata } from "@/components/seo"
|
||||
import Back from "@/components/ui/back"
|
||||
|
||||
import { urlFor } from "@/lib/imageUrlBuilder"
|
||||
import { type SanityImage, urlFor } from "@/lib/imageUrlBuilder"
|
||||
import { getBlogPost } from "@/lib/sanity"
|
||||
|
||||
import { PortableText, PortableTextComponents } from "@portabletext/react"
|
||||
@@ -20,11 +20,7 @@ interface BlogPageParams {
|
||||
}>
|
||||
}
|
||||
|
||||
interface ImageValue {
|
||||
asset?: {
|
||||
_id: string
|
||||
url: string
|
||||
}
|
||||
type ImageValue = Partial<SanityImage> & {
|
||||
alt?: string
|
||||
caption?: string
|
||||
}
|
||||
@@ -67,7 +63,10 @@ export async function generateMetadata({
|
||||
path: `/blog/${slug}`,
|
||||
title: data.title,
|
||||
description: data.excerpt,
|
||||
imageURL: data.mainImage?.asset?.url,
|
||||
// Cropped around the hotspot to the size link previews expect
|
||||
imageURL: data.mainImage?.asset
|
||||
? urlFor(data.mainImage).width(1200).height(630).url()
|
||||
: undefined,
|
||||
author: data.author?.name,
|
||||
publishedTime: data._createdAt,
|
||||
})
|
||||
@@ -80,10 +79,13 @@ const components: PortableTextComponents = {
|
||||
return (
|
||||
<div className={styles.imageContainer}>
|
||||
<Image
|
||||
src={urlFor(value).url()}
|
||||
// Only a width, so the image keeps its shape and just the crop
|
||||
// applies. fit("max") avoids upscaling small images
|
||||
src={urlFor(value).width(1200).fit("max").url()}
|
||||
alt={value.alt || "Blog image"}
|
||||
width={800}
|
||||
height={400}
|
||||
sizes="(max-width: 640px) 100vw, 640px"
|
||||
style={{ width: "100%", height: "auto" }}
|
||||
/>
|
||||
{value.caption && (
|
||||
@@ -144,10 +146,11 @@ const BlogPost = async ({ params }: BlogPageParams) => {
|
||||
<header className={styles.header}>
|
||||
{data.mainImage && (
|
||||
<Image
|
||||
src={urlFor(data.mainImage).url()}
|
||||
src={urlFor(data.mainImage).width(1600).fit("max").url()}
|
||||
alt={data.title}
|
||||
width={800}
|
||||
height={400}
|
||||
sizes="(max-width: 850px) 100vw, 850px"
|
||||
className={styles.headerImage}
|
||||
/>
|
||||
)}
|
||||
@@ -158,7 +161,7 @@ const BlogPost = async ({ params }: BlogPageParams) => {
|
||||
<Image
|
||||
className="circular"
|
||||
alt={data.author.name}
|
||||
src={urlFor(data.author.image).width(50).height(50).url()}
|
||||
src={urlFor(data.author.image).width(100).height(100).url()}
|
||||
width={50}
|
||||
height={50}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { urlFor } from "@/lib/imageUrlBuilder"
|
||||
import { type SanityImage, urlFor } from "@/lib/imageUrlBuilder"
|
||||
import { getBoardMembers } from "@/lib/sanity"
|
||||
|
||||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
@@ -14,12 +14,7 @@ interface BoardMember {
|
||||
no: string
|
||||
en: string
|
||||
}
|
||||
image?: {
|
||||
asset?: {
|
||||
_id: string
|
||||
url: string
|
||||
}
|
||||
}
|
||||
image: SanityImage | null
|
||||
bio?: {
|
||||
no: string
|
||||
en: string
|
||||
@@ -44,11 +39,9 @@ const BoardMembers = async () => {
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
member.image?.asset?.url
|
||||
? urlFor(member.image.asset.url)
|
||||
.width(512)
|
||||
.height(512)
|
||||
.url()
|
||||
member.image?.asset
|
||||
? // Cropped to a square around the hotspot
|
||||
urlFor(member.image).width(512).height(512).url()
|
||||
: "/person.png"
|
||||
}
|
||||
width={256}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
createImageUrlBuilder,
|
||||
type SanityImageObject,
|
||||
type SanityImageSource,
|
||||
} from "@sanity/image-url"
|
||||
|
||||
@@ -7,6 +8,11 @@ import { sanityClient } from "./sanity"
|
||||
|
||||
const builder = createImageUrlBuilder(sanityClient)
|
||||
|
||||
// An image field as stored in Sanity: an asset reference plus the crop and
|
||||
// hotspot chosen in the Studio. Pass the whole object to urlFor, not the
|
||||
// asset URL, or the crop and hotspot are ignored
|
||||
export type SanityImage = SanityImageObject
|
||||
|
||||
export function urlFor(source: SanityImageSource) {
|
||||
return builder.image(source)
|
||||
}
|
||||
+3
-18
@@ -17,19 +17,9 @@ export async function getBlogPost(slug: string) {
|
||||
title,
|
||||
author-> {
|
||||
name,
|
||||
image {
|
||||
asset-> {
|
||||
_id,
|
||||
url
|
||||
}
|
||||
}
|
||||
},
|
||||
mainImage {
|
||||
asset-> {
|
||||
_id,
|
||||
url
|
||||
}
|
||||
image
|
||||
},
|
||||
mainImage,
|
||||
excerpt,
|
||||
body
|
||||
}
|
||||
@@ -119,12 +109,7 @@ export async function getBoardMembers() {
|
||||
no,
|
||||
en
|
||||
},
|
||||
image {
|
||||
asset->{
|
||||
_id,
|
||||
url
|
||||
}
|
||||
},
|
||||
image,
|
||||
bio {
|
||||
no,
|
||||
en
|
||||
|
||||
Reference in new issue
Block a user