feat: ability to pass uploadActions to the Upload component (#6941)

This commit is contained in:
Jarrod Flesch
2024-06-26 13:20:54 -04:00
committed by GitHub
parent f47d6cb23c
commit 35eb16bbec
5 changed files with 90 additions and 29 deletions

View File

@@ -1,5 +1,4 @@
'use client' 'use client'
import { isImage } from 'payload/shared'
import React from 'react' import React from 'react'
import { UploadActions } from '../../elements/Upload/index.js' import { UploadActions } from '../../elements/Upload/index.js'
@@ -13,11 +12,12 @@ const baseClass = 'file-details'
import type { Data, FileSizes, SanitizedCollectionConfig } from 'payload' import type { Data, FileSizes, SanitizedCollectionConfig } from 'payload'
export type FileDetailsProps = { export type FileDetailsProps = {
canEdit?: boolean
collectionSlug: string collectionSlug: string
customUploadActions?: React.ReactNode[]
doc: Data & { doc: Data & {
sizes?: FileSizes sizes?: FileSizes
} }
enableAdjustments?: boolean
handleRemove?: () => void handleRemove?: () => void
hasImageSizes?: boolean hasImageSizes?: boolean
imageCacheTag?: string imageCacheTag?: string
@@ -25,8 +25,16 @@ export type FileDetailsProps = {
} }
export const FileDetails: React.FC<FileDetailsProps> = (props) => { export const FileDetails: React.FC<FileDetailsProps> = (props) => {
const { canEdit, collectionSlug, doc, handleRemove, hasImageSizes, imageCacheTag, uploadConfig } = const {
props collectionSlug,
customUploadActions,
doc,
enableAdjustments,
handleRemove,
hasImageSizes,
imageCacheTag,
uploadConfig,
} = props
const { id, filename, filesize, height, mimeType, thumbnailURL, url, width } = doc const { id, filename, filesize, height, mimeType, thumbnailURL, url, width } = doc
@@ -52,9 +60,12 @@ export const FileDetails: React.FC<FileDetailsProps> = (props) => {
width={width as number} width={width as number}
/> />
{isImage(mimeType as string) && mimeType !== 'image/svg+xml' && ( <UploadActions
<UploadActions canEdit={canEdit} showSizePreviews={hasImageSizes && doc.filename} /> customActions={customUploadActions}
)} enableAdjustments={enableAdjustments}
enablePreviewSizes={hasImageSizes && doc.filename}
mimeType={mimeType}
/>
</div> </div>
{handleRemove && ( {handleRemove && (
<Button <Button

View File

@@ -48,7 +48,7 @@
background-color: var(--theme-bg); background-color: var(--theme-bg);
} }
&__file-mutation { &__upload-actions {
display: flex; display: flex;
gap: calc(var(--base) / 2); gap: calc(var(--base) / 2);
flex-wrap: wrap; flex-wrap: wrap;

View File

@@ -33,33 +33,60 @@ const validate = (value) => {
return true return true
} }
export const UploadActions = ({ canEdit, showSizePreviews }) => { type UploadActionsArgs = {
customActions?: React.ReactNode[]
enableAdjustments: boolean
enablePreviewSizes: boolean
mimeType: string
}
export const UploadActions = ({
customActions,
enableAdjustments,
enablePreviewSizes,
mimeType,
}: UploadActionsArgs) => {
const { t } = useTranslation() const { t } = useTranslation()
const fileTypeIsAdjustable = isImage(mimeType) && mimeType !== 'image/svg+xml'
if (!fileTypeIsAdjustable && (!customActions || customActions.length === 0)) return null
return ( return (
<div className={`${baseClass}__file-mutation`}> <div className={`${baseClass}__upload-actions`}>
{showSizePreviews && ( {fileTypeIsAdjustable && (
<DrawerToggler className={`${baseClass}__previewSizes`} slug={sizePreviewSlug}> <React.Fragment>
{t('upload:previewSizes')} {enablePreviewSizes && (
</DrawerToggler> <DrawerToggler className={`${baseClass}__previewSizes`} slug={sizePreviewSlug}>
)} {t('upload:previewSizes')}
{canEdit && ( </DrawerToggler>
<DrawerToggler className={`${baseClass}__edit`} slug={editDrawerSlug}> )}
{t('upload:editImage')} {enableAdjustments && (
</DrawerToggler> <DrawerToggler className={`${baseClass}__edit`} slug={editDrawerSlug}>
{t('upload:editImage')}
</DrawerToggler>
)}
</React.Fragment>
)} )}
{customActions &&
customActions.map((CustomAction, i) => {
return <React.Fragment key={i}>{CustomAction}</React.Fragment>
})}
</div> </div>
) )
} }
export type UploadProps = { export type UploadProps = {
collectionSlug: string collectionSlug: string
customActions?: React.ReactNode[]
initialState?: FormState initialState?: FormState
onChange?: (file?: File) => void onChange?: (file?: File) => void
uploadConfig: SanitizedCollectionConfig['upload'] uploadConfig: SanitizedCollectionConfig['upload']
} }
export const Upload: React.FC<UploadProps> = (props) => { export const Upload: React.FC<UploadProps> = (props) => {
const { collectionSlug, initialState, onChange, uploadConfig } = props const { collectionSlug, customActions, initialState, onChange, uploadConfig } = props
const [replacingFile, setReplacingFile] = useState(false) const [replacingFile, setReplacingFile] = useState(false)
const [fileSrc, setFileSrc] = useState<null | string>(null) const [fileSrc, setFileSrc] = useState<null | string>(null)
@@ -169,9 +196,9 @@ export const Upload: React.FC<UploadProps> = (props) => {
<FieldError message={errorMessage} showError={showError} /> <FieldError message={errorMessage} showError={showError} />
{doc.filename && !replacingFile && ( {doc.filename && !replacingFile && (
<FileDetails <FileDetails
canEdit={showCrop || showFocalPoint}
collectionSlug={collectionSlug} collectionSlug={collectionSlug}
doc={doc} doc={doc}
enableAdjustments={showCrop || showFocalPoint}
handleRemove={canRemoveUpload ? handleFileRemoval : undefined} handleRemove={canRemoveUpload ? handleFileRemoval : undefined}
hasImageSizes={hasImageSizes} hasImageSizes={hasImageSizes}
imageCacheTag={doc.updatedAt} imageCacheTag={doc.updatedAt}
@@ -203,13 +230,12 @@ export const Upload: React.FC<UploadProps> = (props) => {
type="text" type="text"
value={value.name} value={value.name}
/> />
<UploadActions
{isImage(value.type) && value.type !== 'image/svg+xml' && ( customActions={customActions}
<UploadActions enableAdjustments={showCrop || showFocalPoint}
canEdit={showCrop || showFocalPoint} enablePreviewSizes={hasImageSizes && doc.filename && !replacingFile}
showSizePreviews={hasImageSizes && doc.filename && !replacingFile} mimeType={value.type}
/> />
)}
</div> </div>
<Button <Button
buttonStyle="icon-label" buttonStyle="icon-label"

View File

@@ -25,6 +25,7 @@ const baseClass = 'upload'
export type UploadInputProps = Omit<UploadFieldProps, 'filterOptions'> & { export type UploadInputProps = Omit<UploadFieldProps, 'filterOptions'> & {
api?: string api?: string
collection?: ClientCollectionConfig collection?: ClientCollectionConfig
customUploadActions?: React.ReactNode[]
filterOptions?: FilterOptionsResult filterOptions?: FilterOptionsResult
onChange?: (e) => void onChange?: (e) => void
relationTo?: UploadField['relationTo'] relationTo?: UploadField['relationTo']
@@ -41,6 +42,7 @@ export const UploadInput: React.FC<UploadInputProps> = (props) => {
api = '/api', api = '/api',
className, className,
collection, collection,
customUploadActions,
descriptionProps, descriptionProps,
errorProps, errorProps,
filterOptions, filterOptions,
@@ -147,6 +149,7 @@ export const UploadInput: React.FC<UploadInputProps> = (props) => {
{fileDoc && !missingFile && ( {fileDoc && !missingFile && (
<FileDetails <FileDetails
collectionSlug={relationTo} collectionSlug={relationTo}
customUploadActions={customUploadActions}
doc={fileDoc} doc={fileDoc}
handleRemove={ handleRemove={
readOnly readOnly

View File

@@ -1,8 +1,28 @@
'use client' 'use client'
import { Upload, useDocumentInfo } from '@payloadcms/ui' import { Drawer, DrawerToggler, TextField, Upload, useDocumentInfo } from '@payloadcms/ui'
import React from 'react' import React from 'react'
const customDrawerSlug = 'custom-upload-drawer'
const CustomDrawer = () => {
return (
<Drawer slug={customDrawerSlug}>
<h1>Custom Drawer</h1>
<TextField name="alt" path="alt" />
</Drawer>
)
}
const CustomDrawerToggler = () => {
return (
<React.Fragment>
<DrawerToggler slug={customDrawerSlug}>Custom Drawer</DrawerToggler>
<CustomDrawer />
</React.Fragment>
)
}
export const CustomUploadClient = () => { export const CustomUploadClient = () => {
const { collectionSlug, docConfig, initialState } = useDocumentInfo() const { collectionSlug, docConfig, initialState } = useDocumentInfo()
@@ -11,6 +31,7 @@ export const CustomUploadClient = () => {
<h3>This text was rendered on the client</h3> <h3>This text was rendered on the client</h3>
<Upload <Upload
collectionSlug={collectionSlug} collectionSlug={collectionSlug}
customActions={[<CustomDrawerToggler />]}
initialState={initialState} initialState={initialState}
uploadConfig={'upload' in docConfig ? docConfig.upload : undefined} uploadConfig={'upload' in docConfig ? docConfig.upload : undefined}
/> />