Skip to content

Commit

Permalink
feat: improves crop rendering in thumbnail (#6260)
Browse files Browse the repository at this point in the history
  • Loading branch information
JarrodMFlesch committed May 8, 2024
1 parent 15c7a9d commit a7ac5ef
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 31 deletions.
70 changes: 41 additions & 29 deletions packages/ui/src/elements/EditUpload/index.tsx
Expand Up @@ -9,7 +9,6 @@ import 'react-image-crop/dist/ReactCrop.css'

import { editDrawerSlug } from '../../elements/Upload/index.js'
import { Plus } from '../../icons/Plus/index.js'
import { useFormQueryParams } from '../../providers/FormQueryParams/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { Button } from '../Button/index.js'
import './index.scss'
Expand All @@ -27,40 +26,60 @@ const Input: React.FC<{ name: string; onChange: (value: string) => void; value:
</div>
)

type FocalPosition = {
x: number
y: number
}

export type EditUploadProps = {
doc?: Data
fileName: string
fileSrc: string
imageCacheTag?: string
initialCrop?: CropType
initialFocalPoint?: FocalPosition
onSave?: ({ crop, pointPosition }: { crop: CropType; pointPosition: FocalPosition }) => void
showCrop?: boolean
showFocalPoint?: boolean
}

const defaultCrop: CropType = {
height: 100,
unit: '%',
width: 100,
x: 0,
y: 0,
}

const defaultPointPosition: FocalPosition = {
x: 50,
y: 50,
}

export const EditUpload: React.FC<EditUploadProps> = ({
fileName,
fileSrc,
imageCacheTag,
initialCrop,
initialFocalPoint,
onSave,
showCrop,
showFocalPoint,
}) => {
const { Modal, useModal } = facelessUIImport
const { useModal } = facelessUIImport

const { closeModal } = useModal()
const { t } = useTranslation()
const { dispatchFormQueryParams, formQueryParams } = useFormQueryParams()
const { uploadEdits } = formQueryParams || {}
const [crop, setCrop] = useState<CropType>({
height: uploadEdits?.crop?.height || 100,
unit: '%',
width: uploadEdits?.crop?.width || 100,
x: uploadEdits?.crop?.x || 0,
y: uploadEdits?.crop?.y || 0,
})

const [pointPosition, setPointPosition] = useState<{ x: number; y: number }>({
x: uploadEdits?.focalPoint?.x || 50,
y: uploadEdits?.focalPoint?.y || 50,
})

const [crop, setCrop] = useState<CropType>(() => ({
...defaultCrop,
...initialCrop,
}))

const [pointPosition, setPointPosition] = useState<FocalPosition>(() => ({
...defaultPointPosition,
...initialFocalPoint,
}))
const [checkBounds, setCheckBounds] = useState<boolean>(false)
const [originalHeight, setOriginalHeight] = useState<number>(0)
const [originalWidth, setOriginalWidth] = useState<number>(0)
Expand Down Expand Up @@ -92,18 +111,11 @@ export const EditUpload: React.FC<EditUploadProps> = ({
}

const saveEdits = () => {
dispatchFormQueryParams({
type: 'SET',
params: {
uploadEdits:
crop || pointPosition
? {
crop: crop || null,
focalPoint: pointPosition ? pointPosition : null,
}
: null,
},
})
if (typeof onSave === 'function')
onSave({
crop,
pointPosition,
})
closeModal(editDrawerSlug)
}

Expand Down Expand Up @@ -145,7 +157,7 @@ export const EditUpload: React.FC<EditUploadProps> = ({
aria-label={t('general:applyChanges')}
buttonStyle="primary"
className={`${baseClass}__save`}
onClick={() => saveEdits()}
onClick={saveEdits}
>
{t('general:applyChanges')}
</Button>
Expand Down
13 changes: 12 additions & 1 deletion packages/ui/src/elements/Upload/index.scss
Expand Up @@ -23,13 +23,24 @@
width: 150px;

.thumbnail {
position: absolute;
position: relative;
width: 100%;
height: 100%;
object-fit: contain;
}
}

.file-details {
img {
position: relative;
min-width: 100%;
height: 100%;
transform: scale(var(--file-details-thumbnail--zoom));
top: var(--file-details-thumbnail--top-offset);
left: var(--file-details-thumbnail--left-offset);
}
}

&__remove {
margin: calc($baseline * 1.5) $baseline $baseline 0;
place-self: flex-start;
Expand Down
46 changes: 45 additions & 1 deletion packages/ui/src/elements/Upload/index.tsx
Expand Up @@ -2,11 +2,12 @@
import type { FormState, SanitizedCollectionConfig } from 'payload/types'

import { FieldError } from '@payloadcms/ui/forms/FieldError'
import { useFormQueryParams } from '@payloadcms/ui/providers/FormQueryParams'
import { isImage } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'

import { fieldBaseClass } from '../../fields/shared/index.js'
import { useFormSubmitted } from '../../forms/Form/context.js'
import { useForm, useFormSubmitted } from '../../forms/Form/context.js'
import { useField } from '../../forms/useField/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
Expand Down Expand Up @@ -65,12 +66,15 @@ export const Upload: React.FC<UploadProps> = (props) => {
const [replacingFile, setReplacingFile] = useState(false)
const [fileSrc, setFileSrc] = useState<null | string>(null)
const { t } = useTranslation()
const { setModified } = useForm()
const { dispatchFormQueryParams, formQueryParams } = useFormQueryParams()
const [doc, setDoc] = useState(reduceFieldsToValues(initialState || {}, true))
const { docPermissions } = useDocumentInfo()
const { errorMessage, setValue, showError, value } = useField<File>({
path: 'file',
validate,
})
const [crop, setCrop] = useState({ x: 0, y: 0 })

const handleFileNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const updatedFileName = e.target.value
Expand All @@ -96,6 +100,40 @@ export const Upload: React.FC<UploadProps> = (props) => {
setFileSrc('')
}, [setValue])

const onEditsSave = React.useCallback(
({ crop, pointPosition }) => {
setCrop({
x: crop.x || 0,
y: crop.y || 0,
})
const zoomScale = 100 / Math.min(crop.width, crop.height)

document.documentElement.style.setProperty('--file-details-thumbnail--zoom', `${zoomScale}`)
document.documentElement.style.setProperty(
'--file-details-thumbnail--top-offset',
`${zoomScale * (50 - crop.height / 2 - crop.y)}%`,
)
document.documentElement.style.setProperty(
'--file-details-thumbnail--left-offset',
`${zoomScale * (50 - crop.width / 2 - crop.x)}%`,
)
setModified(true)
dispatchFormQueryParams({
type: 'SET',
params: {
uploadEdits:
crop || pointPosition
? {
crop: crop || null,
focalPoint: pointPosition ? pointPosition : null,
}
: null,
},
})
},
[dispatchFormQueryParams, setModified],
)

useEffect(() => {
setDoc(reduceFieldsToValues(initialState || {}, true))
setReplacingFile(false)
Expand Down Expand Up @@ -200,6 +238,12 @@ export const Upload: React.FC<UploadProps> = (props) => {
fileName={value?.name || doc?.filename}
fileSrc={fileSrc || doc?.url}
imageCacheTag={lastSubmittedTime}
initialCrop={formQueryParams?.uploadEdits?.crop ?? {}}
initialFocalPoint={{
x: formQueryParams?.uploadEdits?.focalPoint.x || 0,
y: formQueryParams?.uploadEdits?.focalPoint.y || 0,
}}
onSave={onEditsSave}
showCrop={showCrop}
showFocalPoint={showFocalPoint}
/>
Expand Down

0 comments on commit a7ac5ef

Please sign in to comment.