mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Added table creation and insertion
This commit is contained in:
parent
b2283106fc
commit
4b08eef12c
3
TODO
3
TODO
@ -4,11 +4,10 @@
|
||||
|
||||
### In-Progress
|
||||
|
||||
//
|
||||
- Tables
|
||||
|
||||
### Features
|
||||
|
||||
- Tables
|
||||
- Images
|
||||
- Drawings
|
||||
- LTR/RTL control
|
||||
|
25
package-lock.json
generated
25
package-lock.json
generated
@ -17,6 +17,7 @@
|
||||
"prosemirror-model": "^1.15.0",
|
||||
"prosemirror-schema-list": "^1.1.6",
|
||||
"prosemirror-state": "^1.3.4",
|
||||
"prosemirror-tables": "^1.1.1",
|
||||
"prosemirror-view": "^1.23.2",
|
||||
"sortablejs": "^1.14.0"
|
||||
},
|
||||
@ -1520,6 +1521,18 @@
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz",
|
||||
"integrity": "sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.1.2",
|
||||
"prosemirror-model": "^1.8.1",
|
||||
"prosemirror-state": "^1.3.1",
|
||||
"prosemirror-transform": "^1.2.1",
|
||||
"prosemirror-view": "^1.13.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz",
|
||||
@ -3083,6 +3096,18 @@
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-tables": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz",
|
||||
"integrity": "sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==",
|
||||
"requires": {
|
||||
"prosemirror-keymap": "^1.1.2",
|
||||
"prosemirror-model": "^1.8.1",
|
||||
"prosemirror-state": "^1.3.1",
|
||||
"prosemirror-transform": "^1.2.1",
|
||||
"prosemirror-view": "^1.13.3"
|
||||
}
|
||||
},
|
||||
"prosemirror-transform": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz",
|
||||
|
@ -37,6 +37,7 @@
|
||||
"prosemirror-model": "^1.15.0",
|
||||
"prosemirror-schema-list": "^1.1.6",
|
||||
"prosemirror-state": "^1.3.4",
|
||||
"prosemirror-tables": "^1.1.1",
|
||||
"prosemirror-view": "^1.23.2",
|
||||
"sortablejs": "^1.14.0"
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {EditorState} from "prosemirror-state";
|
||||
import {EditorView} from "prosemirror-view";
|
||||
import {exampleSetup} from "prosemirror-example-setup";
|
||||
import {tableEditing} from "prosemirror-tables";
|
||||
|
||||
import {DOMParser} from "prosemirror-model";
|
||||
|
||||
@ -22,6 +23,7 @@ class ProseMirrorView {
|
||||
plugins: [
|
||||
...exampleSetup({schema, menuBar: false}),
|
||||
menu,
|
||||
tableEditing(),
|
||||
]
|
||||
}),
|
||||
nodeViews,
|
||||
|
@ -58,6 +58,35 @@ export function insertBlockBefore(blockType) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} rows
|
||||
* @param {Number} columns
|
||||
* @return {PmCommandHandler}
|
||||
*/
|
||||
export function insertTable(rows, columns) {
|
||||
return function (state, dispatch) {
|
||||
if (!dispatch) return true;
|
||||
|
||||
const tr = state.tr;
|
||||
const nodes = state.schema.nodes;
|
||||
|
||||
const rowNodes = [];
|
||||
for (let y = 0; y < rows; y++) {
|
||||
const rowCells = [];
|
||||
for (let x = 0; x < columns; x++) {
|
||||
rowCells.push(nodes.table_cell.create(null));
|
||||
}
|
||||
rowNodes.push(nodes.table_row.create(null, rowCells));
|
||||
}
|
||||
|
||||
const table = nodes.table.create(null, rowNodes);
|
||||
tr.replaceSelectionWith(table);
|
||||
dispatch(tr);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {PmCommandHandler}
|
||||
*/
|
||||
|
80
resources/js/editor/menu/TableCreatorGrid.js
Normal file
80
resources/js/editor/menu/TableCreatorGrid.js
Normal file
@ -0,0 +1,80 @@
|
||||
import crel from "crelt"
|
||||
import {prefix} from "./menu-utils";
|
||||
import {insertTable} from "../commands";
|
||||
|
||||
class TableCreatorGrid {
|
||||
|
||||
constructor() {
|
||||
this.gridItems = [];
|
||||
this.size = 10;
|
||||
this.label = null;
|
||||
}
|
||||
|
||||
// :: (EditorView) → {dom: dom.Node, update: (EditorState) → bool}
|
||||
// Renders the submenu.
|
||||
render(view) {
|
||||
|
||||
for (let y = 0; y < this.size; y++) {
|
||||
for (let x = 0; x < this.size; x++) {
|
||||
const elem = crel("div", {class: prefix + "-table-creator-grid-item"});
|
||||
this.gridItems.push(elem);
|
||||
elem.addEventListener('mouseenter', event => this.updateGridItemActiveStatus(elem));
|
||||
}
|
||||
}
|
||||
|
||||
const gridWrap = crel("div", {
|
||||
class: prefix + "-table-creator-grid",
|
||||
style: `grid-template-columns: repeat(${this.size}, 14px);`,
|
||||
}, this.gridItems);
|
||||
|
||||
gridWrap.addEventListener('mouseleave', event => {
|
||||
this.updateGridItemActiveStatus(null);
|
||||
});
|
||||
gridWrap.addEventListener('click', event => {
|
||||
if (event.target.classList.contains(prefix + "-table-creator-grid-item")) {
|
||||
const {x, y} = this.getPositionOfGridItem(event.target);
|
||||
insertTable(y + 1, x + 1)(view.state, view.dispatch);
|
||||
}
|
||||
});
|
||||
|
||||
const gridLabel = crel("div", {class: prefix + "-table-creator-grid-label"});
|
||||
this.label = gridLabel;
|
||||
const wrap = crel("div", {class: prefix + "-table-creator-grid-container"}, [gridWrap, gridLabel]);
|
||||
|
||||
function update(state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return {dom: wrap, update}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element|null} newTarget
|
||||
*/
|
||||
updateGridItemActiveStatus(newTarget) {
|
||||
const {x: xPos, y: yPos} = this.getPositionOfGridItem(newTarget);
|
||||
|
||||
for (let y = 0; y < this.size; y++) {
|
||||
for (let x = 0; x < this.size; x++) {
|
||||
const active = x <= xPos && y <= yPos;
|
||||
const index = (y * this.size) + x;
|
||||
this.gridItems[index].classList.toggle(prefix + "-table-creator-grid-item-active", active);
|
||||
}
|
||||
}
|
||||
|
||||
this.label.textContent = (xPos + yPos < 0) ? '' : `${xPos + 1} x ${yPos + 1}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} gridItem
|
||||
* @return {{x: number, y: number}}
|
||||
*/
|
||||
getPositionOfGridItem(gridItem) {
|
||||
const index = this.gridItems.indexOf(gridItem);
|
||||
const y = Math.floor(index / this.size);
|
||||
const x = index % this.size;
|
||||
return {x, y};
|
||||
}
|
||||
}
|
||||
|
||||
export default TableCreatorGrid;
|
@ -99,6 +99,10 @@ export const icons = {
|
||||
source_code: {
|
||||
width: 24, height: 24,
|
||||
path: "M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z",
|
||||
},
|
||||
table: {
|
||||
width: 24, height: 24,
|
||||
path: "M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z",
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4,13 +4,11 @@ import {
|
||||
} from "./menu"
|
||||
import {icons} from "./icons";
|
||||
import ColorPickerGrid from "./ColorPickerGrid";
|
||||
import DialogBox from "./DialogBox";
|
||||
import TableCreatorGrid from "./TableCreatorGrid";
|
||||
import {toggleMark} from "prosemirror-commands";
|
||||
import {menuBar} from "./menubar"
|
||||
import schema from "../schema";
|
||||
import {removeMarks} from "../commands";
|
||||
import DialogForm from "./DialogForm";
|
||||
import DialogInput from "./DialogInput";
|
||||
|
||||
import itemAnchorButtonItem from "./item-anchor-button";
|
||||
import itemHtmlSourceButton from "./item-html-source-button";
|
||||
@ -157,6 +155,9 @@ const inserts = [
|
||||
title: "Horizontal Rule",
|
||||
icon: icons.horizontal_rule,
|
||||
}),
|
||||
new DropdownSubmenu([
|
||||
new TableCreatorGrid()
|
||||
], {icon: icons.table}),
|
||||
itemHtmlSourceButton(),
|
||||
];
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {orderedList, bulletList, listItem} from "prosemirror-schema-list";
|
||||
import {tableNodes} from "prosemirror-tables";
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} node
|
||||
@ -200,7 +201,7 @@ const callout = {
|
||||
],
|
||||
toDOM(node) {
|
||||
const type = node.attrs.type || 'info';
|
||||
return ['p', addAlignmentAttr(node, {class: 'callout ' + type}) , 0];
|
||||
return ['p', addAlignmentAttr(node, {class: 'callout ' + type}), 0];
|
||||
}
|
||||
};
|
||||
|
||||
@ -208,6 +209,16 @@ const ordered_list = Object.assign({}, orderedList, {content: "list_item+", grou
|
||||
const bullet_list = Object.assign({}, bulletList, {content: "list_item+", group: "block"});
|
||||
const list_item = Object.assign({}, listItem, {content: 'paragraph block*'});
|
||||
|
||||
const {
|
||||
table,
|
||||
table_row,
|
||||
table_cell,
|
||||
table_header,
|
||||
} = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "block*"
|
||||
});
|
||||
|
||||
const nodes = {
|
||||
doc,
|
||||
paragraph,
|
||||
@ -222,6 +233,10 @@ const nodes = {
|
||||
ordered_list,
|
||||
bullet_list,
|
||||
list_item,
|
||||
table,
|
||||
table_row,
|
||||
table_cell,
|
||||
table_header,
|
||||
};
|
||||
|
||||
export default nodes;
|
@ -371,6 +371,29 @@ img.ProseMirror-separator {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ProseMirror-menu-table-creator-grid {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.ProseMirror-menu-table-creator-grid-item {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid #BBB;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ProseMirror-menu-table-creator-grid-item-active {
|
||||
border: 2px solid #555;
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
.ProseMirror-menu-table-creator-grid-label {
|
||||
padding: $-xs;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ProseMirror-menu-dialog-wrap {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
Loading…
Reference in New Issue
Block a user