UNPKG

@prismicio/types-internal

Version:
197 lines (170 loc) 4.44 kB
import { either } from "fp-ts" import { pipe } from "fp-ts/lib/function" import * as t from "io-ts" import { withOptionals } from "../../../utils/Objects" import { NonEmptyStringOrNull, StringOrNull } from "../../../validators" import type { LegacyContentCtx, WithTypes } from "../../LegacyContentCtx" import { hasContentType } from "../../utils" export const ImageContentType = "ImageContent" export const isImageContent = (u: unknown): u is ImageContent => hasContentType(u) && u.__TYPE__ === ImageContentType const originReader = t.exact( t.type({ id: t.string, url: t.string, width: t.number, height: t.number, }), ) export const ImageContentView = t.exact( t.intersection([ t.type({ origin: originReader, width: t.number, height: t.number, edit: t.type({ zoom: t.number, crop: t.type({ x: t.number, y: t.number, }), background: t.string, }), }), t.partial({ url: t.string, credits: NonEmptyStringOrNull, alt: NonEmptyStringOrNull, provider: StringOrNull, }), ]), ) export type ImageContentView = t.TypeOf<typeof ImageContentView> const legacyReader = t.intersection([ ImageContentView, t.partial({ thumbnails: t.record(t.string, ImageContentView), }), ]) type ImageLegacy = t.TypeOf<typeof legacyReader> export const ImageLegacy = (ctx: LegacyContentCtx) => new t.Type<ImageContent, WithTypes<ImageLegacy>, unknown>( "ImageLegacy", isImageContent, (u) => { return pipe( legacyReader.decode(u), either.map((i) => ImageContent.encode({ ...i, __TYPE__: ImageContentType }), ), ) }, (i: ImageContent) => { return { content: withOptionals<ImageLegacy>( { edit: i.edit, height: i.height, origin: i.origin, width: i.width, }, [ ["alt", i.alt], ["credits", i.credits], ["provider", i.provider], ["thumbnails", i.thumbnails], ["url", i.url], ], ), types: { [ctx.keyOfType]: "Image" }, keys: {}, } }, ) export const ImageContent = t.intersection([ legacyReader, t.strict({ __TYPE__: t.literal(ImageContentType), }), ]) export type ImageContent = t.TypeOf<typeof ImageContent> // Helpers to build image crop URLs (used in editor, migration-api, and prismic-mocks) interface BuildCropUrlArgs { origin: ImageContent["origin"] /** Image crop parameters */ croppedImage: CroppedImage } export function buildCropUrl(args: BuildCropUrlArgs) { const { origin, croppedImage } = args const { x, y, width, height, cropHeight, cropWidth } = croppedImage const url = new URL(origin.url) const hasResize = origin.height !== height || origin.width !== width const hasCrop = x !== 0 || y !== 0 || cropHeight !== origin.height || cropWidth !== origin.width if (hasCrop) { const crop = [x, y, cropWidth, cropHeight].map(Math.round).join(",") url.searchParams.set("rect", crop) url.searchParams.set("w", width.toString()) url.searchParams.set("h", height.toString()) } if (hasResize) { url.searchParams.set("w", width.toString()) url.searchParams.set("h", height.toString()) } return url.toString() } export interface CroppedImage { zoom: number /** X coordinate of the crop rect on the original image */ x: number /** Y coordinate of the crop rect on the original image */ y: number /** The width of the crop rect over the original image */ cropWidth: number /** The height of the crop rect over the original image */ cropHeight: number /** Final (user defined via W input) height of the image */ width: number /** Final (user defined via H input) height of the image */ height: number } export function getCroppedImage({ contentView, }: { contentView: ImageContentView }): CroppedImage { const { edit: { crop: { x, y }, zoom, }, width, height, origin: { width: originWidth, height: originHeight }, } = contentView let cropWidth let cropHeight const originAspect = originWidth / originHeight const cropAspect = width / height if (cropAspect >= originAspect) { // crop and origin image would be the same width if zoom was equal to 1 cropWidth = originWidth / zoom cropHeight = cropWidth / cropAspect } else { // crop and origin image would be the same height if zoom was equal to 1 cropHeight = originHeight / zoom cropWidth = cropHeight * cropAspect } return { x, y, width, height, zoom, cropWidth, cropHeight, } }