mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Added table creator UI
This commit is contained in:
parent
f47f7dd9d2
commit
ac01c62e6e
@ -1,13 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
$createParagraphNode,
|
$createParagraphNode, $getRoot,
|
||||||
$getSelection,
|
$getSelection,
|
||||||
$isTextNode,
|
$isTextNode,
|
||||||
BaseSelection,
|
BaseSelection, ElementNode,
|
||||||
LexicalEditor, LexicalNode, TextFormatType
|
LexicalEditor, LexicalNode, TextFormatType
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
||||||
import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||||
import {$setBlocksType} from "@lexical/selection";
|
import {$setBlocksType} from "@lexical/selection";
|
||||||
|
import {$createDetailsNode} from "./nodes/details";
|
||||||
|
|
||||||
export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
|
export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
|
||||||
const el = document.createElement(tag);
|
const el = document.createElement(tag);
|
||||||
@ -77,4 +78,15 @@ export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: Lex
|
|||||||
$setBlocksType(selection, creator);
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -236,6 +236,10 @@ export const link: EditorButtonDefinition = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const table: EditorBasicButtonDefinition = {
|
||||||
|
label: 'Table',
|
||||||
|
};
|
||||||
|
|
||||||
export const image: EditorButtonDefinition = {
|
export const image: EditorButtonDefinition = {
|
||||||
label: 'Insert/Edit Image',
|
label: 'Insert/Edit Image',
|
||||||
icon: imageIcon,
|
icon: imageIcon,
|
||||||
|
80
resources/js/wysiwyg/ui/framework/blocks/table-creator.ts
Normal file
80
resources/js/wysiwyg/ui/framework/blocks/table-creator.ts
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import {
|
|||||||
h2, h3, h4, h5, highlightColor, image,
|
h2, h3, h4, h5, highlightColor, image,
|
||||||
infoCallout, italic, link, numberList, paragraph,
|
infoCallout, italic, link, numberList, paragraph,
|
||||||
redo, source, strikethrough, subscript,
|
redo, source, strikethrough, subscript,
|
||||||
successCallout, superscript, taskList, textColor, underline,
|
successCallout, superscript, table, taskList, textColor, underline,
|
||||||
undo,
|
undo,
|
||||||
warningCallout
|
warningCallout
|
||||||
} from "./defaults/button-definitions";
|
} from "./defaults/button-definitions";
|
||||||
@ -15,8 +15,7 @@ import {EditorFormatMenu} from "./framework/blocks/format-menu";
|
|||||||
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
||||||
import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
|
import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
|
||||||
import {EditorColorPicker} from "./framework/blocks/color-picker";
|
import {EditorColorPicker} from "./framework/blocks/color-picker";
|
||||||
|
import {EditorTableCreator} from "./framework/blocks/table-creator";
|
||||||
console.log(undo);
|
|
||||||
|
|
||||||
export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
||||||
return new EditorSimpleClassContainer('editor-toolbar-main', [
|
return new EditorSimpleClassContainer('editor-toolbar-main', [
|
||||||
@ -61,6 +60,9 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
|||||||
|
|
||||||
// Insert types
|
// Insert types
|
||||||
new EditorButton(link),
|
new EditorButton(link),
|
||||||
|
new EditorDropdownButton(table, [
|
||||||
|
new EditorTableCreator(),
|
||||||
|
]),
|
||||||
new EditorButton(image),
|
new EditorButton(image),
|
||||||
new EditorButton(details),
|
new EditorButton(details),
|
||||||
|
|
||||||
|
@ -100,6 +100,22 @@
|
|||||||
z-index: 3;
|
z-index: 3;
|
||||||
box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.25);
|
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
|
// In-editor elements
|
||||||
.editor-image-wrap {
|
.editor-image-wrap {
|
||||||
|
Loading…
Reference in New Issue
Block a user