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, $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";
23 import {$showCellPropertiesForm, $showRowPropertiesForm, $showTablePropertiesForm} from "../forms/tables";
25 $clearTableFormatting,
26 $clearTableSizes, $getTableFromSelection,
27 $getTableRowsFromSelection,
28 $mergeTableCellsInSelection
29 } from "../../../utils/tables";
30 import {$isCustomTableRowNode, CustomTableRowNode} from "../../../nodes/custom-table-row";
31 import {NodeClipboard} from "../../../services/node-clipboard";
33 const neverActive = (): boolean => false;
34 const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
36 export const table: EditorBasicButtonDefinition = {
41 export const tableProperties: EditorButtonDefinition = {
42 label: 'Table properties',
44 action(context: EditorUiContext) {
45 context.editor.getEditorState().read(() => {
46 const table = $getTableFromSelection($getSelection());
47 if ($isCustomTableNode(table)) {
48 $showTablePropertiesForm(table, context);
52 isActive: neverActive,
53 isDisabled: cellNotSelected,
56 export const clearTableFormatting: EditorButtonDefinition = {
57 label: 'Clear table formatting',
59 action(context: EditorUiContext) {
60 context.editor.update(() => {
61 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
62 if (!$isCustomTableCellNode(cell)) {
66 const table = $getParentOfType(cell, $isTableNode);
67 if ($isCustomTableNode(table)) {
68 $clearTableFormatting(table);
72 isActive: neverActive,
73 isDisabled: cellNotSelected,
76 export const resizeTableToContents: EditorButtonDefinition = {
77 label: 'Resize to contents',
79 action(context: EditorUiContext) {
80 context.editor.update(() => {
81 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
82 if (!$isCustomTableCellNode(cell)) {
86 const table = $getParentOfType(cell, $isCustomTableNode);
87 if ($isCustomTableNode(table)) {
88 $clearTableSizes(table);
92 isActive: neverActive,
93 isDisabled: cellNotSelected,
96 export const deleteTable: EditorButtonDefinition = {
97 label: 'Delete table',
99 action(context: EditorUiContext) {
100 context.editor.update(() => {
101 const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
112 export const deleteTableMenuAction: EditorButtonDefinition = {
115 isDisabled(selection) {
116 return !$selectionContainsNodeType(selection, $isTableNode);
120 export const insertRowAbove: EditorButtonDefinition = {
121 label: 'Insert row before',
122 icon: insertRowAboveIcon,
123 action(context: EditorUiContext) {
124 context.editor.update(() => {
125 $insertTableRow__EXPERIMENTAL(false);
128 isActive: neverActive,
129 isDisabled: cellNotSelected,
132 export const insertRowBelow: EditorButtonDefinition = {
133 label: 'Insert row after',
134 icon: insertRowBelowIcon,
135 action(context: EditorUiContext) {
136 context.editor.update(() => {
137 $insertTableRow__EXPERIMENTAL(true);
140 isActive: neverActive,
141 isDisabled: cellNotSelected,
144 export const deleteRow: EditorButtonDefinition = {
147 action(context: EditorUiContext) {
148 context.editor.update(() => {
149 $deleteTableRow__EXPERIMENTAL();
152 isActive: neverActive,
153 isDisabled: cellNotSelected,
156 export const rowProperties: EditorButtonDefinition = {
157 label: 'Row properties',
159 action(context: EditorUiContext) {
160 context.editor.getEditorState().read(() => {
161 const rows = $getTableRowsFromSelection($getSelection());
162 if ($isCustomTableRowNode(rows[0])) {
163 $showRowPropertiesForm(rows[0], context);
167 isActive: neverActive,
168 isDisabled: cellNotSelected,
171 const rowClipboard: NodeClipboard<CustomTableRowNode> = new NodeClipboard<CustomTableRowNode>(CustomTableRowNode);
173 export const cutRow: EditorButtonDefinition = {
176 action(context: EditorUiContext) {
177 context.editor.update(() => {
178 const rows = $getTableRowsFromSelection($getSelection());
179 rowClipboard.set(...rows);
180 for (const row of rows) {
185 isActive: neverActive,
186 isDisabled: cellNotSelected,
189 export const copyRow: EditorButtonDefinition = {
192 action(context: EditorUiContext) {
193 context.editor.getEditorState().read(() => {
194 const rows = $getTableRowsFromSelection($getSelection());
195 rowClipboard.set(...rows);
198 isActive: neverActive,
199 isDisabled: cellNotSelected,
202 export const pasteRowBefore: EditorButtonDefinition = {
203 label: 'Paste row before',
205 action(context: EditorUiContext) {
206 context.editor.update(() => {
207 const rows = $getTableRowsFromSelection($getSelection());
208 const lastRow = rows[rows.length - 1];
210 for (const row of rowClipboard.get(context.editor)) {
211 lastRow.insertBefore(row);
216 isActive: neverActive,
217 isDisabled: (selection) => cellNotSelected(selection) || rowClipboard.size() === 0,
220 export const pasteRowAfter: EditorButtonDefinition = {
221 label: 'Paste row after',
223 action(context: EditorUiContext) {
224 context.editor.update(() => {
225 const rows = $getTableRowsFromSelection($getSelection());
226 const lastRow = rows[rows.length - 1];
228 for (const row of rowClipboard.get(context.editor).reverse()) {
229 lastRow.insertAfter(row);
234 isActive: neverActive,
235 isDisabled: (selection) => cellNotSelected(selection) || rowClipboard.size() === 0,
238 export const cutColumn: EditorButtonDefinition = {
241 action(context: EditorUiContext) {
242 context.editor.getEditorState().read(() => {
246 isActive: neverActive,
247 isDisabled: cellNotSelected,
250 export const copyColumn: EditorButtonDefinition = {
251 label: 'Copy column',
253 action(context: EditorUiContext) {
254 context.editor.getEditorState().read(() => {
258 isActive: neverActive,
259 isDisabled: cellNotSelected,
262 export const pasteColumnBefore: EditorButtonDefinition = {
263 label: 'Paste column before',
265 action(context: EditorUiContext) {
266 context.editor.getEditorState().read(() => {
270 isActive: neverActive,
271 isDisabled: cellNotSelected,
274 export const pasteColumnAfter: EditorButtonDefinition = {
275 label: 'Paste column after',
277 action(context: EditorUiContext) {
278 context.editor.getEditorState().read(() => {
282 isActive: neverActive,
283 isDisabled: cellNotSelected,
286 export const insertColumnBefore: EditorButtonDefinition = {
287 label: 'Insert column before',
288 icon: insertColumnBeforeIcon,
289 action(context: EditorUiContext) {
290 context.editor.update(() => {
291 $insertTableColumn__EXPERIMENTAL(false);
299 export const insertColumnAfter: EditorButtonDefinition = {
300 label: 'Insert column after',
301 icon: insertColumnAfterIcon,
302 action(context: EditorUiContext) {
303 context.editor.update(() => {
304 $insertTableColumn__EXPERIMENTAL(true);
312 export const deleteColumn: EditorButtonDefinition = {
313 label: 'Delete column',
314 icon: deleteColumnIcon,
315 action(context: EditorUiContext) {
316 context.editor.update(() => {
317 $deleteTableColumn__EXPERIMENTAL();
325 export const cellProperties: EditorButtonDefinition = {
326 label: 'Cell properties',
327 action(context: EditorUiContext) {
328 context.editor.getEditorState().read(() => {
329 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
330 if ($isCustomTableCellNode(cell)) {
331 $showCellPropertiesForm(cell, context);
335 isActive: neverActive,
336 isDisabled: cellNotSelected,
339 export const mergeCells: EditorButtonDefinition = {
340 label: 'Merge cells',
341 action(context: EditorUiContext) {
342 context.editor.update(() => {
343 const selection = $getSelection();
344 if ($isTableSelection(selection)) {
345 $mergeTableCellsInSelection(selection);
349 isActive: neverActive,
350 isDisabled(selection) {
351 return !$isTableSelection(selection);
355 export const splitCell: EditorButtonDefinition = {
357 action(context: EditorUiContext) {
358 context.editor.update(() => {
362 isActive: neverActive,
363 isDisabled(selection) {
364 const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as TableCellNode|null;
366 const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;