@lexical/table 0.44.1-nightly.20260519.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/{LexicalTable.dev.js → dist/LexicalTable.dev.js} +243 -33
  2. package/{LexicalTable.dev.mjs → dist/LexicalTable.dev.mjs} +239 -33
  3. package/{LexicalTable.mjs → dist/LexicalTable.mjs} +4 -0
  4. package/{LexicalTable.node.mjs → dist/LexicalTable.node.mjs} +4 -0
  5. package/dist/LexicalTable.prod.js +9 -0
  6. package/dist/LexicalTable.prod.mjs +9 -0
  7. package/dist/TableImportExtension.d.ts +38 -0
  8. package/{index.d.ts → dist/index.d.ts} +1 -0
  9. package/package.json +34 -18
  10. package/src/LexicalTableCellNode.ts +479 -0
  11. package/src/LexicalTableCommands.ts +27 -0
  12. package/src/LexicalTableExtension.ts +104 -0
  13. package/src/LexicalTableNode.ts +678 -0
  14. package/src/LexicalTableObserver.ts +575 -0
  15. package/src/LexicalTablePluginHelpers.ts +694 -0
  16. package/src/LexicalTableRowNode.ts +154 -0
  17. package/src/LexicalTableSelection.ts +460 -0
  18. package/src/LexicalTableSelectionHelpers.ts +2409 -0
  19. package/src/LexicalTableUtils.ts +1386 -0
  20. package/src/TableImportExtension.ts +302 -0
  21. package/src/constants.ts +13 -0
  22. package/src/index.ts +97 -0
  23. package/LexicalTable.prod.js +0 -9
  24. package/LexicalTable.prod.mjs +0 -9
  25. /package/{LexicalTable.js → dist/LexicalTable.js} +0 -0
  26. /package/{LexicalTable.js.flow → dist/LexicalTable.js.flow} +0 -0
  27. /package/{LexicalTableCellNode.d.ts → dist/LexicalTableCellNode.d.ts} +0 -0
  28. /package/{LexicalTableCommands.d.ts → dist/LexicalTableCommands.d.ts} +0 -0
  29. /package/{LexicalTableExtension.d.ts → dist/LexicalTableExtension.d.ts} +0 -0
  30. /package/{LexicalTableNode.d.ts → dist/LexicalTableNode.d.ts} +0 -0
  31. /package/{LexicalTableObserver.d.ts → dist/LexicalTableObserver.d.ts} +0 -0
  32. /package/{LexicalTablePluginHelpers.d.ts → dist/LexicalTablePluginHelpers.d.ts} +0 -0
  33. /package/{LexicalTableRowNode.d.ts → dist/LexicalTableRowNode.d.ts} +0 -0
  34. /package/{LexicalTableSelection.d.ts → dist/LexicalTableSelection.d.ts} +0 -0
  35. /package/{LexicalTableSelectionHelpers.d.ts → dist/LexicalTableSelectionHelpers.d.ts} +0 -0
  36. /package/{LexicalTableUtils.d.ts → dist/LexicalTableUtils.d.ts} +0 -0
  37. /package/{constants.d.ts → dist/constants.d.ts} +0 -0
@@ -0,0 +1,479 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {
10
+ DOMConversionMap,
11
+ DOMConversionOutput,
12
+ DOMExportOutput,
13
+ EditorConfig,
14
+ LexicalEditor,
15
+ LexicalNode,
16
+ LexicalUpdateJSON,
17
+ NodeKey,
18
+ ParagraphNode,
19
+ SerializedElementNode,
20
+ Spread,
21
+ } from 'lexical';
22
+
23
+ import {addClassNamesToElement} from '@lexical/utils';
24
+ import {
25
+ $applyNodeReplacement,
26
+ $createParagraphNode,
27
+ $isInlineElementOrDecoratorNode,
28
+ $isLineBreakNode,
29
+ $isTextNode,
30
+ ElementNode,
31
+ isHTMLElement,
32
+ } from 'lexical';
33
+
34
+ import {COLUMN_WIDTH, PIXEL_VALUE_REG_EXP} from './constants';
35
+
36
+ export const TableCellHeaderStates = {
37
+ BOTH: 3,
38
+ COLUMN: 2,
39
+ NO_STATUS: 0,
40
+ ROW: 1,
41
+ };
42
+
43
+ export type TableCellHeaderState =
44
+ (typeof TableCellHeaderStates)[keyof typeof TableCellHeaderStates];
45
+
46
+ export type SerializedTableCellNode = Spread<
47
+ {
48
+ colSpan?: number;
49
+ rowSpan?: number;
50
+ headerState: TableCellHeaderState;
51
+ width?: number;
52
+ backgroundColor?: null | string;
53
+ verticalAlign?: string;
54
+ },
55
+ SerializedElementNode
56
+ >;
57
+
58
+ /** @noInheritDoc */
59
+ export class TableCellNode extends ElementNode {
60
+ /** @internal */
61
+ __colSpan: number;
62
+ /** @internal */
63
+ __rowSpan: number;
64
+ /** @internal */
65
+ __headerState: TableCellHeaderState;
66
+ /** @internal */
67
+ __width?: number | undefined;
68
+ /** @internal */
69
+ __backgroundColor: null | string;
70
+ /** @internal */
71
+ __verticalAlign?: undefined | string;
72
+
73
+ static getType(): string {
74
+ return 'tablecell';
75
+ }
76
+
77
+ static clone(node: TableCellNode): TableCellNode {
78
+ return new TableCellNode(
79
+ node.__headerState,
80
+ node.__colSpan,
81
+ node.__width,
82
+ node.__key,
83
+ );
84
+ }
85
+
86
+ afterCloneFrom(node: this): void {
87
+ super.afterCloneFrom(node);
88
+ this.__rowSpan = node.__rowSpan;
89
+ this.__backgroundColor = node.__backgroundColor;
90
+ this.__verticalAlign = node.__verticalAlign;
91
+ this.__colSpan = node.__colSpan;
92
+ this.__headerState = node.__headerState;
93
+ this.__width = node.__width;
94
+ }
95
+
96
+ static importDOM(): DOMConversionMap | null {
97
+ return {
98
+ td: (node: Node) => ({
99
+ conversion: $convertTableCellNodeElement,
100
+ priority: 0,
101
+ }),
102
+ th: (node: Node) => ({
103
+ conversion: $convertTableCellNodeElement,
104
+ priority: 0,
105
+ }),
106
+ };
107
+ }
108
+
109
+ static importJSON(serializedNode: SerializedTableCellNode): TableCellNode {
110
+ return $createTableCellNode().updateFromJSON(serializedNode);
111
+ }
112
+
113
+ updateFromJSON(
114
+ serializedNode: LexicalUpdateJSON<SerializedTableCellNode>,
115
+ ): this {
116
+ return super
117
+ .updateFromJSON(serializedNode)
118
+ .setHeaderStyles(serializedNode.headerState)
119
+ .setColSpan(serializedNode.colSpan || 1)
120
+ .setRowSpan(serializedNode.rowSpan || 1)
121
+ .setWidth(serializedNode.width || undefined)
122
+ .setBackgroundColor(serializedNode.backgroundColor || null)
123
+ .setVerticalAlign(serializedNode.verticalAlign || undefined);
124
+ }
125
+
126
+ constructor(
127
+ headerState = TableCellHeaderStates.NO_STATUS,
128
+ colSpan = 1,
129
+ width?: number,
130
+ key?: NodeKey,
131
+ ) {
132
+ super(key);
133
+ this.__colSpan = colSpan;
134
+ this.__rowSpan = 1;
135
+ this.__headerState = headerState;
136
+ this.__width = width;
137
+ this.__backgroundColor = null;
138
+ this.__verticalAlign = undefined;
139
+ }
140
+
141
+ createDOM(config: EditorConfig): HTMLTableCellElement {
142
+ const element = document.createElement(this.getTag());
143
+
144
+ if (this.__width) {
145
+ element.style.width = `${this.__width}px`;
146
+ }
147
+ if (this.__colSpan > 1) {
148
+ element.colSpan = this.__colSpan;
149
+ }
150
+ if (this.__rowSpan > 1) {
151
+ element.rowSpan = this.__rowSpan;
152
+ }
153
+ if (this.__backgroundColor !== null) {
154
+ element.style.backgroundColor = this.__backgroundColor;
155
+ }
156
+ if (isValidVerticalAlign(this.__verticalAlign)) {
157
+ element.style.verticalAlign = this.__verticalAlign;
158
+ }
159
+
160
+ addClassNamesToElement(
161
+ element,
162
+ config.theme.tableCell,
163
+ this.hasHeader() && config.theme.tableCellHeader,
164
+ );
165
+
166
+ return element;
167
+ }
168
+
169
+ exportDOM(editor: LexicalEditor): DOMExportOutput {
170
+ const output = super.exportDOM(editor);
171
+
172
+ if (isHTMLElement(output.element)) {
173
+ const element = output.element as HTMLTableCellElement;
174
+ element.setAttribute(
175
+ 'data-temporary-table-cell-lexical-key',
176
+ this.getKey(),
177
+ );
178
+ element.style.border = '1px solid black';
179
+ if (this.__colSpan > 1) {
180
+ element.colSpan = this.__colSpan;
181
+ }
182
+ if (this.__rowSpan > 1) {
183
+ element.rowSpan = this.__rowSpan;
184
+ }
185
+ element.style.width = `${this.getWidth() || COLUMN_WIDTH}px`;
186
+
187
+ element.style.verticalAlign = this.getVerticalAlign() || 'top';
188
+ element.style.textAlign = 'start';
189
+ if (this.__backgroundColor === null && this.hasHeader()) {
190
+ element.style.backgroundColor = '#f2f3f5';
191
+ }
192
+ }
193
+
194
+ return output;
195
+ }
196
+
197
+ exportJSON(): SerializedTableCellNode {
198
+ return {
199
+ ...super.exportJSON(),
200
+ ...(isValidVerticalAlign(this.__verticalAlign) && {
201
+ verticalAlign: this.__verticalAlign,
202
+ }),
203
+ backgroundColor: this.getBackgroundColor(),
204
+ colSpan: this.__colSpan,
205
+ headerState: this.__headerState,
206
+ rowSpan: this.__rowSpan,
207
+ width: this.getWidth(),
208
+ };
209
+ }
210
+
211
+ getColSpan(): number {
212
+ return this.getLatest().__colSpan;
213
+ }
214
+
215
+ setColSpan(colSpan: number): this {
216
+ const self = this.getWritable();
217
+ self.__colSpan = colSpan;
218
+ return self;
219
+ }
220
+
221
+ getRowSpan(): number {
222
+ return this.getLatest().__rowSpan;
223
+ }
224
+
225
+ setRowSpan(rowSpan: number): this {
226
+ const self = this.getWritable();
227
+ self.__rowSpan = rowSpan;
228
+ return self;
229
+ }
230
+
231
+ getTag(): 'th' | 'td' {
232
+ return this.hasHeader() ? 'th' : 'td';
233
+ }
234
+
235
+ setHeaderStyles(
236
+ headerState: TableCellHeaderState,
237
+ mask: TableCellHeaderState = TableCellHeaderStates.BOTH,
238
+ ): this {
239
+ const self = this.getWritable();
240
+ self.__headerState = (headerState & mask) | (self.__headerState & ~mask);
241
+ return self;
242
+ }
243
+
244
+ getHeaderStyles(): TableCellHeaderState {
245
+ return this.getLatest().__headerState;
246
+ }
247
+
248
+ setWidth(width: number | undefined): this {
249
+ const self = this.getWritable();
250
+ self.__width = width;
251
+ return self;
252
+ }
253
+
254
+ getWidth(): number | undefined {
255
+ return this.getLatest().__width;
256
+ }
257
+
258
+ getBackgroundColor(): null | string {
259
+ return this.getLatest().__backgroundColor;
260
+ }
261
+
262
+ setBackgroundColor(newBackgroundColor: null | string): this {
263
+ const self = this.getWritable();
264
+ self.__backgroundColor = newBackgroundColor;
265
+ return self;
266
+ }
267
+
268
+ getVerticalAlign(): undefined | string {
269
+ return this.getLatest().__verticalAlign;
270
+ }
271
+
272
+ setVerticalAlign(newVerticalAlign: null | undefined | string): this {
273
+ const self = this.getWritable();
274
+ self.__verticalAlign = newVerticalAlign || undefined;
275
+ return self;
276
+ }
277
+
278
+ toggleHeaderStyle(headerStateToToggle: TableCellHeaderState): this {
279
+ const self = this.getWritable();
280
+
281
+ if ((self.__headerState & headerStateToToggle) === headerStateToToggle) {
282
+ self.__headerState -= headerStateToToggle;
283
+ } else {
284
+ self.__headerState += headerStateToToggle;
285
+ }
286
+
287
+ return self;
288
+ }
289
+
290
+ hasHeaderState(headerState: TableCellHeaderState): boolean {
291
+ return (this.getHeaderStyles() & headerState) === headerState;
292
+ }
293
+
294
+ hasHeader(): boolean {
295
+ return this.getLatest().__headerState !== TableCellHeaderStates.NO_STATUS;
296
+ }
297
+
298
+ updateDOM(prevNode: this): boolean {
299
+ return (
300
+ prevNode.__headerState !== this.__headerState ||
301
+ prevNode.__width !== this.__width ||
302
+ prevNode.__colSpan !== this.__colSpan ||
303
+ prevNode.__rowSpan !== this.__rowSpan ||
304
+ prevNode.__backgroundColor !== this.__backgroundColor ||
305
+ prevNode.__verticalAlign !== this.__verticalAlign
306
+ );
307
+ }
308
+
309
+ isShadowRoot(): boolean {
310
+ return true;
311
+ }
312
+
313
+ collapseAtStart(): true {
314
+ return true;
315
+ }
316
+
317
+ canBeEmpty(): false {
318
+ return false;
319
+ }
320
+
321
+ canIndent(): false {
322
+ return false;
323
+ }
324
+ }
325
+
326
+ function isValidVerticalAlign(
327
+ verticalAlign?: null | string,
328
+ ): verticalAlign is 'middle' | 'bottom' {
329
+ return verticalAlign === 'middle' || verticalAlign === 'bottom';
330
+ }
331
+
332
+ export function $convertTableCellNodeElement(
333
+ domNode: Node,
334
+ ): DOMConversionOutput {
335
+ const domNode_ = domNode as HTMLTableCellElement;
336
+ const nodeName = domNode.nodeName.toLowerCase();
337
+
338
+ let width: number | undefined = undefined;
339
+
340
+ if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
341
+ width = parseFloat(domNode_.style.width);
342
+ }
343
+
344
+ // Determine header state based on the 'scope' attribute
345
+ let headerState = TableCellHeaderStates.NO_STATUS;
346
+
347
+ if (nodeName === 'th') {
348
+ const scope = domNode_.getAttribute('scope');
349
+ if (scope === 'col') {
350
+ headerState = TableCellHeaderStates.COLUMN;
351
+ } else if (scope === 'row') {
352
+ headerState = TableCellHeaderStates.ROW;
353
+ } else {
354
+ const parentRow = domNode_.parentElement;
355
+ const isInHeaderRow =
356
+ isHTMLElement(parentRow) &&
357
+ parentRow.nodeName.toLowerCase() === 'tr' &&
358
+ isHTMLElement(parentRow.parentElement) &&
359
+ (parentRow.parentElement.nodeName.toLowerCase() === 'thead' ||
360
+ (parentRow as HTMLTableRowElement).rowIndex === 0);
361
+ const isFirstColumn = domNode_.cellIndex === 0;
362
+
363
+ if (isInHeaderRow) {
364
+ headerState |= TableCellHeaderStates.ROW;
365
+ }
366
+ if (isFirstColumn) {
367
+ headerState |= TableCellHeaderStates.COLUMN;
368
+ }
369
+ if (headerState === TableCellHeaderStates.NO_STATUS) {
370
+ headerState = TableCellHeaderStates.ROW;
371
+ }
372
+ }
373
+ }
374
+
375
+ const tableCellNode = $createTableCellNode(
376
+ headerState,
377
+ domNode_.colSpan,
378
+ width,
379
+ );
380
+
381
+ tableCellNode.__rowSpan = domNode_.rowSpan;
382
+ const backgroundColor = domNode_.style.backgroundColor;
383
+ if (backgroundColor !== '') {
384
+ tableCellNode.__backgroundColor = backgroundColor;
385
+ }
386
+ const verticalAlign = domNode_.style.verticalAlign;
387
+ if (isValidVerticalAlign(verticalAlign)) {
388
+ tableCellNode.__verticalAlign = verticalAlign;
389
+ }
390
+
391
+ const style = domNode_.style;
392
+ const textDecoration = ((style && style.textDecoration) || '').split(' ');
393
+ const hasBoldFontWeight =
394
+ style.fontWeight === '700' || style.fontWeight === 'bold';
395
+ const hasLinethroughTextDecoration = textDecoration.includes('line-through');
396
+ const hasItalicFontStyle = style.fontStyle === 'italic';
397
+ const hasUnderlineTextDecoration = textDecoration.includes('underline');
398
+ const color = style.color;
399
+ return {
400
+ after: childLexicalNodes => {
401
+ const result: LexicalNode[] = [];
402
+ let paragraphNode: ParagraphNode | null = null;
403
+
404
+ const removeSingleLineBreakNode = () => {
405
+ if (paragraphNode) {
406
+ const firstChild = paragraphNode.getFirstChild();
407
+ if (
408
+ $isLineBreakNode(firstChild) &&
409
+ paragraphNode.getChildrenSize() === 1
410
+ ) {
411
+ firstChild.remove();
412
+ }
413
+ }
414
+ };
415
+
416
+ for (const child of childLexicalNodes) {
417
+ if (
418
+ $isInlineElementOrDecoratorNode(child) ||
419
+ $isTextNode(child) ||
420
+ $isLineBreakNode(child)
421
+ ) {
422
+ if ($isTextNode(child)) {
423
+ if (hasBoldFontWeight) {
424
+ child.toggleFormat('bold');
425
+ }
426
+ if (hasLinethroughTextDecoration) {
427
+ child.toggleFormat('strikethrough');
428
+ }
429
+ if (hasItalicFontStyle) {
430
+ child.toggleFormat('italic');
431
+ }
432
+ if (hasUnderlineTextDecoration) {
433
+ child.toggleFormat('underline');
434
+ }
435
+ if (color) {
436
+ const existingStyle = child.getStyle();
437
+ if (!existingStyle.includes('color:')) {
438
+ child.setStyle(existingStyle + `color: ${color};`);
439
+ }
440
+ }
441
+ }
442
+
443
+ if (paragraphNode) {
444
+ paragraphNode.append(child);
445
+ } else {
446
+ paragraphNode = $createParagraphNode().append(child);
447
+ result.push(paragraphNode);
448
+ }
449
+ } else {
450
+ result.push(child);
451
+ removeSingleLineBreakNode();
452
+ paragraphNode = null;
453
+ }
454
+ }
455
+
456
+ removeSingleLineBreakNode();
457
+
458
+ if (result.length === 0) {
459
+ result.push($createParagraphNode());
460
+ }
461
+ return result;
462
+ },
463
+ node: tableCellNode,
464
+ };
465
+ }
466
+
467
+ export function $createTableCellNode(
468
+ headerState: TableCellHeaderState = TableCellHeaderStates.NO_STATUS,
469
+ colSpan = 1,
470
+ width?: number,
471
+ ): TableCellNode {
472
+ return $applyNodeReplacement(new TableCellNode(headerState, colSpan, width));
473
+ }
474
+
475
+ export function $isTableCellNode(
476
+ node: LexicalNode | null | undefined,
477
+ ): node is TableCellNode {
478
+ return node instanceof TableCellNode;
479
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {LexicalCommand} from 'lexical';
10
+
11
+ import {createCommand} from 'lexical';
12
+
13
+ export type InsertTableCommandPayloadHeaders =
14
+ | Readonly<{
15
+ rows: boolean;
16
+ columns: boolean;
17
+ }>
18
+ | boolean;
19
+
20
+ export type InsertTableCommandPayload = Readonly<{
21
+ columns: string;
22
+ rows: string;
23
+ includeHeaders?: InsertTableCommandPayloadHeaders;
24
+ }>;
25
+
26
+ export const INSERT_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload> =
27
+ createCommand('INSERT_TABLE_COMMAND');
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import {effect, namedSignals} from '@lexical/extension';
10
+ import {mergeRegister} from '@lexical/utils';
11
+ import {$fullReconcile, defineExtension, safeCast} from 'lexical';
12
+
13
+ import {TableCellNode} from './LexicalTableCellNode';
14
+ import {
15
+ $isScrollableTablesActive,
16
+ setScrollableTablesActive,
17
+ TableNode,
18
+ } from './LexicalTableNode';
19
+ import {
20
+ registerTableCellUnmergeTransform,
21
+ registerTablePlugin,
22
+ registerTableSelectionObserver,
23
+ } from './LexicalTablePluginHelpers';
24
+ import {TableRowNode} from './LexicalTableRowNode';
25
+
26
+ export interface TableConfig {
27
+ /**
28
+ * When `false` (default `true`), merged cell support (colspan and rowspan) will be disabled and all
29
+ * tables will be forced into a regular grid with 1x1 table cells.
30
+ */
31
+ hasCellMerge: boolean;
32
+ /**
33
+ * When `false` (default `true`), the background color of TableCellNode will always be removed.
34
+ */
35
+ hasCellBackgroundColor: boolean;
36
+ /**
37
+ * When `true` (default `true`), the tab key can be used to navigate table cells.
38
+ */
39
+ hasTabHandler: boolean;
40
+ /**
41
+ * When `true` (default `true`), tables will be wrapped in a `<div>` to enable horizontal scrolling
42
+ */
43
+ hasHorizontalScroll: boolean;
44
+ /**
45
+ * When `true` (default `false`), nested tables will be allowed.
46
+ *
47
+ * @experimental Nested tables are not officially supported.
48
+ */
49
+ hasNestedTables: boolean;
50
+ }
51
+
52
+ /**
53
+ * Configures {@link TableNode}, {@link TableRowNode}, {@link TableCellNode} and
54
+ * registers table behaviors (see {@link TableConfig})
55
+ */
56
+ export const TableExtension = defineExtension({
57
+ build(editor, config, state) {
58
+ return namedSignals(config);
59
+ },
60
+ config: safeCast<TableConfig>({
61
+ hasCellBackgroundColor: true,
62
+ hasCellMerge: true,
63
+ hasHorizontalScroll: true,
64
+ hasNestedTables: false,
65
+ hasTabHandler: true,
66
+ }),
67
+ name: '@lexical/table/Table',
68
+ nodes: () => [TableNode, TableRowNode, TableCellNode],
69
+ register(editor, config, state) {
70
+ const stores = state.getOutput();
71
+ return mergeRegister(
72
+ effect(() => {
73
+ const hasHorizontalScroll = stores.hasHorizontalScroll.value;
74
+ const hadHorizontalScroll = $isScrollableTablesActive(editor);
75
+ if (hadHorizontalScroll !== hasHorizontalScroll) {
76
+ setScrollableTablesActive(editor, hasHorizontalScroll);
77
+ // Re-render existing tables through the new scroll-wrapper config
78
+ // without cloning every TableNode the way marking them dirty would. A
79
+ // full reconcile marks no nodes dirty, so it's deferred (no
80
+ // synchronous render from this effect) and produces no history entry.
81
+ editor.update($fullReconcile);
82
+ }
83
+ }),
84
+ registerTablePlugin(editor, stores),
85
+ effect(() =>
86
+ registerTableSelectionObserver(editor, stores.hasTabHandler.value),
87
+ ),
88
+ effect(() =>
89
+ stores.hasCellMerge.value
90
+ ? undefined
91
+ : registerTableCellUnmergeTransform(editor),
92
+ ),
93
+ effect(() =>
94
+ stores.hasCellBackgroundColor.value
95
+ ? undefined
96
+ : editor.registerNodeTransform(TableCellNode, node => {
97
+ if (node.getBackgroundColor() !== null) {
98
+ node.setBackgroundColor(null);
99
+ }
100
+ }),
101
+ ),
102
+ );
103
+ },
104
+ });