Skip to content

Commit dbcc3fb

Browse files
fix: disable lists in table cells (#910)
* fix: disable lists in table cells * lint fixes
1 parent 14c3da3 commit dbcc3fb

8 files changed

Lines changed: 59 additions & 25 deletions

File tree

src/plugins/core/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,33 @@ export const nestedEditorChildren$ = Cell<React.ComponentType[]>([])
744744
*/
745745
export const addNestedEditorChild$ = Appender(nestedEditorChildren$)
746746

747+
/**
748+
* React Components registered to be rendered inside table cell editors. Use this instead of {@link addNestedEditorChild$} for components that should only appear in table cells.
749+
* Plugins that should not be active in table cells (e.g. lists) should not register here.
750+
* @group Core
751+
*/
752+
export const tableCellEditorChildren$ = Cell<React.ComponentType[]>([])
753+
754+
/**
755+
* Lets you add React components as children of table cell editors.
756+
* @group Core
757+
*/
758+
export const addTableCellEditorChild$ = Appender(tableCellEditorChildren$)
759+
760+
/**
761+
* Whether the currently active editor is inside a table cell.
762+
* @group Core
763+
*/
764+
export const editorInTable$ = Cell<boolean>(false, (r) => {
765+
r.link(
766+
r.pipe(
767+
activeEditor$,
768+
map((editor) => ['td', 'th'].includes(editor?.getRootElement()?.parentNode?.nodeName.toLowerCase() ?? ''))
769+
),
770+
editorInTable$
771+
)
772+
})
773+
747774
/** @internal */
748775
export const historyState$ = Cell(createEmptyHistoryState())
749776

src/plugins/link/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ import { LinkPlugin as LexicalLinkPlugin } from '@lexical/react/LexicalLinkPlugi
66
import { LexicalAutoLinkPlugin } from './AutoLinkPlugin'
77
import { Cell } from '@mdxeditor/gurx'
88
import { realmPlugin } from '../../RealmWithPlugins'
9-
import { addImportVisitor$, addLexicalNode$, addExportVisitor$, addComposerChild$, addActivePlugin$, addNestedEditorChild$ } from '../core'
9+
import {
10+
addImportVisitor$,
11+
addLexicalNode$,
12+
addExportVisitor$,
13+
addComposerChild$,
14+
addActivePlugin$,
15+
addNestedEditorChild$,
16+
addTableCellEditorChild$
17+
} from '../core'
1018

1119
/**
1220
* Holds whether the auto-linking of URLs and email addresses is disabled.
@@ -46,6 +54,7 @@ export const linkPlugin = realmPlugin<{
4654
[addExportVisitor$]: LexicalLinkVisitor,
4755
[disableAutoLink$]: disableAutoLink,
4856
[addNestedEditorChild$]: EditorChild,
57+
[addTableCellEditorChild$]: EditorChild,
4958
[addComposerChild$]: EditorChild
5059
})
5160
}

src/plugins/lists/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export const listsPlugin = realmPlugin({
110110
[addToMarkdownExtension$]: gfmTaskListItemToMarkdown(),
111111
[addComposerChild$]: [TabIndentationPlugin, ListPlugin, CheckListPlugin],
112112
[addNestedEditorChild$]: [TabIndentationPlugin, ListPlugin, CheckListPlugin]
113+
// Note: intentionally not registered to addTableCellEditorChild$ — lists are not supported in table cells
113114
})
114115
}
115116
})

src/plugins/markdown-shortcut/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react'
12
import {
23
BOLD_ITALIC_STAR,
34
BOLD_ITALIC_UNDERSCORE,
@@ -19,10 +20,9 @@ import {
1920
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin'
2021
import { $createHeadingNode, $isHeadingNode, HeadingNode, HeadingTagType } from '@lexical/rich-text'
2122
import { ElementNode, LexicalNode } from 'lexical'
22-
import React from 'react'
2323
import { realmPlugin } from '../../RealmWithPlugins'
2424
import { $createCodeBlockNode, CodeBlockNode } from '../codeblock/CodeBlockNode'
25-
import { activePlugins$, addComposerChild$, addNestedEditorChild$ } from '../core'
25+
import { activePlugins$, addComposerChild$, addNestedEditorChild$, addTableCellEditorChild$ } from '../core'
2626
import { HEADING_LEVEL, allowedHeadingLevels$ } from '../headings'
2727
import { $createHorizontalRuleNode, $isHorizontalRuleNode, HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode'
2828

@@ -35,9 +35,11 @@ export const markdownShortcutPlugin = realmPlugin({
3535
const pluginIds = realm.getValue(activePlugins$)
3636
const allowedHeadingLevels: readonly HEADING_LEVEL[] = pluginIds.includes('headings') ? realm.getValue(allowedHeadingLevels$) : []
3737
const transformers = pickTransformersForActivePlugins(pluginIds, allowedHeadingLevels)
38+
const tableCellTransformers = transformers.filter((t) => !LIST_TRANSFORMERS.has(t))
3839
realm.pubIn({
3940
[addComposerChild$]: () => <MarkdownShortcutPlugin transformers={transformers} />,
40-
[addNestedEditorChild$]: () => <MarkdownShortcutPlugin transformers={transformers} />
41+
[addNestedEditorChild$]: () => <MarkdownShortcutPlugin transformers={transformers} />,
42+
[addTableCellEditorChild$]: () => <MarkdownShortcutPlugin transformers={tableCellTransformers} />
4143
})
4244
}
4345
})
@@ -72,6 +74,8 @@ const THEMATIC_BREAK: ElementTransformer = {
7274
type: 'element'
7375
}
7476

77+
const LIST_TRANSFORMERS: ReadonlySet<Transformer> = new Set([ORDERED_LIST, UNORDERED_LIST, CHECK_LIST])
78+
7579
function pickTransformersForActivePlugins(pluginIds: string[], allowedHeadingLevels: readonly HEADING_LEVEL[]) {
7680
const transformers: Transformer[] = [
7781
BOLD_ITALIC_STAR,

src/plugins/table/TableEditor.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
importVisitors$,
4242
jsxComponentDescriptors$,
4343
jsxIsAvailable$,
44-
nestedEditorChildren$,
44+
tableCellEditorChildren$,
4545
readOnly$,
4646
rootEditor$,
4747
useTranslation,
@@ -346,7 +346,7 @@ const CellEditor: React.FC<CellProps> = ({ focus, setActiveCell, parentEditor, l
346346
codeBlockEditorDescriptors,
347347
jsxIsAvailable,
348348
rootEditor,
349-
nestedEditorChildren
349+
tableCellEditorChildren
350350
] = useCellValues(
351351
importVisitors$,
352352
exportVisitors$,
@@ -356,7 +356,7 @@ const CellEditor: React.FC<CellProps> = ({ focus, setActiveCell, parentEditor, l
356356
codeBlockEditorDescriptors$,
357357
jsxIsAvailable$,
358358
rootEditor$,
359-
nestedEditorChildren$
359+
tableCellEditorChildren$
360360
)
361361

362362
const [editor] = React.useState(() => {
@@ -471,7 +471,7 @@ const CellEditor: React.FC<CellProps> = ({ focus, setActiveCell, parentEditor, l
471471
<LexicalNestedComposer initialEditor={editor}>
472472
<RichTextPlugin contentEditable={<ContentEditable />} placeholder={<div></div>} ErrorBoundary={LexicalErrorBoundary} />
473473

474-
{nestedEditorChildren.map((Child, index) => (
474+
{tableCellEditorChildren.map((Child, index) => (
475475
<Child key={index} />
476476
))}
477477
<HistoryPlugin />

src/plugins/toolbar/components/InsertTable.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import { ButtonWithTooltip } from '.././primitives/toolbar'
22
import React from 'react'
33
import { insertTable$ } from '../../table'
4-
import { Cell, map, useCellValue, usePublisher } from '@mdxeditor/gurx'
5-
import { activeEditor$, iconComponentFor$, useTranslation } from '../../core'
6-
7-
const disableInsertTableButton$ = Cell<boolean>(false, (r) => {
8-
r.link(
9-
r.pipe(
10-
activeEditor$,
11-
map((editor) => ['td', 'th'].includes(editor?.getRootElement()?.parentNode?.nodeName.toLowerCase() ?? ''))
12-
),
13-
disableInsertTableButton$
14-
)
15-
})
4+
import { useCellValue, usePublisher } from '@mdxeditor/gurx'
5+
import { editorInTable$, iconComponentFor$, useTranslation } from '../../core'
166

177
/**
188
* A toolbar button that allows the user to insert a table.
@@ -25,7 +15,7 @@ export const InsertTable: React.FC = () => {
2515
const t = useTranslation()
2616

2717
// Do not allow inserting a table inside a table cell, markdown does not support it
28-
const isDisabled = useCellValue(disableInsertTableButton$)
18+
const isDisabled = useCellValue(editorInTable$)
2919

3020
return (
3121
<ButtonWithTooltip

src/plugins/toolbar/components/ListsToggle.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import { applyListType$, currentListType$ } from '../../lists'
33
import { SingleChoiceToggleGroup } from '.././primitives/toolbar'
44
import { useCellValues, usePublisher } from '@mdxeditor/gurx'
5-
import { iconComponentFor$, useTranslation } from '../../core'
5+
import { editorInTable$, iconComponentFor$, useTranslation } from '../../core'
66

77
const ICON_NAME_MAP = {
88
bullet: 'format_list_bulleted',
@@ -18,7 +18,7 @@ const ICON_NAME_MAP = {
1818
* @param options - The list types that the user can toggle between. Defaults to `['bullet', 'number', 'check']`.
1919
*/
2020
export const ListsToggle: React.FC<{ options?: ('bullet' | 'number' | 'check')[] }> = ({ options = ['bullet', 'number', 'check'] }) => {
21-
const [currentListType, iconComponentFor] = useCellValues(currentListType$, iconComponentFor$)
21+
const [currentListType, iconComponentFor, inTable] = useCellValues(currentListType$, iconComponentFor$, editorInTable$)
2222
const applyListType = usePublisher(applyListType$)
2323
const t = useTranslation()
2424

@@ -34,5 +34,5 @@ export const ListsToggle: React.FC<{ options?: ('bullet' | 'number' | 'check')[]
3434
contents: iconComponentFor(ICON_NAME_MAP[type])
3535
}))
3636

37-
return <SingleChoiceToggleGroup value={currentListType || ''} items={items} onChange={applyListType} />
37+
return <SingleChoiceToggleGroup value={currentListType || ''} items={items} onChange={applyListType} disabled={inTable} />
3838
}

src/plugins/toolbar/primitives/toolbar.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ export const SingleChoiceToggleGroup = <T extends string>({
142142
onChange,
143143
className,
144144
ggClassName,
145-
items
145+
items,
146+
disabled
146147
}: {
147148
items: {
148149
title: string
@@ -153,6 +154,7 @@ export const SingleChoiceToggleGroup = <T extends string>({
153154
value: T | ''
154155
ggClassName?: string
155156
className?: string
157+
disabled?: boolean
156158
}) => {
157159
const t = useTranslation()
158160

@@ -164,6 +166,7 @@ export const SingleChoiceToggleGroup = <T extends string>({
164166
className={classNames(styles.toolbarToggleSingleGroup, className)}
165167
onValueChange={onChange}
166168
value={value || ''}
169+
disabled={disabled}
167170
onFocus={(e) => {
168171
e.preventDefault()
169172
}}

0 commit comments

Comments
 (0)