Skip to content

Instantly share code, notes, and snippets.

@tsemachh
Created September 1, 2024 08:41
Show Gist options
  • Save tsemachh/803ab2a117d1ea1d6ce83cf1423cfd7f to your computer and use it in GitHub Desktop.
Save tsemachh/803ab2a117d1ea1d6ce83cf1423cfd7f to your computer and use it in GitHub Desktop.
Compound Relation View in Edit View Component
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { EditView } from '@payloadcms/next/views'
import React from 'react'
import { DocumentDrawerContent } from './DocumentView'
const CustomDefaultEditView: PayloadServerReactComponent<EditViewComponent> = () => {
return (
<div style={{ display: 'flex' }}>
<div style={{ width: '50%' }}>
<EditView />
</div>
<div style={{ border: '1px solid' }}>
<DocumentDrawerContent
collectionSlug={'individualcontent'}
id={'66aa0d24bff0d3e456f6790b'}
/>
</div>
</div>
)
}
export default CustomDefaultEditView
'use client'
import React, { useCallback, useEffect, useState } from 'react'
import { toast } from 'sonner'
import { XIcon } from '@payloadcms/ui/icons/X'
import { RenderComponent } from '@payloadcms/ui'
import { useConfig } from '@payloadcms/ui'
import { DocumentInfoProvider, useDocumentInfo } from '@payloadcms/ui'
import { useLocale } from '@payloadcms/ui'
import { useTranslation } from '@payloadcms/ui'
import { useRelatedCollections } from './useRelatedCollections'
import { Gutter } from '@payloadcms/ui'
import { IDLabel } from '@payloadcms/ui/elements/IDLabel'
import { RenderTitle } from '@payloadcms/ui/elements/RenderTitle'
import { DocumentInfoContext } from '@payloadcms/ui'
export type DrawerProps = {
readonly Header?: React.ReactNode
readonly children: React.ReactNode
readonly className?: string
readonly gutter?: boolean
readonly hoverTitle?: boolean
readonly slug: string
readonly title?: string
}
export type DocumentDrawerProps = {
readonly collectionSlug: string
readonly drawerSlug?: string
readonly id?: null | number | string
readonly onSave?: DocumentInfoContext['onSave']
} & Pick<DrawerProps, 'Header'>
const baseClass = 'doc-drawer'
export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
id: existingDocID,
Header,
collectionSlug,
drawerSlug,
onSave: onSaveFromProps,
}) => {
const { config } = useConfig()
const {
routes: { api: apiRoute },
serverURL,
} = config
const locale = useLocale()
const { t } = useTranslation()
const [docID, setDocID] = useState(existingDocID)
const [isOpen] = useState(false)
const [collectionConfig] = useRelatedCollections(collectionSlug)
const Edit = collectionConfig.admin.components.views.edit.default.Component
const isEditing = Boolean(docID)
const apiURL = docID
? `${serverURL}${apiRoute}/${collectionSlug}/${docID}${
locale?.code ? `?locale=${locale.code}` : ''
}`
: null
const onLoadError = React.useCallback(() => {
if (isOpen) {
toast.error(data.errors?.[0].message || t('error:unspecific'))
}
}, [isOpen, t])
const onSave = useCallback<DocumentDrawerProps['onSave']>(
(args) => {
setDocID(args.doc.id)
if (typeof onSaveFromProps === 'function') {
void onSaveFromProps({
...args,
collectionConfig,
})
}
},
[onSaveFromProps, collectionConfig],
)
return (
<DocumentInfoProvider
BeforeDocument={
<Gutter className={`${baseClass}__header`}>
<div className={`${baseClass}__header-content`}>
<h2 className={`${baseClass}__header-text`}>
{Header || <RenderTitle element="span" />}
</h2>
{/* TODO: the `button` HTML element breaks CSS transitions on the drawer for some reason...
i.e. changing to a `div` element will fix the animation issue but will break accessibility
*/}
<button
aria-label={t('general:close')}
className={`${baseClass}__header-close`}
onClick={() => closePanel()}
type="button"
>
<XIcon />
</button>
</div>
<DocumentTitle />
</Gutter>
}
apiURL={apiURL}
collectionSlug={collectionConfig.slug}
disableActions
disableLeaveWithoutSaving
id={docID}
isEditing={isEditing}
onLoadError={onLoadError}
onSave={onSave}
>
<RenderComponent mappedComponent={Edit} />
</DocumentInfoProvider>
)
}
const DocumentTitle: React.FC = () => {
const { id, title } = useDocumentInfo()
return id && id !== title ? <IDLabel id={id.toString()} /> : null
}
'use client'
import { useConfig } from '@payloadcms/ui';
import type { ClientCollectionConfig } from 'payload'
import { useState } from 'react'
export const useRelatedCollections = (relationTo: string | string[]): ClientCollectionConfig[] => {
const { config } = useConfig()
const [relatedCollections] = useState(() => {
if (relationTo) {
const relations = typeof relationTo === 'string' ? [relationTo] : relationTo
return relations.map((relation) =>
config.collections.find((collection) => collection.slug === relation),
)
}
return []
})
return relatedCollections
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment