UNPKG

docxml

Version:

TypeScript (component) library for building and parsing a DOCX file

168 lines (162 loc) 6.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Cell = void 0; const Component_js_1 = require("../classes/Component.js"); const table_cell_properties_js_1 = require("../properties/table-cell-properties.js"); const parameter_checking_js_1 = require("../utilities/parameter-checking.js"); const components_js_1 = require("../utilities/components.js"); const dom_js_1 = require("../utilities/dom.js"); const namespaces_js_1 = require("../utilities/namespaces.js"); const xquery_js_1 = require("../utilities/xquery.js"); const Paragraph_js_1 = require("./Paragraph.js"); const Row_js_1 = require("./Row.js"); const Table_js_1 = require("./Table.js"); /** * A component that represents a table cell. * * For MS Word to be happy any cell needs to have a paragraph as the last child. This component will * quietly fix that for you if you don't have a paragraph there already. */ class Cell extends Component_js_1.Component { constructor(cellProps, ...cellChild) { // Ensure that properties of type `number` are not `NaN`. (0, parameter_checking_js_1.checkForForbiddenParameters)(cellProps, parameter_checking_js_1.isValidNumber, true); super(cellProps, ...cellChild); } /** * Creates an XML DOM node for this component instance. */ async toNode(ancestry) { const table = ancestry.find((ancestor) => ancestor instanceof Table_js_1.Table); if (!table) { throw new Error('A cell cannot be rendered outside the context of a table'); } const children = (await this.childrenToNode(ancestry)); if (!(this.children[this.children.length - 1] instanceof Paragraph_js_1.Paragraph)) { // Cells must always end with a paragraph, or MS Word will complain about // file corruption. children.push(await new Paragraph_js_1.Paragraph({}).toNode([this, ...ancestry])); } return (0, dom_js_1.create)(`element ${namespaces_js_1.QNS.w}tc { $tcPr, $children }`, { tcPr: (0, table_cell_properties_js_1.tableCellPropertiesToNode)({ colSpan: this.getColSpan(), rowSpan: this.getRowSpan(), width: table.props.columnWidths?.[table.model.getCellInfo(this).column] || null, ...this.props, }, false), children, }); } // eslint-disable-next-line @typescript-eslint/no-unused-vars toRepeatingNode(ancestry, column, _row) { const table = ancestry.find((ancestor) => ancestor instanceof Table_js_1.Table); if (!table) { throw new Error('A cell cannot be rendered outside the context of a table'); } const info = table.model.getCellInfo(this); if (column > info.column) { // Colspans are only recorded on the left-most cell coordinate. No extra node needed; return null; } return (0, dom_js_1.create)(`element ${namespaces_js_1.QNS.w}tc { $tcPr, element ${namespaces_js_1.QNS.w}p {} }`, { tcPr: (0, table_cell_properties_js_1.tableCellPropertiesToNode)({ width: table.props.columnWidths?.[info.column] || null, colSpan: this.getColSpan(), rowSpan: this.getRowSpan(), ...this.props, }, true), }); } /** * Returns `true` when this cell has no visual representation because a column-spanning or row- * spanning neighbour overlaps it. */ isMergedAway(ancestry) { const row = ancestry.find((ancestor) => ancestor instanceof Row_js_1.Row); if (!row) { throw new Error('A cell cannot be rendered outside the context of a row'); } const table = ancestry.find((ancestor) => ancestor instanceof Table_js_1.Table); if (!table) { throw new Error('A cell cannot be rendered outside the context of a table'); } const x = row.children.indexOf(this); const y = table.children.indexOf(row); if (y === -1 || x === -1) { throw new Error('The cell is not part of this table'); } const info = table.model.getCellInfo(this); return info.column !== x || info.row !== y; } getColSpan() { return this.props.colSpan || 1; } getRowSpan() { return this.props.rowSpan || 1; } /** * Asserts whether or not a given XML node correlates with this component. */ static matchesNode(node) { return node.nodeName === 'w:tc'; } /** * Instantiate this component from the XML in an existing DOCX file. */ static fromNode(node, context) { const { mergedAway, children, ...props } = (0, xquery_js_1.evaluateXPathToMap)(` let $colStart := docxml:cell-column(.) let $rowStart := count(../preceding-sibling::${namespaces_js_1.QNS.w}tr) let $firstNextRow := ../following-sibling::${namespaces_js_1.QNS.w}tr[ child::${namespaces_js_1.QNS.w}tc[docxml:spans-cell-column(., $colStart) and not( ./${namespaces_js_1.QNS.w}tcPr/${namespaces_js_1.QNS.w}vMerge[ @${namespaces_js_1.QNS.w}val = "continue" or not(./@${namespaces_js_1.QNS.w}val) ] )] ][1] let $rowEnd := if ($firstNextRow) then count($firstNextRow/preceding-sibling::${namespaces_js_1.QNS.w}tr) else count(../../${namespaces_js_1.QNS.w}tr) let $mergeCell := boolean(./${namespaces_js_1.QNS.w}tcPr/${namespaces_js_1.QNS.w}vMerge[not(./@${namespaces_js_1.QNS.w}val)]) return map { "mergedAway": $mergeCell, "colSpan": if (./${namespaces_js_1.QNS.w}tcPr/${namespaces_js_1.QNS.w}gridSpan) then ./${namespaces_js_1.QNS.w}tcPr/${namespaces_js_1.QNS.w}gridSpan/@${namespaces_js_1.QNS.w}val/number() else 1, "rowSpan": $rowEnd - $rowStart, "children": array{ ./(${namespaces_js_1.QNS.w}p) }, "verticalAlignment": ./${namespaces_js_1.QNS.w}tcPr/${namespaces_js_1.QNS.w}vAlign/@${namespaces_js_1.QNS.w}val/string() } `, node); if (mergedAway) { return null; } return new Cell(props, ...(0, components_js_1.createChildComponentsFromNodes)(this.children, children, context)); } } exports.Cell = Cell; Object.defineProperty(Cell, "children", { enumerable: true, configurable: true, writable: true, value: [ 'Paragraph', 'Table', 'BookmarkRangeStart', 'BookmarkRangeEnd', ] }); Object.defineProperty(Cell, "mixed", { enumerable: true, configurable: true, writable: true, value: false }); (0, components_js_1.registerComponent)(Cell);