diff --git a/packages/next/src/fetchAPI-multipart/index.ts b/packages/next/src/fetchAPI-multipart/index.ts index a9d1cc276..bea99f65c 100644 --- a/packages/next/src/fetchAPI-multipart/index.ts +++ b/packages/next/src/fetchAPI-multipart/index.ts @@ -152,7 +152,7 @@ type FetchAPIFileUpload = (args: { request: Request }) => Promise export const fetchAPIFileUpload: FetchAPIFileUpload = async ({ options, request }) => { - const uploadOptions = { ...DEFAULT_OPTIONS, ...options } + const uploadOptions: FetchAPIFileUploadOptions = { ...DEFAULT_OPTIONS, ...options } if (!isEligibleRequest(request)) { debugLog(uploadOptions, 'Request is not eligible for file upload!') return { diff --git a/packages/payload/src/collections/operations/create.ts b/packages/payload/src/collections/operations/create.ts index beedd144b..a0c3dbe9d 100644 --- a/packages/payload/src/collections/operations/create.ts +++ b/packages/payload/src/collections/operations/create.ts @@ -121,6 +121,7 @@ export const createOperation = async -1 } diff --git a/packages/payload/src/uploads/cropImage.ts b/packages/payload/src/uploads/cropImage.ts index 3f08a58f6..1167983f4 100644 --- a/packages/payload/src/uploads/cropImage.ts +++ b/packages/payload/src/uploads/cropImage.ts @@ -2,7 +2,7 @@ export const percentToPixel = (value, dimension) => { return Math.floor((parseFloat(value) / 100) * dimension) } -export default async function cropImage({ cropData, dimensions, file, sharp }) { +export async function cropImage({ cropData, dimensions, file, sharp }) { try { const { height, width, x, y } = cropData diff --git a/packages/payload/src/uploads/generateFileData.ts b/packages/payload/src/uploads/generateFileData.ts index b9b4aed8a..56098a4d9 100644 --- a/packages/payload/src/uploads/generateFileData.ts +++ b/packages/payload/src/uploads/generateFileData.ts @@ -9,22 +9,24 @@ import sanitize from 'sanitize-filename' import type { Collection } from '../collections/config/types.js' import type { SanitizedConfig } from '../config/types.js' import type { PayloadRequestWithData } from '../types/index.js' -import type { FileData, FileToSave, ProbedImageSize } from './types.js' +import type { FileData, FileToSave, ProbedImageSize, UploadEdits } from './types.js' import { FileRetrievalError, FileUploadError, MissingFile } from '../errors/index.js' -import canResizeImage from './canResizeImage.js' -import cropImage from './cropImage.js' +import { canResizeImage } from './canResizeImage.js' +import { cropImage } from './cropImage.js' import { getExternalFile } from './getExternalFile.js' import { getFileByPath } from './getFileByPath.js' import { getImageSize } from './getImageSize.js' -import getSafeFileName from './getSafeFilename.js' -import resizeAndTransformImageSizes from './imageResizer.js' -import isImage from './isImage.js' +import { getSafeFileName } from './getSafeFilename.js' +import { resizeAndTransformImageSizes } from './imageResizer.js' +import { isImage } from './isImage.js' type Args = { collection: Collection config: SanitizedConfig data: T + operation: 'create' | 'update' + originalDoc?: T overwriteExistingFiles?: boolean req: PayloadRequestWithData throwOnMissingFile?: boolean @@ -38,6 +40,8 @@ type Result = Promise<{ export const generateFileData = async ({ collection: { config: collectionConfig }, data, + operation, + originalDoc, overwriteExistingFiles, req, throwOnMissingFile, @@ -53,10 +57,22 @@ export const generateFileData = async ({ let file = req.file - const uploadEdits = req.query['uploadEdits'] || {} + const uploadEdits = parseUploadEditsFromReqOrIncomingData({ + data, + operation, + originalDoc, + req, + }) - const { disableLocalStorage, formatOptions, imageSizes, resizeOptions, staticDir, trimOptions } = - collectionConfig.upload + const { + disableLocalStorage, + focalPoint: focalPointEnabled = true, + formatOptions, + imageSizes, + resizeOptions, + staticDir, + trimOptions, + } = collectionConfig.upload const staticPath = staticDir @@ -228,9 +244,9 @@ export const generateFileData = async ({ } } - if (Array.isArray(imageSizes) && fileSupportsResize && sharp) { + if (fileSupportsResize && (Array.isArray(imageSizes) || focalPointEnabled !== false)) { req.payloadUploadSizes = {} - const { sizeData, sizesToSave } = await resizeAndTransformImageSizes({ + const { focalPoint, sizeData, sizesToSave } = await resizeAndTransformImageSizes({ config: collectionConfig, dimensions: !cropData ? dimensions @@ -245,13 +261,16 @@ export const generateFileData = async ({ savedFilename: fsSafeName || file.name, sharp, staticPath, + uploadEdits, }) fileData.sizes = sizeData + fileData.focalX = focalPoint?.x + fileData.focalY = focalPoint?.y filesToSave.push(...sizesToSave) } } catch (err) { - console.error(err) + req.payload.logger.error(err) throw new FileUploadError(req.t) } @@ -265,3 +284,50 @@ export const generateFileData = async ({ files: filesToSave, } } + +/** + * Parse upload edits from req or incoming data + */ +function parseUploadEditsFromReqOrIncomingData(args: { + data: unknown + operation: 'create' | 'update' + originalDoc: unknown + req: PayloadRequestWithData +}): UploadEdits { + const { data, operation, originalDoc, req } = args + + // Get intended focal point change from query string or incoming data + const { + uploadEdits = {}, + }: { + uploadEdits?: UploadEdits + } = req.query || {} + + if (uploadEdits.focalPoint) return uploadEdits + + const incomingData = data as FileData + const origDoc = originalDoc as FileData + + // If no change in focal point, return undefined. + // This prevents a refocal operation triggered from admin, because it always sends the focal point. + if (origDoc && incomingData.focalX === origDoc.focalX && incomingData.focalY === origDoc.focalY) { + return undefined + } + + if (incomingData?.focalX && incomingData?.focalY) { + uploadEdits.focalPoint = { + x: incomingData.focalX, + y: incomingData.focalY, + } + return uploadEdits + } + + // If no focal point is set, default to center + if (operation === 'create') { + uploadEdits.focalPoint = { + x: 50, + y: 50, + } + } + return uploadEdits +} diff --git a/packages/payload/src/uploads/getBaseFields.ts b/packages/payload/src/uploads/getBaseFields.ts index 7396fae44..66204c24f 100644 --- a/packages/payload/src/uploads/getBaseFields.ts +++ b/packages/payload/src/uploads/getBaseFields.ts @@ -149,6 +149,25 @@ export const getBaseUploadFields = ({ collection, config }: Options): Field[] => height, ] + // Add focal point fields if not disabled + if ( + uploadOptions.focalPoint !== false || + uploadOptions.imageSizes || + uploadOptions.resizeOptions + ) { + uploadFields = uploadFields.concat( + ['focalX', 'focalY'].map((name) => { + return { + name, + type: 'number', + admin: { + hidden: true, + }, + } + }), + ) + } + if (uploadOptions.mimeTypes) { mimeType.validate = mimeTypeValidator(uploadOptions.mimeTypes) } diff --git a/packages/payload/src/uploads/getSafeFilename.ts b/packages/payload/src/uploads/getSafeFilename.ts index 720c23abe..79fddae80 100644 --- a/packages/payload/src/uploads/getSafeFilename.ts +++ b/packages/payload/src/uploads/getSafeFilename.ts @@ -29,7 +29,7 @@ type Args = { staticPath: string } -async function getSafeFileName({ +export async function getSafeFileName({ collectionSlug, desiredFilename, req, @@ -51,5 +51,3 @@ async function getSafeFileName({ } return modifiedFilename } - -export default getSafeFileName diff --git a/packages/payload/src/uploads/imageResizer.ts b/packages/payload/src/uploads/imageResizer.ts index a057bd7ba..3bc49d85b 100644 --- a/packages/payload/src/uploads/imageResizer.ts +++ b/packages/payload/src/uploads/imageResizer.ts @@ -8,8 +8,15 @@ import sanitize from 'sanitize-filename' import type { SanitizedCollectionConfig } from '../collections/config/types.js' import type { SharpDependency } from '../config/types.js' -import type { PayloadRequestWithData, UploadEdits } from '../types/index.js' -import type { FileSize, FileSizes, FileToSave, ImageSize, ProbedImageSize } from './types.js' +import type { PayloadRequestWithData } from '../types/index.js' +import type { + FileSize, + FileSizes, + FileToSave, + ImageSize, + ProbedImageSize, + UploadEdits, +} from './types.js' import { isNumber } from '../utilities/isNumber.js' import fileExists from './fileExists.js' @@ -19,18 +26,16 @@ type ResizeArgs = { dimensions: ProbedImageSize file: PayloadRequestWithData['file'] mimeType: string - req: PayloadRequestWithData & { - query?: { - uploadEdits?: UploadEdits - } - } + req: PayloadRequestWithData savedFilename: string - sharp: SharpDependency + sharp?: SharpDependency staticPath: string + uploadEdits?: UploadEdits } /** Result from resizing and transforming the requested image sizes */ type ImageSizesResult = { + focalPoint?: UploadEdits['focalPoint'] sizeData: FileSizes sizesToSave: FileToSave[] } @@ -71,6 +76,16 @@ const createImageName = ( extension: string, ) => `${outputImageName}-${width}x${height}.${extension}` +type CreateResultArgs = { + filename?: FileSize['filename'] + filesize?: FileSize['filesize'] + height?: FileSize['height'] + mimeType?: FileSize['mimeType'] + name: string + sizesToSave?: FileToSave[] + width?: FileSize['width'] +} + /** * Create the result object for the image resize operation based on the * provided parameters. If the name is not provided, an empty result object @@ -85,26 +100,28 @@ const createImageName = ( * @param sizesToSave - the sizes to save * @returns the result object */ -const createResult = ( - name: string, - filename: FileSize['filename'] = null, - width: FileSize['width'] = null, - height: FileSize['height'] = null, - filesize: FileSize['filesize'] = null, - mimeType: FileSize['mimeType'] = null, - sizesToSave: FileToSave[] = [], -): ImageSizesResult => ({ - sizeData: { - [name]: { - filename, - filesize, - height, - mimeType, - width, +const createResult = ({ + name, + filename = null, + filesize = null, + height = null, + mimeType = null, + sizesToSave = [], + width = null, +}: CreateResultArgs): ImageSizesResult => { + return { + sizeData: { + [name]: { + filename, + filesize, + height, + mimeType, + width, + }, }, - }, - sizesToSave, -}) + sizesToSave, + } +} /** * Check if the image needs to be resized according to the requested dimensions @@ -208,7 +225,7 @@ const sanitizeResizeConfig = (resizeConfig: ImageSize): ImageSize => { * @param resizeConfig - the resize config * @returns the result of the resize operation(s) */ -export default async function resizeAndTransformImageSizes({ +export async function resizeAndTransformImageSizes({ config, dimensions, file, @@ -217,10 +234,27 @@ export default async function resizeAndTransformImageSizes({ savedFilename, sharp, staticPath, + uploadEdits, }: ResizeArgs): Promise { - const { imageSizes } = config.upload - // Noting to resize here so return as early as possible - if (!imageSizes) return { sizeData: {}, sizesToSave: [] } + const { focalPoint: focalPointEnabled = true, imageSizes } = config.upload + + // Focal point adjustments + const incomingFocalPoint = uploadEdits.focalPoint + ? { + x: isNumber(uploadEdits.focalPoint.x) ? Math.round(uploadEdits.focalPoint.x) : 50, + y: isNumber(uploadEdits.focalPoint.y) ? Math.round(uploadEdits.focalPoint.y) : 50, + } + : undefined + + const defaultResult: ImageSizesResult = { + ...(focalPointEnabled && incomingFocalPoint && { focalPoint: incomingFocalPoint }), + sizeData: {}, + sizesToSave: [], + } + + if (!imageSizes || !sharp) { + return defaultResult + } const sharpBase = sharp(file.tempFilePath || file.data).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081 @@ -232,16 +266,13 @@ export default async function resizeAndTransformImageSizes({ // skipped COMPLETELY and thus will not be included in the resulting images. // All further format/trim options will thus be skipped as well. if (preventResize(imageResizeConfig, dimensions)) { - return createResult(imageResizeConfig.name) + return createResult({ name: imageResizeConfig.name }) } const imageToResize = sharpBase.clone() let resized = imageToResize - if ( - req.query?.uploadEdits?.focalPoint && - applyPayloadAdjustments(imageResizeConfig, dimensions) - ) { + if (incomingFocalPoint && applyPayloadAdjustments(imageResizeConfig, dimensions)) { const { height: resizeHeight, width: resizeWidth } = imageResizeConfig const resizeAspectRatio = resizeWidth / resizeHeight const originalAspectRatio = dimensions.width / dimensions.height @@ -254,27 +285,17 @@ export default async function resizeAndTransformImageSizes({ }) const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true }) - // Focal point adjustments - const focalPoint = { - x: isNumber(req.query.uploadEdits.focalPoint?.x) - ? req.query.uploadEdits.focalPoint.x - : 50, - y: isNumber(req.query.uploadEdits.focalPoint?.y) - ? req.query.uploadEdits.focalPoint.y - : 50, - } - const safeResizeWidth = resizeWidth ?? scaledImageInfo.width const maxOffsetX = scaledImageInfo.width - safeResizeWidth const leftFocalEdge = Math.round( - scaledImageInfo.width * (focalPoint.x / 100) - safeResizeWidth / 2, + scaledImageInfo.width * (incomingFocalPoint.x / 100) - safeResizeWidth / 2, ) const safeOffsetX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX) const safeResizeHeight = resizeHeight ?? scaledImageInfo.height const maxOffsetY = scaledImageInfo.height - safeResizeHeight const topFocalEdge = Math.round( - scaledImageInfo.height * (focalPoint.y / 100) - safeResizeHeight / 2, + scaledImageInfo.height * (incomingFocalPoint.y / 100) - safeResizeHeight / 2, ) const safeOffsetY = Math.min(Math.max(0, topFocalEdge), maxOffsetY) @@ -306,7 +327,9 @@ export default async function resizeAndTransformImageSizes({ const sanitizedImage = getSanitizedImageData(savedFilename) - req.payloadUploadSizes[imageResizeConfig.name] = bufferData + if (req.payloadUploadSizes) { + req.payloadUploadSizes[imageResizeConfig.name] = bufferData + } const mimeInfo = await fromBuffer(bufferData) @@ -327,15 +350,15 @@ export default async function resizeAndTransformImageSizes({ } const { height, size, width } = bufferInfo - return createResult( - imageResizeConfig.name, - imageNameWithDimensions, - width, + return createResult({ + name: imageResizeConfig.name, + filename: imageNameWithDimensions, + filesize: size, height, - size, - mimeInfo?.mime || mimeType, - [{ buffer: bufferData, path: imagePath }], - ) + mimeType: mimeInfo?.mime || mimeType, + sizesToSave: [{ buffer: bufferData, path: imagePath }], + width, + }) }), ) @@ -345,6 +368,6 @@ export default async function resizeAndTransformImageSizes({ acc.sizesToSave.push(...result.sizesToSave) return acc }, - { sizeData: {}, sizesToSave: [] }, + { ...defaultResult }, ) } diff --git a/packages/payload/src/uploads/isImage.ts b/packages/payload/src/uploads/isImage.ts index 01873bb94..62e6e0586 100644 --- a/packages/payload/src/uploads/isImage.ts +++ b/packages/payload/src/uploads/isImage.ts @@ -1,4 +1,4 @@ -export default function isImage(mimeType: string): boolean { +export function isImage(mimeType: string): boolean { return ( ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', 'image/avif'].indexOf( mimeType, diff --git a/packages/payload/src/uploads/types.ts b/packages/payload/src/uploads/types.ts index ec44b7129..a58016680 100644 --- a/packages/payload/src/uploads/types.ts +++ b/packages/payload/src/uploads/types.ts @@ -18,6 +18,8 @@ export type FileSizes = { export type FileData = { filename: string filesize: number + focalX?: number + focalY?: number height: number mimeType: string sizes: FileSizes @@ -117,3 +119,16 @@ export type FileToSave = { buffer: Buffer path: string } + +export type UploadEdits = { + crop?: { + height?: number + width?: number + x?: number + y?: number + } + focalPoint?: { + x?: number + y?: number + } +} diff --git a/packages/ui/src/elements/EditUpload/index.tsx b/packages/ui/src/elements/EditUpload/index.tsx index fcef403a3..7c1e4a880 100644 --- a/packages/ui/src/elements/EditUpload/index.tsx +++ b/packages/ui/src/elements/EditUpload/index.tsx @@ -32,13 +32,12 @@ type FocalPosition = { } export type EditUploadProps = { - doc?: Data fileName: string fileSrc: string imageCacheTag?: string initialCrop?: CropType initialFocalPoint?: FocalPosition - onSave?: ({ crop, pointPosition }: { crop: CropType; pointPosition: FocalPosition }) => void + onSave?: ({ crop, focalPosition }: { crop: CropType; focalPosition: FocalPosition }) => void showCrop?: boolean showFocalPoint?: boolean } @@ -51,11 +50,6 @@ const defaultCrop: CropType = { y: 0, } -const defaultPointPosition: FocalPosition = { - x: 50, - y: 50, -} - export const EditUpload: React.FC = ({ fileName, fileSrc, @@ -76,8 +70,15 @@ export const EditUpload: React.FC = ({ ...initialCrop, })) - const [pointPosition, setPointPosition] = useState(() => ({ - ...defaultPointPosition, + const defaultFocalPosition: FocalPosition = { + x: 50, + y: 50, + } + + console.log({ initialFocalPoint }) + + const [focalPosition, setFocalPosition] = useState(() => ({ + ...defaultFocalPosition, ...initialFocalPoint, })) const [checkBounds, setCheckBounds] = useState(false) @@ -103,10 +104,16 @@ export const EditUpload: React.FC = ({ }) } - const fineTuneFocalPoint = ({ coordinate, value }: { coordinate: 'x' | 'y'; value: string }) => { + const fineTuneFocalPosition = ({ + coordinate, + value, + }: { + coordinate: 'x' | 'y' + value: string + }) => { const intValue = parseInt(value) if (intValue >= 0 && intValue <= 100) { - setPointPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue })) + setFocalPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue })) } } @@ -114,13 +121,13 @@ export const EditUpload: React.FC = ({ if (typeof onSave === 'function') onSave({ crop, - pointPosition, + focalPosition, }) closeModal(editDrawerSlug) } const onDragEnd = React.useCallback(({ x, y }) => { - setPointPosition({ x, y }) + setFocalPosition({ x, y }) setCheckBounds(false) }, []) @@ -133,7 +140,7 @@ export const EditUpload: React.FC = ({ ((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100 const yCenter = ((boundsRect.top - containerRect.top + boundsRect.height / 2) / containerRect.height) * 100 - setPointPosition({ x: xCenter, y: yCenter }) + setFocalPosition({ x: xCenter, y: yCenter }) } const fileSrcToUse = imageCacheTag ? `${fileSrc}?${imageCacheTag}` : fileSrc @@ -209,7 +216,7 @@ export const EditUpload: React.FC = ({ checkBounds={showCrop ? checkBounds : false} className={`${baseClass}__focalPoint`} containerRef={focalWrapRef} - initialPosition={pointPosition} + initialPosition={focalPosition} onDragEnd={onDragEnd} setCheckBounds={showCrop ? setCheckBounds : false} > @@ -280,13 +287,13 @@ export const EditUpload: React.FC = ({
fineTuneFocalPoint({ coordinate: 'x', value })} - value={pointPosition.x.toFixed(0)} + onChange={(value) => fineTuneFocalPosition({ coordinate: 'x', value })} + value={focalPosition.x.toFixed(0)} /> fineTuneFocalPoint({ coordinate: 'y', value })} - value={pointPosition.y.toFixed(0)} + onChange={(value) => fineTuneFocalPosition({ coordinate: 'y', value })} + value={focalPosition.y.toFixed(0)} />
diff --git a/packages/ui/src/elements/Upload/index.tsx b/packages/ui/src/elements/Upload/index.tsx index d3b78e9b6..f0255e558 100644 --- a/packages/ui/src/elements/Upload/index.tsx +++ b/packages/ui/src/elements/Upload/index.tsx @@ -101,7 +101,7 @@ export const Upload: React.FC = (props) => { }, [setValue]) const onEditsSave = React.useCallback( - ({ crop, pointPosition }) => { + ({ crop, focalPosition }) => { setCrop({ x: crop.x || 0, y: crop.y || 0, @@ -122,10 +122,10 @@ export const Upload: React.FC = (props) => { type: 'SET', params: { uploadEdits: - crop || pointPosition + crop || focalPosition ? { crop: crop || null, - focalPoint: pointPosition ? pointPosition : null, + focalPoint: focalPosition ? focalPosition : null, } : null, }, @@ -164,10 +164,12 @@ export const Upload: React.FC = (props) => { const hasImageSizes = uploadConfig?.imageSizes?.length > 0 const hasResizeOptions = Boolean(uploadConfig?.resizeOptions) + // Explicity check if set to true, default is undefined + const focalPointEnabled = uploadConfig?.focalPoint === true const { crop: showCrop = true, focalPoint = true } = uploadConfig - const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions) + const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled) const lastSubmittedTime = submitted ? new Date().toISOString() : null @@ -234,14 +236,13 @@ export const Upload: React.FC = (props) => { {(value || doc.filename) && ( { // Check api response expect(doc.mimeType).toEqual('image/png') + expect(doc.focalX).toEqual(50) + expect(doc.focalY).toEqual(50) expect(sizes.maintainedAspectRatio.url).toContain('/api/media/file/image') expect(sizes.maintainedAspectRatio.url).toContain('.png') expect(sizes.maintainedAspectRatio.width).toEqual(1024) @@ -286,7 +290,6 @@ describe('Collections - Uploads', () => { expect(await fileExists(path.join(expectedPath, mediaDoc.sizes.icon.filename))).toBe(false) }) }) - describe('delete', () => { it('should remove related files when deleting by ID', async () => { const formData = new FormData() @@ -527,6 +530,88 @@ describe('Collections - Uploads', () => { }) }) + describe('focal point', () => { + let file + + beforeAll(async () => { + // Create image + const filePath = path.resolve(dirname, './image.png') + file = await getFileByPath(filePath) + file.name = 'focal.png' + }) + + it('should be able to set focal point through local API', async () => { + const doc = await payload.create({ + collection: focalOnlySlug, + data: { + focalX: 5, + focalY: 5, + }, + file, + }) + + expect(doc.focalX).toEqual(5) + expect(doc.focalY).toEqual(5) + + const updatedFocal = await payload.update({ + collection: focalOnlySlug, + id: doc.id, + data: { + focalX: 10, + focalY: 10, + }, + }) + + expect(updatedFocal.focalX).toEqual(10) + expect(updatedFocal.focalY).toEqual(10) + + const updateWithoutFocal = await payload.update({ + collection: focalOnlySlug, + id: doc.id, + data: {}, + }) + + // Expect focal point to be the same + expect(updateWithoutFocal.focalX).toEqual(10) + expect(updateWithoutFocal.focalY).toEqual(10) + }) + + it('should default focal point to 50, 50', async () => { + const doc = await payload.create({ + collection: focalOnlySlug, + data: { + // No focal point + }, + file, + }) + + expect(doc.focalX).toEqual(50) + expect(doc.focalY).toEqual(50) + + const updateWithoutFocal = await payload.update({ + collection: focalOnlySlug, + id: doc.id, + data: {}, + }) + + expect(updateWithoutFocal.focalX).toEqual(50) + expect(updateWithoutFocal.focalY).toEqual(50) + }) + + it('should set focal point even if no sizes defined', async () => { + const doc = await payload.create({ + collection: focalNoSizesSlug, // config without sizes + data: { + // No focal point + }, + file, + }) + + expect(doc.focalX).toEqual(50) + expect(doc.focalY).toEqual(50) + }) + }) + describe('Image Manipulation', () => { it('should enlarge images if resize options `withoutEnlargement` is set to false', async () => { const small = await getFileByPath(path.resolve(dirname, './small.png')) diff --git a/test/uploads/payload-types.ts b/test/uploads/payload-types.ts index ac68fdf43..ac96f94b0 100644 --- a/test/uploads/payload-types.ts +++ b/test/uploads/payload-types.ts @@ -15,6 +15,7 @@ export interface Config { 'object-fit': ObjectFit; 'crop-only': CropOnly; 'focal-only': FocalOnly; + 'focal-no-sizes': FocalNoSize; media: Media; enlarge: Enlarge; reduce: Reduce; @@ -64,6 +65,8 @@ export interface Media { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { maintainedAspectRatio?: { url?: string | null; @@ -204,6 +207,8 @@ export interface Version { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -230,6 +235,8 @@ export interface GifResize { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { small?: { url?: string | null; @@ -264,6 +271,8 @@ export interface NoImageSize { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -280,6 +289,8 @@ export interface ObjectFit { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { fitContain?: { url?: string | null; @@ -330,6 +341,8 @@ export interface CropOnly { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { focalTest?: { url?: string | null; @@ -372,6 +385,8 @@ export interface FocalOnly { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { focalTest?: { url?: string | null; @@ -399,6 +414,24 @@ export interface FocalOnly { }; }; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "focal-no-sizes". + */ +export interface FocalNoSize { + id: string; + updatedAt: string; + createdAt: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "enlarge". @@ -414,6 +447,8 @@ export interface Enlarge { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { accidentalSameSize?: { url?: string | null; @@ -472,6 +507,8 @@ export interface Reduce { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { accidentalSameSize?: { url?: string | null; @@ -522,6 +559,8 @@ export interface MediaTrim { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { trimNumber?: { url?: string | null; @@ -564,6 +603,8 @@ export interface UnstoredMedia { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -580,6 +621,8 @@ export interface ExternallyServedMedia { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -612,6 +655,8 @@ export interface Uploads1 { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -629,6 +674,8 @@ export interface Uploads2 { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -645,6 +692,8 @@ export interface AdminThumbnailFunction { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -661,6 +710,8 @@ export interface AdminThumbnailSize { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; sizes?: { small?: { url?: string | null; @@ -695,6 +746,8 @@ export interface OptionalFile { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -711,6 +764,8 @@ export interface RequiredFile { filesize?: number | null; width?: number | null; height?: number | null; + focalX?: number | null; + focalY?: number | null; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/test/uploads/shared.ts b/test/uploads/shared.ts index 301f4bb75..890422aed 100644 --- a/test/uploads/shared.ts +++ b/test/uploads/shared.ts @@ -1,19 +1,12 @@ export const usersSlug = 'users' - export const mediaSlug = 'media' - export const relationSlug = 'relation' - export const audioSlug = 'audio' - export const enlargeSlug = 'enlarge' - +export const focalNoSizesSlug = 'focal-no-sizes' +export const focalOnlySlug = 'focal-only' export const reduceSlug = 'reduce' - export const adminThumbnailFunctionSlug = 'admin-thumbnail-function' - export const adminThumbnailSizeSlug = 'admin-thumbnail-size' - export const unstoredMediaSlug = 'unstored-media' - export const versionSlug = 'versions'