Skip to content

Commit 3e64187

Browse files
committed
Finish feature
1 parent a671c85 commit 3e64187

39 files changed

Lines changed: 1299 additions & 529 deletions

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@
3737
"semantic-release": "^17.3.1",
3838
"sinon": "^9.2.2",
3939
"ts-node": "^9.1.1",
40-
"typescript": "4.2.4"
40+
"typescript": "4.4.4"
4141
},
4242
"engines": {
4343
"node": ">=10.0.0"
4444
},
4545
"husky": {
4646
"hooks": {
47-
"post-commit": "npm run create-readme && git add README.md && git commit -m 'docs: generate docs' --no-verify",
48-
"pre-commit": "npm run build && npm run lint && npm run test"
4947
}
5048
},
5149
"keywords": [

src/alignCellSpanning.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {
2+
alignString,
3+
} from './alignString';
4+
import {
5+
padCellVertically,
6+
} from './mapDataUsingRowHeights';
7+
import {
8+
padString,
9+
} from './padTableData';
10+
import type {
11+
SpanningCellContext,
12+
} from './spanningCellManager';
13+
import {
14+
truncateString,
15+
} from './truncateTableData';
16+
import type {
17+
RangeConfig,
18+
} from './types/internal';
19+
import {
20+
sequence, sumArray,
21+
} from './utils';
22+
import {
23+
wrapCell,
24+
} from './wrapCell';
25+
26+
export const wrapContentRange = (rangeConfig: RangeConfig, rangeWidth: number, context: SpanningCellContext): string[] => {
27+
const {topLeft, paddingRight, paddingLeft, truncate, wrapWord, alignment} = rangeConfig;
28+
29+
const originalContent = context.rows[topLeft.row][topLeft.col];
30+
const contentWidth = rangeWidth - paddingLeft - paddingRight;
31+
32+
return wrapCell(truncateString(originalContent, truncate), contentWidth, wrapWord).map((line) => {
33+
const alignedLine = alignString(line, contentWidth, alignment);
34+
35+
return padString(alignedLine, paddingLeft, paddingRight);
36+
});
37+
};
38+
39+
export const alignVerticalContentRange = (range: RangeConfig, content: string[], context: SpanningCellContext) => {
40+
const {rows, drawHorizontalLine, rowHeights} = context;
41+
const {topLeft, bottomRight, verticalAlignment} = range;
42+
43+
if (rowHeights.length === 0) {
44+
return [];
45+
}
46+
47+
const totalCellHeight = sumArray(rowHeights.slice(topLeft.row, bottomRight.row + 1));
48+
const totalBorderHeight = bottomRight.row - topLeft.row;
49+
const hiddenHorizontalBorderCount = sequence(topLeft.row + 1, bottomRight.row).filter((horizontalBorderIndex) => {
50+
return !drawHorizontalLine(horizontalBorderIndex, rows.length);
51+
}).length;
52+
53+
const availableRangeHeight = totalCellHeight + totalBorderHeight - hiddenHorizontalBorderCount;
54+
55+
return padCellVertically(content, availableRangeHeight, verticalAlignment).map((line) => {
56+
if (line.length === 0) {
57+
return ' '.repeat(content[0].length);
58+
}
59+
60+
return line;
61+
});
62+
};

src/alignTableData.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ import type {
77
} from './types/internal';
88

99
export const alignTableData = (rows: Row[], config: BaseConfig): Row[] => {
10-
return rows.map((row) => {
10+
return rows.map((row, rowIndex) => {
1111
return row.map((cell, cellIndex) => {
1212
const {width, alignment} = config.columns[cellIndex];
1313

14+
const containingRange = config.spanningCellManager?.getContainingRange({col: cellIndex,
15+
row: rowIndex}, {mapped: true});
16+
if (containingRange) {
17+
return cell;
18+
}
19+
1420
return alignString(cell, width, alignment);
1521
});
1622
});

src/calculatCellSpanningWidth.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type {
2+
SpanningCellParameters,
3+
} from './spanningCellManager';
4+
import type {
5+
RangeConfig,
6+
} from './types/internal';
7+
import {
8+
sequence, sumArray,
9+
} from './utils';
10+
11+
export const calculateCellSpanningWidth = (range: RangeConfig, dependencies: SpanningCellParameters): number => {
12+
const {columnsConfig, drawVerticalLine} = dependencies;
13+
const {topLeft, bottomRight} = range;
14+
15+
const totalWidth = sumArray(
16+
columnsConfig.slice(topLeft.col, bottomRight.col + 1).map(({width}) => {
17+
return width;
18+
}),
19+
);
20+
21+
const totalPadding =
22+
topLeft.col === bottomRight.col ?
23+
columnsConfig[topLeft.col].paddingRight +
24+
columnsConfig[bottomRight.col].paddingLeft :
25+
sumArray(
26+
columnsConfig
27+
.slice(topLeft.col, bottomRight.col + 1)
28+
.map(({paddingLeft, paddingRight}) => {
29+
return paddingLeft + paddingRight;
30+
}),
31+
);
32+
const totalBorderWidths = bottomRight.col - topLeft.col;
33+
34+
const totalHiddenVerticalBorders = sequence(topLeft.col + 1, bottomRight.col).filter((verticalBorderIndex) => {
35+
return !drawVerticalLine(verticalBorderIndex, columnsConfig.length);
36+
}).length;
37+
38+
return totalWidth + totalPadding + totalBorderWidths - totalHiddenVerticalBorders;
39+
};

src/calculateCellWidths.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/calculateColumnWidths.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import stringWidth from 'string-width';
2+
import type {
3+
SpanningCellConfig,
4+
} from './types/api';
5+
import type {
6+
Row,
7+
Cell,
8+
} from './types/internal';
9+
import {
10+
calculateRangeCoordinate, isCellInRange,
11+
} from './utils';
12+
13+
export const calculateMaximumCellWidth = (cell: Cell): number => {
14+
return Math.max(
15+
...cell.split('\n').map(stringWidth),
16+
);
17+
};
18+
19+
/**
20+
* Produces an array of values that describe the largest value length (width) in every column.
21+
*/
22+
export const calculateMaximumColumnWidths = (rows: Row[], cellSpanningConfigs: SpanningCellConfig[] = []): number[] => {
23+
const columnWidths = new Array(rows[0].length).fill(0);
24+
const rangeCoordinates = cellSpanningConfigs.map(calculateRangeCoordinate);
25+
const isCellSpanning = (rowIndex: number, columnIndex: number): boolean => {
26+
return rangeCoordinates.some((rangeCoordinate) => {
27+
return isCellInRange({col: columnIndex,
28+
row: rowIndex}, rangeCoordinate);
29+
});
30+
};
31+
32+
rows.forEach((row, rowIndex) => {
33+
row.forEach((cell, cellIndex) => {
34+
if (isCellSpanning(rowIndex, cellIndex)) {
35+
return;
36+
}
37+
columnWidths[cellIndex] = Math.max(columnWidths[cellIndex], calculateMaximumCellWidth(cell));
38+
});
39+
});
40+
41+
return columnWidths;
42+
};

src/calculateOutputColumnWidths.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type {
2+
TableConfig,
3+
} from './types/internal';
4+
5+
export const calculateOutputColumnWidths = (config: TableConfig): number[] => {
6+
return config.columns.map((col) => {
7+
return col.paddingLeft + col.width + col.paddingRight;
8+
});
9+
};

src/calculateRowHeights.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,49 @@ import type {
55
BaseConfig,
66
Row,
77
} from './types/internal';
8+
import {
9+
sequence,
10+
sumArray,
11+
} from './utils';
812

913
/**
1014
* Produces an array of values that describe the largest value length (height) in every row.
1115
*/
1216
export const calculateRowHeights = (rows: Row[], config: BaseConfig): number[] => {
13-
return rows.map((row) => {
17+
const rowHeights: number[] = [];
18+
19+
for (const [rowIndex, row] of rows.entries()) {
1420
let rowHeight = 1;
1521

1622
row.forEach((cell, cellIndex) => {
17-
const cellHeight = calculateCellHeight(cell, config.columns[cellIndex].width, config.columns[cellIndex].wrapWord);
23+
const containingRange = config.spanningCellManager?.getContainingRange({col: cellIndex,
24+
row: rowIndex});
25+
26+
if (!containingRange) {
27+
const cellHeight = calculateCellHeight(cell, config.columns[cellIndex].width, config.columns[cellIndex].wrapWord);
28+
rowHeight = Math.max(rowHeight, cellHeight);
29+
30+
return;
31+
}
32+
const {topLeft, bottomRight, height} = containingRange;
1833

19-
rowHeight = Math.max(rowHeight, cellHeight);
34+
// bottom-most cell of a range needs to contain all remain lines of spanning cells
35+
if (rowIndex === bottomRight.row) {
36+
const totalOccupiedSpanningCellHeight = sumArray(rowHeights.slice(topLeft.row));
37+
const totalHorizontalBorderHeight = bottomRight.row - topLeft.row;
38+
const totalHiddenHorizontalBorderHeight = sequence(topLeft.row + 1, bottomRight.row).filter((horizontalBorderIndex) => {
39+
return !config.drawHorizontalLine?.(horizontalBorderIndex, rows.length);
40+
}).length;
41+
42+
const cellHeight = height - totalOccupiedSpanningCellHeight - totalHorizontalBorderHeight + totalHiddenHorizontalBorderHeight;
43+
rowHeight = Math.max(rowHeight, cellHeight);
44+
}
45+
46+
// otherwise, just depend on other sibling cell heights in the row
2047
});
2148

22-
return rowHeight;
23-
});
49+
rowHeights.push(rowHeight);
50+
}
51+
52+
return rowHeights;
2453
};

src/createStream.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ import type {
3434
import type {
3535
Row, StreamConfig,
3636
} from './types/internal';
37+
import {
38+
extractTruncates,
39+
} from './utils';
3740

3841
const prepareData = (data: Row[], config: StreamConfig) => {
3942
let rows = stringifyTableData(data);
4043

41-
rows = truncateTableData(rows, config);
44+
rows = truncateTableData(rows, extractTruncates(config));
4245

4346
const rowHeights = calculateRowHeights(rows, config);
4447

0 commit comments

Comments
 (0)