From acfda5d8d40eb59d4cd37ae861215748d64d0265 Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Sat, 7 Oct 2023 00:22:13 +0200 Subject: [PATCH] chore(richtext-lexical): updated lexical incoming config API --- .../src/field/lexical/config/default.ts | 58 ++++++++------ packages/richtext-lexical/src/index.ts | 53 ++++++++++--- test/fields/collections/RichText/blocks.ts | 2 +- test/fields/collections/RichText/index.ts | 79 ++++++++----------- 4 files changed, 109 insertions(+), 83 deletions(-) diff --git a/packages/richtext-lexical/src/field/lexical/config/default.ts b/packages/richtext-lexical/src/field/lexical/config/default.ts index 44a6740f4f..2b75e97fdc 100644 --- a/packages/richtext-lexical/src/field/lexical/config/default.ts +++ b/packages/richtext-lexical/src/field/lexical/config/default.ts @@ -1,7 +1,9 @@ +import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor' + +import type { FeatureProvider } from '../../features/types' import type { EditorConfig, SanitizedEditorConfig } from './types' import { BlockQuoteFeature } from '../../features/BlockQuote' -import { BlocksFeature } from '../../features/Blocks' import { HeadingFeature } from '../../features/Heading' import { LinkFeature } from '../../features/Link' import { ParagraphFeature } from '../../features/Paragraph' @@ -22,32 +24,36 @@ import { UnoderedListFeature } from '../../features/lists/UnorderedList' import { LexicalEditorTheme } from '../theme/EditorTheme' import { sanitizeEditorConfig } from './sanitize' +export const defaultEditorFeatures: FeatureProvider[] = [ + BoldTextFeature(), + ItalicTextFeature(), + UnderlineTextFeature(), + StrikethroughTextFeature(), + SubscriptTextFeature(), + SuperscriptTextFeature(), + InlineCodeTextFeature(), + ParagraphFeature(), + HeadingFeature({}), + AlignFeature(), + IndentFeature(), + UnoderedListFeature(), + OrderedListFeature(), + CheckListFeature(), + LinkFeature({}), + RelationshipFeature(), + BlockQuoteFeature(), + UploadFeature(), + //BlocksFeature(), // Adding this by default makes no sense if no blocks are defined +] + +export const defaultEditorLexicalConfig: LexicalEditorConfig = { + namespace: 'lexical', + theme: LexicalEditorTheme, +} + export const defaultEditorConfig: EditorConfig = { - features: [ - BoldTextFeature(), - ItalicTextFeature(), - UnderlineTextFeature(), - StrikethroughTextFeature(), - SubscriptTextFeature(), - SuperscriptTextFeature(), - InlineCodeTextFeature(), - ParagraphFeature(), - HeadingFeature({}), - AlignFeature(), - IndentFeature(), - UnoderedListFeature(), - OrderedListFeature(), - CheckListFeature(), - LinkFeature({}), - RelationshipFeature(), - BlockQuoteFeature(), - UploadFeature(), - //BlocksFeature(), // Adding this by default makes no sense if no blocks are defined - ], - lexical: { - namespace: 'lexical', - theme: LexicalEditorTheme, - }, + features: defaultEditorFeatures, + lexical: defaultEditorLexicalConfig, } export const defaultSanitizedEditorConfig: SanitizedEditorConfig = diff --git a/packages/richtext-lexical/src/index.ts b/packages/richtext-lexical/src/index.ts index 18bdae8022..ed47814840 100644 --- a/packages/richtext-lexical/src/index.ts +++ b/packages/richtext-lexical/src/index.ts @@ -1,27 +1,51 @@ +import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor' import type { RichTextAdapter } from 'payload/types' import { withMergedProps } from 'payload/components/utilities' +import type { FeatureProvider } from './field/features/types' import type { EditorConfig, SanitizedEditorConfig } from './field/lexical/config/types' import type { AdapterProps } from './types' import { RichTextCell } from './cell' import { RichTextField } from './field' -import { defaultEditorConfig, defaultSanitizedEditorConfig } from './field/lexical/config/default' +import { + defaultEditorFeatures, + defaultEditorLexicalConfig, + defaultSanitizedEditorConfig, +} from './field/lexical/config/default' import { sanitizeEditorConfig } from './field/lexical/config/sanitize' import { cloneDeep } from './field/lexical/utils/cloneDeep' import { richTextRelationshipPromise } from './populate/richTextRelationshipPromise' import { richTextValidateHOC } from './validate' -export function lexicalEditor({ - userConfig, -}: { - userConfig?: (defaultEditorConfig: EditorConfig) => EditorConfig -}): RichTextAdapter { - const finalSanitizedEditorConfig: SanitizedEditorConfig = - userConfig == null || typeof userConfig != 'function' - ? cloneDeep(defaultSanitizedEditorConfig) - : sanitizeEditorConfig(userConfig(cloneDeep(defaultEditorConfig))) +export type LexicalEditorProps = { + features?: + | (({ defaultFeatures }: { defaultFeatures: FeatureProvider[] }) => FeatureProvider[]) + | FeatureProvider[] + lexical?: LexicalEditorConfig +} + +export function lexicalEditor(props?: LexicalEditorProps): RichTextAdapter { + let finalSanitizedEditorConfig: SanitizedEditorConfig = null + if (!props || (!props.features && !props.lexical)) { + finalSanitizedEditorConfig = cloneDeep(defaultSanitizedEditorConfig) + } else { + let features: FeatureProvider[] = + props.features && typeof props.features === 'function' + ? props.features({ defaultFeatures: cloneDeep(defaultEditorFeatures) }) + : (props.features as FeatureProvider[]) + if (!features) { + features = cloneDeep(defaultEditorFeatures) + } + + const lexical: LexicalEditorConfig = props.lexical || cloneDeep(defaultEditorLexicalConfig) + + finalSanitizedEditorConfig = sanitizeEditorConfig({ + features, + lexical, + }) + } return { CellComponent: withMergedProps({ @@ -88,9 +112,11 @@ export { CheckListFeature } from './field/features/lists/CheckList' export { OrderedListFeature } from './field/features/lists/OrderedList' export { UnoderedListFeature } from './field/features/lists/UnorderedList' export type { + AfterReadPromise, Feature, FeatureProvider, FeatureProviderMap, + NodeValidation, ResolvedFeature, ResolvedFeatureMap, SanitizedFeatures, @@ -100,7 +126,12 @@ export { EditorConfigProvider, useEditorConfigContext, } from './field/lexical/config/EditorConfigProvider' -export { defaultEditorConfig, defaultSanitizedEditorConfig } from './field/lexical/config/default' +export { + defaultEditorConfig, + defaultEditorFeatures, + defaultEditorLexicalConfig, + defaultSanitizedEditorConfig, +} from './field/lexical/config/default' export { loadFeatures, sortFeaturesForOptimalLoading } from './field/lexical/config/loader' export { sanitizeEditorConfig, sanitizeFeatures } from './field/lexical/config/sanitize' // export SanitizedEditorConfig diff --git a/test/fields/collections/RichText/blocks.ts b/test/fields/collections/RichText/blocks.ts index 84c4e1add4..0b09efd8b0 100644 --- a/test/fields/collections/RichText/blocks.ts +++ b/test/fields/collections/RichText/blocks.ts @@ -24,7 +24,7 @@ export const UploadAndRichTextBlock: Block = { { name: 'richText', type: 'richText', - editor: lexicalEditor({}), + editor: lexicalEditor(), }, ], slug: 'uploadAndRichText', diff --git a/test/fields/collections/RichText/index.ts b/test/fields/collections/RichText/index.ts index dc3a09ba87..356e260e11 100644 --- a/test/fields/collections/RichText/index.ts +++ b/test/fields/collections/RichText/index.ts @@ -31,59 +31,48 @@ const RichTextFields: CollectionConfig = { type: 'richText', required: true, editor: lexicalEditor({ - userConfig(defaultEditorConfig) { - defaultEditorConfig.features.push(TreeviewFeature()) - defaultEditorConfig.features.push( - LinkFeature({ - fields: [ - { - name: 'rel', - label: 'Rel Attribute', - type: 'select', - hasMany: true, - options: ['noopener', 'noreferrer', 'nofollow'], - admin: { - description: - 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.', - }, - }, - ], - }), - ) - - defaultEditorConfig.features.push( - UploadFeature({ - collections: { - uploads: { - fields: [ - { - name: 'caption', - type: 'richText', - editor: lexicalEditor({}), - }, - ], + features: ({ defaultFeatures }) => [ + ...defaultFeatures, + TreeviewFeature(), + LinkFeature({ + fields: [ + { + name: 'rel', + label: 'Rel Attribute', + type: 'select', + hasMany: true, + options: ['noopener', 'noreferrer', 'nofollow'], + admin: { + description: + 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.', }, }, - }), - ) - - defaultEditorConfig.features.push( - BlocksFeature({ - blocks: [TextBlock, UploadAndRichTextBlock], - }), - ) - return defaultEditorConfig - }, + ], + }), + UploadFeature({ + collections: { + uploads: { + fields: [ + { + name: 'caption', + type: 'richText', + editor: lexicalEditor(), + }, + ], + }, + }, + }), + BlocksFeature({ + blocks: [TextBlock, UploadAndRichTextBlock], + }), + ], }), }, { name: 'richTextLexical', type: 'richText', editor: lexicalEditor({ - userConfig(defaultEditorConfig) { - defaultEditorConfig.features.push(TreeviewFeature()) - return defaultEditorConfig - }, + features: ({ defaultFeatures }) => [...defaultFeatures, TreeviewFeature()], }), }, {