diff --git a/resources/js/wysiwyg/helpers.ts b/resources/js/wysiwyg/helpers.ts index d7cd23a35..62e945721 100644 --- a/resources/js/wysiwyg/helpers.ts +++ b/resources/js/wysiwyg/helpers.ts @@ -1,13 +1,14 @@ import { - $createParagraphNode, + $createParagraphNode, $getRoot, $getSelection, $isTextNode, - BaseSelection, + BaseSelection, ElementNode, LexicalEditor, LexicalNode, TextFormatType } from "lexical"; import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes"; import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils"; import {$setBlocksType} from "@lexical/selection"; +import {$createDetailsNode} from "./nodes/details"; export function el(tag: string, attrs: Record = {}, children: (string|HTMLElement)[] = []): HTMLElement { const el = document.createElement(tag); @@ -77,4 +78,15 @@ export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: Lex $setBlocksType(selection, creator); } }); +} + +export function insertNewBlockNodeAtSelection(node: LexicalNode) { + const selection = $getSelection(); + const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null; + + if (blockElement) { + blockElement.insertAfter(node); + } else { + $getRoot().append(node); + } } \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts index 7fa1fb5f8..bf1846b8f 100644 --- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts +++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts @@ -236,6 +236,10 @@ export const link: EditorButtonDefinition = { } }; +export const table: EditorBasicButtonDefinition = { + label: 'Table', +}; + export const image: EditorButtonDefinition = { label: 'Insert/Edit Image', icon: imageIcon, diff --git a/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts b/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts new file mode 100644 index 000000000..c54645856 --- /dev/null +++ b/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts @@ -0,0 +1,80 @@ +import {el, insertNewBlockNodeAtSelection} from "../../../helpers"; +import {EditorUiElement} from "../core"; +import {$createTableNodeWithDimensions} from "@lexical/table"; + + +export class EditorTableCreator extends EditorUiElement { + + buildDOM(): HTMLElement { + const size = 10; + const rows: HTMLElement[] = []; + const cells: HTMLElement[] = []; + + for (let row = 1; row < size + 1; row++) { + const rowCells = []; + for (let column = 1; column < size + 1; column++) { + const cell = el('div', { + class: 'editor-table-creator-cell', + 'data-rows': String(row), + 'data-columns': String(column), + }); + rowCells.push(cell); + cells.push(cell); + } + rows.push(el('div', { + class: 'editor-table-creator-row' + }, rowCells)); + } + + const display = el('div', {class: 'editor-table-creator-display'}, ['0 x 0']); + const grid = el('div', {class: 'editor-table-creator-grid'}, rows); + grid.addEventListener('mousemove', event => { + const cell = (event.target as HTMLElement).closest('.editor-table-creator-cell') as HTMLElement|null; + if (cell) { + const row = Number(cell.dataset.rows || 0); + const column = Number(cell.dataset.columns || 0); + this.updateGridSelection(row, column, cells, display) + } + }); + + grid.addEventListener('click', event => { + const cell = (event.target as HTMLElement).closest('.editor-table-creator-cell'); + if (cell) { + this.onCellClick(cell as HTMLElement); + } + }); + + grid.addEventListener('mouseleave', event => { + this.updateGridSelection(0, 0, cells, display); + }); + + return el('div', { + class: 'editor-table-creator', + }, [ + grid, + display, + ]); + } + + updateGridSelection(rows: number, columns: number, cells: HTMLElement[], display: HTMLElement) { + for (const cell of cells) { + const active = Number(cell.dataset.rows) <= rows && Number(cell.dataset.columns) <= columns; + cell.classList.toggle('active', active); + } + + display.textContent = `${rows} x ${columns}`; + } + + onCellClick(cell: HTMLElement) { + const rows = Number(cell.dataset.rows || 0); + const columns = Number(cell.dataset.columns || 0); + if (rows < 1 || columns < 1) { + return; + } + + this.getContext().editor.update(() => { + const table = $createTableNodeWithDimensions(rows, columns, false); + insertNewBlockNodeAtSelection(table); + }); + } +} \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts index 559e9a87c..4dbf9bb7e 100644 --- a/resources/js/wysiwyg/ui/toolbars.ts +++ b/resources/js/wysiwyg/ui/toolbars.ts @@ -5,7 +5,7 @@ import { h2, h3, h4, h5, highlightColor, image, infoCallout, italic, link, numberList, paragraph, redo, source, strikethrough, subscript, - successCallout, superscript, taskList, textColor, underline, + successCallout, superscript, table, taskList, textColor, underline, undo, warningCallout } from "./defaults/button-definitions"; @@ -15,8 +15,7 @@ import {EditorFormatMenu} from "./framework/blocks/format-menu"; import {FormatPreviewButton} from "./framework/blocks/format-preview-button"; import {EditorDropdownButton} from "./framework/blocks/dropdown-button"; import {EditorColorPicker} from "./framework/blocks/color-picker"; - -console.log(undo); +import {EditorTableCreator} from "./framework/blocks/table-creator"; export function getMainEditorFullToolbar(): EditorContainerUiElement { return new EditorSimpleClassContainer('editor-toolbar-main', [ @@ -61,6 +60,9 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement { // Insert types new EditorButton(link), + new EditorDropdownButton(table, [ + new EditorTableCreator(), + ]), new EditorButton(image), new EditorButton(details), diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss index ad1f5a339..69027ea69 100644 --- a/resources/sass/_editor.scss +++ b/resources/sass/_editor.scss @@ -100,6 +100,22 @@ z-index: 3; box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.25); } +.editor-table-creator-row { + display: flex; +} +.editor-table-creator-cell { + border: 1px solid #DDD; + width: 15px; + height: 15px; + cursor: pointer; + &.active { + background-color: var(--editor-color-primary); + } +} +.editor-table-creator-display { + text-align: center; + padding: 0.2em; +} // In-editor elements .editor-image-wrap {