Skip to content

Commit

Permalink
Merge eb5d69d into acadfbf
Browse files Browse the repository at this point in the history
  • Loading branch information
nam-hle committed May 19, 2021
2 parents acadfbf + eb5d69d commit 9aabb4e
Show file tree
Hide file tree
Showing 15 changed files with 276 additions and 80 deletions.
3 changes: 2 additions & 1 deletion .README/api/table/columns/width.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
###### config.columns[*].width

Type: `number`\
Type: `number` | `'auto'`\
Default: the maximum cell widths of the column

Column width (excluding the paddings).
When one or more columns have `auto` width, they will expand to fill the console evenly.

```js

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,11 @@ Column specific configurations.
<a name="table-api-table-1-config-columns-config-columns-width"></a>
###### config.columns[*].width

Type: `number`\
Type: `number` | `'auto'`\
Default: the maximum cell widths of the column

Column width (excluding the paddings).
When one or more columns have `auto` width, they will expand to fill the console evenly.

```js

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
"extensions": [
".ts"
],
"include": ["src/**/*.ts"],
"exclude": [
"src/generated/validators.js",
"test/**/*.ts"
"src/generated/validators.js"
],
"reporter": [
"text-lcov", "text"
Expand Down
2 changes: 1 addition & 1 deletion src/calculateCellWidths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
} from './types/internal';

/**
* Calculates width of each cell contents in a row.
* Calculates maximum width of each cell contents in a row.
*/
export const calculateCellWidths = (cells: Cell[]): number[] => {
return cells.map((cell) => {
Expand Down
86 changes: 71 additions & 15 deletions src/calculateColumnWidths.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,79 @@
import {
calculateCellWidths,
} from './calculateCellWidths';
import type {
Row,
} from './types/internal';
ColumnUserConfig, Indexable,
} from './types/api';
import {
distributeUnevenly,
} from './utils';

const DEFAULT_PADDING = 1;
type ColumnConfigs = Indexable<ColumnUserConfig>;

const calculateRemainWidth = (
{widths, totalWidth, columnConfigs, columnDefault}: {
widths: Array<number | 'auto'>,
totalWidth: number,
columnConfigs?: ColumnConfigs,
columnDefault?: ColumnUserConfig,
},
): number => {
const totalBorderWidth = widths.length + 1;
const totalPadding = widths.reduce<number>((total, _, columnIndex) => {
const paddingLeft = columnConfigs?.[columnIndex]?.paddingLeft ?? columnDefault?.paddingLeft ?? DEFAULT_PADDING;
const paddingRight = columnConfigs?.[columnIndex]?.paddingRight ?? columnDefault?.paddingRight ?? DEFAULT_PADDING;

return total + paddingLeft + paddingRight;
}, 0);
const totalFixedWidth = widths.reduce<number>((total, width) => {
return total + (width === 'auto' ? 0 : width);
}, 0);

return totalWidth - totalBorderWidth - totalPadding - totalFixedWidth;
};

/**
* Produces an array of values that describe the largest value length (width) in every column.
* Creates a configuration for every column using default
* values for the missing configuration properties.
*/
export default (rows: Row[]): number[] => {
const columnWidths = new Array(rows[0].length).fill(0);
export const calculateColumnWidths = (maxColumnWidths: ReadonlyArray<number | 'auto'>,
columnsConfig?: ColumnConfigs,
columnDefault?: ColumnUserConfig,
totalWidth = process.stdout.columns): number[] => {
const widths = maxColumnWidths.map((maxColumnWidth, columnIndex) => {
return columnsConfig?.[columnIndex]?.width ?? columnDefault?.width ?? maxColumnWidth;
});

rows.forEach((row) => {
const cellWidths = calculateCellWidths(row);
const remainWidth = calculateRemainWidth(
{columnConfigs: columnsConfig,
columnDefault,
totalWidth,
widths},
);
const autoColumnCount = maxColumnWidths.filter((_, columnIndex) => {
return 'auto' === (columnsConfig?.[columnIndex]?.width ?? columnDefault?.width);
}).length;
const autoWidths = distributeUnevenly(remainWidth, autoColumnCount);

cellWidths.forEach((cellWidth, cellIndex) => {
columnWidths[cellIndex] = Math.max(columnWidths[cellIndex], cellWidth);
});
});
return widths.map((width, index) => {
if (width !== 'auto') {
return width;
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const autoWidth = autoWidths.shift()!;

if (autoWidth >= 0) {
return autoWidth;
}

return columnWidths;
if (typeof columnDefault?.width === 'number') {
return columnDefault.width;
}

const maxWidth = maxColumnWidths[index];
if (typeof maxWidth === 'number') {
return maxWidth;
}

throw new Error('There is not available space for draw the table');
});
};
23 changes: 23 additions & 0 deletions src/calculateMaximumColumnWidths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
calculateCellWidths,
} from './calculateCellWidths';
import type {
Row,
} from './types/internal';

/**
* Produces an array of values that describe the largest value length (width) in every column.
*/
export const calculateMaximumColumnWidths = (rows: Row[]): number[] => {
const columnWidths = new Array(rows[0].length).fill(0);

rows.forEach((row) => {
const cellWidths = calculateCellWidths(row);

cellWidths.forEach((cellWidth, cellIndex) => {
columnWidths[cellIndex] = Math.max(columnWidths[cellIndex], cellWidth);
});
});

return columnWidths;
};
12 changes: 11 additions & 1 deletion src/makeStreamConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import cloneDeep from 'lodash.clonedeep';
import {
calculateColumnWidths,
} from './calculateColumnWidths';
import type {
ColumnUserConfig,
Indexable,
Expand All @@ -19,9 +22,15 @@ import {
* Creates a configuration for every column using default
* values for the missing configuration properties.
*/
const makeColumnsConfig = (columnCount: number,
export const makeColumnsConfig = (columnCount: number,
columns: Indexable<ColumnUserConfig> = {},
columnDefault: StreamUserConfig['columnDefault']): ColumnConfig[] => {
const maxColumnWidths = Array.from({length: columnCount}).map((_, index) => {
/* istanbul ignore next */
return columns[index]?.width ?? columnDefault?.width;
});
const widths = calculateColumnWidths(maxColumnWidths, columns, columnDefault);

return Array.from({length: columnCount}).map((_, index) => {
return {
alignment: 'left',
Expand All @@ -32,6 +41,7 @@ const makeColumnsConfig = (columnCount: number,
wrapWord: false,
...columnDefault,
...columns[index],
width: widths[index],
};
});
};
Expand Down
17 changes: 12 additions & 5 deletions src/makeTableConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import cloneDeep from 'lodash.clonedeep';
import calculateColumnWidths from './calculateColumnWidths';
import {
calculateColumnWidths,
} from './calculateColumnWidths';
import {
calculateMaximumColumnWidths,
} from './calculateMaximumColumnWidths';
import type {
ColumnUserConfig, Indexable,
ColumnUserConfig,
Indexable,
TableUserConfig,
} from './types/api';
import type {
ColumnConfig, HeaderConfig, Row, TableConfig,
ColumnConfig,
HeaderConfig, Row, TableConfig,
} from './types/internal';
import {
makeBorderConfig,
Expand All @@ -21,7 +28,7 @@ import {
const makeColumnsConfig = (rows: Row[],
columns?: Indexable<ColumnUserConfig>,
columnDefault?: ColumnUserConfig): ColumnConfig[] => {
const columnWidths = calculateColumnWidths(rows);
const widths = calculateColumnWidths(calculateMaximumColumnWidths(rows), columns, columnDefault);

return rows[0].map((_, columnIndex) => {
return {
Expand All @@ -30,10 +37,10 @@ const makeColumnsConfig = (rows: Row[],
paddingRight: 1,
truncate: Number.POSITIVE_INFINITY,
verticalAlignment: 'top',
width: columnWidths[columnIndex],
wrapWord: false,
...columnDefault,
...columns?.[columnIndex],
width: widths[columnIndex],
};
});
};
Expand Down
14 changes: 12 additions & 2 deletions src/schemas/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,18 @@
]
},
"width": {
"type": "integer",
"minimum": 1
"oneOf": [
{
"type": "integer",
"minimum": 1
},
{
"type": "string",
"enum": [
"auto"
]
}
]
},
"wrapWord": {
"type": "boolean"
Expand Down
4 changes: 2 additions & 2 deletions src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export type ColumnUserConfig = {
/**
* Column width (default: auto calculation based on the cell content)
*/
readonly width?: number,
readonly width?: number | 'auto',

/**
* Number of characters are which the content will be truncated (default: Infinity)
Expand Down Expand Up @@ -133,7 +133,7 @@ export type StreamUserConfig = BaseUserConfig & {
/**
* The default width for each column
*/
readonly width: number,
readonly width: number | 'auto',
},
};

Expand Down
14 changes: 8 additions & 6 deletions src/types/internal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
BorderConfig,
ColumnUserConfig, DrawVerticalLine, HeaderUserConfig, Indexable, StreamUserConfig, TableUserConfig,
ColumnUserConfig, DrawVerticalLine, HeaderUserConfig, StreamUserConfig, TableUserConfig,
} from './api';

/** @internal */
Expand All @@ -22,28 +22,30 @@ export type BodyBorderConfig = Pick<BorderConfig, 'bodyJoin' | 'bodyLeft' | 'bod
export type JoinBorderConfig = Pick<BorderConfig, 'joinBody' | 'joinJoin' | 'joinLeft' | 'joinRight'>;

/** @internal */
export type ColumnConfig = Required<ColumnUserConfig>;
export type ColumnConfig = Required<ColumnUserConfig> & {
width: number,
};

/** @internal */
export type HeaderConfig = Required<HeaderUserConfig>;

/** @internal */
export type TableConfig = Required<Omit<TableUserConfig, 'border' | 'columnDefault' | 'columns' | 'header'>> & {
export type TableConfig = Required<Omit<TableUserConfig, 'columnDefault' | 'header'>> & {
readonly border: BorderConfig,
readonly columns: ColumnConfig[],
readonly header?: HeaderConfig,
};

/** @internal */
export type StreamConfig = Required<Omit<StreamUserConfig, 'border' | 'columnDefault' | 'columns'>> & {
export type StreamConfig = Required<Omit<StreamUserConfig, 'columnDefault' >> & {
readonly border: BorderConfig,
readonly columns: Indexable<ColumnConfig>,
readonly columns: ColumnConfig[],
};

/** @internal */
export type BaseConfig = {
readonly border: BorderConfig,
readonly columns: Indexable<ColumnConfig>,
readonly columns: ColumnConfig[],
readonly drawVerticalLine: DrawVerticalLine,
};

Expand Down

0 comments on commit 9aabb4e

Please sign in to comment.