@prismicio/types-internal
Version:
Prismic types for Custom Types and Prismic Data
197 lines (170 loc) • 4.44 kB
text/typescript
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,
}
}