Skip to content

Instantly share code, notes, and snippets.

@notflip
Created January 12, 2025 12:31
Show Gist options
  • Select an option

  • Save notflip/e33c0b45299674ce56d80f1c6d08bd09 to your computer and use it in GitHub Desktop.

Select an option

Save notflip/e33c0b45299674ce56d80f1c6d08bd09 to your computer and use it in GitHub Desktop.

Revisions

  1. notflip created this gist Jan 12, 2025.
    7 changes: 7 additions & 0 deletions HeaderBlockComponent.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    {image &&
    <ImageBox
    fill
    media={image as Media}
    sizes="(max-width: 1024px) 100vw, 50vw"
    />
    }
    41 changes: 41 additions & 0 deletions ImageBox.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    import type {Media} from "@payload-types";
    import Image from 'next/image'

    interface ImageBoxProps {
    media: Media
    fill?: boolean
    sizes?: string
    imageClassName?: string
    }

    export const ImageBox: React.FC<ImageBoxProps> = (props) => {
    const {media, fill, imageClassName, sizes} = props

    const {width: imageWidth, height: imageHeight} = media;

    const width = imageWidth ?? undefined;
    const height = imageHeight ?? undefined;

    const objectPosition =
    media.focalX != null && media.focalY != null
    ? `${media.focalX}% ${media.focalY}%`
    : 'center';

    return (
    <Image
    src={`${media.url!}?${media.updatedAt}`} alt={`Image box alt`}
    quality={95}
    fill={fill}
    width={!fill ? width : undefined}
    height={!fill ? height : undefined}
    className={imageClassName}
    sizes={sizes}
    style={{
    objectFit: "cover",
    objectPosition
    }}
    placeholder={media.blurhash ? "blur" : "empty"}
    blurDataURL={media.blurhash || undefined}
    />
    )
    }
    44 changes: 44 additions & 0 deletions Media.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    import path from 'path'
    import {fileURLToPath} from 'url'
    import {CollectionConfig} from "payload";
    import {generateBlurHash} from "@/hooks/generateBlurhash";

    const filename = fileURLToPath(import.meta.url)
    const dirname = path.dirname(filename)

    export const Media: CollectionConfig = {
    slug: "media",
    upload: {
    staticDir: path.resolve(dirname, '../../public/media'),
    adminThumbnail: 'thumbnail',
    mimeTypes: ['image/*'],
    imageSizes: [
    {
    name: "medium",
    width: 1400,
    height: 800,
    },
    ],
    },
    fields: [
    {
    name: "alt",
    type: "text",
    label: "Alt Text",
    },
    {
    name: "blurhash",
    type: "text",
    admin: {
    hidden: true,
    disableListColumn: true,
    disableListFilter: true
    }
    }
    ],
    hooks: {
    beforeValidate: [generateBlurHash]
    }
    };

    export default Media;
    21 changes: 21 additions & 0 deletions generateBlurHash.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    import {APIError, CollectionBeforeValidateHook} from 'payload';
    import {getPlaiceholder} from 'plaiceholder';

    export const generateBlurHash: CollectionBeforeValidateHook = async ({data, operation, req}) => {
    if (operation === 'create' || operation === 'update') {
    try {
    const buffer = req?.file?.data;

    if (buffer) {
    const {base64} = await getPlaiceholder(buffer, {size: 32});

    return {
    ...data,
    blurhash: base64,
    };
    }
    } catch (error) {
    throw new APIError('Failed to generate blur data url');
    }
    }
    };