From 4a7137850e429f9a665966858bf728b57164b11e Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Wed, 30 Oct 2024 14:17:33 -0600 Subject: [PATCH] fix: improve table width calculation --- examples/long-column-names.ts | 18 ++++++++++++++++ src/utils.ts | 39 +++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 examples/long-column-names.ts diff --git a/examples/long-column-names.ts b/examples/long-column-names.ts new file mode 100644 index 0000000..d84143c --- /dev/null +++ b/examples/long-column-names.ts @@ -0,0 +1,18 @@ +import {printTable} from '../src/index.js' + +const height = 10 +const data = Array.from({length: height}, (_, i) => ({age: i, name: `Foo ${i}`})) + +printTable({ + columns: [ + {key: 'name', name: 'Name'.repeat(100)}, + {key: 'age', name: 'Age'.repeat(100)}, + ], + data, + headerOptions: { + formatter: 'capitalCase', + }, + horizontalAlignment: 'center', + title: 'Long Column Names', + titleOptions: {bold: true}, +}) diff --git a/src/utils.ts b/src/utils.ts index 3792734..746be64 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -84,27 +84,34 @@ export function getColumns>(config: Config, const numberOfBorders = widths.length + 1 const calculateTableWidth = (widths: Column[]) => - widths.map((w) => w.width + w.padding * 2).reduce((a, b) => a + b, 0) + numberOfBorders + widths.map((w) => w.width).reduce((a, b) => a + b, 0) + numberOfBorders - // If the table is too wide, reduce the width of the largest column as little as possible to fit the table. - // At most, it will reduce the width to the length of the column's header plus padding. - // If the table is still too wide, it will reduce the width of the next largest column and so on let tableWidth = calculateTableWidth(widths) const seen = new Set() - while (tableWidth > maxWidth) { - const largestColumn = widths.filter((w) => !seen.has(w.key)).sort((a, b) => b.width - a.width)[0] - if (!largestColumn) break - if (seen.has(largestColumn.key)) break - const header = String(headings[largestColumn.key]).length - // The minimum width of a column is the width of the header plus padding on both sides - const minWidth = header + largestColumn.padding * 2 - const difference = tableWidth - maxWidth - const newWidth = largestColumn.width - difference < minWidth ? minWidth : largestColumn.width - difference - largestColumn.width = newWidth - tableWidth = calculateTableWidth(widths) - seen.add(largestColumn.key) + + const reduceColumnWidths = (calcMinWidth: (col: Column) => number) => { + // If the table is too wide, reduce the width of the largest column as little as possible to fit the table. + // If the table is still too wide, it will reduce the width of the next largest column and so on + while (tableWidth > maxWidth) { + const largestColumn = widths.filter((w) => !seen.has(w.key)).sort((a, b) => b.width - a.width)[0] + if (!largestColumn) break + if (seen.has(largestColumn.key)) break + + const minWidth = calcMinWidth(largestColumn) + const difference = tableWidth - maxWidth + const newWidth = largestColumn.width - difference < minWidth ? minWidth : largestColumn.width - difference + largestColumn.width = newWidth + tableWidth = calculateTableWidth(widths) + seen.add(largestColumn.key) + } } + // At most, reduce the width to the length of the column's header plus padding. + reduceColumnWidths((col) => stripAnsi(String(headings[col.key])).length + col.padding * 2) + + seen.clear() + // At most, reduce the width to the padding + 3 + reduceColumnWidths((col) => col.padding * 2 + 3) return widths }