feat(richtext-lexical)!: lazy import React components to prevent client-only code from leaking into the server (#4290)
* chore(richtext-lexical): lazy import all React things * chore(richtext-lexical): use useMemo for lazy-loaded React Components to prevent lag and flashes when parent component re-renders * chore: make exportPointerFiles.ts script usable for other packages as well by hoisting it up to the workspace root and making it configurable * chore(richtext-lexical): make sure no client-side code is imported by default from Features * chore(richtext-lexical): remove unnecessary scss files * chore(richtext-lexical): adjust package.json exports * chore(richtext-*): lazy-import Field & Cell Components, move Client-only exports to /components subpath export * chore(richtext-lexical): make sure nothing client-side is directly exported from the / subpath export anymore * add missing imports * chore: remove breaking changes for Slate * LazyCellComponent & LazyFieldComponent
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types && pnpm build:components && ts-node -T ./scripts/exportPointerFiles.ts",
|
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types && pnpm build:components && ts-node -T ../../scripts/exportPointerFiles.ts ../packages/payload dist/exports",
|
||||||
"build:components": "webpack --config dist/admin/components.config.js",
|
"build:components": "webpack --config dist/admin/components.config.js",
|
||||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||||
|
|||||||
@@ -1,13 +1,34 @@
|
|||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
import type { RichTextField } from '../../../../../fields/config/types'
|
import type { RichTextField } from '../../../../../fields/config/types'
|
||||||
import type { RichTextAdapter } from './types'
|
import type { RichTextAdapter } from './types'
|
||||||
const RichText: React.FC<RichTextField> = (fieldprops) => {
|
const RichText: React.FC<RichTextField> = (fieldprops) => {
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
// eslint-disable-next-line react/destructuring-assignment
|
||||||
const editor: RichTextAdapter = fieldprops.editor
|
const editor: RichTextAdapter = fieldprops.editor
|
||||||
const { FieldComponent } = editor
|
|
||||||
|
|
||||||
return <FieldComponent {...fieldprops} />
|
const isLazy = 'LazyFieldComponent' in editor
|
||||||
|
|
||||||
|
const ImportedFieldComponent: React.FC<any> = useMemo(() => {
|
||||||
|
return isLazy
|
||||||
|
? React.lazy(() => {
|
||||||
|
return editor.LazyFieldComponent().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
}, [editor, isLazy])
|
||||||
|
|
||||||
|
if (isLazy) {
|
||||||
|
return (
|
||||||
|
ImportedFieldComponent && (
|
||||||
|
<React.Suspense>
|
||||||
|
<ImportedFieldComponent {...fieldprops} />
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <editor.FieldComponent {...fieldprops} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RichText
|
export default RichText
|
||||||
|
|||||||
@@ -13,15 +13,11 @@ export type RichTextFieldProps<
|
|||||||
path?: string
|
path?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RichTextAdapter<
|
type RichTextAdapterBase<
|
||||||
Value extends object = object,
|
Value extends object = object,
|
||||||
AdapterProps = any,
|
AdapterProps = any,
|
||||||
ExtraFieldProperties = {},
|
ExtraFieldProperties = {},
|
||||||
> = {
|
> = {
|
||||||
CellComponent: React.FC<
|
|
||||||
CellComponentProps<RichTextField<Value, AdapterProps, ExtraFieldProperties>>
|
|
||||||
>
|
|
||||||
FieldComponent: React.FC<RichTextFieldProps<Value, AdapterProps, ExtraFieldProperties>>
|
|
||||||
afterReadPromise?: ({
|
afterReadPromise?: ({
|
||||||
field,
|
field,
|
||||||
incomingEditorState,
|
incomingEditorState,
|
||||||
@@ -31,7 +27,6 @@ export type RichTextAdapter<
|
|||||||
incomingEditorState: Value
|
incomingEditorState: Value
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
}) => Promise<void> | null
|
}) => Promise<void> | null
|
||||||
|
|
||||||
outputSchema?: ({
|
outputSchema?: ({
|
||||||
field,
|
field,
|
||||||
isRequired,
|
isRequired,
|
||||||
@@ -59,3 +54,25 @@ export type RichTextAdapter<
|
|||||||
RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RichTextAdapter<
|
||||||
|
Value extends object = object,
|
||||||
|
AdapterProps = any,
|
||||||
|
ExtraFieldProperties = {},
|
||||||
|
> = RichTextAdapterBase<Value, AdapterProps, ExtraFieldProperties> &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
CellComponent: React.FC<
|
||||||
|
CellComponentProps<RichTextField<Value, AdapterProps, ExtraFieldProperties>>
|
||||||
|
>
|
||||||
|
FieldComponent: React.FC<RichTextFieldProps<Value, AdapterProps, ExtraFieldProperties>>
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
LazyCellComponent: () => Promise<
|
||||||
|
React.FC<CellComponentProps<RichTextField<Value, AdapterProps, ExtraFieldProperties>>>
|
||||||
|
>
|
||||||
|
LazyFieldComponent: () => Promise<
|
||||||
|
React.FC<RichTextFieldProps<Value, AdapterProps, ExtraFieldProperties>>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
import type { RichTextField } from '../../../../../../../../fields/config/types'
|
import type { RichTextField } from '../../../../../../../../fields/config/types'
|
||||||
import type { RichTextAdapter } from '../../../../../../forms/field-types/RichText/types'
|
import type { RichTextAdapter } from '../../../../../../forms/field-types/RichText/types'
|
||||||
@@ -7,9 +7,30 @@ import type { CellComponentProps } from '../../types'
|
|||||||
const RichTextCell: React.FC<CellComponentProps<RichTextField>> = (props) => {
|
const RichTextCell: React.FC<CellComponentProps<RichTextField>> = (props) => {
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
// eslint-disable-next-line react/destructuring-assignment
|
||||||
const editor: RichTextAdapter = props.field.editor
|
const editor: RichTextAdapter = props.field.editor
|
||||||
const { CellComponent } = editor
|
|
||||||
|
|
||||||
return <CellComponent {...props} />
|
const isLazy = 'LazyCellComponent' in editor
|
||||||
|
|
||||||
|
const ImportedCellComponent: React.FC<any> = useMemo(() => {
|
||||||
|
return isLazy
|
||||||
|
? React.lazy(() => {
|
||||||
|
return editor.LazyCellComponent().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
}, [editor, isLazy])
|
||||||
|
|
||||||
|
if (isLazy) {
|
||||||
|
return (
|
||||||
|
ImportedCellComponent && (
|
||||||
|
<React.Suspense>
|
||||||
|
<ImportedCellComponent {...props} />
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <editor.CellComponent {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RichTextCell
|
export default RichTextCell
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import joi from 'joi'
|
import joi from 'joi'
|
||||||
|
|
||||||
import { adminViewSchema } from './shared/adminViewSchema'
|
import { adminViewSchema } from './shared/adminViewSchema'
|
||||||
import { livePreviewSchema } from './shared/componentSchema'
|
import { componentSchema, livePreviewSchema } from './shared/componentSchema'
|
||||||
|
|
||||||
const component = joi.alternatives().try(joi.object().unknown(), joi.func())
|
const component = joi.alternatives().try(joi.object().unknown(), joi.func())
|
||||||
|
|
||||||
@@ -94,8 +94,10 @@ export default joi.object({
|
|||||||
.object()
|
.object()
|
||||||
.required()
|
.required()
|
||||||
.keys({
|
.keys({
|
||||||
CellComponent: component.required(),
|
CellComponent: componentSchema.optional(),
|
||||||
FieldComponent: component.required(),
|
FieldComponent: componentSchema.optional(),
|
||||||
|
LazyCellComponent: joi.func().optional(),
|
||||||
|
LazyFieldComponent: joi.func().optional(),
|
||||||
afterReadPromise: joi.func().optional(),
|
afterReadPromise: joi.func().optional(),
|
||||||
outputSchema: joi.func().optional(),
|
outputSchema: joi.func().optional(),
|
||||||
populationPromise: joi.func().optional(),
|
populationPromise: joi.func().optional(),
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Important:
|
||||||
|
|
||||||
|
When you export anything with a scss or svg, or any component with a hook, it should be exported from a file within payload/components
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -436,8 +436,10 @@ export const richText = baseField.keys({
|
|||||||
editor: joi
|
editor: joi
|
||||||
.object()
|
.object()
|
||||||
.keys({
|
.keys({
|
||||||
CellComponent: componentSchema.required(),
|
CellComponent: componentSchema.optional(),
|
||||||
FieldComponent: componentSchema.required(),
|
FieldComponent: componentSchema.optional(),
|
||||||
|
LazyCellComponent: joi.func().optional(),
|
||||||
|
LazyFieldComponent: joi.func().optional(),
|
||||||
afterReadPromise: joi.func().optional(),
|
afterReadPromise: joi.func().optional(),
|
||||||
outputSchema: joi.func().optional(),
|
outputSchema: joi.func().optional(),
|
||||||
populationPromise: joi.func().optional(),
|
populationPromise: joi.func().optional(),
|
||||||
|
|||||||
4
packages/richtext-lexical/.gitignore
vendored
Normal file
4
packages/richtext-lexical/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/utilities.d.ts
|
||||||
|
/utilities.js
|
||||||
|
/components.d.ts
|
||||||
|
/components.js
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types && ts-node -T ../../scripts/exportPointerFiles.ts ../packages/richtext-lexical dist/exports",
|
||||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||||
"build:clean": "find . \\( -type d \\( -name build -o -name dist -o -name .cache \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} + && pnpm build",
|
"build:clean": "find . \\( -type d \\( -name build -o -name dist -o -name .cache \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} + && pnpm build",
|
||||||
@@ -51,8 +51,14 @@
|
|||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"default": "./src/index.ts",
|
"import": "./src/index.ts",
|
||||||
|
"require": "./src/index.ts",
|
||||||
"types": "./src/index.ts"
|
"types": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"./*": {
|
||||||
|
"import": "./src/exports/*.ts",
|
||||||
|
"require": "./src/exports/*.ts",
|
||||||
|
"types": "./src/exports/*.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -62,6 +68,8 @@
|
|||||||
"types": "./dist/index.d.ts"
|
"types": "./dist/index.d.ts"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist",
|
||||||
|
"components.js",
|
||||||
|
"components.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { SerializedEditorState } from 'lexical'
|
import type { SerializedEditorState } from 'lexical'
|
||||||
|
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
||||||
import type { CellComponentProps, RichTextField } from 'payload/types'
|
import type { CellComponentProps, RichTextField } from 'payload/types'
|
||||||
|
|
||||||
import { createHeadlessEditor } from '@lexical/headless'
|
import { createHeadlessEditor } from '@lexical/headless'
|
||||||
@@ -50,11 +51,12 @@ export const RichTextCell: React.FC<
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editorConfig.lexical().then((lexicalConfig: LexicalEditorConfig) => {
|
||||||
// initialize headless editor
|
// initialize headless editor
|
||||||
const headlessEditor = createHeadlessEditor({
|
const headlessEditor = createHeadlessEditor({
|
||||||
namespace: editorConfig.lexical.namespace,
|
namespace: lexicalConfig.namespace,
|
||||||
nodes: getEnabledNodes({ editorConfig }),
|
nodes: getEnabledNodes({ editorConfig }),
|
||||||
theme: editorConfig.lexical.theme,
|
theme: lexicalConfig.theme,
|
||||||
})
|
})
|
||||||
headlessEditor.setEditorState(headlessEditor.parseEditorState(dataToUse))
|
headlessEditor.setEditorState(headlessEditor.parseEditorState(dataToUse))
|
||||||
|
|
||||||
@@ -65,6 +67,7 @@ export const RichTextCell: React.FC<
|
|||||||
|
|
||||||
// Limiting the number of characters shown is done in a CSS rule
|
// Limiting the number of characters shown is done in a CSS rule
|
||||||
setPreview(textContent)
|
setPreview(textContent)
|
||||||
|
})
|
||||||
}, [data, editorConfig])
|
}, [data, editorConfig])
|
||||||
|
|
||||||
return <span>{preview}</span>
|
return <span>{preview}</span>
|
||||||
|
|||||||
6
packages/richtext-lexical/src/exports/components.ts
Normal file
6
packages/richtext-lexical/src/exports/components.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export { RichTextCell } from '../cell'
|
||||||
|
export { RichTextField } from '../field'
|
||||||
|
|
||||||
|
export { defaultEditorLexicalConfig } from '../field/lexical/config/defaultClient'
|
||||||
|
export { ToolbarButton } from '../field/lexical/plugins/FloatingSelectToolbar/ToolbarButton'
|
||||||
|
export { ToolbarDropdown } from '../field/lexical/plugins/FloatingSelectToolbar/ToolbarDropdown/index'
|
||||||
@@ -73,11 +73,11 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
<div className={`${baseClass}__wrap`}>
|
<div className={`${baseClass}__wrap`}>
|
||||||
<Error message={errorMessage} showError={showError} />
|
<Error message={errorMessage} showError={showError} />
|
||||||
<Label htmlFor={`field-${path.replace(/\./g, '__')}`} label={label} required={required} />
|
<Label htmlFor={`field-${path.replace(/\./g, '__')}`} label={label} required={required} />
|
||||||
<ErrorBoundary fallbackRender={fallbackRender} onReset={(details) => {}}>
|
<ErrorBoundary fallbackRender={fallbackRender} onReset={() => {}}>
|
||||||
<LexicalProvider
|
<LexicalProvider
|
||||||
editorConfig={editorConfig}
|
editorConfig={editorConfig}
|
||||||
fieldProps={props}
|
fieldProps={props}
|
||||||
onChange={(editorState, editor, tags) => {
|
onChange={(editorState) => {
|
||||||
let serializedEditorState = editorState.toJSON()
|
let serializedEditorState = editorState.toJSON()
|
||||||
|
|
||||||
// Transform state through save hooks
|
// Transform state through save hooks
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import type { HTMLConverter } from '../converters/html/converter/types'
|
|||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { BlockquoteIcon } from '../../lexical/ui/icons/Blockquote'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
||||||
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
||||||
import { MarkdownTransformer } from './markdownTransformer'
|
import { MarkdownTransformer } from './markdownTransformer'
|
||||||
@@ -21,8 +20,12 @@ export const BlockQuoteFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: BlockquoteIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Blockquote').then(
|
||||||
|
(module) => module.BlockquoteIcon,
|
||||||
|
),
|
||||||
|
isActive: () => false,
|
||||||
key: 'blockquote',
|
key: 'blockquote',
|
||||||
label: `Blockquote`,
|
label: `Blockquote`,
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
@@ -70,7 +73,11 @@ export const BlockQuoteFeature = (): FeatureProvider => {
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption(`blockquote`, {
|
new SlashMenuOption(`blockquote`, {
|
||||||
Icon: BlockquoteIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Blockquote').then(
|
||||||
|
(module) => module.BlockquoteIcon,
|
||||||
|
),
|
||||||
displayName: `Blockquote`,
|
displayName: `Blockquote`,
|
||||||
keywords: ['quote', 'blockquote'],
|
keywords: ['quote', 'blockquote'],
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -18,8 +18,7 @@ import type { BlocksFeatureProps } from '..'
|
|||||||
|
|
||||||
import { useEditorConfigContext } from '../../../lexical/config/EditorConfigProvider'
|
import { useEditorConfigContext } from '../../../lexical/config/EditorConfigProvider'
|
||||||
import { $createBlockNode } from '../nodes/BlocksNode'
|
import { $createBlockNode } from '../nodes/BlocksNode'
|
||||||
import { INSERT_BLOCK_COMMAND } from '../plugin'
|
import { INSERT_BLOCK_COMMAND } from '../plugin/commands'
|
||||||
import './index.scss'
|
|
||||||
const baseClass = 'lexical-blocks-drawer'
|
const baseClass = 'lexical-blocks-drawer'
|
||||||
|
|
||||||
export const INSERT_BLOCK_WITH_DRAWER_COMMAND: LexicalCommand<{
|
export const INSERT_BLOCK_WITH_DRAWER_COMMAND: LexicalCommand<{
|
||||||
@@ -64,7 +63,7 @@ export const BlocksDrawerComponent: React.FC = () => {
|
|||||||
const [replaceNodeKey, setReplaceNodeKey] = useState<null | string>(null)
|
const [replaceNodeKey, setReplaceNodeKey] = useState<null | string>(null)
|
||||||
const editDepth = useEditDepth()
|
const editDepth = useEditDepth()
|
||||||
const { t } = useTranslation('fields')
|
const { t } = useTranslation('fields')
|
||||||
const { closeModal, openModal } = useModal()
|
const { openModal } = useModal()
|
||||||
|
|
||||||
const labels = {
|
const labels = {
|
||||||
plural: t('blocks') || 'Blocks',
|
plural: t('blocks') || 'Blocks',
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -6,10 +6,8 @@ import { formatLabels, getTranslation } from 'payload/utilities'
|
|||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { BlockIcon } from '../../lexical/ui/icons/Block'
|
|
||||||
import './index.scss'
|
|
||||||
import { BlockNode } from './nodes/BlocksNode'
|
import { BlockNode } from './nodes/BlocksNode'
|
||||||
import { BlocksPlugin, INSERT_BLOCK_COMMAND } from './plugin'
|
import { INSERT_BLOCK_COMMAND } from './plugin/commands'
|
||||||
import { blockPopulationPromiseHOC } from './populationPromise'
|
import { blockPopulationPromiseHOC } from './populationPromise'
|
||||||
import { blockValidationHOC } from './validate'
|
import { blockValidationHOC } from './validate'
|
||||||
|
|
||||||
@@ -43,7 +41,9 @@ export const BlocksFeature = (props?: BlocksFeatureProps): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: BlocksPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.BlocksPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -56,7 +56,9 @@ export const BlocksFeature = (props?: BlocksFeatureProps): FeatureProvider => {
|
|||||||
options: [
|
options: [
|
||||||
...props.blocks.map((block) => {
|
...props.blocks.map((block) => {
|
||||||
return new SlashMenuOption('block-' + block.slug, {
|
return new SlashMenuOption('block-' + block.slug, {
|
||||||
Icon: BlockIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Block').then((module) => module.BlockIcon),
|
||||||
displayName: ({ i18n }) => {
|
displayName: ({ i18n }) => {
|
||||||
return getTranslation(block.labels.singular, i18n)
|
return getTranslation(block.labels.singular, i18n)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
|
|||||||
import ObjectID from 'bson-objectid'
|
import ObjectID from 'bson-objectid'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { BlockComponent } from '../component'
|
|
||||||
import { transformInputFormData } from '../utils/transformInputFormData'
|
import { transformInputFormData } from '../utils/transformInputFormData'
|
||||||
|
|
||||||
export type BlockFields = {
|
export type BlockFields = {
|
||||||
@@ -25,6 +24,13 @@ export type BlockFields = {
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BlockComponent = React.lazy(() =>
|
||||||
|
// @ts-expect-error TypeScript being dumb
|
||||||
|
import('../component').then((module) => ({
|
||||||
|
default: module.BlockComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
export type SerializedBlockNode = Spread<
|
export type SerializedBlockNode = Spread<
|
||||||
{
|
{
|
||||||
fields: BlockFields
|
fields: BlockFields
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { LexicalCommand } from 'lexical'
|
||||||
|
|
||||||
|
import { createCommand } from 'lexical'
|
||||||
|
|
||||||
|
import type { InsertBlockPayload } from './index'
|
||||||
|
|
||||||
|
export const INSERT_BLOCK_COMMAND: LexicalCommand<InsertBlockPayload> =
|
||||||
|
createCommand('INSERT_BLOCK_COMMAND')
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
import { $insertNodeToNearestRoot, mergeRegister } from '@lexical/utils'
|
import { $insertNodeToNearestRoot, mergeRegister } from '@lexical/utils'
|
||||||
import { COMMAND_PRIORITY_EDITOR, type LexicalCommand, createCommand } from 'lexical'
|
import { COMMAND_PRIORITY_EDITOR } from 'lexical'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
import type { BlockFields } from '../nodes/BlocksNode'
|
import type { BlockFields } from '../nodes/BlocksNode'
|
||||||
|
|
||||||
import { BlocksDrawerComponent } from '../drawer'
|
import { BlocksDrawerComponent } from '../drawer'
|
||||||
import { $createBlockNode, BlockNode } from '../nodes/BlocksNode'
|
import { $createBlockNode, BlockNode } from '../nodes/BlocksNode'
|
||||||
|
import { INSERT_BLOCK_COMMAND } from './commands'
|
||||||
|
|
||||||
export type InsertBlockPayload = Exclude<BlockFields, 'id'>
|
export type InsertBlockPayload = Exclude<BlockFields, 'id'>
|
||||||
|
|
||||||
export const INSERT_BLOCK_COMMAND: LexicalCommand<InsertBlockPayload> =
|
|
||||||
createCommand('INSERT_BLOCK_COMMAND')
|
|
||||||
|
|
||||||
export function BlocksPlugin(): JSX.Element | null {
|
export function BlocksPlugin(): JSX.Element | null {
|
||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { HeadingTagType, SerializedHeadingNode } from '@lexical/rich-text'
|
import type { HeadingTagType, SerializedHeadingNode } from '@lexical/rich-text'
|
||||||
import type React from 'react'
|
|
||||||
|
|
||||||
import { $createHeadingNode, HeadingNode } from '@lexical/rich-text'
|
import { $createHeadingNode, HeadingNode } from '@lexical/rich-text'
|
||||||
import { $setBlocksType } from '@lexical/selection'
|
import { $setBlocksType } from '@lexical/selection'
|
||||||
@@ -9,12 +8,6 @@ import type { HTMLConverter } from '../converters/html/converter/types'
|
|||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { H1Icon } from '../../lexical/ui/icons/H1'
|
|
||||||
import { H2Icon } from '../../lexical/ui/icons/H2'
|
|
||||||
import { H3Icon } from '../../lexical/ui/icons/H3'
|
|
||||||
import { H4Icon } from '../../lexical/ui/icons/H4'
|
|
||||||
import { H5Icon } from '../../lexical/ui/icons/H5'
|
|
||||||
import { H6Icon } from '../../lexical/ui/icons/H6'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
||||||
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
||||||
import { MarkdownTransformer } from './markdownTransformer'
|
import { MarkdownTransformer } from './markdownTransformer'
|
||||||
@@ -30,13 +23,19 @@ type Props = {
|
|||||||
enabledHeadingSizes?: HeadingTagType[]
|
enabledHeadingSizes?: HeadingTagType[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeadingToIconMap: Record<HeadingTagType, React.FC> = {
|
const iconImports = {
|
||||||
h1: H1Icon,
|
// @ts-expect-error
|
||||||
h2: H2Icon,
|
h1: () => import('../../lexical/ui/icons/H1').then((module) => module.H1Icon),
|
||||||
h3: H3Icon,
|
// @ts-expect-error
|
||||||
h4: H4Icon,
|
h2: () => import('../../lexical/ui/icons/H2').then((module) => module.H2Icon),
|
||||||
h5: H5Icon,
|
// @ts-expect-error
|
||||||
h6: H6Icon,
|
h3: () => import('../../lexical/ui/icons/H3').then((module) => module.H3Icon),
|
||||||
|
// @ts-expect-error
|
||||||
|
h4: () => import('../../lexical/ui/icons/H4').then((module) => module.H4Icon),
|
||||||
|
// @ts-expect-error
|
||||||
|
h5: () => import('../../lexical/ui/icons/H5').then((module) => module.H5Icon),
|
||||||
|
// @ts-expect-error
|
||||||
|
h6: () => import('../../lexical/ui/icons/H6').then((module) => module.H6Icon),
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeadingFeature = (props: Props): FeatureProvider => {
|
export const HeadingFeature = (props: Props): FeatureProvider => {
|
||||||
@@ -50,7 +49,7 @@ export const HeadingFeature = (props: Props): FeatureProvider => {
|
|||||||
...enabledHeadingSizes.map((headingSize, i) =>
|
...enabledHeadingSizes.map((headingSize, i) =>
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: HeadingToIconMap[headingSize],
|
ChildComponent: iconImports[headingSize],
|
||||||
isActive: () => false,
|
isActive: () => false,
|
||||||
key: headingSize,
|
key: headingSize,
|
||||||
label: `Heading ${headingSize.charAt(1)}`,
|
label: `Heading ${headingSize.charAt(1)}`,
|
||||||
@@ -98,7 +97,7 @@ export const HeadingFeature = (props: Props): FeatureProvider => {
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption(`heading-${headingSize.charAt(1)}`, {
|
new SlashMenuOption(`heading-${headingSize.charAt(1)}`, {
|
||||||
Icon: HeadingToIconMap[headingSize],
|
Icon: iconImports[headingSize],
|
||||||
displayName: `Heading ${headingSize.charAt(1)}`,
|
displayName: `Heading ${headingSize.charAt(1)}`,
|
||||||
keywords: ['heading', headingSize],
|
keywords: ['heading', headingSize],
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -4,25 +4,18 @@ import type { Field } from 'payload/types'
|
|||||||
|
|
||||||
import { $findMatchingParent } from '@lexical/utils'
|
import { $findMatchingParent } from '@lexical/utils'
|
||||||
import { $getSelection, $isRangeSelection } from 'lexical'
|
import { $getSelection, $isRangeSelection } from 'lexical'
|
||||||
import { withMergedProps } from 'payload/utilities'
|
|
||||||
|
|
||||||
import type { HTMLConverter } from '../converters/html/converter/types'
|
import type { HTMLConverter } from '../converters/html/converter/types'
|
||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
import type { SerializedAutoLinkNode } from './nodes/AutoLinkNode'
|
import type { SerializedAutoLinkNode } from './nodes/AutoLinkNode'
|
||||||
import type { LinkFields, SerializedLinkNode } from './nodes/LinkNode'
|
import type { LinkFields, SerializedLinkNode } from './nodes/LinkNode'
|
||||||
|
|
||||||
import { LinkIcon } from '../../lexical/ui/icons/Link'
|
|
||||||
import { getSelectedNode } from '../../lexical/utils/getSelectedNode'
|
import { getSelectedNode } from '../../lexical/utils/getSelectedNode'
|
||||||
import { FeaturesSectionWithEntries } from '../common/floatingSelectToolbarFeaturesButtonsSection'
|
import { FeaturesSectionWithEntries } from '../common/floatingSelectToolbarFeaturesButtonsSection'
|
||||||
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
import { convertLexicalNodesToHTML } from '../converters/html/converter'
|
||||||
import './index.scss'
|
|
||||||
import { AutoLinkNode } from './nodes/AutoLinkNode'
|
import { AutoLinkNode } from './nodes/AutoLinkNode'
|
||||||
import { $isLinkNode, LinkNode, TOGGLE_LINK_COMMAND } from './nodes/LinkNode'
|
import { $isLinkNode, LinkNode, TOGGLE_LINK_COMMAND } from './nodes/LinkNode'
|
||||||
import { AutoLinkPlugin } from './plugins/autoLink'
|
import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './plugins/floatingLinkEditor/LinkEditor/commands'
|
||||||
import { ClickableLinkPlugin } from './plugins/clickableLink'
|
|
||||||
import { FloatingLinkEditorPlugin } from './plugins/floatingLinkEditor'
|
|
||||||
import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './plugins/floatingLinkEditor/LinkEditor'
|
|
||||||
import { LinkPlugin } from './plugins/link'
|
|
||||||
import { linkPopulationPromiseHOC } from './populationPromise'
|
import { linkPopulationPromiseHOC } from './populationPromise'
|
||||||
|
|
||||||
export type LinkFeatureProps = {
|
export type LinkFeatureProps = {
|
||||||
@@ -38,7 +31,9 @@ export const LinkFeature = (props: LinkFeatureProps): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
FeaturesSectionWithEntries([
|
FeaturesSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: LinkIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Link').then((module) => module.LinkIcon),
|
||||||
isActive: ({ selection }) => {
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
const selectedNode = getSelectedNode(selection)
|
const selectedNode = getSelectedNode(selection)
|
||||||
@@ -134,22 +129,35 @@ export const LinkFeature = (props: LinkFeatureProps): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: LinkPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugins/link').then((module) => module.LinkPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: AutoLinkPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugins/autoLink').then((module) => module.AutoLinkPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: ClickableLinkPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugins/clickableLink').then((module) => module.ClickableLinkPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: withMergedProps({
|
Component: () =>
|
||||||
Component: FloatingLinkEditorPlugin,
|
// @ts-expect-error
|
||||||
|
import('./plugins/floatingLinkEditor').then((module) => {
|
||||||
|
const floatingLinkEditorPlugin = module.FloatingLinkEditorPlugin
|
||||||
|
return import('payload/utilities').then((module) =>
|
||||||
|
module.withMergedProps({
|
||||||
|
Component: floatingLinkEditorPlugin,
|
||||||
toMergeIntoProps: props,
|
toMergeIntoProps: props,
|
||||||
}),
|
}),
|
||||||
|
)
|
||||||
|
}),
|
||||||
position: 'floatingAnchorElem',
|
position: 'floatingAnchorElem',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { LexicalCommand } from 'lexical'
|
||||||
|
|
||||||
|
import { createCommand } from 'lexical'
|
||||||
|
|
||||||
|
import type { LinkPayload } from '../types'
|
||||||
|
|
||||||
|
export const TOGGLE_LINK_WITH_MODAL_COMMAND: LexicalCommand<LinkPayload | null> = createCommand(
|
||||||
|
'TOGGLE_LINK_WITH_MODAL_COMMAND',
|
||||||
|
)
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { LexicalCommand } from 'lexical'
|
|
||||||
import type { Data, Fields } from 'payload/types'
|
import type { Data, Fields } from 'payload/types'
|
||||||
|
|
||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
@@ -12,7 +11,6 @@ import {
|
|||||||
COMMAND_PRIORITY_LOW,
|
COMMAND_PRIORITY_LOW,
|
||||||
KEY_ESCAPE_COMMAND,
|
KEY_ESCAPE_COMMAND,
|
||||||
SELECTION_CHANGE_COMMAND,
|
SELECTION_CHANGE_COMMAND,
|
||||||
createCommand,
|
|
||||||
} from 'lexical'
|
} from 'lexical'
|
||||||
import { formatDrawerSlug } from 'payload/components/elements'
|
import { formatDrawerSlug } from 'payload/components/elements'
|
||||||
import {
|
import {
|
||||||
@@ -38,10 +36,7 @@ import { setFloatingElemPositionForLinkEditor } from '../../../../../lexical/uti
|
|||||||
import { LinkDrawer } from '../../../drawer'
|
import { LinkDrawer } from '../../../drawer'
|
||||||
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '../../../nodes/LinkNode'
|
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '../../../nodes/LinkNode'
|
||||||
import { transformExtraFields } from '../utilities'
|
import { transformExtraFields } from '../utilities'
|
||||||
|
import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './commands'
|
||||||
export const TOGGLE_LINK_WITH_MODAL_COMMAND: LexicalCommand<LinkPayload | null> = createCommand(
|
|
||||||
'TOGGLE_LINK_WITH_MODAL_COMMAND',
|
|
||||||
)
|
|
||||||
|
|
||||||
export function LinkEditor({
|
export function LinkEditor({
|
||||||
anchorElem,
|
anchorElem,
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ import { $createParagraphNode, $getSelection, $isRangeSelection } from 'lexical'
|
|||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { TextIcon } from '../../lexical/ui/icons/Text'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection'
|
||||||
|
|
||||||
export const ParagraphFeature = (): FeatureProvider => {
|
export const ParagraphFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: TextIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Text').then((module) => module.TextIcon),
|
||||||
|
isActive: () => false,
|
||||||
key: 'normal-text',
|
key: 'normal-text',
|
||||||
label: 'Normal Text',
|
label: 'Normal Text',
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
@@ -40,7 +41,9 @@ export const ParagraphFeature = (): FeatureProvider => {
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('paragraph', {
|
new SlashMenuOption('paragraph', {
|
||||||
Icon: TextIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Text').then((module) => module.TextIcon),
|
||||||
displayName: 'Paragraph',
|
displayName: 'Paragraph',
|
||||||
keywords: ['normal', 'paragraph', 'p', 'text'],
|
keywords: ['normal', 'paragraph', 'p', 'text'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { LexicalCommand } from 'lexical'
|
||||||
|
|
||||||
|
import { createCommand } from 'lexical'
|
||||||
|
|
||||||
|
export const INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND: LexicalCommand<{
|
||||||
|
replace: { nodeKey: string } | false
|
||||||
|
}> = createCommand('INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND')
|
||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -1,26 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
import {
|
import { $getNodeByKey, COMMAND_PRIORITY_EDITOR, type LexicalEditor } from 'lexical'
|
||||||
$getNodeByKey,
|
|
||||||
COMMAND_PRIORITY_EDITOR,
|
|
||||||
type LexicalCommand,
|
|
||||||
type LexicalEditor,
|
|
||||||
createCommand,
|
|
||||||
} from 'lexical'
|
|
||||||
import { useListDrawer } from 'payload/components/elements'
|
import { useListDrawer } from 'payload/components/elements'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { $createRelationshipNode } from '../nodes/RelationshipNode'
|
import { $createRelationshipNode } from '../nodes/RelationshipNode'
|
||||||
import { INSERT_RELATIONSHIP_COMMAND } from '../plugins'
|
import { INSERT_RELATIONSHIP_COMMAND } from '../plugins'
|
||||||
import { EnabledRelationshipsCondition } from '../utils/EnabledRelationshipsCondition'
|
import { EnabledRelationshipsCondition } from '../utils/EnabledRelationshipsCondition'
|
||||||
import './index.scss'
|
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from './commands'
|
||||||
|
|
||||||
const baseClass = 'lexical-relationship-drawer'
|
const baseClass = 'lexical-relationship-drawer'
|
||||||
|
|
||||||
export const INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND: LexicalCommand<{
|
|
||||||
replace: { nodeKey: string } | false
|
|
||||||
}> = createCommand('INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND')
|
|
||||||
|
|
||||||
const insertRelationship = ({
|
const insertRelationship = ({
|
||||||
id,
|
id,
|
||||||
editor,
|
editor,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { RelationshipIcon } from '../../lexical/ui/icons/Relationship'
|
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from './drawer/commands'
|
||||||
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from './drawer'
|
|
||||||
import './index.scss'
|
|
||||||
import { RelationshipNode } from './nodes/RelationshipNode'
|
import { RelationshipNode } from './nodes/RelationshipNode'
|
||||||
import RelationshipPlugin from './plugins'
|
|
||||||
import { relationshipPopulationPromise } from './populationPromise'
|
import { relationshipPopulationPromise } from './populationPromise'
|
||||||
|
|
||||||
export const RelationshipFeature = (): FeatureProvider => {
|
export const RelationshipFeature = (): FeatureProvider => {
|
||||||
@@ -22,7 +19,9 @@ export const RelationshipFeature = (): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: RelationshipPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugins').then((module) => module.RelationshipPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -34,7 +33,11 @@ export const RelationshipFeature = (): FeatureProvider => {
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('relationship', {
|
new SlashMenuOption('relationship', {
|
||||||
Icon: RelationshipIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Relationship').then(
|
||||||
|
(module) => module.RelationshipIcon,
|
||||||
|
),
|
||||||
displayName: 'Relationship',
|
displayName: 'Relationship',
|
||||||
keywords: ['relationship', 'relation', 'rel'],
|
keywords: ['relationship', 'relation', 'rel'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ import {
|
|||||||
} from '@lexical/react/LexicalDecoratorBlockNode'
|
} from '@lexical/react/LexicalDecoratorBlockNode'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import { RelationshipComponent } from './components/RelationshipComponent'
|
const RelationshipComponent = React.lazy(() =>
|
||||||
|
// @ts-expect-error TypeScript being dumb
|
||||||
|
import('./components/RelationshipComponent').then((module) => ({
|
||||||
|
default: module.RelationshipComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
export type RelationshipData = {
|
export type RelationshipData = {
|
||||||
relationTo: string
|
relationTo: string
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import type { RelationshipData } from '../RelationshipNode'
|
import type { RelationshipData } from '../RelationshipNode'
|
||||||
|
|
||||||
import { useEditorConfigContext } from '../../../../lexical/config/EditorConfigProvider'
|
import { useEditorConfigContext } from '../../../../lexical/config/EditorConfigProvider'
|
||||||
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from '../../drawer'
|
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from '../../drawer/commands'
|
||||||
import { EnabledRelationshipsCondition } from '../../utils/EnabledRelationshipsCondition'
|
import { EnabledRelationshipsCondition } from '../../utils/EnabledRelationshipsCondition'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const INSERT_RELATIONSHIP_COMMAND: LexicalCommand<RelationshipData> = cre
|
|||||||
'INSERT_RELATIONSHIP_COMMAND',
|
'INSERT_RELATIONSHIP_COMMAND',
|
||||||
)
|
)
|
||||||
|
|
||||||
export default function RelationshipPlugin(): JSX.Element | null {
|
export function RelationshipPlugin(): JSX.Element | null {
|
||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
const { collections } = useConfig()
|
const { collections } = useConfig()
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { UploadData } from '../nodes/UploadNode'
|
|||||||
|
|
||||||
import { useEditorConfigContext } from '../../../lexical/config/EditorConfigProvider'
|
import { useEditorConfigContext } from '../../../lexical/config/EditorConfigProvider'
|
||||||
import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition'
|
import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition'
|
||||||
import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from '../drawer'
|
import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from '../drawer/commands'
|
||||||
import { ExtraFieldsUploadDrawer } from './ExtraFieldsDrawer'
|
import { ExtraFieldsUploadDrawer } from './ExtraFieldsDrawer'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { LexicalCommand } from 'lexical'
|
||||||
|
|
||||||
|
import { createCommand } from 'lexical'
|
||||||
|
|
||||||
|
export const INSERT_UPLOAD_WITH_DRAWER_COMMAND: LexicalCommand<{
|
||||||
|
replace: { nodeKey: string } | false
|
||||||
|
}> = createCommand('INSERT_UPLOAD_WITH_DRAWER_COMMAND')
|
||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -1,26 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
import {
|
import { $getNodeByKey, COMMAND_PRIORITY_EDITOR, type LexicalEditor } from 'lexical'
|
||||||
$getNodeByKey,
|
|
||||||
COMMAND_PRIORITY_EDITOR,
|
|
||||||
type LexicalCommand,
|
|
||||||
type LexicalEditor,
|
|
||||||
createCommand,
|
|
||||||
} from 'lexical'
|
|
||||||
import { useListDrawer } from 'payload/components/elements'
|
import { useListDrawer } from 'payload/components/elements'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition'
|
import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition'
|
||||||
import { $createUploadNode } from '../nodes/UploadNode'
|
import { $createUploadNode } from '../nodes/UploadNode'
|
||||||
import { INSERT_UPLOAD_COMMAND } from '../plugin'
|
import { INSERT_UPLOAD_COMMAND } from '../plugin'
|
||||||
import './index.scss'
|
import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from './commands'
|
||||||
|
|
||||||
const baseClass = 'lexical-upload-drawer'
|
const baseClass = 'lexical-upload-drawer'
|
||||||
|
|
||||||
export const INSERT_UPLOAD_WITH_DRAWER_COMMAND: LexicalCommand<{
|
|
||||||
replace: { nodeKey: string } | false
|
|
||||||
}> = createCommand('INSERT_UPLOAD_WITH_DRAWER_COMMAND')
|
|
||||||
|
|
||||||
const insertUpload = ({
|
const insertUpload = ({
|
||||||
id,
|
id,
|
||||||
editor,
|
editor,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'payload/scss';
|
|
||||||
@@ -7,11 +7,8 @@ import type { FeatureProvider } from '../types'
|
|||||||
import type { SerializedUploadNode } from './nodes/UploadNode'
|
import type { SerializedUploadNode } from './nodes/UploadNode'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { UploadIcon } from '../../lexical/ui/icons/Upload'
|
import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from './drawer/commands'
|
||||||
import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from './drawer'
|
|
||||||
import './index.scss'
|
|
||||||
import { UploadNode } from './nodes/UploadNode'
|
import { UploadNode } from './nodes/UploadNode'
|
||||||
import { UploadPlugin } from './plugin'
|
|
||||||
import { uploadPopulationPromiseHOC } from './populationPromise'
|
import { uploadPopulationPromiseHOC } from './populationPromise'
|
||||||
import { uploadValidation } from './validate'
|
import { uploadValidation } from './validate'
|
||||||
|
|
||||||
@@ -55,7 +52,9 @@ export const UploadFeature = (props?: UploadFeatureProps): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: UploadPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.UploadPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -67,7 +66,9 @@ export const UploadFeature = (props?: UploadFeatureProps): FeatureProvider => {
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('upload', {
|
new SlashMenuOption('upload', {
|
||||||
Icon: UploadIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/Upload').then((module) => module.UploadIcon),
|
||||||
displayName: 'Upload',
|
displayName: 'Upload',
|
||||||
keywords: ['upload', 'image', 'file', 'img', 'picture', 'photo', 'media'],
|
keywords: ['upload', 'image', 'file', 'img', 'picture', 'photo', 'media'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import type {
|
|||||||
FloatingToolbarSectionEntry,
|
FloatingToolbarSectionEntry,
|
||||||
} from '../../lexical/plugins/FloatingSelectToolbar/types'
|
} from '../../lexical/plugins/FloatingSelectToolbar/types'
|
||||||
|
|
||||||
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft'
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const AlignDropdownSectionWithEntries = (
|
export const AlignDropdownSectionWithEntries = (
|
||||||
entries: FloatingToolbarSectionEntry[],
|
entries: FloatingToolbarSectionEntry[],
|
||||||
): FloatingToolbarSection => {
|
): FloatingToolbarSection => {
|
||||||
return {
|
return {
|
||||||
ChildComponent: AlignLeftIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/AlignLeft').then((module) => module.AlignLeftIcon),
|
||||||
entries,
|
entries,
|
||||||
key: 'dropdown-align',
|
key: 'dropdown-align',
|
||||||
order: 2,
|
order: 2,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.floating-select-toolbar-popup__section-dropdown-align {
|
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
@@ -2,21 +2,20 @@ import { FORMAT_ELEMENT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { AlignCenterIcon } from '../../lexical/ui/icons/AlignCenter'
|
|
||||||
import { AlignLeftIcon } from '../../lexical/ui/icons/AlignLeft'
|
|
||||||
import { AlignDropdownSectionWithEntries } from './floatingSelectToolbarAlignDropdownSection'
|
import { AlignDropdownSectionWithEntries } from './floatingSelectToolbarAlignDropdownSection'
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const AlignFeature = (): FeatureProvider => {
|
export const AlignFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
AlignDropdownSectionWithEntries([
|
AlignDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: AlignLeftIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/AlignLeft').then((module) => module.AlignLeftIcon),
|
||||||
|
isActive: () => false,
|
||||||
key: 'align-left',
|
key: 'align-left',
|
||||||
label: `Align Left`,
|
label: `Align Left`,
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
@@ -27,8 +26,12 @@ export const AlignFeature = (): FeatureProvider => {
|
|||||||
]),
|
]),
|
||||||
AlignDropdownSectionWithEntries([
|
AlignDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: AlignCenterIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/AlignCenter').then(
|
||||||
|
(module) => module.AlignCenterIcon,
|
||||||
|
),
|
||||||
|
isActive: () => false,
|
||||||
key: 'align-center',
|
key: 'align-center',
|
||||||
label: `Align Center`,
|
label: `Align Center`,
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
@@ -39,8 +42,12 @@ export const AlignFeature = (): FeatureProvider => {
|
|||||||
]),
|
]),
|
||||||
AlignDropdownSectionWithEntries([
|
AlignDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: AlignLeftIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/AlignRight').then(
|
||||||
|
(module) => module.AlignRightIcon,
|
||||||
|
),
|
||||||
|
isActive: () => false,
|
||||||
key: 'align-right',
|
key: 'align-right',
|
||||||
label: `Align Right`,
|
label: `Align Right`,
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.floating-select-toolbar-popup__section-features {
|
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,6 @@ import type {
|
|||||||
FloatingToolbarSectionEntry,
|
FloatingToolbarSectionEntry,
|
||||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const FeaturesSectionWithEntries = (
|
export const FeaturesSectionWithEntries = (
|
||||||
entries: FloatingToolbarSectionEntry[],
|
entries: FloatingToolbarSectionEntry[],
|
||||||
): FloatingToolbarSection => {
|
): FloatingToolbarSection => {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.floating-select-toolbar-popup__section-dropdown-text {
|
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
@@ -3,14 +3,13 @@ import type {
|
|||||||
FloatingToolbarSectionEntry,
|
FloatingToolbarSectionEntry,
|
||||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
||||||
|
|
||||||
import { TextIcon } from '../../../lexical/ui/icons/Text'
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const TextDropdownSectionWithEntries = (
|
export const TextDropdownSectionWithEntries = (
|
||||||
entries: FloatingToolbarSectionEntry[],
|
entries: FloatingToolbarSectionEntry[],
|
||||||
): FloatingToolbarSection => {
|
): FloatingToolbarSection => {
|
||||||
return {
|
return {
|
||||||
ChildComponent: TextIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Text').then((module) => module.TextIcon),
|
||||||
entries,
|
entries,
|
||||||
key: 'dropdown-text',
|
key: 'dropdown-text',
|
||||||
order: 1,
|
order: 1,
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ export const HTMLConverterFeature = (props?: HTMLConverterFeatureProps): Feature
|
|||||||
if (!props) {
|
if (!props) {
|
||||||
props = {}
|
props = {}
|
||||||
}
|
}
|
||||||
/*const defaultConvertersWithConvertersFromFeatures = defaultConverters
|
|
||||||
defaultConvertersWithConver tersFromFeatures.set(props?
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
feature: () => {
|
feature: () => {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { TestRecorderPlugin } from './plugin'
|
|
||||||
|
|
||||||
export const TestRecorderFeature = (): FeatureProvider => {
|
export const TestRecorderFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: () => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: TestRecorderPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.TestRecorderPlugin),
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
import { TreeViewPlugin } from './plugin'
|
|
||||||
|
|
||||||
export const TreeviewFeature = (): FeatureProvider => {
|
export const TreeviewFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: TreeViewPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.TreeViewPlugin),
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
|
|||||||
import { TreeView } from '@lexical/react/LexicalTreeView'
|
import { TreeView } from '@lexical/react/LexicalTreeView'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
export function TreeViewPlugin(): JSX.Element {
|
export function TreeViewPlugin(): JSX.Element {
|
||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { BoldIcon } from '../../../lexical/ui/icons/Bold'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
import {
|
import {
|
||||||
BOLD_ITALIC_STAR,
|
BOLD_ITALIC_STAR,
|
||||||
@@ -25,8 +24,10 @@ export const BoldTextFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: BoldIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Bold').then((module) => module.BoldIcon),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('bold')
|
return selection.hasFormat('bold')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,21 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { CodeIcon } from '../../../lexical/ui/icons/Code'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
import { INLINE_CODE } from './markdownTransformers'
|
import { INLINE_CODE } from './markdownTransformers'
|
||||||
|
|
||||||
export const InlineCodeTextFeature = (): FeatureProvider => {
|
export const InlineCodeTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ featureProviderMap }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: CodeIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Code').then((module) => module.CodeIcon),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('code')
|
return selection.hasFormat('code')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,21 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { ItalicIcon } from '../../../lexical/ui/icons/Italic'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
import { ITALIC_STAR, ITALIC_UNDERSCORE } from './markdownTransformers'
|
import { ITALIC_STAR, ITALIC_UNDERSCORE } from './markdownTransformers'
|
||||||
|
|
||||||
export const ItalicTextFeature = (): FeatureProvider => {
|
export const ItalicTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: ItalicIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Italic').then((module) => module.ItalicIcon),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('italic')
|
return selection.hasFormat('italic')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import type {
|
|||||||
FloatingToolbarSectionEntry,
|
FloatingToolbarSectionEntry,
|
||||||
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
} from '../../../lexical/plugins/FloatingSelectToolbar/types'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const SectionWithEntries = (
|
export const SectionWithEntries = (
|
||||||
entries: FloatingToolbarSectionEntry[],
|
entries: FloatingToolbarSectionEntry[],
|
||||||
): FloatingToolbarSection => {
|
): FloatingToolbarSection => {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.floating-select-toolbar-popup__section-format {
|
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
@@ -2,20 +2,23 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { StrikethroughIcon } from '../../../lexical/ui/icons/Strikethrough'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
import { STRIKETHROUGH } from './markdownTransformers'
|
import { STRIKETHROUGH } from './markdownTransformers'
|
||||||
|
|
||||||
export const StrikethroughTextFeature = (): FeatureProvider => {
|
export const StrikethroughTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: StrikethroughIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Strikethrough').then(
|
||||||
|
(module) => module.StrikethroughIcon,
|
||||||
|
),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('strikethrough')
|
return selection.hasFormat('strikethrough')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { SubscriptIcon } from '../../../lexical/ui/icons/Subscript'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
|
|
||||||
export const SubscriptTextFeature = (): FeatureProvider => {
|
export const SubscriptTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: SubscriptIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Subscript').then(
|
||||||
|
(module) => module.SubscriptIcon,
|
||||||
|
),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('subscript')
|
return selection.hasFormat('subscript')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { SuperscriptIcon } from '../../../lexical/ui/icons/Superscript'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
|
|
||||||
export const SuperscriptTextFeature = (): FeatureProvider => {
|
export const SuperscriptTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: SuperscriptIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Superscript').then(
|
||||||
|
(module) => module.SuperscriptIcon,
|
||||||
|
),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('superscript')
|
return selection.hasFormat('superscript')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { UnderlineIcon } from '../../../lexical/ui/icons/Underline'
|
|
||||||
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
import { SectionWithEntries } from '../common/floatingSelectToolbarSection'
|
||||||
|
|
||||||
export const UnderlineTextFeature = (): FeatureProvider => {
|
export const UnderlineTextFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
feature: () => {
|
||||||
return {
|
return {
|
||||||
floatingSelectToolbar: {
|
floatingSelectToolbar: {
|
||||||
sections: [
|
sections: [
|
||||||
SectionWithEntries([
|
SectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: UnderlineIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => {
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Underline').then(
|
||||||
|
(module) => module.UnderlineIcon,
|
||||||
|
),
|
||||||
|
isActive: ({ selection }) => {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
return selection.hasFormat('underline')
|
return selection.hasFormat('underline')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import type {
|
|||||||
FloatingToolbarSectionEntry,
|
FloatingToolbarSectionEntry,
|
||||||
} from '../../lexical/plugins/FloatingSelectToolbar/types'
|
} from '../../lexical/plugins/FloatingSelectToolbar/types'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const IndentSectionWithEntries = (
|
export const IndentSectionWithEntries = (
|
||||||
entries: FloatingToolbarSectionEntry[],
|
entries: FloatingToolbarSectionEntry[],
|
||||||
): FloatingToolbarSection => {
|
): FloatingToolbarSection => {
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import { INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND } from 'lexical'
|
|||||||
|
|
||||||
import type { FeatureProvider } from '../types'
|
import type { FeatureProvider } from '../types'
|
||||||
|
|
||||||
import { IndentDecreaseIcon } from '../../lexical/ui/icons/IndentDecrease'
|
|
||||||
import { IndentIncreaseIcon } from '../../lexical/ui/icons/IndentIncrease'
|
|
||||||
import { IndentSectionWithEntries } from './floatingSelectToolbarIndentSection'
|
import { IndentSectionWithEntries } from './floatingSelectToolbarIndentSection'
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export const IndentFeature = (): FeatureProvider => {
|
export const IndentFeature = (): FeatureProvider => {
|
||||||
return {
|
return {
|
||||||
@@ -15,9 +12,13 @@ export const IndentFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
IndentSectionWithEntries([
|
IndentSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: IndentDecreaseIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
isEnabled: ({ editor, selection }) => {
|
import('../../lexical/ui/icons/IndentDecrease').then(
|
||||||
|
(module) => module.IndentDecreaseIcon,
|
||||||
|
),
|
||||||
|
isActive: () => false,
|
||||||
|
isEnabled: ({ selection }) => {
|
||||||
if (!selection || !selection?.getNodes()?.length) {
|
if (!selection || !selection?.getNodes()?.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -39,8 +40,12 @@ export const IndentFeature = (): FeatureProvider => {
|
|||||||
]),
|
]),
|
||||||
IndentSectionWithEntries([
|
IndentSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: IndentIncreaseIcon,
|
ChildComponent: () =>
|
||||||
isActive: ({ editor, selection }) => false,
|
// @ts-expect-error
|
||||||
|
import('../../lexical/ui/icons/IndentIncrease').then(
|
||||||
|
(module) => module.IndentIncreaseIcon,
|
||||||
|
),
|
||||||
|
isActive: () => false,
|
||||||
key: 'indent-increase',
|
key: 'indent-increase',
|
||||||
label: `Increase Indent`,
|
label: `Increase Indent`,
|
||||||
onClick: ({ editor }) => {
|
onClick: ({ editor }) => {
|
||||||
@@ -51,6 +56,14 @@ export const IndentFeature = (): FeatureProvider => {
|
|||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.IndentPlugin),
|
||||||
|
position: 'normal',
|
||||||
|
},
|
||||||
|
],
|
||||||
props: null,
|
props: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
export function IndentPlugin(): null {
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -3,11 +3,9 @@ import { INSERT_CHECK_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/list
|
|||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { ChecklistIcon } from '../../../lexical/ui/icons/Checklist'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||||
import { CHECK_LIST } from './markdownTransformers'
|
import { CHECK_LIST } from './markdownTransformers'
|
||||||
import { LexicalCheckListPlugin } from './plugin'
|
|
||||||
|
|
||||||
// 345
|
// 345
|
||||||
// carbs 7
|
// carbs 7
|
||||||
@@ -19,7 +17,11 @@ export const CheckListFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: ChecklistIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Checklist').then(
|
||||||
|
(module) => module.ChecklistIcon,
|
||||||
|
),
|
||||||
isActive: () => false,
|
isActive: () => false,
|
||||||
key: 'checkList',
|
key: 'checkList',
|
||||||
label: `Check List`,
|
label: `Check List`,
|
||||||
@@ -53,7 +55,9 @@ export const CheckListFeature = (): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: LexicalCheckListPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./plugin').then((module) => module.LexicalCheckListPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -65,7 +69,11 @@ export const CheckListFeature = (): FeatureProvider => {
|
|||||||
key: 'lists',
|
key: 'lists',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('checklist', {
|
new SlashMenuOption('checklist', {
|
||||||
Icon: ChecklistIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/Checklist').then(
|
||||||
|
(module) => module.ChecklistIcon,
|
||||||
|
),
|
||||||
displayName: 'Check List',
|
displayName: 'Check List',
|
||||||
keywords: ['check list', 'check', 'checklist', 'cl'],
|
keywords: ['check list', 'check', 'checklist', 'cl'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ import { INSERT_ORDERED_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/li
|
|||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { OrderedListIcon } from '../../../lexical/ui/icons/OrderedList'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||||
import { LexicalListPlugin } from '../plugin'
|
|
||||||
import { ORDERED_LIST } from './markdownTransformer'
|
import { ORDERED_LIST } from './markdownTransformer'
|
||||||
|
|
||||||
export const OrderedListFeature = (): FeatureProvider => {
|
export const OrderedListFeature = (): FeatureProvider => {
|
||||||
@@ -17,7 +15,11 @@ export const OrderedListFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: OrderedListIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/OrderedList').then(
|
||||||
|
(module) => module.OrderedListIcon,
|
||||||
|
),
|
||||||
isActive: () => false,
|
isActive: () => false,
|
||||||
key: 'orderedList',
|
key: 'orderedList',
|
||||||
label: `Ordered List`,
|
label: `Ordered List`,
|
||||||
@@ -52,7 +54,9 @@ export const OrderedListFeature = (): FeatureProvider => {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
Component: LexicalListPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../plugin').then((module) => module.LexicalListPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -64,7 +68,11 @@ export const OrderedListFeature = (): FeatureProvider => {
|
|||||||
key: 'lists',
|
key: 'lists',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('orderedlist', {
|
new SlashMenuOption('orderedlist', {
|
||||||
Icon: OrderedListIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/OrderedList').then(
|
||||||
|
(module) => module.OrderedListIcon,
|
||||||
|
),
|
||||||
displayName: 'Ordered List',
|
displayName: 'Ordered List',
|
||||||
keywords: ['ordered list', 'ol'],
|
keywords: ['ordered list', 'ol'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ import { INSERT_UNORDERED_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/
|
|||||||
import type { FeatureProvider } from '../../types'
|
import type { FeatureProvider } from '../../types'
|
||||||
|
|
||||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||||
import { UnorderedListIcon } from '../../../lexical/ui/icons/UnorderedList'
|
|
||||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||||
import { LexicalListPlugin } from '../plugin'
|
|
||||||
import { UNORDERED_LIST } from './markdownTransformer'
|
import { UNORDERED_LIST } from './markdownTransformer'
|
||||||
|
|
||||||
export const UnorderedListFeature = (): FeatureProvider => {
|
export const UnorderedListFeature = (): FeatureProvider => {
|
||||||
@@ -17,7 +15,11 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
|||||||
sections: [
|
sections: [
|
||||||
TextDropdownSectionWithEntries([
|
TextDropdownSectionWithEntries([
|
||||||
{
|
{
|
||||||
ChildComponent: UnorderedListIcon,
|
ChildComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/UnorderedList').then(
|
||||||
|
(module) => module.UnorderedListIcon,
|
||||||
|
),
|
||||||
isActive: () => false,
|
isActive: () => false,
|
||||||
key: 'unorderedList',
|
key: 'unorderedList',
|
||||||
label: `Unordered List`,
|
label: `Unordered List`,
|
||||||
@@ -48,7 +50,9 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
|||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
Component: LexicalListPlugin,
|
Component: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../plugin').then((module) => module.LexicalListPlugin),
|
||||||
position: 'normal',
|
position: 'normal',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -60,7 +64,11 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
|||||||
key: 'lists',
|
key: 'lists',
|
||||||
options: [
|
options: [
|
||||||
new SlashMenuOption('unorderedlist', {
|
new SlashMenuOption('unorderedlist', {
|
||||||
Icon: UnorderedListIcon,
|
Icon: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('../../../lexical/ui/icons/UnorderedList').then(
|
||||||
|
(module) => module.UnorderedListIcon,
|
||||||
|
),
|
||||||
displayName: 'Unordered List',
|
displayName: 'Unordered List',
|
||||||
keywords: ['unordered list', 'ul'],
|
keywords: ['unordered list', 'ul'],
|
||||||
onSelect: ({ editor }) => {
|
onSelect: ({ editor }) => {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { UnknownConvertedNodeData } from './index'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: UnknownConvertedNodeData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UnknownConvertedNodeComponent: React.FC<Props> = (props) => {
|
||||||
|
const { data } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Unknown converted payload-plugin-lexical node: <strong>{data?.nodeType}</strong>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,9 +2,7 @@ import type { SerializedLexicalNode, Spread } from 'lexical'
|
|||||||
|
|
||||||
import { addClassNamesToElement } from '@lexical/utils'
|
import { addClassNamesToElement } from '@lexical/utils'
|
||||||
import { DecoratorNode, type EditorConfig, type LexicalNode, type NodeKey } from 'lexical'
|
import { DecoratorNode, type EditorConfig, type LexicalNode, type NodeKey } from 'lexical'
|
||||||
import React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export type UnknownConvertedNodeData = {
|
export type UnknownConvertedNodeData = {
|
||||||
nodeData: unknown
|
nodeData: unknown
|
||||||
@@ -18,6 +16,13 @@ export type SerializedUnknownConvertedNode = Spread<
|
|||||||
SerializedLexicalNode
|
SerializedLexicalNode
|
||||||
>
|
>
|
||||||
|
|
||||||
|
const Component = React.lazy(() =>
|
||||||
|
// @ts-expect-error TypeScript being dumb
|
||||||
|
import('./Component').then((module) => ({
|
||||||
|
default: module.UnknownConvertedNodeComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
/** @noInheritDoc */
|
/** @noInheritDoc */
|
||||||
export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
||||||
__data: UnknownConvertedNodeData
|
__data: UnknownConvertedNodeData
|
||||||
@@ -58,11 +63,7 @@ export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decorate(): JSX.Element | null {
|
decorate(): JSX.Element | null {
|
||||||
return (
|
return <Component data={this.__data} />
|
||||||
<div>
|
|
||||||
Unknown converted payload-plugin-lexical node: <strong>{this.__data?.nodeType}</strong>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedUnknownConvertedNode {
|
exportJSON(): SerializedUnknownConvertedNode {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { UnknownConvertedNodeData } from './index'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: UnknownConvertedNodeData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UnknownConvertedNodeComponent: React.FC<Props> = (props) => {
|
||||||
|
const { data } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Unknown converted Slate node: <strong>{data?.nodeType}</strong>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,9 +2,7 @@ import type { SerializedLexicalNode, Spread } from 'lexical'
|
|||||||
|
|
||||||
import { addClassNamesToElement } from '@lexical/utils'
|
import { addClassNamesToElement } from '@lexical/utils'
|
||||||
import { DecoratorNode, type EditorConfig, type LexicalNode, type NodeKey } from 'lexical'
|
import { DecoratorNode, type EditorConfig, type LexicalNode, type NodeKey } from 'lexical'
|
||||||
import React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
export type UnknownConvertedNodeData = {
|
export type UnknownConvertedNodeData = {
|
||||||
nodeData: unknown
|
nodeData: unknown
|
||||||
@@ -18,6 +16,13 @@ export type SerializedUnknownConvertedNode = Spread<
|
|||||||
SerializedLexicalNode
|
SerializedLexicalNode
|
||||||
>
|
>
|
||||||
|
|
||||||
|
const Component = React.lazy(() =>
|
||||||
|
// @ts-expect-error TypeScript being dumb
|
||||||
|
import('./Component').then((module) => ({
|
||||||
|
default: module.UnknownConvertedNodeComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
/** @noInheritDoc */
|
/** @noInheritDoc */
|
||||||
export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
||||||
__data: UnknownConvertedNodeData
|
__data: UnknownConvertedNodeData
|
||||||
@@ -58,11 +63,7 @@ export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decorate(): JSX.Element | null {
|
decorate(): JSX.Element | null {
|
||||||
return (
|
return <Component data={this.__data} />
|
||||||
<div>
|
|
||||||
Unknown converted Slate node: <strong>{this.__data?.nodeType}</strong>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedUnknownConvertedNode {
|
exportJSON(): SerializedUnknownConvertedNode {
|
||||||
|
|||||||
@@ -99,23 +99,23 @@ export type Feature = {
|
|||||||
plugins?: Array<
|
plugins?: Array<
|
||||||
| {
|
| {
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
Component: React.FC
|
Component: () => Promise<React.FC<{ anchorElem: HTMLElement }>>
|
||||||
position: 'normal' // Determines at which position the Component will be added.
|
position: 'floatingAnchorElem' // Determines at which position the Component will be added.
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
Component: React.FC
|
Component: () => Promise<React.FC>
|
||||||
position: 'top' // Determines at which position the Component will be added.
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
|
||||||
Component: React.FC<{ anchorElem: HTMLElement }>
|
|
||||||
position: 'bottom' // Determines at which position the Component will be added.
|
position: 'bottom' // Determines at which position the Component will be added.
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
Component: React.FC<{ anchorElem: HTMLElement }>
|
Component: () => Promise<React.FC>
|
||||||
position: 'floatingAnchorElem' // Determines at which position the Component will be added.
|
position: 'normal' // Determines at which position the Component will be added.
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
|
Component: () => Promise<React.FC>
|
||||||
|
position: 'top' // Determines at which position the Component will be added.
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
@@ -161,6 +161,33 @@ export type ResolvedFeatureMap = Map<string, ResolvedFeature>
|
|||||||
|
|
||||||
export type FeatureProviderMap = Map<string, FeatureProvider>
|
export type FeatureProviderMap = Map<string, FeatureProvider>
|
||||||
|
|
||||||
|
export type SanitizedPlugin =
|
||||||
|
| {
|
||||||
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
|
Component: () => Promise<React.FC<{ anchorElem: HTMLElement }>>
|
||||||
|
desktopOnly?: boolean
|
||||||
|
key: string
|
||||||
|
position: 'floatingAnchorElem' // Determines at which position the Component will be added.
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
|
Component: () => Promise<React.FC>
|
||||||
|
key: string
|
||||||
|
position: 'bottom' // Determines at which position the Component will be added.
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
|
Component: () => Promise<React.FC>
|
||||||
|
key: string
|
||||||
|
position: 'normal' // Determines at which position the Component will be added.
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
||||||
|
Component: () => Promise<React.FC>
|
||||||
|
key: string
|
||||||
|
position: 'top' // Determines at which position the Component will be added.
|
||||||
|
}
|
||||||
|
|
||||||
export type SanitizedFeatures = Required<
|
export type SanitizedFeatures = Required<
|
||||||
Pick<ResolvedFeature, 'markdownTransformers' | 'nodes'>
|
Pick<ResolvedFeature, 'markdownTransformers' | 'nodes'>
|
||||||
> & {
|
> & {
|
||||||
@@ -200,33 +227,7 @@ export type SanitizedFeatures = Required<
|
|||||||
}) => SerializedEditorState
|
}) => SerializedEditorState
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
plugins?: Array<
|
plugins?: Array<SanitizedPlugin>
|
||||||
| {
|
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
|
||||||
Component: React.FC
|
|
||||||
key: string
|
|
||||||
position: 'bottom' // Determines at which position the Component will be added.
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
|
||||||
Component: React.FC
|
|
||||||
key: string
|
|
||||||
position: 'normal' // Determines at which position the Component will be added.
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
|
||||||
Component: React.FC
|
|
||||||
key: string
|
|
||||||
position: 'top' // Determines at which position the Component will be added.
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
// plugins are anything which is not directly part of the editor. Like, creating a command which creates a node, or opens a modal, or some other more "outside" functionality
|
|
||||||
Component: React.FC<{ anchorElem: HTMLElement }>
|
|
||||||
desktopOnly?: boolean
|
|
||||||
key: string
|
|
||||||
position: 'floatingAnchorElem' // Determines at which position the Component will be added.
|
|
||||||
}
|
|
||||||
>
|
|
||||||
/** The node types mapped to their populationPromises */
|
/** The node types mapped to their populationPromises */
|
||||||
populationPromises: Map<string, Array<PopulationPromise>>
|
populationPromises: Map<string, Array<PopulationPromise>>
|
||||||
slashMenu: {
|
slashMenu: {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import type { FieldProps } from '../types'
|
|||||||
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
|
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
|
||||||
const RichTextEditor = lazy(() => import('./Field'))
|
const RichTextEditor = lazy(() => import('./Field'))
|
||||||
|
|
||||||
export const RichTextField: React.FC<FieldProps> = (props) => (
|
export const RichTextField: React.FC<FieldProps> = (props) => {
|
||||||
|
return (
|
||||||
<Suspense fallback={<ShimmerEffect height="35vh" />}>
|
<Suspense fallback={<ShimmerEffect height="35vh" />}>
|
||||||
<RichTextEditor {...props} />
|
<RichTextEditor {...props} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|||||||
37
packages/richtext-lexical/src/field/lexical/EditorPlugin.tsx
Normal file
37
packages/richtext-lexical/src/field/lexical/EditorPlugin.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
import type { SanitizedPlugin } from '../features/types'
|
||||||
|
|
||||||
|
export const EditorPlugin: React.FC<{
|
||||||
|
anchorElem?: HTMLDivElement
|
||||||
|
plugin: SanitizedPlugin
|
||||||
|
}> = ({ anchorElem, plugin }) => {
|
||||||
|
const Component: React.FC<any> = useMemo(() => {
|
||||||
|
return plugin?.Component
|
||||||
|
? React.lazy(() =>
|
||||||
|
plugin.Component().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [plugin]) // Dependency array ensures this is only recomputed if entry changes
|
||||||
|
|
||||||
|
if (plugin.position === 'floatingAnchorElem') {
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<React.Suspense>
|
||||||
|
<Component anchorElem={anchorElem} />
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<React.Suspense>
|
||||||
|
<Component />
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -39,3 +39,21 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-select-toolbar-popup__section-dropdown-align {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.floating-select-toolbar-popup__section-features {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.floating-select-toolbar-popup__section-dropdown-text {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-select-toolbar-popup__section-format {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
import type { LexicalProviderProps } from './LexicalProvider'
|
import type { LexicalProviderProps } from './LexicalProvider'
|
||||||
|
|
||||||
|
import { EditorPlugin } from './EditorPlugin'
|
||||||
import './LexicalEditor.scss'
|
import './LexicalEditor.scss'
|
||||||
import { FloatingSelectToolbarPlugin } from './plugins/FloatingSelectToolbar'
|
import { FloatingSelectToolbarPlugin } from './plugins/FloatingSelectToolbar'
|
||||||
import { MarkdownShortcutPlugin } from './plugins/MarkdownShortcut'
|
import { MarkdownShortcutPlugin } from './plugins/MarkdownShortcut'
|
||||||
@@ -53,7 +54,7 @@ export const LexicalEditor: React.FC<Pick<LexicalProviderProps, 'editorConfig' |
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{editorConfig.features.plugins.map((plugin) => {
|
{editorConfig.features.plugins.map((plugin) => {
|
||||||
if (plugin.position === 'top') {
|
if (plugin.position === 'top') {
|
||||||
return <plugin.Component key={plugin.key} />
|
return <EditorPlugin key={plugin.key} plugin={plugin} />
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
<RichTextPlugin
|
<RichTextPlugin
|
||||||
@@ -65,10 +66,12 @@ export const LexicalEditor: React.FC<Pick<LexicalProviderProps, 'editorConfig' |
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
placeholder={<p className="editor-placeholder">Start typing, or press '/' for commands...</p>}
|
placeholder={
|
||||||
|
<p className="editor-placeholder">Start typing, or press '/' for commands...</p>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<OnChangePlugin
|
<OnChangePlugin
|
||||||
// Selection changes can be ignore here, reducing the
|
// Selection changes can be ignored here, reducing the
|
||||||
// frequency that the FieldComponent and Payload receive updates.
|
// frequency that the FieldComponent and Payload receive updates.
|
||||||
// Selection changes are only needed if you are saving selection state
|
// Selection changes are only needed if you are saving selection state
|
||||||
ignoreSelectionChange
|
ignoreSelectionChange
|
||||||
@@ -92,7 +95,9 @@ export const LexicalEditor: React.FC<Pick<LexicalProviderProps, 'editorConfig' |
|
|||||||
plugin.position === 'floatingAnchorElem' &&
|
plugin.position === 'floatingAnchorElem' &&
|
||||||
!(plugin.desktopOnly === true && isSmallWidthViewport)
|
!(plugin.desktopOnly === true && isSmallWidthViewport)
|
||||||
) {
|
) {
|
||||||
return <plugin.Component anchorElem={floatingAnchorElem} key={plugin.key} />
|
return (
|
||||||
|
<EditorPlugin anchorElem={floatingAnchorElem} key={plugin.key} plugin={plugin} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
{editor.isEditable() && (
|
{editor.isEditable() && (
|
||||||
@@ -113,12 +118,12 @@ export const LexicalEditor: React.FC<Pick<LexicalProviderProps, 'editorConfig' |
|
|||||||
<TabIndentationPlugin />
|
<TabIndentationPlugin />
|
||||||
{editorConfig.features.plugins.map((plugin) => {
|
{editorConfig.features.plugins.map((plugin) => {
|
||||||
if (plugin.position === 'normal') {
|
if (plugin.position === 'normal') {
|
||||||
return <plugin.Component key={plugin.key} />
|
return <EditorPlugin key={plugin.key} plugin={plugin} />
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
{editorConfig.features.plugins.map((plugin) => {
|
{editorConfig.features.plugins.map((plugin) => {
|
||||||
if (plugin.position === 'bottom') {
|
if (plugin.position === 'bottom') {
|
||||||
return <plugin.Component key={plugin.key} />
|
return <EditorPlugin key={plugin.key} plugin={plugin} />
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import type { InitialConfigType } from '@lexical/react/LexicalComposer'
|
import type { InitialConfigType } from '@lexical/react/LexicalComposer'
|
||||||
import type { EditorState, SerializedEditorState } from 'lexical'
|
import type { EditorState, SerializedEditorState } from 'lexical'
|
||||||
import type { LexicalEditor } from 'lexical'
|
import type { LexicalEditor } from 'lexical'
|
||||||
|
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
||||||
|
|
||||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
@@ -24,6 +25,25 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
|
|||||||
const { editorConfig, fieldProps, onChange, path, readOnly } = props
|
const { editorConfig, fieldProps, onChange, path, readOnly } = props
|
||||||
let { value } = props
|
let { value } = props
|
||||||
|
|
||||||
|
const [initialConfig, setInitialConfig] = React.useState<InitialConfigType | null>(null)
|
||||||
|
|
||||||
|
// set lexical config in useffect async:
|
||||||
|
React.useEffect(() => {
|
||||||
|
void editorConfig.lexical().then((lexicalConfig: LexicalEditorConfig) => {
|
||||||
|
const newInitialConfig: InitialConfigType = {
|
||||||
|
editable: readOnly !== true,
|
||||||
|
editorState: value != null ? JSON.stringify(value) : undefined,
|
||||||
|
namespace: lexicalConfig.namespace,
|
||||||
|
nodes: [...getEnabledNodes({ editorConfig })],
|
||||||
|
onError: (error: Error) => {
|
||||||
|
throw error
|
||||||
|
},
|
||||||
|
theme: lexicalConfig.theme,
|
||||||
|
}
|
||||||
|
setInitialConfig(newInitialConfig)
|
||||||
|
})
|
||||||
|
}, [editorConfig, readOnly, value])
|
||||||
|
|
||||||
if (editorConfig?.features?.hooks?.load?.length) {
|
if (editorConfig?.features?.hooks?.load?.length) {
|
||||||
editorConfig.features.hooks.load.forEach((hook) => {
|
editorConfig.features.hooks.load.forEach((hook) => {
|
||||||
value = hook({ incomingEditorState: value })
|
value = hook({ incomingEditorState: value })
|
||||||
@@ -49,15 +69,8 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialConfig: InitialConfigType = {
|
if (!initialConfig) {
|
||||||
editable: readOnly === true ? false : true,
|
return <p>Loading...</p>
|
||||||
editorState: value != null ? JSON.stringify(value) : undefined,
|
|
||||||
namespace: editorConfig.lexical.namespace,
|
|
||||||
nodes: [...getEnabledNodes({ editorConfig })],
|
|
||||||
onError: (error: Error) => {
|
|
||||||
throw error
|
|
||||||
},
|
|
||||||
theme: editorConfig.lexical.theme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
|
||||||
|
|
||||||
import type { FeatureProvider } from '../../features/types'
|
import type { FeatureProvider } from '../../features/types'
|
||||||
import type { EditorConfig, SanitizedEditorConfig } from './types'
|
import type { EditorConfig, SanitizedEditorConfig } from './types'
|
||||||
|
|
||||||
@@ -21,7 +19,6 @@ import { IndentFeature } from '../../features/indent'
|
|||||||
import { CheckListFeature } from '../../features/lists/CheckList'
|
import { CheckListFeature } from '../../features/lists/CheckList'
|
||||||
import { OrderedListFeature } from '../../features/lists/OrderedList'
|
import { OrderedListFeature } from '../../features/lists/OrderedList'
|
||||||
import { UnorderedListFeature } from '../../features/lists/UnorderedList'
|
import { UnorderedListFeature } from '../../features/lists/UnorderedList'
|
||||||
import { LexicalEditorTheme } from '../theme/EditorTheme'
|
|
||||||
import { sanitizeEditorConfig } from './sanitize'
|
import { sanitizeEditorConfig } from './sanitize'
|
||||||
|
|
||||||
export const defaultEditorFeatures: FeatureProvider[] = [
|
export const defaultEditorFeatures: FeatureProvider[] = [
|
||||||
@@ -46,14 +43,14 @@ export const defaultEditorFeatures: FeatureProvider[] = [
|
|||||||
//BlocksFeature(), // Adding this by default makes no sense if no blocks are defined
|
//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 = {
|
export const defaultEditorConfig: EditorConfig = {
|
||||||
features: defaultEditorFeatures,
|
features: defaultEditorFeatures,
|
||||||
lexical: defaultEditorLexicalConfig,
|
lexical: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./defaultClient').then((module) => {
|
||||||
|
const defaultEditorLexicalConfig = module.defaultEditorLexicalConfig
|
||||||
|
return defaultEditorLexicalConfig
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSanitizedEditorConfig: SanitizedEditorConfig =
|
export const defaultSanitizedEditorConfig: SanitizedEditorConfig =
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
||||||
|
|
||||||
|
import { LexicalEditorTheme } from '../theme/EditorTheme'
|
||||||
|
|
||||||
|
export const defaultEditorLexicalConfig: LexicalEditorConfig = {
|
||||||
|
namespace: 'lexical',
|
||||||
|
theme: LexicalEditorTheme,
|
||||||
|
}
|
||||||
@@ -4,11 +4,11 @@ import type { FeatureProvider, ResolvedFeatureMap, SanitizedFeatures } from '../
|
|||||||
|
|
||||||
export type EditorConfig = {
|
export type EditorConfig = {
|
||||||
features: FeatureProvider[]
|
features: FeatureProvider[]
|
||||||
lexical: LexicalEditorConfig
|
lexical?: () => Promise<LexicalEditorConfig>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SanitizedEditorConfig = {
|
export type SanitizedEditorConfig = {
|
||||||
features: SanitizedFeatures
|
features: SanitizedFeatures
|
||||||
lexical: LexicalEditorConfig
|
lexical: () => Promise<LexicalEditorConfig>
|
||||||
resolvedFeatureMap: ResolvedFeatureMap
|
resolvedFeatureMap: ResolvedFeatureMap
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
const baseClass = 'floating-select-toolbar-popup__dropdown'
|
const baseClass = 'floating-select-toolbar-popup__dropdown'
|
||||||
|
|
||||||
@@ -10,6 +10,57 @@ import type { FloatingToolbarSectionEntry } from '../types'
|
|||||||
import { DropDown, DropDownItem } from './DropDown'
|
import { DropDown, DropDownItem } from './DropDown'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
export const ToolbarEntry = ({
|
||||||
|
anchorElem,
|
||||||
|
editor,
|
||||||
|
entry,
|
||||||
|
}: {
|
||||||
|
anchorElem: HTMLElement
|
||||||
|
editor: LexicalEditor
|
||||||
|
entry: FloatingToolbarSectionEntry
|
||||||
|
}) => {
|
||||||
|
const Component = useMemo(() => {
|
||||||
|
return entry?.Component
|
||||||
|
? React.lazy(() =>
|
||||||
|
entry.Component().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [entry])
|
||||||
|
|
||||||
|
const ChildComponent = useMemo(() => {
|
||||||
|
return entry?.ChildComponent
|
||||||
|
? React.lazy(() =>
|
||||||
|
entry.ChildComponent().then((resolvedChildComponent) => ({
|
||||||
|
default: resolvedChildComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [entry])
|
||||||
|
|
||||||
|
if (entry.Component) {
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<React.Suspense>
|
||||||
|
<Component anchorElem={anchorElem} editor={editor} entry={entry} key={entry.key} />
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropDownItem entry={entry} key={entry.key}>
|
||||||
|
{ChildComponent && (
|
||||||
|
<React.Suspense>
|
||||||
|
<ChildComponent />
|
||||||
|
</React.Suspense>
|
||||||
|
)}
|
||||||
|
<span className="text">{entry.label}</span>
|
||||||
|
</DropDownItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const ToolbarDropdown = ({
|
export const ToolbarDropdown = ({
|
||||||
Icon,
|
Icon,
|
||||||
anchorElem,
|
anchorElem,
|
||||||
@@ -31,21 +82,8 @@ export const ToolbarDropdown = ({
|
|||||||
>
|
>
|
||||||
{entries.length &&
|
{entries.length &&
|
||||||
entries.map((entry) => {
|
entries.map((entry) => {
|
||||||
if (entry.Component) {
|
|
||||||
return (
|
return (
|
||||||
<entry.Component
|
<ToolbarEntry anchorElem={anchorElem} editor={editor} entry={entry} key={entry.key} />
|
||||||
anchorElem={anchorElem}
|
|
||||||
editor={editor}
|
|
||||||
entry={entry}
|
|
||||||
key={entry.key}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<DropDownItem entry={entry} key={entry.key}>
|
|
||||||
<entry.ChildComponent />
|
|
||||||
<span className="text">{entry.label}</span>
|
|
||||||
</DropDownItem>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</DropDown>
|
</DropDown>
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import {
|
|||||||
COMMAND_PRIORITY_LOW,
|
COMMAND_PRIORITY_LOW,
|
||||||
SELECTION_CHANGE_COMMAND,
|
SELECTION_CHANGE_COMMAND,
|
||||||
} from 'lexical'
|
} from 'lexical'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
|
import type { FloatingToolbarSection, FloatingToolbarSectionEntry } from './types'
|
||||||
|
|
||||||
import { useEditorConfigContext } from '../../config/EditorConfigProvider'
|
import { useEditorConfigContext } from '../../config/EditorConfigProvider'
|
||||||
import { getDOMRangeRect } from '../../utils/getDOMRangeRect'
|
import { getDOMRangeRect } from '../../utils/getDOMRangeRect'
|
||||||
import { setFloatingElemPosition } from '../../utils/setFloatingElemPosition'
|
import { setFloatingElemPosition } from '../../utils/setFloatingElemPosition'
|
||||||
@@ -21,6 +23,117 @@ import { ToolbarButton } from './ToolbarButton'
|
|||||||
import { ToolbarDropdown } from './ToolbarDropdown'
|
import { ToolbarDropdown } from './ToolbarDropdown'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
function ButtonSectionEntry({
|
||||||
|
anchorElem,
|
||||||
|
editor,
|
||||||
|
entry,
|
||||||
|
}: {
|
||||||
|
anchorElem: HTMLElement
|
||||||
|
editor: LexicalEditor
|
||||||
|
entry: FloatingToolbarSectionEntry
|
||||||
|
}): JSX.Element {
|
||||||
|
const Component = useMemo(() => {
|
||||||
|
return entry?.Component
|
||||||
|
? React.lazy(() =>
|
||||||
|
entry.Component().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [entry])
|
||||||
|
|
||||||
|
const ChildComponent = useMemo(() => {
|
||||||
|
return entry?.ChildComponent
|
||||||
|
? React.lazy(() =>
|
||||||
|
entry.ChildComponent().then((resolvedChildComponent) => ({
|
||||||
|
default: resolvedChildComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [entry])
|
||||||
|
|
||||||
|
if (entry.Component) {
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<React.Suspense>
|
||||||
|
<Component anchorElem={anchorElem} editor={editor} entry={entry} key={entry.key} />{' '}
|
||||||
|
</React.Suspense>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolbarButton entry={entry} key={entry.key}>
|
||||||
|
{ChildComponent && (
|
||||||
|
<React.Suspense>
|
||||||
|
<ChildComponent />
|
||||||
|
</React.Suspense>
|
||||||
|
)}
|
||||||
|
</ToolbarButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToolbarSection({
|
||||||
|
anchorElem,
|
||||||
|
editor,
|
||||||
|
index,
|
||||||
|
section,
|
||||||
|
}: {
|
||||||
|
anchorElem: HTMLElement
|
||||||
|
editor: LexicalEditor
|
||||||
|
index: number
|
||||||
|
section: FloatingToolbarSection
|
||||||
|
}): JSX.Element {
|
||||||
|
const { editorConfig } = useEditorConfigContext()
|
||||||
|
|
||||||
|
const Icon = useMemo(() => {
|
||||||
|
return section?.type === 'dropdown' && section.entries.length && section.ChildComponent
|
||||||
|
? React.lazy(() =>
|
||||||
|
section.ChildComponent().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [section])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`floating-select-toolbar-popup__section floating-select-toolbar-popup__section-${section.key}`}
|
||||||
|
key={section.key}
|
||||||
|
>
|
||||||
|
{section.type === 'dropdown' &&
|
||||||
|
section.entries.length &&
|
||||||
|
(Icon ? (
|
||||||
|
<React.Suspense>
|
||||||
|
<ToolbarDropdown
|
||||||
|
Icon={Icon}
|
||||||
|
anchorElem={anchorElem}
|
||||||
|
editor={editor}
|
||||||
|
entries={section.entries}
|
||||||
|
/>
|
||||||
|
</React.Suspense>
|
||||||
|
) : (
|
||||||
|
<ToolbarDropdown anchorElem={anchorElem} editor={editor} entries={section.entries} />
|
||||||
|
))}
|
||||||
|
{section.type === 'buttons' &&
|
||||||
|
section.entries.length &&
|
||||||
|
section.entries.map((entry) => {
|
||||||
|
return (
|
||||||
|
<ButtonSectionEntry
|
||||||
|
anchorElem={anchorElem}
|
||||||
|
editor={editor}
|
||||||
|
entry={entry}
|
||||||
|
key={entry.key}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
{index < editorConfig.features.floatingSelectToolbar?.sections.length - 1 && (
|
||||||
|
<div className="divider" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function FloatingSelectToolbar({
|
function FloatingSelectToolbar({
|
||||||
anchorElem,
|
anchorElem,
|
||||||
editor,
|
editor,
|
||||||
@@ -176,41 +289,13 @@ function FloatingSelectToolbar({
|
|||||||
{editorConfig?.features &&
|
{editorConfig?.features &&
|
||||||
editorConfig.features?.floatingSelectToolbar?.sections.map((section, i) => {
|
editorConfig.features?.floatingSelectToolbar?.sections.map((section, i) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<ToolbarSection
|
||||||
className={`floating-select-toolbar-popup__section floating-select-toolbar-popup__section-${section.key}`}
|
anchorElem={anchorElem}
|
||||||
|
editor={editor}
|
||||||
|
index={i}
|
||||||
key={section.key}
|
key={section.key}
|
||||||
>
|
section={section}
|
||||||
{section.type === 'dropdown' && section.entries.length && (
|
|
||||||
<ToolbarDropdown
|
|
||||||
Icon={section.ChildComponent}
|
|
||||||
anchorElem={anchorElem}
|
|
||||||
editor={editor}
|
|
||||||
entries={section.entries}
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{section.type === 'buttons' &&
|
|
||||||
section.entries.length &&
|
|
||||||
section.entries.map((entry) => {
|
|
||||||
if (entry.Component) {
|
|
||||||
return (
|
|
||||||
<entry.Component
|
|
||||||
anchorElem={anchorElem}
|
|
||||||
editor={editor}
|
|
||||||
entry={entry}
|
|
||||||
key={entry.key}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ToolbarButton entry={entry} key={entry.key}>
|
|
||||||
<entry.ChildComponent />
|
|
||||||
</ToolbarButton>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{i < editorConfig.features.floatingSelectToolbar?.sections.length - 1 && (
|
|
||||||
<div className="divider" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import type { BaseSelection, LexicalEditor } from 'lexical'
|
import type { BaseSelection, LexicalEditor } from 'lexical'
|
||||||
|
import type React from 'react'
|
||||||
|
|
||||||
export type FloatingToolbarSection =
|
export type FloatingToolbarSection =
|
||||||
| {
|
| {
|
||||||
ChildComponent?: React.FC
|
ChildComponent?: () => Promise<React.FC>
|
||||||
entries: Array<FloatingToolbarSectionEntry>
|
entries: Array<FloatingToolbarSectionEntry>
|
||||||
key: string
|
key: string
|
||||||
order?: number
|
order?: number
|
||||||
@@ -16,13 +17,15 @@ export type FloatingToolbarSection =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FloatingToolbarSectionEntry = {
|
export type FloatingToolbarSectionEntry = {
|
||||||
ChildComponent?: React.FC
|
ChildComponent?: () => Promise<React.FC>
|
||||||
/** Use component to ignore the children and onClick properties. It does not use the default, pre-defined format Button component */
|
/** Use component to ignore the children and onClick properties. It does not use the default, pre-defined format Button component */
|
||||||
Component?: React.FC<{
|
Component?: () => Promise<
|
||||||
|
React.FC<{
|
||||||
anchorElem: HTMLElement
|
anchorElem: HTMLElement
|
||||||
editor: LexicalEditor
|
editor: LexicalEditor
|
||||||
entry: FloatingToolbarSectionEntry
|
entry: FloatingToolbarSectionEntry
|
||||||
}>
|
}>
|
||||||
|
>
|
||||||
isActive?: ({ editor, selection }: { editor: LexicalEditor; selection: BaseSelection }) => boolean
|
isActive?: ({ editor, selection }: { editor: LexicalEditor; selection: BaseSelection }) => boolean
|
||||||
isEnabled?: ({
|
isEnabled?: ({
|
||||||
editor,
|
editor,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { i18n } from 'i18next'
|
import type { i18n } from 'i18next'
|
||||||
import type { LexicalEditor } from 'lexical'
|
import type { LexicalEditor } from 'lexical'
|
||||||
import type { MutableRefObject } from 'react'
|
import type { MutableRefObject } from 'react'
|
||||||
|
import type React from 'react'
|
||||||
|
|
||||||
export class SlashMenuOption {
|
export class SlashMenuOption {
|
||||||
// Icon for display
|
// Icon for display
|
||||||
Icon: React.FC
|
Icon: () => Promise<React.FC>
|
||||||
|
|
||||||
displayName?: (({ i18n }: { i18n: i18n }) => string) | string
|
displayName?: (({ i18n }: { i18n: i18n }) => string) | string
|
||||||
// Used for class names and, if displayName is not provided, for display.
|
// Used for class names and, if displayName is not provided, for display.
|
||||||
@@ -21,7 +22,7 @@ export class SlashMenuOption {
|
|||||||
constructor(
|
constructor(
|
||||||
key: string,
|
key: string,
|
||||||
options: {
|
options: {
|
||||||
Icon: React.FC
|
Icon: () => Promise<React.FC>
|
||||||
displayName?: (({ i18n }: { i18n: i18n }) => string) | string
|
displayName?: (({ i18n }: { i18n: i18n }) => string) | string
|
||||||
keyboardShortcut?: string
|
keyboardShortcut?: string
|
||||||
keywords?: Array<string>
|
keywords?: Array<string>
|
||||||
|
|||||||
@@ -45,6 +45,16 @@ function SlashMenuItem({
|
|||||||
title = title.substring(0, 25) + '...'
|
title = title.substring(0, 25) + '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LazyIcon = useMemo(() => {
|
||||||
|
return option?.Icon
|
||||||
|
? React.lazy(() =>
|
||||||
|
option.Icon().then((resolvedIcon) => ({
|
||||||
|
default: resolvedIcon,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}, [option])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
aria-selected={isSelected}
|
aria-selected={isSelected}
|
||||||
@@ -58,7 +68,12 @@ function SlashMenuItem({
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<option.Icon />
|
{LazyIcon && (
|
||||||
|
<React.Suspense>
|
||||||
|
<LazyIcon />
|
||||||
|
</React.Suspense>
|
||||||
|
)}
|
||||||
|
|
||||||
<span className={`${baseClass}__item-text`}>{title}</span>
|
<span className={`${baseClass}__item-text`}>{title}</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,17 +2,15 @@ import type { SerializedEditorState } from 'lexical'
|
|||||||
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
||||||
import type { RichTextAdapter } from 'payload/types'
|
import type { RichTextAdapter } from 'payload/types'
|
||||||
|
|
||||||
import { withMergedProps, withNullableJSONSchemaType } from 'payload/utilities'
|
import { withNullableJSONSchemaType } from 'payload/utilities'
|
||||||
|
|
||||||
import type { FeatureProvider } from './field/features/types'
|
import type { FeatureProvider } from './field/features/types'
|
||||||
import type { EditorConfig, SanitizedEditorConfig } from './field/lexical/config/types'
|
import type { EditorConfig, SanitizedEditorConfig } from './field/lexical/config/types'
|
||||||
import type { AdapterProps } from './types'
|
import type { AdapterProps } from './types'
|
||||||
|
|
||||||
import { RichTextCell } from './cell'
|
|
||||||
import { RichTextField } from './field'
|
|
||||||
import {
|
import {
|
||||||
|
defaultEditorConfig,
|
||||||
defaultEditorFeatures,
|
defaultEditorFeatures,
|
||||||
defaultEditorLexicalConfig,
|
|
||||||
defaultSanitizedEditorConfig,
|
defaultSanitizedEditorConfig,
|
||||||
} from './field/lexical/config/default'
|
} from './field/lexical/config/default'
|
||||||
import { sanitizeEditorConfig } from './field/lexical/config/sanitize'
|
import { sanitizeEditorConfig } from './field/lexical/config/sanitize'
|
||||||
@@ -48,23 +46,38 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
|
|||||||
features = cloneDeep(defaultEditorFeatures)
|
features = cloneDeep(defaultEditorFeatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
const lexical: LexicalEditorConfig = props.lexical || cloneDeep(defaultEditorLexicalConfig)
|
const lexical: LexicalEditorConfig = props.lexical
|
||||||
|
|
||||||
finalSanitizedEditorConfig = sanitizeEditorConfig({
|
finalSanitizedEditorConfig = sanitizeEditorConfig({
|
||||||
features,
|
features,
|
||||||
lexical,
|
lexical: props.lexical ? () => Promise.resolve(lexical) : defaultEditorConfig.lexical,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
CellComponent: withMergedProps({
|
LazyCellComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./cell').then((module) => {
|
||||||
|
const RichTextCell = module.RichTextCell
|
||||||
|
return import('payload/utilities').then((module2) =>
|
||||||
|
module2.withMergedProps({
|
||||||
Component: RichTextCell,
|
Component: RichTextCell,
|
||||||
toMergeIntoProps: { editorConfig: finalSanitizedEditorConfig },
|
toMergeIntoProps: { editorConfig: finalSanitizedEditorConfig },
|
||||||
}),
|
}),
|
||||||
FieldComponent: withMergedProps({
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
LazyFieldComponent: () =>
|
||||||
|
// @ts-expect-error
|
||||||
|
import('./field').then((module) => {
|
||||||
|
const RichTextField = module.RichTextField
|
||||||
|
return import('payload/utilities').then((module2) =>
|
||||||
|
module2.withMergedProps({
|
||||||
Component: RichTextField,
|
Component: RichTextField,
|
||||||
toMergeIntoProps: { editorConfig: finalSanitizedEditorConfig },
|
toMergeIntoProps: { editorConfig: finalSanitizedEditorConfig },
|
||||||
}),
|
}),
|
||||||
|
)
|
||||||
|
}),
|
||||||
afterReadPromise: ({ field, incomingEditorState, siblingDoc }) => {
|
afterReadPromise: ({ field, incomingEditorState, siblingDoc }) => {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const promises: Promise<void>[] = []
|
const promises: Promise<void>[] = []
|
||||||
@@ -307,15 +320,12 @@ export {
|
|||||||
export {
|
export {
|
||||||
defaultEditorConfig,
|
defaultEditorConfig,
|
||||||
defaultEditorFeatures,
|
defaultEditorFeatures,
|
||||||
defaultEditorLexicalConfig,
|
|
||||||
defaultSanitizedEditorConfig,
|
defaultSanitizedEditorConfig,
|
||||||
} from './field/lexical/config/default'
|
} from './field/lexical/config/default'
|
||||||
export { loadFeatures, sortFeaturesForOptimalLoading } from './field/lexical/config/loader'
|
export { loadFeatures, sortFeaturesForOptimalLoading } from './field/lexical/config/loader'
|
||||||
export { sanitizeEditorConfig, sanitizeFeatures } from './field/lexical/config/sanitize'
|
export { sanitizeEditorConfig, sanitizeFeatures } from './field/lexical/config/sanitize'
|
||||||
export { getEnabledNodes } from './field/lexical/nodes'
|
export { getEnabledNodes } from './field/lexical/nodes'
|
||||||
|
|
||||||
export { ToolbarButton } from './field/lexical/plugins/FloatingSelectToolbar/ToolbarButton'
|
|
||||||
export { ToolbarDropdown } from './field/lexical/plugins/FloatingSelectToolbar/ToolbarDropdown/index'
|
|
||||||
export {
|
export {
|
||||||
type FloatingToolbarSection,
|
type FloatingToolbarSection,
|
||||||
type FloatingToolbarSectionEntry,
|
type FloatingToolbarSectionEntry,
|
||||||
@@ -324,8 +334,7 @@ export { ENABLE_SLASH_MENU_COMMAND } from './field/lexical/plugins/SlashMenu/Lex
|
|||||||
// export SanitizedEditorConfig
|
// export SanitizedEditorConfig
|
||||||
export type { EditorConfig, SanitizedEditorConfig }
|
export type { EditorConfig, SanitizedEditorConfig }
|
||||||
export type { AdapterProps }
|
export type { AdapterProps }
|
||||||
export { RichTextCell }
|
|
||||||
export { RichTextField }
|
|
||||||
export {
|
export {
|
||||||
SlashMenuGroup,
|
SlashMenuGroup,
|
||||||
SlashMenuOption,
|
SlashMenuOption,
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
const [baseDirRelativePath] = process.argv.slice(2)
|
||||||
|
const [sourceDirRelativePath] = process.argv.slice(3)
|
||||||
|
|
||||||
// Base directory
|
// Base directory
|
||||||
const baseDir = path.resolve(__dirname, '..')
|
const baseDir = path.resolve(__dirname, baseDirRelativePath)
|
||||||
const sourceDir = path.join(baseDir, 'dist', 'exports')
|
const sourceDir = path.join(baseDir, sourceDirRelativePath)
|
||||||
const targetDir = baseDir
|
const targetDir = baseDir
|
||||||
|
|
||||||
// Helper function to read directories recursively and exclude .map files
|
// Helper function to read directories recursively and exclude .map files
|
||||||
Reference in New Issue
Block a user