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";
14 $deleteTableColumn__EXPERIMENTAL,
15 $deleteTableRow__EXPERIMENTAL,
16 $insertTableColumn__EXPERIMENTAL,
17 $insertTableRow__EXPERIMENTAL,
18 $isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode,
19 } from "@lexical/table";
20 import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
21 import {$getParentOfType} from "../../../utils/nodes";
22 import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell-node";
23 import {showCellPropertiesForm} from "../forms/tables";
25 const neverActive = (): boolean => false;
26 const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
28 export const table: EditorBasicButtonDefinition = {
33 export const tableProperties: EditorButtonDefinition = {
34 label: 'Table properties',
36 action(context: EditorUiContext) {
37 context.editor.getEditorState().read(() => {
38 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
39 if (!$isCustomTableCellNode(cell)) {
43 const table = $getParentOfType(cell, $isTableNode);
44 const modalForm = context.manager.createModal('table_properties');
49 isActive: neverActive,
50 isDisabled: cellNotSelected,
53 export const clearTableFormatting: EditorButtonDefinition = {
54 label: 'Clear table formatting',
56 action(context: EditorUiContext) {
57 context.editor.getEditorState().read(() => {
58 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
59 if (!$isCustomTableCellNode(cell)) {
63 const table = $getParentOfType(cell, $isTableNode);
67 isActive: neverActive,
68 isDisabled: cellNotSelected,
71 export const resizeTableToContents: EditorButtonDefinition = {
72 label: 'Resize to contents',
74 action(context: EditorUiContext) {
75 context.editor.getEditorState().read(() => {
76 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
77 if (!$isCustomTableCellNode(cell)) {
81 const table = $getParentOfType(cell, $isCustomTableNode);
82 if (!$isCustomTableNode(table)) {
86 for (const row of table.getChildren()) {
87 if ($isTableRowNode(row)) {
88 // TODO - Come back later as this may depend on if we
89 // are using a custom table row
94 isActive: neverActive,
95 isDisabled: cellNotSelected,
98 export const deleteTable: EditorButtonDefinition = {
99 label: 'Delete table',
101 action(context: EditorUiContext) {
102 context.editor.update(() => {
103 const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
114 export const deleteTableMenuAction: EditorButtonDefinition = {
117 isDisabled(selection) {
118 return !$selectionContainsNodeType(selection, $isTableNode);
122 export const insertRowAbove: EditorButtonDefinition = {
123 label: 'Insert row before',
124 icon: insertRowAboveIcon,
125 action(context: EditorUiContext) {
126 context.editor.update(() => {
127 $insertTableRow__EXPERIMENTAL(false);
130 isActive: neverActive,
131 isDisabled: cellNotSelected,
134 export const insertRowBelow: EditorButtonDefinition = {
135 label: 'Insert row after',
136 icon: insertRowBelowIcon,
137 action(context: EditorUiContext) {
138 context.editor.update(() => {
139 $insertTableRow__EXPERIMENTAL(true);
142 isActive: neverActive,
143 isDisabled: cellNotSelected,
146 export const deleteRow: EditorButtonDefinition = {
149 action(context: EditorUiContext) {
150 context.editor.update(() => {
151 $deleteTableRow__EXPERIMENTAL();
154 isActive: neverActive,
155 isDisabled: cellNotSelected,
158 export const rowProperties: EditorButtonDefinition = {
159 label: 'Row properties',
161 action(context: EditorUiContext) {
162 context.editor.getEditorState().read(() => {
163 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
164 if (!$isCustomTableCellNode(cell)) {
168 const row = $getParentOfType(cell, $isTableRowNode);
169 const modalForm = context.manager.createModal('row_properties');
174 isActive: neverActive,
175 isDisabled: cellNotSelected,
178 export const cutRow: EditorButtonDefinition = {
181 action(context: EditorUiContext) {
182 context.editor.getEditorState().read(() => {
186 isActive: neverActive,
187 isDisabled: cellNotSelected,
190 export const copyRow: EditorButtonDefinition = {
193 action(context: EditorUiContext) {
194 context.editor.getEditorState().read(() => {
198 isActive: neverActive,
199 isDisabled: cellNotSelected,
202 export const pasteRowBefore: EditorButtonDefinition = {
203 label: 'Paste row before',
205 action(context: EditorUiContext) {
206 context.editor.getEditorState().read(() => {
210 isActive: neverActive,
211 isDisabled: cellNotSelected,
214 export const pasteRowAfter: EditorButtonDefinition = {
215 label: 'Paste row after',
217 action(context: EditorUiContext) {
218 context.editor.getEditorState().read(() => {
222 isActive: neverActive,
223 isDisabled: cellNotSelected,
226 export const cutColumn: EditorButtonDefinition = {
229 action(context: EditorUiContext) {
230 context.editor.getEditorState().read(() => {
234 isActive: neverActive,
235 isDisabled: cellNotSelected,
238 export const copyColumn: EditorButtonDefinition = {
239 label: 'Copy column',
241 action(context: EditorUiContext) {
242 context.editor.getEditorState().read(() => {
246 isActive: neverActive,
247 isDisabled: cellNotSelected,
250 export const pasteColumnBefore: EditorButtonDefinition = {
251 label: 'Paste column before',
253 action(context: EditorUiContext) {
254 context.editor.getEditorState().read(() => {
258 isActive: neverActive,
259 isDisabled: cellNotSelected,
262 export const pasteColumnAfter: EditorButtonDefinition = {
263 label: 'Paste column after',
265 action(context: EditorUiContext) {
266 context.editor.getEditorState().read(() => {
270 isActive: neverActive,
271 isDisabled: cellNotSelected,
274 export const insertColumnBefore: EditorButtonDefinition = {
275 label: 'Insert column before',
276 icon: insertColumnBeforeIcon,
277 action(context: EditorUiContext) {
278 context.editor.update(() => {
279 $insertTableColumn__EXPERIMENTAL(false);
287 export const insertColumnAfter: EditorButtonDefinition = {
288 label: 'Insert column after',
289 icon: insertColumnAfterIcon,
290 action(context: EditorUiContext) {
291 context.editor.update(() => {
292 $insertTableColumn__EXPERIMENTAL(true);
300 export const deleteColumn: EditorButtonDefinition = {
301 label: 'Delete column',
302 icon: deleteColumnIcon,
303 action(context: EditorUiContext) {
304 context.editor.update(() => {
305 $deleteTableColumn__EXPERIMENTAL();
313 export const cellProperties: EditorButtonDefinition = {
314 label: 'Cell properties',
315 action(context: EditorUiContext) {
316 context.editor.getEditorState().read(() => {
317 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
318 if ($isCustomTableCellNode(cell)) {
319 showCellPropertiesForm(cell, context);
323 isActive: neverActive,
324 isDisabled: cellNotSelected,
327 export const mergeCells: EditorButtonDefinition = {
328 label: 'Merge cells',
329 action(context: EditorUiContext) {
330 context.editor.update(() => {
331 // Todo - Needs to be done manually
332 // Playground reference:
333 // https://github.com/facebook/lexical/blob/f373759a7849f473d34960a6bf4e34b2a011e762/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx#L299
336 isActive: neverActive,
337 isDisabled(selection) {
338 return !$isTableSelection(selection);
342 export const splitCell: EditorButtonDefinition = {
344 action(context: EditorUiContext) {
345 context.editor.update(() => {
349 isActive: neverActive,
350 isDisabled(selection) {
351 const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as TableCellNode|null;
353 const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;