import chalk from 'chalk'; import { getStringWidth as sw, flipArray2D } from '../../../'; import { ICompareFn } from '../../../common'; import { ILogData, ILogOptions, ICharMatrix } from '../type'; import { parse } from './'; export function parseTable(table: ILogData[][], options: ILogOptions): ICharMatrix { const { showColHead, showRowHead, cols = [], rows = [], select, flip, sort, valueTransform, margin = 0, vMargin = 0, } = options; const H = (flip ? (table = flipArray2D(table)) : table).length; const W = Math.max(0, ...table.map(v => v.length)); const d = [] as ICharMatrix[][]; const showHead = H <= 5 && W <= 5 ? 0 : 1; const hasCol = showColHead === undefined ? showHead : +showColHead; const hasRow = showRowHead === undefined ? showHead : +showRowHead; const colLen = new Array(W + hasRow).fill(1); const rowLen = new Array(H + hasCol).fill(1); let count = 0; let divider = !!options.showDivider; if (W === 0) { return { v: [''], w: 13, h: 1, }; } if (sort) { const parseSort = sort .map(v => { if (typeof v.key === 'number') { return { k: v.key, f: v.compareFn, }; } else { const col = cols.find(c => c.key === v.key); if (col) { return { k: cols.indexOf(col), f: v.compareFn, }; } return false; } }) .filter(Boolean) as { k: number; f: ICompareFn }[]; if (parseSort.length) { const sortFn = parseSort.reduce( (p, v) => (a: any, b: any) => p(a, b) || v.f(a[v.k], b[v.k]), (a: any, b: any) => 0 ); table.sort(sortFn); } } // 存在列头时先添加列头的数据 if (hasCol) { const row: ICharMatrix[] = []; if (hasRow) { row.push({ v: [''], w: 0, h: 1 }); } for (let i = 0; i < W; ++i) { const v = ((cols[i] && (cols[i].key || cols[i].title)) || i).toString(); row.push({ v: [chalk.grey(v)], w: sw(v), h: 1 }); colLen[i + hasRow] = Math.max(colLen[i + hasRow], sw(v) + margin * 2); } d.push(row); } // 解析整个表的数据 for (let h = 0; h < H; ++h) { const row: ICharMatrix[] = []; let maxRowLen = 1; if (hasRow) { const v = ((rows[h] && (rows[h].key || rows[h].title)) || h).toString(); const w = sw(v); row.push({ v: [chalk.grey(v)], w, h: 1 }); colLen[0] = Math.max(colLen[0], w); } for (let w = 0, wl = table[h].length; w < W; ++w) { if (w < wl) { const v = parse(valueTransform ? valueTransform(table[h][w]) : table[h][w]); row.push(v); colLen[w + hasRow] = Math.max(colLen[w + hasRow], v.w + margin * 2); maxRowLen = Math.max(maxRowLen, v.h + vMargin * 2); divider = divider || v.h > 1; count++; } else { row.push({ v: [''], w: 0, h: 1 }); } } rowLen[h + hasCol] = Math.max(rowLen[h + hasCol], maxRowLen); d.push(row); } // 如果列头与行头都存在 写入数据的总数到左上角 if (hasCol && hasRow) { const v = count.toString(); const w = sw(v); d[0][0] = { v: [chalk.grey(v)], w, h: 1 }; colLen[0] = Math.max(colLen[0], w); } // 将每个单元格的数据格式化(添加左右空格) d.forEach( (row, hi) => (d[hi] = row.map((ceil, wi) => { const shouldW = colLen[wi]; const shouldH = rowLen[hi]; const v = ceil.v; const u = shouldW - ceil.w; v.forEach((str, i) => (v[i] = ' '.repeat(u >> 1) + str + ' '.repeat((u + 1) >> 1))); if (shouldH > ceil.h) { const y = shouldH - ceil.h; v.unshift(...new Array(y >> 1).fill(' '.repeat(shouldW))); v.push(...new Array((y + 1) >> 1).fill(' '.repeat(shouldW))); } ceil.w = shouldW; ceil.h = shouldH; return ceil; })) ); // 存在选择 if (select) { const selectArr = Array.isArray(select) ? select : [select]; selectArr.forEach(({ row, col, cell, color }) => { if (row !== undefined) { for (let i = 0; i < W; ++i) { d[row + hasCol][i + hasRow].b = color || 'bgCyan'; } } if (col !== undefined) { for (let i = 0; i < H; ++i) { d[i + hasCol][col + hasRow].b = color || 'bgCyan'; } } if (cell !== undefined) { d[cell[0] + hasCol][cell[1] + hasRow].b = color || 'bgCyan'; } }); } const r: string[] = []; r.push(chalk.grey(`╭${colLen.map(v => '─'.repeat(v)).join('┬')}╮`)); for (let h = 0, hl = d.length; h < hl; ++h) { if (divider ? h !== 0 : hasCol && h === 1) { r.push(chalk.grey(`├${colLen.map(v => '─'.repeat(v)).join('┼')}┤`)); } for (let rh = 0, rhl = rowLen[h]; rh < rhl; ++rh) { let s = chalk.grey('│'); for (let w = 0, wl = d[h].length; w < wl; ++w) { if (w !== 0) { s += chalk.grey('│'); } s += d[h][w].b ? chalk[d[h][w].b!](d[h][w].v[rh]) : d[h][w].v[rh]; } r.push(s + chalk.grey('│')); } } r.push(chalk.grey(`╰${colLen.map(v => '─'.repeat(v)).join('┴')}╯`)); return { v: r, w: sw(r[0]), h: r.length, }; }