chore(richtext-lexical): adjust node data formats, adjust population behavior, fix some validation issues
This commit is contained in:
@@ -314,7 +314,7 @@ export const upload: Validate<unknown, unknown, UploadField> = (value: string, o
|
||||
}
|
||||
|
||||
if (!canUseDOM && typeof value !== 'undefined' && value !== null) {
|
||||
const idField = options.payload.collections[options.relationTo].config.fields.find(
|
||||
const idField = options?.payload?.collections[options.relationTo]?.config?.fields?.find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
const type = getIDType(idField, options?.payload?.db?.defaultIDType)
|
||||
|
||||
@@ -18,6 +18,7 @@ export const blockAfterReadPromiseHOC = (
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}) => {
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
@@ -47,6 +48,7 @@ export const blockAfterReadPromiseHOC = (
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,16 +17,17 @@ export const linkAfterReadPromiseHOC = (
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}) => {
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
if (node?.fields?.doc?.value && node?.fields?.doc?.relationTo) {
|
||||
if (node?.fields?.doc?.value?.id && node?.fields?.doc?.relationTo) {
|
||||
const collection = req.payload.collections[node?.fields?.doc?.relationTo]
|
||||
|
||||
if (collection) {
|
||||
promises.push(
|
||||
populate({
|
||||
id: node?.fields?.doc.value,
|
||||
id: node?.fields?.doc?.value?.id,
|
||||
collection,
|
||||
currentDepth,
|
||||
data: node?.fields?.doc,
|
||||
@@ -51,6 +52,7 @@ export const linkAfterReadPromiseHOC = (
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
return promises
|
||||
|
||||
@@ -35,9 +35,12 @@ export type LinkFields = {
|
||||
// unknown, custom fields:
|
||||
[key: string]: unknown
|
||||
doc: {
|
||||
data?: any // Will be populated in afterRead hook
|
||||
relationTo: string
|
||||
value: string
|
||||
value: {
|
||||
// Actual doc data, populated in afterRead hook
|
||||
[key: string]: unknown
|
||||
id: string
|
||||
}
|
||||
} | null
|
||||
linkType: 'custom' | 'internal'
|
||||
newTab: boolean
|
||||
@@ -124,23 +127,10 @@ export class LinkNode extends ElementNode {
|
||||
element.target = '_blank'
|
||||
}
|
||||
|
||||
element.rel = ''
|
||||
|
||||
if (this.__fields?.newTab === true && this.__fields?.linkType === 'custom') {
|
||||
element.rel = manageRel(element.rel, 'add', 'noopener')
|
||||
}
|
||||
|
||||
if (this.__fields?.sponsored ?? false) {
|
||||
element.rel = manageRel(element.rel, 'add', 'sponsored')
|
||||
}
|
||||
|
||||
if (this.__fields?.nofollow ?? false) {
|
||||
element.rel = manageRel(element.rel, 'add', 'nofollow')
|
||||
}
|
||||
|
||||
if (this.__fields?.rel !== null) {
|
||||
element.rel += ` ${this.__rel}`
|
||||
}
|
||||
addClassNamesToElement(element, config.theme.link)
|
||||
return element
|
||||
}
|
||||
@@ -212,9 +202,6 @@ export class LinkNode extends ElementNode {
|
||||
updateDOM(prevNode: LinkNode, anchor: HTMLAnchorElement, config: EditorConfig): boolean {
|
||||
const url = this.__fields?.url
|
||||
const newTab = this.__fields?.newTab
|
||||
const sponsored = this.__fields?.sponsored
|
||||
const nofollow = this.__fields?.nofollow
|
||||
const rel = this.__fields?.rel
|
||||
if (url != null && url !== prevNode.__fields?.url && this.__fields?.linkType === 'custom') {
|
||||
anchor.href = url
|
||||
}
|
||||
@@ -240,33 +227,6 @@ export class LinkNode extends ElementNode {
|
||||
}
|
||||
}
|
||||
|
||||
if (nofollow !== prevNode.__fields.nofollow) {
|
||||
if (nofollow ?? false) {
|
||||
anchor.rel = manageRel(anchor.rel, 'add', 'nofollow')
|
||||
} else {
|
||||
anchor.rel = manageRel(anchor.rel, 'remove', 'nofollow')
|
||||
}
|
||||
}
|
||||
|
||||
if (sponsored !== prevNode.__fields.sponsored) {
|
||||
if (sponsored ?? false) {
|
||||
anchor.rel = manageRel(anchor.rel, 'add', 'sponsored')
|
||||
} else {
|
||||
anchor.rel = manageRel(anchor.rel, 'remove', 'sponsored')
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - revisit - I don't think there can be any other rel
|
||||
// values other than nofollow and noopener - so not
|
||||
// sure why anchor.rel += rel below
|
||||
if (rel !== prevNode.__fields.rel) {
|
||||
if (rel != null) {
|
||||
anchor.rel += rel
|
||||
} else {
|
||||
anchor.removeAttribute('rel')
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -281,9 +241,6 @@ function convertAnchorElement(domNode: Node): DOMConversionOutput {
|
||||
doc: null,
|
||||
linkType: 'custom',
|
||||
newTab: domNode.getAttribute('target') === '_blank',
|
||||
nofollow: domNode.getAttribute('rel')?.includes('nofollow') ?? false,
|
||||
rel: domNode.getAttribute('rel'),
|
||||
sponsored: domNode.getAttribute('rel')?.includes('sponsored') ?? false,
|
||||
url: domNode.getAttribute('href') ?? '',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -109,8 +109,6 @@ export function LinkEditor({
|
||||
doc: undefined,
|
||||
linkType: undefined,
|
||||
newTab: undefined,
|
||||
nofollow: undefined,
|
||||
sponsored: undefined,
|
||||
url: '',
|
||||
...linkParent.getFields(),
|
||||
},
|
||||
@@ -124,7 +122,7 @@ export function LinkEditor({
|
||||
// internal link
|
||||
setLinkUrl(
|
||||
`/admin/collections/${linkParent.getFields()?.doc?.relationTo}/${linkParent.getFields()
|
||||
?.doc?.value}`,
|
||||
?.doc?.value?.id}`,
|
||||
)
|
||||
|
||||
const relatedField = config.collections.find(
|
||||
@@ -321,6 +319,12 @@ export function LinkEditor({
|
||||
|
||||
const data = reduceFieldsToValues(fields, true)
|
||||
|
||||
if (data?.fields?.doc?.value) {
|
||||
data.fields.doc.value = {
|
||||
id: data.fields.doc.value,
|
||||
}
|
||||
}
|
||||
|
||||
const newLinkPayload: LinkPayload = data as LinkPayload
|
||||
|
||||
editor.dispatchCommand(TOGGLE_LINK_COMMAND, newLinkPayload)
|
||||
|
||||
@@ -14,19 +14,19 @@ export const relationshipAfterReadPromise: AfterReadPromise<SerializedRelationsh
|
||||
}) => {
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
if (node?.fields?.id) {
|
||||
const collection = req.payload.collections[node?.fields?.relationTo]
|
||||
if (node?.value?.id) {
|
||||
const collection = req.payload.collections[node?.relationTo]
|
||||
|
||||
if (collection) {
|
||||
promises.push(
|
||||
populate({
|
||||
id: node?.fields?.id,
|
||||
id: node.value.id,
|
||||
collection,
|
||||
currentDepth,
|
||||
data: node.fields,
|
||||
data: node,
|
||||
depth,
|
||||
field,
|
||||
key: 'data',
|
||||
key: 'value',
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -33,15 +33,16 @@ const insertRelationship = ({
|
||||
}) => {
|
||||
if (!replaceNodeKey) {
|
||||
editor.dispatchCommand(INSERT_RELATIONSHIP_COMMAND, {
|
||||
id,
|
||||
data: null,
|
||||
relationTo,
|
||||
value: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
editor.update(() => {
|
||||
const node = $getNodeByKey(replaceNodeKey)
|
||||
if (node) {
|
||||
node.replace($createRelationshipNode({ id, data: null, relationTo }))
|
||||
node.replace($createRelationshipNode({ relationTo, value: { id } }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,18 +18,16 @@ import * as React from 'react'
|
||||
|
||||
import { RelationshipComponent } from './components/RelationshipComponent'
|
||||
|
||||
export type RelationshipFields = {
|
||||
data: null | unknown
|
||||
id: string
|
||||
export type RelationshipData = {
|
||||
relationTo: string
|
||||
value: {
|
||||
// Actual relationship, populated in afterRead hook
|
||||
[key: string]: unknown
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export type SerializedRelationshipNode = Spread<
|
||||
{
|
||||
fields: RelationshipFields
|
||||
},
|
||||
SerializedDecoratorBlockNode
|
||||
>
|
||||
export type SerializedRelationshipNode = Spread<RelationshipData, SerializedDecoratorBlockNode>
|
||||
|
||||
function relationshipElementToNode(domNode: HTMLDivElement): DOMConversionOutput | null {
|
||||
const id = domNode.getAttribute('data-lexical-relationship-id')
|
||||
@@ -37,9 +35,10 @@ function relationshipElementToNode(domNode: HTMLDivElement): DOMConversionOutput
|
||||
|
||||
if (id != null && relationTo != null) {
|
||||
const node = $createRelationshipNode({
|
||||
id,
|
||||
data: null,
|
||||
relationTo,
|
||||
value: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
return { node }
|
||||
}
|
||||
@@ -47,24 +46,24 @@ function relationshipElementToNode(domNode: HTMLDivElement): DOMConversionOutput
|
||||
}
|
||||
|
||||
export class RelationshipNode extends DecoratorBlockNode {
|
||||
__fields: RelationshipFields
|
||||
__data: RelationshipData
|
||||
|
||||
constructor({
|
||||
fields,
|
||||
data,
|
||||
format,
|
||||
key,
|
||||
}: {
|
||||
fields: RelationshipFields
|
||||
data: RelationshipData
|
||||
format?: ElementFormatType
|
||||
key?: NodeKey
|
||||
}) {
|
||||
super(format, key)
|
||||
this.__fields = fields
|
||||
this.__data = data
|
||||
}
|
||||
|
||||
static clone(node: RelationshipNode): RelationshipNode {
|
||||
return new RelationshipNode({
|
||||
fields: node.__fields,
|
||||
data: node.__data,
|
||||
format: node.__format,
|
||||
key: node.__key,
|
||||
})
|
||||
@@ -92,7 +91,11 @@ export class RelationshipNode extends DecoratorBlockNode {
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedRelationshipNode): RelationshipNode {
|
||||
const node = $createRelationshipNode(serializedNode.fields)
|
||||
const importedData: RelationshipData = {
|
||||
relationTo: serializedNode.relationTo,
|
||||
value: serializedNode.value,
|
||||
}
|
||||
const node = $createRelationshipNode(importedData)
|
||||
node.setFormat(serializedNode.format)
|
||||
return node
|
||||
}
|
||||
@@ -104,7 +107,7 @@ export class RelationshipNode extends DecoratorBlockNode {
|
||||
return (
|
||||
<RelationshipComponent
|
||||
className={config.theme.relationship ?? 'LexicalEditorTheme__relationship'}
|
||||
fields={this.__fields}
|
||||
data={this.__data}
|
||||
format={this.__format}
|
||||
nodeKey={this.getKey()}
|
||||
/>
|
||||
@@ -113,8 +116,8 @@ export class RelationshipNode extends DecoratorBlockNode {
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
const element = document.createElement('div')
|
||||
element.setAttribute('data-lexical-relationship-id', this.__fields?.id)
|
||||
element.setAttribute('data-lexical-relationship-relationTo', this.__fields?.relationTo)
|
||||
element.setAttribute('data-lexical-relationship-id', this.__data?.value?.id)
|
||||
element.setAttribute('data-lexical-relationship-relationTo', this.__data?.relationTo)
|
||||
|
||||
const text = document.createTextNode(this.getTextContent())
|
||||
element.append(text)
|
||||
@@ -124,14 +127,14 @@ export class RelationshipNode extends DecoratorBlockNode {
|
||||
exportJSON(): SerializedRelationshipNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
fields: this.getFields(),
|
||||
...this.getData(),
|
||||
type: this.getType(),
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
getFields(): RelationshipFields {
|
||||
return this.getLatest().__fields
|
||||
getData(): RelationshipData {
|
||||
return this.getLatest().__data
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
@@ -139,18 +142,18 @@ export class RelationshipNode extends DecoratorBlockNode {
|
||||
}
|
||||
|
||||
getTextContent(): string {
|
||||
return `${this?.__fields?.relationTo} relation to ${this.__fields?.id}`
|
||||
return `${this?.__data?.relationTo} relation to ${this.__data?.value?.id}`
|
||||
}
|
||||
|
||||
setFields(fields: RelationshipFields): void {
|
||||
setData(data: RelationshipData): void {
|
||||
const writable = this.getWritable()
|
||||
writable.__fields = fields
|
||||
writable.__data = data
|
||||
}
|
||||
}
|
||||
|
||||
export function $createRelationshipNode(fields: RelationshipFields): RelationshipNode {
|
||||
export function $createRelationshipNode(data: RelationshipData): RelationshipNode {
|
||||
return new RelationshipNode({
|
||||
fields,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getTranslation } from 'payload/utilities'
|
||||
import React, { useCallback, useReducer, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { RelationshipFields } from '../RelationshipNode'
|
||||
import type { RelationshipData } from '../RelationshipNode'
|
||||
|
||||
import { useEditorConfigContext } from '../../../../lexical/config/EditorConfigProvider'
|
||||
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from '../../drawer'
|
||||
@@ -25,7 +25,7 @@ const initialParams = {
|
||||
type Props = {
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
fields: RelationshipFields
|
||||
data: RelationshipData
|
||||
format?: ElementFormatType
|
||||
nodeKey?: string
|
||||
}
|
||||
@@ -33,7 +33,10 @@ type Props = {
|
||||
const Component: React.FC<Props> = (props) => {
|
||||
const {
|
||||
children,
|
||||
fields: { id, relationTo },
|
||||
data: {
|
||||
relationTo,
|
||||
value: { id },
|
||||
},
|
||||
nodeKey,
|
||||
} = props
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import { useConfig } from 'payload/components/utilities'
|
||||
import { useEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import type { RelationshipFields } from '../nodes/RelationshipNode'
|
||||
import type { RelationshipData } from '../nodes/RelationshipNode'
|
||||
|
||||
import { RelationshipDrawer } from '../drawer'
|
||||
import { $createRelationshipNode, RelationshipNode } from '../nodes/RelationshipNode'
|
||||
|
||||
export const INSERT_RELATIONSHIP_COMMAND: LexicalCommand<RelationshipFields> = createCommand(
|
||||
export const INSERT_RELATIONSHIP_COMMAND: LexicalCommand<RelationshipData> = createCommand(
|
||||
'INSERT_RELATIONSHIP_COMMAND',
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function RelationshipPlugin(): JSX.Element | null {
|
||||
throw new Error('RelationshipPlugin: RelationshipNode not registered on editor')
|
||||
}
|
||||
|
||||
return editor.registerCommand<RelationshipFields>(
|
||||
return editor.registerCommand<RelationshipData>(
|
||||
INSERT_RELATIONSHIP_COMMAND,
|
||||
(payload) => {
|
||||
const relationshipNode = $createRelationshipNode(payload)
|
||||
|
||||
@@ -17,19 +17,20 @@ export const uploadAfterReadPromiseHOC = (
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}) => {
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
if (node?.fields?.value?.id) {
|
||||
const collection = req.payload.collections[node?.fields?.relationTo]
|
||||
if (node?.value?.id) {
|
||||
const collection = req.payload.collections[node?.relationTo]
|
||||
|
||||
if (collection) {
|
||||
promises.push(
|
||||
populate({
|
||||
id: node?.fields?.value?.id,
|
||||
id: node?.value?.id,
|
||||
collection,
|
||||
currentDepth,
|
||||
data: node.fields,
|
||||
data: node,
|
||||
depth,
|
||||
field,
|
||||
key: 'value',
|
||||
@@ -39,17 +40,18 @@ export const uploadAfterReadPromiseHOC = (
|
||||
}),
|
||||
)
|
||||
}
|
||||
if (Array.isArray(props?.collections?.[node?.fields?.relationTo]?.fields)) {
|
||||
if (Array.isArray(props?.collections?.[node?.relationTo]?.fields)) {
|
||||
recurseNestedFields({
|
||||
afterReadPromises,
|
||||
currentDepth,
|
||||
data: node.fields || {},
|
||||
depth,
|
||||
fields: props?.collections?.[node?.fields?.relationTo]?.fields,
|
||||
fields: props?.collections?.[node?.relationTo]?.fields,
|
||||
overrideAccess,
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { ElementProps } from '..'
|
||||
import type { UploadFeatureProps } from '../..'
|
||||
import type { UploadNode } from '../../nodes/UploadNode'
|
||||
import type { UploadData, UploadNode } from '../../nodes/UploadNode'
|
||||
|
||||
import { useEditorConfigContext } from '../../../../lexical/config/EditorConfigProvider'
|
||||
|
||||
@@ -34,9 +34,8 @@ export const ExtraFieldsUploadDrawer: React.FC<
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
data: { fields, relationTo, value },
|
||||
drawerSlug,
|
||||
fields: { relationTo, value },
|
||||
fields,
|
||||
nodeKey,
|
||||
relatedCollection,
|
||||
} = props
|
||||
@@ -69,11 +68,11 @@ export const ExtraFieldsUploadDrawer: React.FC<
|
||||
editor.update(() => {
|
||||
const uploadNode: UploadNode | null = $getNodeByKey(nodeKey)
|
||||
if (uploadNode) {
|
||||
const newFields = {
|
||||
...uploadNode.getFields(),
|
||||
...data,
|
||||
const newData: UploadData = {
|
||||
...uploadNode.getData(),
|
||||
fields: data,
|
||||
}
|
||||
uploadNode.setFields(newFields)
|
||||
uploadNode.setData(newData)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import React, { useCallback, useReducer, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { UploadFeatureProps } from '..'
|
||||
import type { UploadFields } from '../nodes/UploadNode'
|
||||
import type { UploadData } from '../nodes/UploadNode'
|
||||
|
||||
import { useEditorConfigContext } from '../../../lexical/config/EditorConfigProvider'
|
||||
import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition'
|
||||
@@ -28,14 +28,14 @@ const initialParams = {
|
||||
}
|
||||
|
||||
export type ElementProps = {
|
||||
fields: UploadFields
|
||||
data: UploadData
|
||||
nodeKey: string
|
||||
//uploadProps: UploadFeatureProps
|
||||
}
|
||||
|
||||
const Component: React.FC<ElementProps> = (props) => {
|
||||
const {
|
||||
fields: { relationTo, value },
|
||||
data: { fields, relationTo, value },
|
||||
nodeKey,
|
||||
} = props
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ const insertUpload = ({
|
||||
if (!replaceNodeKey) {
|
||||
editor.dispatchCommand(INSERT_UPLOAD_COMMAND, {
|
||||
id,
|
||||
fields: null,
|
||||
relationTo,
|
||||
})
|
||||
} else {
|
||||
@@ -42,7 +43,8 @@ const insertUpload = ({
|
||||
if (node) {
|
||||
node.replace(
|
||||
$createUploadNode({
|
||||
fields: {
|
||||
data: {
|
||||
fields: null,
|
||||
relationTo,
|
||||
value: {
|
||||
id,
|
||||
|
||||
@@ -16,13 +16,19 @@ import * as React from 'react'
|
||||
const RawUploadComponent = React.lazy(async () => await import('../component'))
|
||||
|
||||
export interface RawUploadPayload {
|
||||
fields: {
|
||||
// unknown, custom fields:
|
||||
[key: string]: unknown
|
||||
}
|
||||
id: string
|
||||
relationTo: string
|
||||
}
|
||||
|
||||
export type UploadFields = {
|
||||
// unknown, custom fields:
|
||||
[key: string]: unknown
|
||||
export type UploadData = {
|
||||
fields: {
|
||||
// unknown, custom fields:
|
||||
[key: string]: unknown
|
||||
}
|
||||
relationTo: string
|
||||
value: {
|
||||
// Actual upload data, populated in afterRead hook
|
||||
@@ -41,32 +47,27 @@ function convertUploadElement(domNode: Node): DOMConversionOutput | null {
|
||||
return null
|
||||
}
|
||||
|
||||
export type SerializedUploadNode = Spread<
|
||||
{
|
||||
fields: UploadFields
|
||||
},
|
||||
SerializedDecoratorBlockNode
|
||||
>
|
||||
export type SerializedUploadNode = Spread<UploadData, SerializedDecoratorBlockNode>
|
||||
|
||||
export class UploadNode extends DecoratorBlockNode {
|
||||
__fields: UploadFields
|
||||
__data: UploadData
|
||||
|
||||
constructor({
|
||||
fields,
|
||||
data,
|
||||
format,
|
||||
key,
|
||||
}: {
|
||||
fields: UploadFields
|
||||
data: UploadData
|
||||
format?: ElementFormatType
|
||||
key?: NodeKey
|
||||
}) {
|
||||
super(format, key)
|
||||
this.__fields = fields
|
||||
this.__data = data
|
||||
}
|
||||
|
||||
static clone(node: UploadNode): UploadNode {
|
||||
return new UploadNode({
|
||||
fields: node.__fields,
|
||||
data: node.__data,
|
||||
format: node.__format,
|
||||
key: node.__key,
|
||||
})
|
||||
@@ -86,9 +87,13 @@ export class UploadNode extends DecoratorBlockNode {
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedUploadNode): UploadNode {
|
||||
const node = $createUploadNode({
|
||||
const importedData: UploadData = {
|
||||
fields: serializedNode.fields,
|
||||
})
|
||||
relationTo: serializedNode.relationTo,
|
||||
value: serializedNode.value,
|
||||
}
|
||||
|
||||
const node = $createUploadNode({ data: importedData })
|
||||
node.setFormat(serializedNode.format)
|
||||
|
||||
return node
|
||||
@@ -99,9 +104,7 @@ export class UploadNode extends DecoratorBlockNode {
|
||||
}
|
||||
|
||||
decorate(): JSX.Element {
|
||||
return (
|
||||
<RawUploadComponent fields={this.__fields} format={this.__format} nodeKey={this.getKey()} />
|
||||
)
|
||||
return <RawUploadComponent data={this.__data} format={this.__format} nodeKey={this.getKey()} />
|
||||
}
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
@@ -114,19 +117,19 @@ export class UploadNode extends DecoratorBlockNode {
|
||||
exportJSON(): SerializedUploadNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
fields: this.getFields(),
|
||||
...this.getData(),
|
||||
type: this.getType(),
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
getFields(): UploadFields {
|
||||
return this.getLatest().__fields
|
||||
getData(): UploadData {
|
||||
return this.getLatest().__data
|
||||
}
|
||||
|
||||
setFields(fields: UploadFields): void {
|
||||
setData(data: UploadData): void {
|
||||
const writable = this.getWritable()
|
||||
writable.__fields = fields
|
||||
writable.__data = data
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
@@ -135,8 +138,8 @@ export class UploadNode extends DecoratorBlockNode {
|
||||
}
|
||||
}
|
||||
|
||||
export function $createUploadNode({ fields }: { fields: UploadFields }): UploadNode {
|
||||
return $applyNodeReplacement(new UploadNode({ fields }))
|
||||
export function $createUploadNode({ data }: { data: UploadData }): UploadNode {
|
||||
return $applyNodeReplacement(new UploadNode({ data }))
|
||||
}
|
||||
|
||||
export function $isUploadNode(node: LexicalNode | null | undefined): node is UploadNode {
|
||||
|
||||
@@ -29,7 +29,8 @@ export function UploadPlugin(): JSX.Element | null {
|
||||
(payload: InsertUploadPayload) => {
|
||||
editor.update(() => {
|
||||
const uploadNode = $createUploadNode({
|
||||
fields: {
|
||||
data: {
|
||||
fields: payload.fields,
|
||||
relationTo: payload.relationTo,
|
||||
value: {
|
||||
id: payload.id,
|
||||
|
||||
@@ -14,12 +14,12 @@ export const uploadValidation = (): NodeValidation<SerializedUploadNode> => {
|
||||
}) => {
|
||||
if (!CAN_USE_DOM) {
|
||||
const idField = payloadConfig.collections
|
||||
.find(({ slug }) => slug === node.fields.relationTo)
|
||||
.find(({ slug }) => slug === node.relationTo)
|
||||
.fields.find((field) => fieldAffectsData(field) && field.name === 'id')
|
||||
|
||||
const type = getIDType(idField, validation?.options?.payload?.db?.defaultIDType)
|
||||
|
||||
if (!isValidID(node.fields.value.id, type)) {
|
||||
if (!isValidID(node.value?.id, type)) {
|
||||
return validation.options.t('validation:validUploadID')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export type AfterReadPromise<T extends SerializedLexicalNode = SerializedLexical
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}: {
|
||||
afterReadPromises: Map<string, Array<AfterReadPromise>>
|
||||
currentDepth: number
|
||||
@@ -28,6 +29,7 @@ export type AfterReadPromise<T extends SerializedLexicalNode = SerializedLexical
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
siblingDoc: Record<string, unknown>
|
||||
}) => Promise<void>[]
|
||||
|
||||
export type NodeValidation<T extends SerializedLexicalNode = SerializedLexicalNode> = ({
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export function cloneDeep<T>(object: T): T {
|
||||
export function cloneDeep<T>(object: T, cache: WeakMap<any, any> = new WeakMap()): T {
|
||||
if (object === null) return null
|
||||
|
||||
if (cache.has(object)) {
|
||||
return cache.get(object)
|
||||
}
|
||||
|
||||
// Handle Date
|
||||
if (object instanceof Date) {
|
||||
return new Date(object.getTime()) as unknown as T
|
||||
@@ -11,19 +15,39 @@ export function cloneDeep<T>(object: T): T {
|
||||
return new RegExp(object.source, object.flags) as unknown as T
|
||||
}
|
||||
|
||||
// Handle Array
|
||||
if (Array.isArray(object)) {
|
||||
return object.map((item) => cloneDeep(item)) as unknown as T
|
||||
// Handle Map
|
||||
if (object instanceof Map) {
|
||||
const clonedMap = new Map()
|
||||
cache.set(object, clonedMap)
|
||||
for (const [key, value] of object.entries()) {
|
||||
clonedMap.set(key, cloneDeep(value, cache))
|
||||
}
|
||||
return clonedMap as unknown as T
|
||||
}
|
||||
|
||||
// Handle plain Object
|
||||
// Handle Set
|
||||
if (object instanceof Set) {
|
||||
const clonedSet = new Set()
|
||||
cache.set(object, clonedSet)
|
||||
for (const value of object.values()) {
|
||||
clonedSet.add(cloneDeep(value, cache))
|
||||
}
|
||||
return clonedSet as unknown as T
|
||||
}
|
||||
|
||||
// Handle Array and Object
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const clonedObject: any = {}
|
||||
const clonedObject: any = Array.isArray(object)
|
||||
? []
|
||||
: Object.create(Object.getPrototypeOf(object))
|
||||
cache.set(object, clonedObject)
|
||||
|
||||
for (const key in object) {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
clonedObject[key] = cloneDeep(object[key])
|
||||
if (object.hasOwnProperty(key) || Object.getOwnPropertySymbols(object).includes(key as any)) {
|
||||
clonedObject[key] = cloneDeep(object[key], cache)
|
||||
}
|
||||
}
|
||||
|
||||
return clonedObject as T
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import type { Field, PayloadRequest, RichTextField } from 'payload/types'
|
||||
import type { Field, PayloadRequest, RichTextAdapter } from 'payload/types'
|
||||
|
||||
import { fieldAffectsData, fieldHasSubFields, fieldIsArrayType } from 'payload/types'
|
||||
|
||||
import type { AfterReadPromise } from '../field/features/types'
|
||||
import type { AdapterProps } from '../types'
|
||||
|
||||
import { populate } from './populate'
|
||||
import { recurseRichText } from './richTextRelationshipPromise'
|
||||
|
||||
type NestedRichTextFieldsArgs = {
|
||||
afterReadPromises: Map<string, Array<AfterReadPromise>>
|
||||
@@ -19,6 +16,7 @@ type NestedRichTextFieldsArgs = {
|
||||
promises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
siblingDoc: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const recurseNestedFields = ({
|
||||
@@ -31,6 +29,7 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}: NestedRichTextFieldsArgs): void => {
|
||||
fields.forEach((field) => {
|
||||
if (field.type === 'relationship' || field.type === 'upload') {
|
||||
@@ -128,6 +127,7 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
} else {
|
||||
recurseNestedFields({
|
||||
@@ -140,6 +140,7 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
} else if (field.type === 'tabs') {
|
||||
@@ -154,6 +155,7 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
})
|
||||
} else if (Array.isArray(data[field.name])) {
|
||||
@@ -171,6 +173,7 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -188,29 +191,31 @@ export const recurseNestedFields = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'richText' && Array.isArray(data[field.name])) {
|
||||
;(data[field.name] as SerializedEditorState).root.children.forEach((node) => {
|
||||
if ('children' in node && Array.isArray(node.children)) {
|
||||
// This assumes that the richText editor is using lexical and not slate.
|
||||
// TODO: Throw an error if Slate is used. That would be cursed, who'd do that?
|
||||
recurseRichText({
|
||||
afterReadPromises,
|
||||
children: node.children,
|
||||
currentDepth,
|
||||
depth,
|
||||
field: field as RichTextField<AdapterProps>,
|
||||
overrideAccess,
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
})
|
||||
if (field.type === 'richText') {
|
||||
// TODO: This does not properly work yet. E.g. it does not handle a relationship inside of lexical inside of block inside of lexical
|
||||
const editor: RichTextAdapter = field?.editor
|
||||
|
||||
if (editor?.afterReadPromise) {
|
||||
const afterReadPromise = editor.afterReadPromise({
|
||||
currentDepth,
|
||||
depth,
|
||||
field,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
|
||||
if (afterReadPromise) {
|
||||
promises.push(afterReadPromise)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type RecurseRichTextArgs = {
|
||||
promises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
siblingDoc?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const recurseRichText = ({
|
||||
@@ -30,6 +31,7 @@ export const recurseRichText = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}: RecurseRichTextArgs): void => {
|
||||
if (depth <= 0 || currentDepth > depth) {
|
||||
return
|
||||
@@ -49,6 +51,7 @@ export const recurseRichText = ({
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -65,6 +68,7 @@ export const recurseRichText = ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -93,6 +97,7 @@ export const richTextRelationshipPromise = async ({
|
||||
promises,
|
||||
req,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
})
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
@@ -90,7 +90,9 @@ export function generateLexicalRichText() {
|
||||
fields: {
|
||||
url: 'https://',
|
||||
doc: {
|
||||
value: '{{ARRAY_DOC_ID}}',
|
||||
value: {
|
||||
id: '{{ARRAY_DOC_ID}}',
|
||||
},
|
||||
relationTo: 'array-fields',
|
||||
},
|
||||
newTab: false,
|
||||
@@ -117,13 +119,10 @@ export function generateLexicalRichText() {
|
||||
format: '',
|
||||
type: 'relationship',
|
||||
version: 1,
|
||||
fields: {
|
||||
value: {
|
||||
id: '{{TEXT_DOC_ID}}',
|
||||
data: {
|
||||
id: '{{TEXT_DOC_ID}}',
|
||||
},
|
||||
relationTo: 'text-fields',
|
||||
},
|
||||
relationTo: 'text-fields',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
@@ -234,11 +233,11 @@ export function generateLexicalRichText() {
|
||||
format: '',
|
||||
type: 'upload',
|
||||
version: 1,
|
||||
relationTo: 'uploads',
|
||||
value: {
|
||||
id: '{{UPLOAD_DOC_ID}}',
|
||||
},
|
||||
fields: {
|
||||
relationTo: 'uploads',
|
||||
value: {
|
||||
id: '{{UPLOAD_DOC_ID}}',
|
||||
},
|
||||
caption: {
|
||||
root: {
|
||||
type: 'root',
|
||||
|
||||
Reference in New Issue
Block a user