Add table viewing code.
This commit is contained in:
parent
f6f29adacc
commit
d69b78469c
4 changed files with 87 additions and 2 deletions
|
@ -64,6 +64,15 @@ export class ListBlock {
|
|||
get type() { return "list"; }
|
||||
}
|
||||
|
||||
export class TableBlock {
|
||||
constructor(head, body) {
|
||||
this.head = head;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
get type() { return "table"; }
|
||||
}
|
||||
|
||||
export class RulePart {
|
||||
get type( ) { return "rule"; }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { MessageBody, HeaderBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
||||
import { MessageBody, HeaderBlock, TableBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
||||
import { linkify } from "./linkify/linkify.js";
|
||||
import { parsePillLink } from "./pills.js"
|
||||
|
||||
|
@ -53,6 +53,12 @@ class Deserializer {
|
|||
return new ListBlock(start, nodes);
|
||||
}
|
||||
|
||||
_ensureElement(node, tag) {
|
||||
return node &&
|
||||
this.result.isElementNode(node) &&
|
||||
this.result.getNodeElementName(node) === tag;
|
||||
}
|
||||
|
||||
parseCodeBlock(node) {
|
||||
const result = this.result;
|
||||
let codeNode;
|
||||
|
@ -60,7 +66,7 @@ class Deserializer {
|
|||
codeNode = child;
|
||||
break;
|
||||
}
|
||||
if (!(codeNode && result.getNodeElementName(codeNode) === "CODE")) {
|
||||
if (!this._ensureElement(codeNode, "CODE")) {
|
||||
return null;
|
||||
}
|
||||
let language = "";
|
||||
|
@ -89,6 +95,56 @@ class Deserializer {
|
|||
return new ImagePart(url, width, height, alt, title);
|
||||
}
|
||||
|
||||
parseTableRow(row, tag) {
|
||||
const cells = [];
|
||||
for (const node of this.result.getChildNodes(row)) {
|
||||
if(!this._ensureElement(node, tag)) {
|
||||
continue;
|
||||
}
|
||||
const children = this.result.getChildNodes(node);
|
||||
const inlines = this.parseInlineNodes(children);
|
||||
cells.push(inlines);
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
parseTableHead(head) {
|
||||
let headRow = null;
|
||||
for (const node of this.result.getChildNodes(head)) {
|
||||
headRow = node;
|
||||
break;
|
||||
}
|
||||
if (this._ensureElement(headRow, "TR")) {
|
||||
return this.parseTableRow(headRow, "TH");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
parseTableBody(body) {
|
||||
const rows = [];
|
||||
for (const node of this.result.getChildNodes(body)) {
|
||||
if(!this._ensureElement(node, "TR")) {
|
||||
continue;
|
||||
}
|
||||
rows.push(this.parseTableRow(node, "TD"));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
parseTable(node) {
|
||||
// We are only assuming iterable, so convert to arrary for indexing.
|
||||
const children = Array.from(this.result.getChildNodes(node));
|
||||
let head, body;
|
||||
if (this._ensureElement(children[0], "THEAD") && this._ensureElement(children[1], "TBODY")) {
|
||||
head = this.parseTableHead(children[0]);
|
||||
body = this.parseTableBody(children[1]);
|
||||
} else if (this._ensureElement(children[0], "TBODY")) {
|
||||
head = null;
|
||||
body = this.parseTableBody(children[0]);
|
||||
}
|
||||
return new TableBlock(head, body);
|
||||
}
|
||||
|
||||
/** Once a node is known to be an element,
|
||||
* attempt to interpret it as an inline element.
|
||||
*
|
||||
|
@ -161,6 +217,8 @@ class Deserializer {
|
|||
const inlines = this.parseInlineNodes(children);
|
||||
return new FormatPart(tag, inlines);
|
||||
}
|
||||
case "TABLE":
|
||||
return this.parseTable(node);
|
||||
default: {
|
||||
if (!basicBlock.includes(tag)) {
|
||||
return null;
|
||||
|
|
|
@ -94,6 +94,7 @@ export const TAG_NAMES = {
|
|||
[HTML_NS]: [
|
||||
"br", "a", "ol", "ul", "li", "div", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
"p", "strong", "em", "span", "img", "section", "main", "article", "aside", "del", "blockquote",
|
||||
"table", "thead", "tbody", "tr", "th", "td",
|
||||
"pre", "code", "button", "time", "input", "textarea", "label", "form", "progress", "output", "video"],
|
||||
[SVG_NS]: ["svg", "circle"]
|
||||
};
|
||||
|
|
|
@ -64,12 +64,29 @@ function renderPill(pillPart) {
|
|||
return tag.a({ class: "pill", href: pillPart.href }, children);
|
||||
}
|
||||
|
||||
function renderTable(tablePart) {
|
||||
const children = [];
|
||||
if (tablePart.head) {
|
||||
const headers = tablePart.head
|
||||
.map(cell => tag.th({}, renderParts(cell)));
|
||||
children.push(tag.thead({}, tag.tr({}, headers)))
|
||||
}
|
||||
const rows = [];
|
||||
for (const row of tablePart.body) {
|
||||
const data = row.map(cell => tag.td({}, renderParts(cell)));
|
||||
rows.push(tag.tr({}, data));
|
||||
}
|
||||
children.push(tag.tbody({}, rows));
|
||||
return tag.table({}, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map from part to function that outputs DOM for the part
|
||||
*/
|
||||
const formatFunction = {
|
||||
header: headerBlock => tag["h" + Math.min(6,headerBlock.level)]({}, renderParts(headerBlock.inlines)),
|
||||
codeblock: codeBlock => tag.pre({}, tag.code({}, text(codeBlock.text))),
|
||||
table: tableBlock => renderTable(tableBlock),
|
||||
emph: emphPart => tag.em({}, renderParts(emphPart.inlines)),
|
||||
code: codePart => tag.code({}, text(codePart.text)),
|
||||
text: textPart => text(textPart.text),
|
||||
|
|
Reference in a new issue