feat(richtext-lexical): lists
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
import { INSERT_CHECK_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { ClientFeature, FeatureProviderProviderClient } from '../../types'
|
||||
|
||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { ChecklistIcon } from '../../../lexical/ui/icons/Checklist'
|
||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||
import { createClientComponent } from '../../createClientComponent'
|
||||
import { LexicalListPlugin } from '../plugin'
|
||||
import { CHECK_LIST } from './markdownTransformers'
|
||||
import { LexicalCheckListPlugin } from './plugin'
|
||||
|
||||
const CheckListFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
feature: ({ featureProviderMap }) => {
|
||||
const plugins: ClientFeature<undefined>['plugins'] = [
|
||||
{
|
||||
Component: LexicalCheckListPlugin,
|
||||
position: 'normal',
|
||||
},
|
||||
]
|
||||
|
||||
if (!featureProviderMap.has('unorderedlist') && !featureProviderMap.has('orderedlist')) {
|
||||
plugins.push({
|
||||
Component: LexicalListPlugin,
|
||||
position: 'normal',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
TextDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: ChecklistIcon,
|
||||
isActive: () => false,
|
||||
key: 'checkList',
|
||||
label: `Check List`,
|
||||
onClick: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined)
|
||||
},
|
||||
order: 12,
|
||||
},
|
||||
]),
|
||||
],
|
||||
},
|
||||
markdownTransformers: [CHECK_LIST],
|
||||
nodes:
|
||||
featureProviderMap.has('unorderedlist') || featureProviderMap.has('orderedlist')
|
||||
? []
|
||||
: [ListNode, ListItemNode],
|
||||
plugins,
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
displayName: 'Lists',
|
||||
key: 'lists',
|
||||
options: [
|
||||
new SlashMenuOption('checklist', {
|
||||
Icon: ChecklistIcon,
|
||||
displayName: 'Check List',
|
||||
keywords: ['check list', 'check', 'checklist', 'cl'],
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined)
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const CheckListFeatureClientComponent = createClientComponent(CheckListFeatureClient)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProviderProviderServer } from '../../types'
|
||||
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { CheckListFeatureClientComponent } from './feature.client'
|
||||
import { CHECK_LIST } from './markdownTransformers'
|
||||
|
||||
export const CheckListFeature: FeatureProviderProviderServer<undefined, undefined> = (props) => {
|
||||
return {
|
||||
feature: ({ featureProviderMap }) => {
|
||||
return {
|
||||
ClientComponent: CheckListFeatureClientComponent,
|
||||
markdownTransformers: [CHECK_LIST],
|
||||
nodes:
|
||||
featureProviderMap.has('unorderedlist') || featureProviderMap.has('orderedlist')
|
||||
? []
|
||||
: [
|
||||
{
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'checklist',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { INSERT_CHECK_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProvider } from '../../types'
|
||||
|
||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { CHECK_LIST } from './markdownTransformers'
|
||||
|
||||
// 345
|
||||
// carbs 7
|
||||
export const CheckListFeature = (): FeatureProvider => {
|
||||
return {
|
||||
feature: ({ featureProviderMap }) => {
|
||||
return {
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
TextDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/Checklist').then(
|
||||
(module) => module.ChecklistIcon,
|
||||
),
|
||||
isActive: () => false,
|
||||
key: 'checkList',
|
||||
label: `Check List`,
|
||||
onClick: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined)
|
||||
},
|
||||
order: 12,
|
||||
},
|
||||
]),
|
||||
],
|
||||
},
|
||||
markdownTransformers: [CHECK_LIST],
|
||||
nodes:
|
||||
featureProviderMap.has('unorderedList') || featureProviderMap.has('orderedList')
|
||||
? []
|
||||
: [
|
||||
{
|
||||
type: ListNode.getType(),
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
type: ListItemNode.getType(),
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
{
|
||||
Component: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('./plugin').then((module) => module.LexicalCheckListPlugin),
|
||||
position: 'normal',
|
||||
},
|
||||
],
|
||||
props: null,
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
displayName: 'Lists',
|
||||
key: 'lists',
|
||||
options: [
|
||||
new SlashMenuOption('checklist', {
|
||||
Icon: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/Checklist').then(
|
||||
(module) => module.ChecklistIcon,
|
||||
),
|
||||
displayName: 'Check List',
|
||||
keywords: ['check list', 'check', 'checklist', 'cl'],
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined)
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
key: 'checklist',
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import { INSERT_ORDERED_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProvider } from '../../types'
|
||||
import type { FeatureProviderProviderClient } from '../../types'
|
||||
|
||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { OrderedListIcon } from '../../../lexical/ui/icons/OrderedList'
|
||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { createClientComponent } from '../../createClientComponent'
|
||||
import { LexicalListPlugin } from '../plugin'
|
||||
import { ORDERED_LIST } from './markdownTransformer'
|
||||
|
||||
export const OrderedListFeature = (): FeatureProvider => {
|
||||
const OrderedListFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
feature: ({ featureProviderMap }) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
TextDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/OrderedList').then(
|
||||
(module) => module.OrderedListIcon,
|
||||
),
|
||||
ChildComponent: OrderedListIcon,
|
||||
isActive: () => false,
|
||||
key: 'orderedList',
|
||||
label: `Ordered List`,
|
||||
@@ -32,35 +34,15 @@ export const OrderedListFeature = (): FeatureProvider => {
|
||||
],
|
||||
},
|
||||
markdownTransformers: [ORDERED_LIST],
|
||||
nodes: featureProviderMap.has('unorderedList')
|
||||
nodes: featureProviderMap.has('unorderedlist') ? [] : [ListNode, ListItemNode],
|
||||
plugins: featureProviderMap.has('unorderedlist')
|
||||
? []
|
||||
: [
|
||||
{
|
||||
type: ListNode.getType(),
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
type: ListItemNode.getType(),
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
plugins: featureProviderMap.has('unorderedList')
|
||||
? []
|
||||
: [
|
||||
{
|
||||
Component: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../plugin').then((module) => module.LexicalListPlugin),
|
||||
Component: LexicalListPlugin,
|
||||
position: 'normal',
|
||||
},
|
||||
],
|
||||
props: null,
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
@@ -68,11 +50,7 @@ export const OrderedListFeature = (): FeatureProvider => {
|
||||
key: 'lists',
|
||||
options: [
|
||||
new SlashMenuOption('orderedlist', {
|
||||
Icon: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/OrderedList').then(
|
||||
(module) => module.OrderedListIcon,
|
||||
),
|
||||
Icon: OrderedListIcon,
|
||||
displayName: 'Ordered List',
|
||||
keywords: ['ordered list', 'ol'],
|
||||
onSelect: ({ editor }) => {
|
||||
@@ -85,6 +63,7 @@ export const OrderedListFeature = (): FeatureProvider => {
|
||||
},
|
||||
}
|
||||
},
|
||||
key: 'orderedlist',
|
||||
}
|
||||
}
|
||||
|
||||
export const OrderedListFeatureClientComponent = createClientComponent(OrderedListFeatureClient)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProviderProviderServer } from '../../types'
|
||||
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { OrderedListFeatureClientComponent } from './feature.client'
|
||||
import { ORDERED_LIST } from './markdownTransformer'
|
||||
|
||||
export const OrderedListFeature: FeatureProviderProviderServer<undefined, undefined> = (props) => {
|
||||
return {
|
||||
feature: ({ featureProviderMap }) => {
|
||||
return {
|
||||
ClientComponent: OrderedListFeatureClientComponent,
|
||||
markdownTransformers: [ORDERED_LIST],
|
||||
nodes: featureProviderMap.has('unorderedlist')
|
||||
? []
|
||||
: [
|
||||
{
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'orderedlist',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import { INSERT_UNORDERED_LIST_COMMAND, ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProvider } from '../../types'
|
||||
import type { FeatureProviderProviderClient } from '../../types'
|
||||
|
||||
import { SlashMenuOption } from '../../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { UnorderedListIcon } from '../../../lexical/ui/icons/UnorderedList'
|
||||
import { TextDropdownSectionWithEntries } from '../../common/floatingSelectToolbarTextDropdownSection'
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { createClientComponent } from '../../createClientComponent'
|
||||
import { LexicalListPlugin } from '../plugin'
|
||||
import { UNORDERED_LIST } from './markdownTransformer'
|
||||
|
||||
export const UnorderedListFeature = (): FeatureProvider => {
|
||||
const UnorderedListFeatureClient: FeatureProviderProviderClient<undefined> = (props) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
feature: () => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
floatingSelectToolbar: {
|
||||
sections: [
|
||||
TextDropdownSectionWithEntries([
|
||||
{
|
||||
ChildComponent: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/UnorderedList').then(
|
||||
(module) => module.UnorderedListIcon,
|
||||
),
|
||||
ChildComponent: UnorderedListIcon,
|
||||
isActive: () => false,
|
||||
key: 'unorderedList',
|
||||
label: `Unordered List`,
|
||||
@@ -32,31 +34,13 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
||||
],
|
||||
},
|
||||
markdownTransformers: [UNORDERED_LIST],
|
||||
nodes: [
|
||||
{
|
||||
type: ListNode.getType(),
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
type: ListItemNode.getType(),
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
nodes: [ListNode, ListItemNode],
|
||||
plugins: [
|
||||
{
|
||||
Component: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../plugin').then((module) => module.LexicalListPlugin),
|
||||
Component: LexicalListPlugin,
|
||||
position: 'normal',
|
||||
},
|
||||
],
|
||||
props: null,
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
@@ -64,11 +48,7 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
||||
key: 'lists',
|
||||
options: [
|
||||
new SlashMenuOption('unorderedlist', {
|
||||
Icon: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../../lexical/ui/icons/UnorderedList').then(
|
||||
(module) => module.UnorderedListIcon,
|
||||
),
|
||||
Icon: UnorderedListIcon,
|
||||
displayName: 'Unordered List',
|
||||
keywords: ['unordered list', 'ul'],
|
||||
onSelect: ({ editor }) => {
|
||||
@@ -81,6 +61,7 @@ export const UnorderedListFeature = (): FeatureProvider => {
|
||||
},
|
||||
}
|
||||
},
|
||||
key: 'unorderedlist',
|
||||
}
|
||||
}
|
||||
|
||||
export const UnorderedListFeatureClientComponent = createClientComponent(UnorderedListFeatureClient)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ListItemNode, ListNode } from '@lexical/list'
|
||||
|
||||
import type { FeatureProviderProviderServer } from '../../types'
|
||||
|
||||
import { ListHTMLConverter, ListItemHTMLConverter } from '../htmlConverter'
|
||||
import { UnorderedListFeatureClientComponent } from './feature.client'
|
||||
import { UNORDERED_LIST } from './markdownTransformer'
|
||||
|
||||
export const UnorderedListFeature: FeatureProviderProviderServer<undefined, undefined> = (
|
||||
props,
|
||||
) => {
|
||||
return {
|
||||
feature: () => {
|
||||
return {
|
||||
ClientComponent: UnorderedListFeatureClientComponent,
|
||||
markdownTransformers: [UNORDERED_LIST],
|
||||
nodes: [
|
||||
{
|
||||
converters: {
|
||||
html: ListHTMLConverter,
|
||||
},
|
||||
node: ListNode,
|
||||
},
|
||||
{
|
||||
converters: {
|
||||
html: ListItemHTMLConverter,
|
||||
},
|
||||
node: ListItemNode,
|
||||
},
|
||||
],
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'unorderedlist',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ import { UnderlineFeature } from '../../../features/format/underline/feature.ser
|
||||
import { HeadingFeature } from '../../../features/heading/feature.server'
|
||||
import { IndentFeature } from '../../../features/indent/feature.server'
|
||||
import { LinkFeature } from '../../../features/link/feature.server'
|
||||
import { CheckListFeature } from '../../../features/lists/checklist'
|
||||
import { OrderedListFeature } from '../../../features/lists/orderedlist'
|
||||
import { UnorderedListFeature } from '../../../features/lists/unorderedlist'
|
||||
import { CheckListFeature } from '../../../features/lists/checklist/feature.server'
|
||||
import { OrderedListFeature } from '../../../features/lists/orderedlist/feature.server'
|
||||
import { UnorderedListFeature } from '../../../features/lists/unorderedlist/feature.server'
|
||||
import { ParagraphFeature } from '../../../features/paragraph'
|
||||
import { RelationshipFeature } from '../../../features/relationship'
|
||||
import { UploadFeature } from '../../../features/upload'
|
||||
|
||||
@@ -291,9 +291,9 @@ export type {
|
||||
SerializedAutoLinkNode,
|
||||
SerializedLinkNode,
|
||||
} from './field/features/link/nodes/types'
|
||||
export { CheckListFeature } from './field/features/lists/checklist'
|
||||
export { OrderedListFeature } from './field/features/lists/orderedlist'
|
||||
export { UnorderedListFeature } from './field/features/lists/unorderedlist'
|
||||
export { CheckListFeature } from './field/features/lists/checklist/feature.server'
|
||||
export { OrderedListFeature } from './field/features/lists/orderedlist/feature.server'
|
||||
export { UnorderedListFeature } from './field/features/lists/unorderedlist/feature.server'
|
||||
export { LexicalPluginToLexicalFeature } from './field/features/migrations/lexicalPluginToLexical'
|
||||
export { SlateToLexicalFeature } from './field/features/migrations/slateToLexical'
|
||||
export { SlateBlockquoteConverter } from './field/features/migrations/slateToLexical/converter/converters/blockquote'
|
||||
|
||||
@@ -3,16 +3,19 @@ import {
|
||||
BlockQuoteFeature,
|
||||
BlocksFeature,
|
||||
BoldFeature,
|
||||
CheckListFeature,
|
||||
HeadingFeature,
|
||||
IndentFeature,
|
||||
InlineCodeFeature,
|
||||
ItalicFeature,
|
||||
LinkFeature,
|
||||
OrderedListFeature,
|
||||
StrikethroughFeature,
|
||||
SubscriptFeature,
|
||||
SuperscriptFeature,
|
||||
TreeViewFeature,
|
||||
UnderlineFeature,
|
||||
UnorderedListFeature,
|
||||
lexicalEditor,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
import path from 'path'
|
||||
@@ -91,6 +94,9 @@ export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<S
|
||||
editor: lexicalEditor({
|
||||
features: [
|
||||
LinkFeature(),
|
||||
CheckListFeature(),
|
||||
UnorderedListFeature(),
|
||||
OrderedListFeature(),
|
||||
AlignFeature(),
|
||||
BlockQuoteFeature(),
|
||||
BoldFeature(),
|
||||
|
||||
Reference in New Issue
Block a user