1 import {EditorBasicButtonDefinition, EditorButtonDefinition} from "../../framework/buttons";
2 import tableIcon from "@icons/editor/table.svg";
3 import deleteIcon from "@icons/editor/table-delete.svg";
4 import deleteColumnIcon from "@icons/editor/table-delete-column.svg";
5 import deleteRowIcon from "@icons/editor/table-delete-row.svg";
6 import insertColumnAfterIcon from "@icons/editor/table-insert-column-after.svg";
7 import insertColumnBeforeIcon from "@icons/editor/table-insert-column-before.svg";
8 import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
9 import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
10 import {EditorUiContext} from "../../framework/core";
11 import {$getSelection, BaseSelection} from "lexical";
12 import {$isCustomTableNode} from "../../../nodes/custom-table";
15 $deleteTableColumn__EXPERIMENTAL,
16 $deleteTableRow__EXPERIMENTAL,
17 $insertTableColumn__EXPERIMENTAL,
18 $insertTableRow__EXPERIMENTAL, $isTableCellNode,
19 $isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode, TableNode,
20 } from "@lexical/table";
21 import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
22 import {$getParentOfType} from "../../../utils/nodes";
24 const neverActive = (): boolean => false;
25 const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
27 export const table: EditorBasicButtonDefinition = {
32 export const tableProperties: EditorButtonDefinition = {
33 label: 'Table properties',
35 action(context: EditorUiContext) {
36 context.editor.getEditorState().read(() => {
37 const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
38 if (!$isTableCellNode(cell)) {
42 const table = $getParentOfType(cell, $isTableNode);
43 const modalForm = context.manager.createModal('table_properties');
48 isActive: neverActive,
49 isDisabled: cellNotSelected,
52 export const clearTableFormatting: EditorButtonDefinition = {
53 label: 'Clear table formatting',
55 action(context: EditorUiContext) {
56 context.editor.getEditorState().read(() => {
57 const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
58 if (!$isTableCellNode(cell)) {
62 const table = $getParentOfType(cell, $isTableNode);
66 isActive: neverActive,
67 isDisabled: cellNotSelected,
70 export const resizeTableToContents: EditorButtonDefinition = {
71 label: 'Resize to contents',
73 action(context: EditorUiContext) {
74 context.editor.getEditorState().read(() => {
75 const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
76 if (!$isTableCellNode(cell)) {
80 const table = $getParentOfType(cell, $isCustomTableNode);
81 if (!$isCustomTableNode(table)) {
85 for (const row of table.getChildren()) {
86 if ($isTableRowNode(row)) {
87 // TODO - Come back later as this may depend on if we
88 // are using a custom table row
93 isActive: neverActive,
94 isDisabled: cellNotSelected,
97 export const deleteTable: EditorButtonDefinition = {
98 label: 'Delete table',
100 action(context: EditorUiContext) {
101 context.editor.update(() => {
102 const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
113 export const deleteTableMenuAction: EditorButtonDefinition = {
116 isDisabled(selection) {
117 return !$selectionContainsNodeType(selection, $isTableNode);
121 export const insertRowAbove: EditorButtonDefinition = {
122 label: 'Insert row before',
123 icon: insertRowAboveIcon,
124 action(context: EditorUiContext) {
125 context.editor.update(() => {
126 $insertTableRow__EXPERIMENTAL(false);
129 isActive: neverActive,
130 isDisabled: cellNotSelected,
133 export const insertRowBelow: EditorButtonDefinition = {
134 label: 'Insert row after',
135 icon: insertRowBelowIcon,
136 action(context: EditorUiContext) {
137 context.editor.update(() => {
138 $insertTableRow__EXPERIMENTAL(true);
141 isActive: neverActive,
142 isDisabled: cellNotSelected,
145 export const deleteRow: EditorButtonDefinition = {
148 action(context: EditorUiContext) {
149 context.editor.update(() => {
150 $deleteTableRow__EXPERIMENTAL();
153 isActive: neverActive,
154 isDisabled: cellNotSelected,
157 export const rowProperties: EditorButtonDefinition = {
158 label: 'Row properties',
160 action(context: EditorUiContext) {
161 context.editor.getEditorState().read(() => {
162 const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
163 if (!$isTableCellNode(cell)) {
167 const row = $getParentOfType(cell, $isTableRowNode);
168 const modalForm = context.manager.createModal('row_properties');
173 isActive: neverActive,
174 isDisabled: cellNotSelected,
177 export const cutRow: EditorButtonDefinition = {
180 action(context: EditorUiContext) {
181 context.editor.getEditorState().read(() => {
185 isActive: neverActive,
186 isDisabled: cellNotSelected,
189 export const copyRow: EditorButtonDefinition = {
192 action(context: EditorUiContext) {
193 context.editor.getEditorState().read(() => {
197 isActive: neverActive,
198 isDisabled: cellNotSelected,
201 export const pasteRowBefore: EditorButtonDefinition = {
202 label: 'Paste row before',
204 action(context: EditorUiContext) {
205 context.editor.getEditorState().read(() => {
209 isActive: neverActive,
210 isDisabled: cellNotSelected,
213 export const pasteRowAfter: EditorButtonDefinition = {
214 label: 'Paste row after',
216 action(context: EditorUiContext) {
217 context.editor.getEditorState().read(() => {
221 isActive: neverActive,
222 isDisabled: cellNotSelected,
225 export const cutColumn: EditorButtonDefinition = {
228 action(context: EditorUiContext) {
229 context.editor.getEditorState().read(() => {
233 isActive: neverActive,
234 isDisabled: cellNotSelected,
237 export const copyColumn: EditorButtonDefinition = {
238 label: 'Copy column',
240 action(context: EditorUiContext) {
241 context.editor.getEditorState().read(() => {
245 isActive: neverActive,
246 isDisabled: cellNotSelected,
249 export const pasteColumnBefore: EditorButtonDefinition = {
250 label: 'Paste column before',
252 action(context: EditorUiContext) {
253 context.editor.getEditorState().read(() => {
257 isActive: neverActive,
258 isDisabled: cellNotSelected,
261 export const pasteColumnAfter: EditorButtonDefinition = {
262 label: 'Paste column after',
264 action(context: EditorUiContext) {
265 context.editor.getEditorState().read(() => {
269 isActive: neverActive,
270 isDisabled: cellNotSelected,
273 export const insertColumnBefore: EditorButtonDefinition = {
274 label: 'Insert column before',
275 icon: insertColumnBeforeIcon,
276 action(context: EditorUiContext) {
277 context.editor.update(() => {
278 $insertTableColumn__EXPERIMENTAL(false);
286 export const insertColumnAfter: EditorButtonDefinition = {
287 label: 'Insert column after',
288 icon: insertColumnAfterIcon,
289 action(context: EditorUiContext) {
290 context.editor.update(() => {
291 $insertTableColumn__EXPERIMENTAL(true);
299 export const deleteColumn: EditorButtonDefinition = {
300 label: 'Delete column',
301 icon: deleteColumnIcon,
302 action(context: EditorUiContext) {
303 context.editor.update(() => {
304 $deleteTableColumn__EXPERIMENTAL();
312 export const cellProperties: EditorButtonDefinition = {
313 label: 'Cell properties',
314 action(context: EditorUiContext) {
315 context.editor.getEditorState().read(() => {
316 const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
317 if ($isTableCellNode(cell)) {
319 const modalForm = context.manager.createModal('cell_properties');
324 isActive: neverActive,
325 isDisabled: cellNotSelected,
328 export const mergeCells: EditorButtonDefinition = {
329 label: 'Merge cells',
330 action(context: EditorUiContext) {
331 context.editor.update(() => {
332 // Todo - Needs to be done manually
333 // Playground reference:
334 // https://github.com/facebook/lexical/blob/f373759a7849f473d34960a6bf4e34b2a011e762/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx#L299
337 isActive: neverActive,
338 isDisabled(selection) {
339 return !$isTableSelection(selection);
343 export const splitCell: EditorButtonDefinition = {
345 action(context: EditorUiContext) {
346 context.editor.update(() => {
350 isActive: neverActive,
351 isDisabled(selection) {
352 const cell = $getNodeFromSelection(selection, $isTableCellNode) as TableCellNode|null;
354 const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;