1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-07-28 17:02:04 +03:00

Started on table editing/resizing

This commit is contained in:
Dan Brown
2022-01-19 16:46:45 +00:00
parent 4b08eef12c
commit 9b4ea368dc
8 changed files with 196 additions and 48 deletions

View File

@ -1,7 +1,7 @@
import {EditorState} from "prosemirror-state";
import {EditorView} from "prosemirror-view";
import {exampleSetup} from "prosemirror-example-setup";
import {tableEditing} from "prosemirror-tables";
import {tableEditing, columnResizing} from "prosemirror-tables";
import {DOMParser} from "prosemirror-model";
@ -23,11 +23,16 @@ class ProseMirrorView {
plugins: [
...exampleSetup({schema, menuBar: false}),
menu,
columnResizing(),
tableEditing(),
]
}),
nodeViews,
});
// Fix for native handles (Such as table size handling) in some browsers
document.execCommand("enableObjectResizing", false, "false")
document.execCommand("enableInlineTableEditing", false, "false")
}
get content() {

View File

@ -61,9 +61,10 @@ export function insertBlockBefore(blockType) {
/**
* @param {Number} rows
* @param {Number} columns
* @param {Object} tableAttrs
* @return {PmCommandHandler}
*/
export function insertTable(rows, columns) {
export function insertTable(rows, columns, tableAttrs) {
return function (state, dispatch) {
if (!dispatch) return true;
@ -74,12 +75,13 @@ export function insertTable(rows, columns) {
for (let y = 0; y < rows; y++) {
const rowCells = [];
for (let x = 0; x < columns; x++) {
rowCells.push(nodes.table_cell.create(null));
const cellText = nodes.paragraph.create(null);
rowCells.push(nodes.table_cell.create(null, cellText));
}
rowNodes.push(nodes.table_row.create(null, rowCells));
}
const table = nodes.table.create(null, rowNodes);
const table = nodes.table.create(tableAttrs, rowNodes);
tr.replaceSelectionWith(table);
dispatch(tr);

View File

@ -9,6 +9,10 @@ nodes.callout = function (state, node) {
writeNodeAsHtml(state, node);
};
nodes.table = function (state, node) {
writeNodeAsHtml(state, node);
};
function isPlainURL(link, parent, index, side) {
if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) {
return false

View File

@ -5,7 +5,6 @@ import {insertTable} from "../commands";
class TableCreatorGrid {
constructor() {
this.gridItems = [];
this.size = 10;
this.label = null;
}
@ -14,26 +13,31 @@ class TableCreatorGrid {
// Renders the submenu.
render(view) {
const gridItems = [];
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));
gridItems.push(elem);
elem.addEventListener('mouseenter', event => {
this.updateGridItemActiveStatus(elem, gridItems);
});
}
}
const gridWrap = crel("div", {
class: prefix + "-table-creator-grid",
style: `grid-template-columns: repeat(${this.size}, 14px);`,
}, this.gridItems);
}, gridItems);
gridWrap.addEventListener('mouseleave', event => {
this.updateGridItemActiveStatus(null);
this.updateGridItemActiveStatus(null, gridItems);
});
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 {x, y} = this.getPositionOfGridItem(event.target, gridItems);
insertTable(y + 1, x + 1, {
style: 'width: 100%;',
})(view.state, view.dispatch);
}
});
@ -50,15 +54,16 @@ class TableCreatorGrid {
/**
* @param {Element|null} newTarget
* @param {Element[]} gridItems
*/
updateGridItemActiveStatus(newTarget) {
const {x: xPos, y: yPos} = this.getPositionOfGridItem(newTarget);
updateGridItemActiveStatus(newTarget, gridItems) {
const {x: xPos, y: yPos} = this.getPositionOfGridItem(newTarget, gridItems);
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);
gridItems[index].classList.toggle(prefix + "-table-creator-grid-item-active", active);
}
}
@ -67,10 +72,11 @@ class TableCreatorGrid {
/**
* @param {Element} gridItem
* @param {Element[]} gridItems
* @return {{x: number, y: number}}
*/
getPositionOfGridItem(gridItem) {
const index = this.gridItems.indexOf(gridItem);
getPositionOfGridItem(gridItem, gridItems) {
const index = gridItems.indexOf(gridItem);
const y = Math.floor(index / this.size);
const x = index % this.size;
return {x, y};

View File

@ -17,17 +17,6 @@ function getAlignAttrFromDomNode(node) {
return null;
}
/**
* @param {String} className
* @param {Object} attrs
* @return {Object}
*/
function addClassToAttrs(className, attrs) {
return Object.assign({}, attrs, {
class: attrs.class ? attrs.class + ' ' + className : className,
});
}
/**
* @param node
* @param {Object} attrs
@ -49,6 +38,45 @@ function getAttrsParserForAlignment(node) {
};
}
/**
* @param {String} className
* @param {Object} attrs
* @return {Object}
*/
function addClassToAttrs(className, attrs) {
return Object.assign({}, attrs, {
class: attrs.class ? attrs.class + ' ' + className : className,
});
}
/**
* @param {String[]} attrNames
* @return {function(Element): {}}
*/
function domAttrsToAttrsParser(attrNames) {
return function (node) {
const attrs = {};
for (const attr of attrNames) {
attrs[attr] = node.hasAttribute(attr) ? node.getAttribute(attr) : null;
}
return attrs;
};
}
/**
* @param {PmNode} node
* @param {String[]} attrNames
*/
function extractAttrsForDom(node, attrNames) {
const domAttrs = {};
for (const attr of attrNames) {
if (node.attrs[attr]) {
domAttrs[attr] = node.attrs[attr];
}
}
return domAttrs;
}
const doc = {
content: "block+",
};
@ -210,15 +238,29 @@ const bullet_list = Object.assign({}, bulletList, {content: "list_item+", group:
const list_item = Object.assign({}, listItem, {content: 'paragraph block*'});
const {
table,
table_row,
table_cell,
table_header,
} = tableNodes({
tableGroup: "block",
cellContent: "block*"
cellContent: "block+"
});
const table = {
content: "table_row+",
attrs: {
style: {default: null},
},
tableRole: "table",
isolating: true,
group: "block",
parseDOM: [{tag: "table", getAttrs: domAttrsToAttrsParser(['style'])}],
toDOM(node) {
console.log(extractAttrsForDom(node, ['style']));
return ["table", extractAttrsForDom(node, ['style']), ["tbody", 0]]
}
};
const nodes = {
doc,
paragraph,