feat: ability to pass uploadActions to the Upload component (#6941)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user