This noticeably improves performance in the admin panel, for example
when there are multiple richtext editors on one page (& likely
performance in other areas too, though I mainly tested rich text).
The babel plugin currently only optimizes files with a 'use client'
directive at the top - thus we have to make sure to add use client
wherever possible, even if it's imported by a parent client component.
There's one single component that broke when it was compiled using the
React compiler (it stopped being reactive and failed one of our admin
e2e tests):
150808f608
opting out of it completely fixed that issue
Fixes https://github.com/payloadcms/payload/issues/7366
114 lines
3.3 KiB
TypeScript
114 lines
3.3 KiB
TypeScript
'use client'
|
|
import type { SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode.js'
|
|
import type { DOMConversionMap, DOMConversionOutput, LexicalNode, Spread } from 'lexical'
|
|
import type { JSX } from 'react'
|
|
|
|
import ObjectID from 'bson-objectid'
|
|
import { $applyNodeReplacement } from 'lexical'
|
|
import * as React from 'react'
|
|
|
|
import type { UploadData } from '../../server/nodes/UploadNode.js'
|
|
|
|
import { UploadServerNode, isGoogleDocCheckboxImg } from '../../server/nodes/UploadNode.js'
|
|
|
|
const RawUploadComponent = React.lazy(() =>
|
|
import('../../client/component/index.js').then((module) => ({ default: module.UploadComponent })),
|
|
)
|
|
|
|
function $convertUploadElement(domNode: HTMLImageElement): DOMConversionOutput | null {
|
|
if (
|
|
domNode.hasAttribute('data-lexical-upload-relation-to') &&
|
|
domNode.hasAttribute('data-lexical-upload-id')
|
|
) {
|
|
const id = domNode.getAttribute('data-lexical-upload-id')
|
|
const relationTo = domNode.getAttribute('data-lexical-upload-relation-to')
|
|
|
|
if (id != null && relationTo != null) {
|
|
const node = $createUploadNode({
|
|
data: {
|
|
fields: {},
|
|
relationTo,
|
|
value: id,
|
|
},
|
|
})
|
|
return { node }
|
|
}
|
|
}
|
|
const img = domNode
|
|
if (img.src.startsWith('file:///') || isGoogleDocCheckboxImg(img)) {
|
|
return null
|
|
}
|
|
// TODO: Auto-upload functionality here!
|
|
//}
|
|
return null
|
|
}
|
|
|
|
export type SerializedUploadNode = {
|
|
children?: never // required so that our typed editor state doesn't automatically add children
|
|
type: 'upload'
|
|
} & Spread<UploadData, SerializedDecoratorBlockNode>
|
|
|
|
export class UploadNode extends UploadServerNode {
|
|
static clone(node: UploadServerNode): UploadServerNode {
|
|
return super.clone(node)
|
|
}
|
|
|
|
static getType(): string {
|
|
return super.getType()
|
|
}
|
|
|
|
static importDOM(): DOMConversionMap | null {
|
|
return {
|
|
img: (node: HTMLImageElement) => ({
|
|
conversion: $convertUploadElement,
|
|
priority: 0,
|
|
}),
|
|
}
|
|
}
|
|
|
|
static importJSON(serializedNode: SerializedUploadNode): UploadNode {
|
|
if (serializedNode.version === 1 && (serializedNode?.value as unknown as { id: string })?.id) {
|
|
serializedNode.value = (serializedNode.value as unknown as { id: string }).id
|
|
}
|
|
if (serializedNode.version === 2 && !serializedNode?.id) {
|
|
serializedNode.id = new ObjectID.default().toHexString()
|
|
serializedNode.version = 3
|
|
}
|
|
|
|
const importedData: UploadData = {
|
|
id: serializedNode.id,
|
|
fields: serializedNode.fields,
|
|
relationTo: serializedNode.relationTo,
|
|
value: serializedNode.value,
|
|
}
|
|
|
|
const node = $createUploadNode({ data: importedData })
|
|
node.setFormat(serializedNode.format)
|
|
|
|
return node
|
|
}
|
|
|
|
decorate(): JSX.Element {
|
|
return <RawUploadComponent data={this.__data} nodeKey={this.getKey()} />
|
|
}
|
|
|
|
exportJSON(): SerializedUploadNode {
|
|
return super.exportJSON()
|
|
}
|
|
}
|
|
|
|
export function $createUploadNode({
|
|
data,
|
|
}: {
|
|
data: Omit<UploadData, 'id'> & Partial<Pick<UploadData, 'id'>>
|
|
}): UploadNode {
|
|
if (!data?.id) {
|
|
data.id = new ObjectID.default().toHexString()
|
|
}
|
|
return $applyNodeReplacement(new UploadNode({ data: data as UploadData }))
|
|
}
|
|
|
|
export function $isUploadNode(node: LexicalNode | null | undefined): node is UploadNode {
|
|
return node instanceof UploadNode
|
|
}
|