UNPKG

@thi.ng/text-canvas

Version:

Text based canvas, drawing, plotting, tables with arbitrary formatting (incl. ANSI/HTML)

149 lines (148 loc) 4.24 kB
import { peek } from "@thi.ng/arrays/peek"; import { isString } from "@thi.ng/checks/is-string"; import { wordWrapLines } from "@thi.ng/strings/word-wrap"; import { Border } from "./api.js"; import { beginClip, beginStyle, canvas, endClip, endStyle, setAt } from "./canvas.js"; import { hline, vline } from "./hvline.js"; import { fillRect, strokeRect } from "./rect.js"; import { horizontalOnly, verticalOnly } from "./style.js"; import { textLines } from "./text.js"; const initTable = (opts, cells) => { const b = opts.border !== void 0 ? opts.border : Border.ALL; const bH = b & Border.H ? 1 : 0; const bV = b & Border.V ? 1 : 0; const bF = bH && bV || b & Border.FRAME ? 1 : 0; const bFH = bF | bH; const bFV = bF | bV; const [padH, padV] = (opts.padding || [0, 0]).map((x) => x << 1); const cols = opts.cols; const numCols = cols.length - 1; const numRows = cells.length - 1; const rowHeights = new Array(numRows + 1).fill(0); const wrapped = []; for (let i = 0; i <= numRows; i++) { const row = cells[i]; const wrappedRow = []; for (let j = 0; j <= numCols; j++) { const cell = isString(row[j]) ? { body: row[j] } : row[j]; const lines = cell.wrap !== false ? wordWrapLines(cell.body, { width: cols[j].width, hard: cell.hard || opts.hard }).map((l) => l.toString()) : cell.body.split(/\r?\n/g); wrappedRow.push({ body: lines, format: cell.format }); rowHeights[i] = Math.max( rowHeights[i], lines.length, cell.height || 0 ); } wrapped.push(wrappedRow); } return { style: opts.style, format: opts.format, formatHead: opts.formatHead, width: cols.reduce((acc, x) => acc + x.width, 0) + 2 * bFV + numCols * bV + (numCols + 1) * padH, height: rowHeights.reduce((acc, x) => acc + x, 0) + 2 * bFH + numRows * bH + (numRows + 1) * padV, cells: wrapped, rowHeights, cols, numCols, numRows, padH, padV, b, bH, bV, bFH, bFV }; }; const drawTable = (canvas2, x, y, opts) => { const { cells, cols, numCols, numRows, rowHeights, width, height, padH, padV, bH, bV, bFH, bFV } = opts; const fmt = opts.format !== void 0 ? opts.format : canvas2.format; const fmtHd = opts.formatHead !== void 0 ? opts.formatHead : fmt; const currFormat = canvas2.format; canvas2.format = fmt; let style = opts.style || peek(canvas2.styles); style = opts.b === Border.H ? horizontalOnly(style) : opts.b === Border.V ? verticalOnly(style) : style; beginStyle(canvas2, style); fillRect(canvas2, x + bFV, y + bFH, width - 2 * bFV, height - 2 * bFH, " "); opts.b && strokeRect(canvas2, x, y, width, height); if (bV) { for (let i = 1, xx = x + cols[0].width + padH + 1; i <= numCols; xx += cols[i].width + padH + 1, i++) { vline(canvas2, xx, y, height, style.tjt, style.tjb, style.vl); } } for (let i = 0, yy = y + bFH; i <= numRows; i++) { const row = cells[i]; const rowH = rowHeights[i]; const y2 = yy + rowH + padV; if (bH && i < numRows) { hline(canvas2, x, y2, width, style.tjl, style.tjr, style.hl); } for (let j = 0, xx = x + bFV; j <= numCols; j++) { const col = cols[j]; const curr = row[j]; if (curr.body) { beginClip(canvas2, xx, yy, col.width + padH, rowH + padV); textLines( canvas2, xx + padH / 2, yy + padV / 2, curr.body, curr.format || (i ? fmt : fmtHd) ); endClip(canvas2); } if (bH && bV && j > 0 && i < numRows) { setAt(canvas2, xx - 1, y2, style.jct); } xx += col.width + bV + padH; } yy = y2 + bH; } endStyle(canvas2); canvas2.format = currFormat; }; const table = (canvas2, x, y, opts, cells) => { const spec = initTable(opts, cells); drawTable(canvas2, x, y, spec); return [spec.width, spec.height]; }; const tableCanvas = (opts, cells) => { const tbl = initTable(opts, cells); const result = canvas(tbl.width, tbl.height); drawTable(result, 0, 0, tbl); return result; }; export { drawTable, initTable, table, tableCanvas };