Initial
This commit is contained in:
218
resources/app/common/prosemirror/schema/tables.mjs
Normal file
218
resources/app/common/prosemirror/schema/tables.mjs
Normal file
@@ -0,0 +1,218 @@
|
||||
import {tableNodes} from "prosemirror-tables";
|
||||
import {isElementEmpty, onlyInlineContent} from "./utils.mjs";
|
||||
|
||||
const CELL_ATTRS = {
|
||||
colspan: {default: 1},
|
||||
rowspan: {default: 1},
|
||||
colwidth: {default: null}
|
||||
};
|
||||
|
||||
const MANAGED_CELL_ATTRS = {
|
||||
attributes: ["colspan", "rowspan", "data-colwidth"]
|
||||
};
|
||||
|
||||
// If any of these elements are part of a table, consider it a 'complex' table and do not attempt to make it editable.
|
||||
const COMPLEX_TABLE_ELEMENTS = new Set(["CAPTION", "COLGROUP", "THEAD", "TFOOT"]);
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Utilities */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Determine node attributes for a table cell when parsing the DOM.
|
||||
* @param {HTMLTableCellElement} cell The table cell DOM node.
|
||||
* @returns {{colspan: number, rowspan: number}}
|
||||
*/
|
||||
function getTableCellAttrs(cell) {
|
||||
const colspan = cell.getAttribute("colspan") || 1;
|
||||
const rowspan = cell.getAttribute("rowspan") || 1;
|
||||
return {
|
||||
colspan: Number(colspan),
|
||||
rowspan: Number(rowspan)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the HTML attributes to be set on the table cell DOM node based on its ProseMirror node attributes.
|
||||
* @param {Node} node The table cell ProseMirror node.
|
||||
* @returns {object} An object of attribute name -> attribute value.
|
||||
*/
|
||||
function setTableCellAttrs(node) {
|
||||
const attrs = {};
|
||||
const {colspan, rowspan} = node.attrs;
|
||||
if ( colspan !== 1 ) attrs.colspan = colspan;
|
||||
if ( rowspan !== 1 ) attrs.rowspan = rowspan;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this element exists as part of a 'complex' table.
|
||||
* @param {HTMLElement} el The element to test.
|
||||
* @returns {boolean|void}
|
||||
*/
|
||||
function inComplexTable(el) {
|
||||
const table = el.closest("table");
|
||||
if ( !table ) return;
|
||||
return Array.from(table.children).some(child => COMPLEX_TABLE_ELEMENTS.has(child.tagName));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Built-in Tables */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const builtInTableNodes = tableNodes({
|
||||
tableGroup: "block",
|
||||
cellContent: "block+"
|
||||
});
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* 'Complex' Tables */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableComplex = {
|
||||
content: "(caption | caption_block)? colgroup? thead? tbody tfoot?",
|
||||
isolating: true,
|
||||
group: "block",
|
||||
parseDOM: [{tag: "table", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
}}],
|
||||
toDOM: () => ["table", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const colgroup = {
|
||||
content: "col*",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "colgroup"}],
|
||||
toDOM: () => ["colgroup", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const col = {
|
||||
tableRole: "col",
|
||||
parseDOM: [{tag: "col"}],
|
||||
toDOM: () => ["col"]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const thead = {
|
||||
content: "table_row_complex+",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "thead"}],
|
||||
toDOM: () => ["thead", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tbody = {
|
||||
content: "table_row_complex+",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "tbody", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
}}],
|
||||
toDOM: () => ["tbody", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tfoot = {
|
||||
content: "table_row_complex+",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "tfoot"}],
|
||||
toDOM: () => ["tfoot", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const caption = {
|
||||
content: "text*",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "caption", getAttrs: el => {
|
||||
if ( !isElementEmpty(el) && !onlyInlineContent(el) ) return false;
|
||||
}}],
|
||||
toDOM: () => ["caption", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const captionBlock = {
|
||||
content: "block*",
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "caption", getAttrs: el => {
|
||||
if ( isElementEmpty(el) || onlyInlineContent(el) ) return false;
|
||||
}}],
|
||||
toDOM: () => ["caption", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableRowComplex = {
|
||||
content: "(table_cell_complex | table_header_complex | table_cell_complex_block | table_header_complex_block)*",
|
||||
parseDOM: [{tag: "tr", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
}}],
|
||||
toDOM: () => ["tr", 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableCellComplex = {
|
||||
content: "text*",
|
||||
attrs: CELL_ATTRS,
|
||||
managed: MANAGED_CELL_ATTRS,
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "td", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
if ( !isElementEmpty(el) && !onlyInlineContent(el) ) return false;
|
||||
return getTableCellAttrs(el);
|
||||
}}],
|
||||
toDOM: node => ["td", setTableCellAttrs(node), 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableCellComplexBlock = {
|
||||
content: "block*",
|
||||
attrs: CELL_ATTRS,
|
||||
managed: MANAGED_CELL_ATTRS,
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "td", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
if ( isElementEmpty(el) || onlyInlineContent(el) ) return false;
|
||||
return getTableCellAttrs(el);
|
||||
}}],
|
||||
toDOM: node => ["td", setTableCellAttrs(node), 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableHeaderComplex = {
|
||||
content: "text*",
|
||||
attrs: CELL_ATTRS,
|
||||
managed: MANAGED_CELL_ATTRS,
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "th", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
if ( !isElementEmpty(el) && !onlyInlineContent(el) ) return false;
|
||||
return getTableCellAttrs(el);
|
||||
}}],
|
||||
toDOM: node => ["th", setTableCellAttrs(node), 0]
|
||||
};
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
export const tableHeaderComplexBlock = {
|
||||
content: "block*",
|
||||
attrs: CELL_ATTRS,
|
||||
managed: MANAGED_CELL_ATTRS,
|
||||
isolating: true,
|
||||
parseDOM: [{tag: "th", getAttrs: el => {
|
||||
if ( inComplexTable(el) === false ) return false;
|
||||
if ( isElementEmpty(el) || onlyInlineContent(el) ) return false;
|
||||
return getTableCellAttrs(el);
|
||||
}}],
|
||||
toDOM: node => ["th", setTableCellAttrs(node), 0]
|
||||
};
|
||||
Reference in New Issue
Block a user