Kristel Voets
Designer
Visit Kristel Voets's profile
Preview images of your documents
import React, { useMemo } from 'react';
import get from 'lodash.get';
import { withDocument } from 'part:@sanity/form-builder';
import Fieldset from 'part:@sanity/components/fieldsets/default';
import { urlFor, calculateRatio } from '../lib/image';
const ImagePreview = (props) => {
const image = props.image;
const options = props.options ?? {};
const ratio = options.ratio ?? image?.ratio ?? props.ratio;
const url = useMemo(() => {
if (typeof image === 'object' && image?.asset?._ref) {
const imageUrl = urlFor(image).auto('format').format('jpg');
if (typeof ratio === 'string' && ratio.indexOf(':') > 0) {
const [width, height] = calculateRatio(ratio);
return imageUrl.width(width).height(height).url();
} else {
return imageUrl.maxWidth(1200).maxHeight(1200).fit('max').url();
}
}
}, [image, ratio]);
return (
<div
style={{
width: '100%',
height: '400px',
paddingLeft: '6px',
paddingRight: '6px',
}}
>
{url && (
<img
src={url}
style={{
display: 'block',
width: '100%',
height: '100%',
objectFit: 'contain',
}}
/>
)}
</div>
);
};
const ImagePreviewField = React.forwardRef((props, ref) => {
const { document, level, type } = props;
const property = get(type, ['options', 'imageProperty'], 'image');
const limit = get(type, ['options', 'limit'], 2);
const images = [].concat(get(document, property, [])).slice(0, limit);
if (images.length > 0) {
return (
<Fieldset
ref={ref}
level={level}
legend={type.title}
description={type.description}
columns={images.length > 1 ? 2 : 1}
isCollapsible={true}
>
{images.map((image) => (
<ImagePreview
image={image}
ratio={document.ratio}
options={type.options}
/>
))}
</Fieldset>
);
} else {
return <div ref={ref}></div>;
}
});
export default withDocument(ImagePreviewField);
import trim from 'lodash.trim';
import imageUrlBuilder from '@sanity/image-url';
import client from 'part:@sanity/base/client';
export const builder = imageUrlBuilder(client);
export const urlFor = (source) => {
return builder.image(source);
};
export function parseRatio(ratio) {
if (typeof ratio === 'number') {
return ratio;
} else if (typeof ratio === 'string') {
const [a, b] = ratio.split(':').map((p) => parseInt(trim(p), 10));
return a / b;
} else {
return 3 / 2;
}
}
export function calculateRatio(ratio, scale = 1) {
const r = parseRatio(ratio);
const a = r > 1 ? 1200 : 900;
const b = r > 1 ? a / r : a * r;
const width = round(scale * (r > 1 ? a : b));
const height = round(scale * (r > 1 ? b : a));
return [width, height, r];
}
function round(x) {
return Math.ceil(x / 5) * 5;
}
export const PreviewField = {
name: 'imagePreview',
title: 'Preview',
type: 'boolean', // could be any type
inputComponent: ImagePreviewField,
options: {
imageProperty: 'images', // the property (single image or array of images)
limit: 1, // optionally set the max. number of items to display
ratio: '1:1', // optionally set a fixed ratio
}
}
This field allows you to preview an image, or a number of images from an array.
The source field is set by the 'imageProperty’ option, which can refer to nested fields using dot-syntax.
You can set a fixed display ratio using the options, as well as the maximum number of images to display.
If the document or the particular image has a ‘ratio’ field (string, like: "3:2”), it will be used unless a fixed ratio has been specified.
Whenever a ratio is given, the image’s hotspot/cropping will be applied automatically.
Designer