feat(richtext-lexical)!: initialize lexical during sanitization (#6119)

BREAKING:

- sanitizeFields is now an async function
- the richText adapters now return a function instead of returning the adapter directly
This commit is contained in:
Alessio Gravili
2024-04-30 15:09:32 -04:00
committed by GitHub
parent 9a636a3cfb
commit d9bb51fdc7
44 changed files with 829 additions and 795 deletions

View File

@@ -1,8 +1,8 @@
import type { User } from 'payload/auth'
import type { Config } from 'payload/config'
import type { SanitizedConfig } from 'payload/config'
import type { Field } from 'payload/types'
export const getBaseFields = (config: Config): Field[] => [
export const getBaseFields = (config: SanitizedConfig): Field[] => [
{
name: 'text',
type: 'text',

View File

@@ -1,5 +1,5 @@
import type { I18n } from '@payloadcms/translations'
import type { SanitizedConfig } from 'payload/config'
import type { Config, SanitizedConfig } from 'payload/config'
import type { Field } from 'payload/types'
import type { Editor } from 'slate'
@@ -35,16 +35,15 @@ export const wrapLink = (editor: Editor): void => {
*/
export function transformExtraFields(
customFieldSchema:
| ((args: { config: SanitizedConfig; defaultFields: Field[]; i18n: I18n }) => Field[])
| ((args: { config: SanitizedConfig; defaultFields: Field[] }) => Field[])
| Field[],
config: SanitizedConfig,
i18n: I18n,
): Field[] {
const baseFields: Field[] = getBaseFields(config)
const fields =
typeof customFieldSchema === 'function'
? customFieldSchema({ config, defaultFields: baseFields, i18n })
? customFieldSchema({ config, defaultFields: baseFields })
: baseFields
// Wrap fields which are not part of the base schema in a group named 'fields' - otherwise they will be rendered but not saved

View File

@@ -1,14 +1,12 @@
import type { RichTextAdapter } from 'payload/types'
import type { Field, RichTextAdapter } from 'payload/types'
import { mapFields } from '@payloadcms/ui/utilities/buildComponentMap'
import { sanitizeFields } from 'payload/config'
import React from 'react'
import type { AdapterArguments, RichTextCustomElement, RichTextCustomLeaf } from './types.js'
import { elements as elementTypes } from './field/elements/index.js'
import { linkFieldsSchemaPath } from './field/elements/link/shared.js'
import { transformExtraFields } from './field/elements/link/utilities.js'
import { uploadFieldsSchemaPath } from './field/elements/upload/shared.js'
import { defaultLeaves as leafTypes } from './field/leaves/index.js'
@@ -17,8 +15,6 @@ export const getGenerateComponentMap =
({ WithServerSideProps, config, i18n }) => {
const componentMap = new Map()
const validRelationships = config.collections.map((c) => c.slug) || []
;(args?.admin?.leaves || Object.values(leafTypes)).forEach((leaf) => {
let leafObject: RichTextCustomLeaf
@@ -66,16 +62,10 @@ export const getGenerateComponentMap =
switch (element.name) {
case 'link': {
const linkFields = sanitizeFields({
config,
fields: transformExtraFields(args.admin?.link?.fields, config, i18n),
validRelationships,
})
const mappedFields = mapFields({
WithServerSideProps,
config,
fieldSchema: linkFields,
fieldSchema: args.admin?.link?.fields as Field[],
i18n,
readOnly: false,
})
@@ -98,16 +88,10 @@ export const getGenerateComponentMap =
uploadEnabledCollections.forEach((collection) => {
if (args?.admin?.upload?.collections[collection.slug]?.fields) {
const uploadFields = sanitizeFields({
config,
fields: args?.admin?.upload?.collections[collection.slug]?.fields,
validRelationships,
})
const mappedFields = mapFields({
WithServerSideProps,
config,
fieldSchema: uploadFields,
fieldSchema: args?.admin?.upload?.collections[collection.slug]?.fields,
i18n,
readOnly: false,
})

View File

@@ -1,19 +1,14 @@
import type { RichTextAdapter } from 'payload/types'
import { sanitizeFields } from 'payload/config'
import type { Field, RichTextAdapter } from 'payload/types'
import type { AdapterArguments, RichTextCustomElement } from './types.js'
import { elements as elementTypes } from './field/elements/index.js'
import { linkFieldsSchemaPath } from './field/elements/link/shared.js'
import { transformExtraFields } from './field/elements/link/utilities.js'
import { uploadFieldsSchemaPath } from './field/elements/upload/shared.js'
export const getGenerateSchemaMap =
(args: AdapterArguments): RichTextAdapter['generateSchemaMap'] =>
({ config, i18n, schemaMap, schemaPath }) => {
const validRelationships = config.collections.map((c) => c.slug) || []
({ config, schemaMap, schemaPath }) => {
;(args?.admin?.elements || Object.values(elementTypes)).forEach((el) => {
let element: RichTextCustomElement
@@ -26,13 +21,10 @@ export const getGenerateSchemaMap =
if (element) {
switch (element.name) {
case 'link': {
const linkFields = sanitizeFields({
config,
fields: transformExtraFields(args.admin?.link?.fields, config, i18n),
validRelationships,
})
schemaMap.set(`${schemaPath}.${linkFieldsSchemaPath}`, linkFields)
schemaMap.set(
`${schemaPath}.${linkFieldsSchemaPath}`,
args.admin?.link?.fields as Field[],
)
break
}
@@ -50,15 +42,9 @@ export const getGenerateSchemaMap =
uploadEnabledCollections.forEach((collection) => {
if (args?.admin?.upload?.collections[collection.slug]?.fields) {
const uploadFields = sanitizeFields({
config,
fields: args?.admin?.upload?.collections[collection.slug]?.fields,
validRelationships,
})
schemaMap.set(
`${schemaPath}.${uploadFieldsSchemaPath}.${collection.slug}`,
uploadFields,
args?.admin?.upload?.collections[collection.slug]?.fields,
)
}
})

View File

@@ -1,5 +1,7 @@
import type { RichTextAdapter } from 'payload/types'
import type { Config } from 'payload/config'
import type { RichTextAdapterProvider } from 'payload/types'
import { sanitizeFields } from 'payload/config'
import { withNullableJSONSchemaType } from 'payload/utilities'
import type { AdapterArguments } from './types.js'
@@ -7,61 +9,95 @@ import type { AdapterArguments } from './types.js'
import { RichTextCell } from './cell/index.js'
import { richTextRelationshipPromise } from './data/richTextRelationshipPromise.js'
import { richTextValidate } from './data/validation.js'
import { transformExtraFields } from './field/elements/link/utilities.js'
import { RichTextField } from './field/index.js'
import { getGenerateComponentMap } from './generateComponentMap.js'
import { getGenerateSchemaMap } from './generateSchemaMap.js'
export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], AdapterArguments, any> {
return {
CellComponent: RichTextCell,
FieldComponent: RichTextField,
generateComponentMap: getGenerateComponentMap(args),
generateSchemaMap: getGenerateSchemaMap(args),
outputSchema: ({ isRequired }) => {
return {
type: withNullableJSONSchemaType('array', isRequired),
items: {
type: 'object',
},
export function slateEditor(
args: AdapterArguments,
): RichTextAdapterProvider<any[], AdapterArguments, any> {
return async ({ config }) => {
const validRelationships = config.collections.map((c) => c.slug) || []
if (!args.admin) {
args.admin = {}
}
if (!args.admin.link) {
args.admin.link = {}
}
if (!args.admin.link.fields) {
args.admin.link.fields = []
}
args.admin.link.fields = await sanitizeFields({
config: config as unknown as Config,
fields: transformExtraFields(args.admin?.link?.fields, config),
validRelationships,
})
if (args?.admin?.upload?.collections) {
for (const collection of Object.keys(args.admin.upload.collections)) {
if (args?.admin?.upload?.collections[collection]?.fields) {
args.admin.upload.collections[collection].fields = await sanitizeFields({
config: config as unknown as Config,
fields: args.admin?.upload?.collections[collection]?.fields,
validRelationships,
})
}
}
},
populationPromises({
context,
currentDepth,
depth,
field,
fieldPromises,
findMany,
flattenLocales,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
}) {
if (
field.admin?.elements?.includes('relationship') ||
field.admin?.elements?.includes('upload') ||
field.admin?.elements?.includes('link') ||
!field?.admin?.elements
) {
richTextRelationshipPromise({
context,
currentDepth,
depth,
field,
fieldPromises,
findMany,
flattenLocales,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
})
}
},
validate: richTextValidate,
}
return {
CellComponent: RichTextCell,
FieldComponent: RichTextField,
generateComponentMap: getGenerateComponentMap(args),
generateSchemaMap: getGenerateSchemaMap(args),
outputSchema: ({ isRequired }) => {
return {
type: withNullableJSONSchemaType('array', isRequired),
items: {
type: 'object',
},
}
},
populationPromises({
context,
currentDepth,
depth,
field,
fieldPromises,
findMany,
flattenLocales,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
}) {
if (
field.admin?.elements?.includes('relationship') ||
field.admin?.elements?.includes('upload') ||
field.admin?.elements?.includes('link') ||
!field?.admin?.elements
) {
richTextRelationshipPromise({
context,
currentDepth,
depth,
field,
fieldPromises,
findMany,
flattenLocales,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
})
}
},
validate: richTextValidate,
}
}
}

View File

@@ -1,5 +1,4 @@
import type { I18n } from '@payloadcms/translations'
import type { SanitizedConfig } from 'payload/config'
import type { Config, SanitizedConfig } from 'payload/config'
import type { Field, RichTextFieldProps } from 'payload/types'
import type { Editor } from 'slate'
@@ -58,9 +57,7 @@ export type AdapterArguments = {
hideGutter?: boolean
leaves?: RichTextLeaf[]
link?: {
fields?:
| ((args: { config: SanitizedConfig; defaultFields: Field[]; i18n: I18n }) => Field[])
| Field[]
fields?: ((args: { config: SanitizedConfig; defaultFields: Field[] }) => Field[]) | Field[]
}
placeholder?: Record<string, string> | string
rtl?: boolean