astro-sanity-picture
Version:
An Astro component for rendering a responsive <picture> element for an image fetched from Sanity
94 lines (88 loc) • 2.42 kB
text/typescript
import type { SanityImageSource } from "@sanity/image-url/lib/types/types";
import type {
AutoWidths,
SanityDimensionedImage,
SanityImageWithLqip,
} from "./types";
/**
* Perform a shallow (non-recursive) merge of multiple objects
* Equivalent to {...a, ...b, ...etc}, just a bit more terse
* @param items objects to merge
* @returns merged object
*/
export function shallowMerge<T extends Record<string, any>>(...items: T[]): T {
let merged = {} as T;
items.forEach((item) => {
Object.entries(item).forEach(([k, v]) => {
merged[k as keyof T] = v;
});
});
return merged;
}
/**
* Single-level recursive merge
* @param items objects to merge
* @returns merged object
*/
export function mergeSingleRecursive<T extends Record<string, any>>(
...items: T[]
): T {
let merged = {} as T;
items.forEach((item) => {
Object.entries(item).forEach(([k, v]) => {
merged[k as keyof T] =
merged[k] && v && typeof v === "object"
? shallowMerge(merged[k], v)
: v;
});
});
return merged;
}
/**
* Guard whether our provided image has dimensions
* @param image
*/
export function isSanityDimensionedImage(
image: SanityImageSource
): image is SanityDimensionedImage {
return (
typeof image == "object" &&
"asset" in image &&
"metadata" in image.asset &&
"dimensions" in image.asset.metadata
);
}
/**
* Guard whether our provided image has an lqip
* @param image
*/
export function isSanityImageWithLqip(
image: SanityImageSource
): image is SanityImageWithLqip {
return (
typeof image == "object" &&
"asset" in image &&
"metadata" in image.asset &&
"lqip" in image.asset.metadata
);
}
/**
* Calculate automatically determined widths for an image
* If image dimensions are not available, maximum width is bounded by autoWidths.maxWidth
* Incrementing by autoWidths.step px
* @param autoWidths autowidths settings to use
* @param image image to calculate off of
* @returns array of widths
*/
export function generateWidths(
autoWidths: AutoWidths,
image: SanityImageSource
): number[] {
const maxWidth = isSanityDimensionedImage(image)
? image.asset.metadata.dimensions.width
: autoWidths.maxWidth;
const divisions = Math.ceil(maxWidth / autoWidths.step);
return Array.from({ length: divisions }, (_, i) =>
Math.min(Math.floor(autoWidths.step * (i + 1)), maxWidth)
);
}