Skip to content

Commit

Permalink
feat(richtext-lexical)!: simplify creation of features (#6885)
Browse files Browse the repository at this point in the history
**BREAKING:**
- ServerFeature: `ClientComponent` has been renamed to `ClientFeature`
- ServerFeature: The nested `serverFeatureProps` has been renamed to
`sanitizedServerFeatureProps`
- ServerFeature: The FeatureProviderProviderServer type now expects 3
generics instead of 2. We have split the props generic into sanitized &
unsanitized props
- ClientFeature: The FeatureProviderProviderClient type now expects 2
generics instead of 1. We have split the props generic into sanitized &
unsanitized props
- ClientFeature: The nested `clientFeatureProps` has been renamed to
`sanitizedClientFeatureProps`
  • Loading branch information
AlessioGr authored Jun 21, 2024
1 parent ead7d95 commit d66b348
Show file tree
Hide file tree
Showing 92 changed files with 1,588 additions and 1,795 deletions.
8 changes: 5 additions & 3 deletions packages/richtext-lexical/src/cell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export const RichTextCell: React.FC<
const clientFunctions = useClientFunctions()
const [hasLoadedFeatures, setHasLoadedFeatures] = useState(false)

const [featureProviders, setFeatureProviders] = useState<FeatureProviderClient<unknown>[]>([])
const [featureProviders, setFeatureProviders] = useState<
FeatureProviderClient<unknown, unknown>[]
>([])

const [finalSanitizedEditorConfig, setFinalSanitizedEditorConfig] =
useState<SanitizedClientEditorConfig>(null)
Expand All @@ -55,7 +57,7 @@ export const RichTextCell: React.FC<

useEffect(() => {
if (!hasLoadedFeatures) {
const featureProvidersLocal: FeatureProviderClient<unknown>[] = []
const featureProvidersLocal: FeatureProviderClient<unknown, unknown>[] = []
let featureProvidersAndComponentsLoaded = 0 // feature providers and components only

Object.entries(clientFunctions).forEach(([key, plugin]) => {
Expand Down Expand Up @@ -179,7 +181,7 @@ export const RichTextCell: React.FC<
return FeatureComponent
})
: null}
{featureProvider.ClientComponent}
{featureProvider.ClientFeature}
</React.Fragment>
)
})}
Expand Down
46 changes: 23 additions & 23 deletions packages/richtext-lexical/src/exports/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@
'use client'

export { RichTextCell } from '../../cell/index.js'
export { AlignFeatureClientComponent } from '../../features/align/feature.client.js'
export { BlockquoteFeatureClientComponent } from '../../features/blockquote/feature.client.js'
export { BlocksFeatureClientComponent } from '../../features/blocks/feature.client.js'
export { AlignFeatureClient } from '../../features/align/feature.client.js'
export { BlockquoteFeatureClient } from '../../features/blockquote/feature.client.js'
export { BlocksFeatureClient } from '../../features/blocks/feature.client.js'
export { createClientComponent } from '../../features/createClientComponent.js'
export { TestRecorderFeatureClientComponent } from '../../features/debug/testRecorder/feature.client.js'
export { TreeViewFeatureClientComponent } from '../../features/debug/treeView/feature.client.js'
export { BoldFeatureClientComponent } from '../../features/format/bold/feature.client.js'
export { InlineCodeFeatureClientComponent } from '../../features/format/inlineCode/feature.client.js'
export { ItalicFeatureClientComponent } from '../../features/format/italic/feature.client.js'
export { TestRecorderFeatureClient } from '../../features/debug/testRecorder/feature.client.js'
export { TreeViewFeatureClient } from '../../features/debug/treeView/feature.client.js'
export { BoldFeatureClient } from '../../features/format/bold/feature.client.js'
export { InlineCodeFeatureClient } from '../../features/format/inlineCode/feature.client.js'
export { ItalicFeatureClient } from '../../features/format/italic/feature.client.js'
export { toolbarFormatGroupWithItems } from '../../features/format/shared/toolbarFormatGroup.js'
export { StrikethroughFeatureClientComponent } from '../../features/format/strikethrough/feature.client.js'
export { SubscriptFeatureClientComponent } from '../../features/format/subscript/feature.client.js'
export { SuperscriptFeatureClientComponent } from '../../features/format/superscript/feature.client.js'
export { UnderlineFeatureClientComponent } from '../../features/format/underline/feature.client.js'
export { HeadingFeatureClientComponent } from '../../features/heading/feature.client.js'
export { HorizontalRuleFeatureClientComponent } from '../../features/horizontalRule/feature.client.js'
export { IndentFeatureClientComponent } from '../../features/indent/feature.client.js'
export { LinkFeatureClientComponent } from '../../features/link/feature.client.js'
export { ChecklistFeatureClientComponent } from '../../features/lists/checklist/feature.client.js'
export { OrderedListFeatureClientComponent } from '../../features/lists/orderedList/feature.client.js'
export { UnorderedListFeatureClientComponent } from '../../features/lists/unorderedList/feature.client.js'
export { LexicalPluginToLexicalFeatureClientComponent } from '../../features/migrations/lexicalPluginToLexical/feature.client.js'
export { SlateToLexicalFeatureClientComponent } from '../../features/migrations/slateToLexical/feature.client.js'
export { ParagraphFeatureClientComponent } from '../../features/paragraph/feature.client.js'
export { StrikethroughFeatureClient } from '../../features/format/strikethrough/feature.client.js'
export { SubscriptFeatureClient } from '../../features/format/subscript/feature.client.js'
export { SuperscriptFeatureClient } from '../../features/format/superscript/feature.client.js'
export { UnderlineFeatureClient } from '../../features/format/underline/feature.client.js'
export { HeadingFeatureClient } from '../../features/heading/feature.client.js'
export { HorizontalRuleFeatureClient } from '../../features/horizontalRule/feature.client.js'
export { IndentFeatureClient } from '../../features/indent/feature.client.js'
export { LinkFeatureClient } from '../../features/link/feature.client.js'
export { ChecklistFeatureClient } from '../../features/lists/checklist/feature.client.js'
export { OrderedListFeatureClient } from '../../features/lists/orderedList/feature.client.js'
export { UnorderedListFeatureClient } from '../../features/lists/unorderedList/feature.client.js'
export { LexicalPluginToLexicalFeatureClient } from '../../features/migrations/lexicalPluginToLexical/feature.client.js'
export { SlateToLexicalFeatureClient } from '../../features/migrations/slateToLexical/feature.client.js'
export { ParagraphFeatureClient } from '../../features/paragraph/feature.client.js'

export { RelationshipFeatureClientComponent } from '../../features/relationship/feature.client.js'
export { RelationshipFeatureClient } from '../../features/relationship/feature.client.js'

export { toolbarAddDropdownGroupWithItems } from '../../features/shared/toolbar/addDropdownGroup.js'
export { toolbarFeatureButtonsGroupWithItems } from '../../features/shared/toolbar/featureButtonsGroup.js'
Expand Down
27 changes: 9 additions & 18 deletions packages/richtext-lexical/src/features/align/feature.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import { $isElementNode, $isRangeSelection, FORMAT_ELEMENT_COMMAND } from 'lexical'

import type { ToolbarGroup } from '../toolbars/types.js'
import type { FeatureProviderProviderClient } from '../types.js'

import { AlignCenterIcon } from '../../lexical/ui/icons/AlignCenter/index.js'
import { AlignJustifyIcon } from '../../lexical/ui/icons/AlignJustify/index.js'
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft/index.js'
import { AlignRightIcon } from '../../lexical/ui/icons/AlignRight/index.js'
import { createClientComponent } from '../createClientComponent.js'
import { createClientFeature } from '../../utilities/createClientFeature.js'
import { toolbarAlignGroupWithItems } from './toolbarAlignGroup.js'

const toolbarGroups: ToolbarGroup[] = [
Expand Down Expand Up @@ -149,19 +148,11 @@ const toolbarGroups: ToolbarGroup[] = [
]),
]

const AlignFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
return {
clientFeatureProps: props,
feature: () => ({
clientFeatureProps: props,
toolbarFixed: {
groups: toolbarGroups,
},
toolbarInline: {
groups: toolbarGroups,
},
}),
}
}

export const AlignFeatureClientComponent = createClientComponent(AlignFeatureClient)
export const AlignFeatureClient = createClientFeature({
toolbarFixed: {
groups: toolbarGroups,
},
toolbarInline: {
groups: toolbarGroups,
},
})
26 changes: 9 additions & 17 deletions packages/richtext-lexical/src/features/align/feature.server.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import type { FeatureProviderProviderServer } from '../types.js'

// eslint-disable-next-line payload/no-imports-from-exports-dir
import { AlignFeatureClientComponent } from '../../exports/client/index.js'
import { AlignFeatureClient } from '../../exports/client/index.js'
import { createServerFeature } from '../../utilities/createServerFeature.js'
import { i18n } from './i18n.js'

export const AlignFeature: FeatureProviderProviderServer<undefined, undefined> = (props) => {
return {
feature: () => {
return {
ClientComponent: AlignFeatureClientComponent,
clientFeatureProps: null,
i18n,
serverFeatureProps: props,
}
},
key: 'align',
serverFeatureProps: props,
}
}
export const AlignFeature = createServerFeature({
feature: {
ClientFeature: AlignFeatureClient,
i18n,
},
key: 'align',
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { $setBlocksType } from '@lexical/selection'
import { $getSelection, $isRangeSelection } from 'lexical'

import type { ToolbarGroup } from '../toolbars/types.js'
import type { FeatureProviderProviderClient } from '../types.js'

import { BlockquoteIcon } from '../../lexical/ui/icons/Blockquote/index.js'
import { createClientComponent } from '../createClientComponent.js'
import { createClientFeature } from '../../utilities/createClientFeature.js'
import { slashMenuBasicGroupWithItems } from '../shared/slashMenu/basicGroup.js'
import { toolbarTextDropdownGroupWithItems } from '../shared/toolbar/textDropdownGroup.js'
import { MarkdownTransformer } from './markdownTransformer.js'
Expand Down Expand Up @@ -43,42 +42,34 @@ const toolbarGroups: ToolbarGroup[] = [
]),
]

const BlockquoteFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
return {
clientFeatureProps: props,
feature: () => ({
clientFeatureProps: props,
markdownTransformers: [MarkdownTransformer],
nodes: [QuoteNode],
export const BlockquoteFeatureClient = createClientFeature({
markdownTransformers: [MarkdownTransformer],
nodes: [QuoteNode],

slashMenu: {
groups: [
slashMenuBasicGroupWithItems([
{
Icon: BlockquoteIcon,
key: 'blockquote',
keywords: ['quote', 'blockquote'],
label: ({ i18n }) => {
return i18n.t('lexical:blockquote:label')
},
onSelect: ({ editor }) => {
editor.update(() => {
const selection = $getSelection()
$setBlocksType(selection, () => $createQuoteNode())
})
},
},
]),
],
},
toolbarFixed: {
groups: toolbarGroups,
},
toolbarInline: {
groups: toolbarGroups,
},
}),
}
}

export const BlockquoteFeatureClientComponent = createClientComponent(BlockquoteFeatureClient)
slashMenu: {
groups: [
slashMenuBasicGroupWithItems([
{
Icon: BlockquoteIcon,
key: 'blockquote',
keywords: ['quote', 'blockquote'],
label: ({ i18n }) => {
return i18n.t('lexical:blockquote:label')
},
onSelect: ({ editor }) => {
editor.update(() => {
const selection = $getSelection()
$setBlocksType(selection, () => $createQuoteNode())
})
},
},
]),
],
},
toolbarFixed: {
groups: toolbarGroups,
},
toolbarInline: {
groups: toolbarGroups,
},
})
71 changes: 32 additions & 39 deletions packages/richtext-lexical/src/features/blockquote/feature.server.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,42 @@
import { QuoteNode } from '@lexical/rich-text'

import type { FeatureProviderProviderServer } from '../types.js'

// eslint-disable-next-line payload/no-imports-from-exports-dir
import { BlockquoteFeatureClientComponent } from '../../exports/client/index.js'
import { BlockquoteFeatureClient } from '../../exports/client/index.js'
import { createServerFeature } from '../../utilities/createServerFeature.js'
import { convertLexicalNodesToHTML } from '../converters/html/converter/index.js'
import { createNode } from '../typeUtilities.js'
import { i18n } from './i18n.js'
import { MarkdownTransformer } from './markdownTransformer.js'

export const BlockquoteFeature: FeatureProviderProviderServer<undefined, undefined> = (props) => {
return {
feature: () => {
return {
ClientComponent: BlockquoteFeatureClientComponent,
clientFeatureProps: null,
i18n,
markdownTransformers: [MarkdownTransformer],
nodes: [
createNode({
converters: {
html: {
converter: async ({ converters, node, parent, req }) => {
const childrenText = await convertLexicalNodesToHTML({
converters,
lexicalNodes: node.children,
parent: {
...node,
parent,
},
req,
})

return `<blockquote>${childrenText}</blockquote>`
export const BlockquoteFeature = createServerFeature({
feature: {
ClientFeature: BlockquoteFeatureClient,
clientFeatureProps: null,
i18n,
markdownTransformers: [MarkdownTransformer],
nodes: [
createNode({
converters: {
html: {
converter: async ({ converters, node, parent, req }) => {
const childrenText = await convertLexicalNodesToHTML({
converters,
lexicalNodes: node.children,
parent: {
...node,
parent,
},
nodeTypes: [QuoteNode.getType()],
},
req,
})

return `<blockquote>${childrenText}</blockquote>`
},
node: QuoteNode,
}),
],
serverFeatureProps: props,
}
},
key: 'blockquote',
serverFeatureProps: props,
}
}
nodeTypes: [QuoteNode.getType()],
},
},
node: QuoteNode,
}),
],
},
key: 'blockquote',
})
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const BlockComponent: React.FC<Props> = (props) => {

const reducedBlock: ReducedBlock = (
editorConfig?.resolvedFeatureMap?.get('blocks')
?.clientFeatureProps as ClientComponentProps<BlocksFeatureClientProps>
?.sanitizedClientFeatureProps as ClientComponentProps<BlocksFeatureClientProps>
)?.reducedBlocks?.find((block) => block.slug === formData?.blockType)

const fieldMap = richTextComponentMap.get(componentMapRenderedFieldsPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const BlocksDrawerComponent: React.FC = () => {

const reducedBlocks = (
editorConfig?.resolvedFeatureMap?.get('blocks')
?.clientFeatureProps as ClientComponentProps<BlocksFeatureClientProps>
?.sanitizedClientFeatureProps as ClientComponentProps<BlocksFeatureClientProps>
)?.reducedBlocks

useEffect(() => {
Expand Down
Loading

0 comments on commit d66b348

Please sign in to comment.