/
table.ts
276 lines (243 loc) 路 6.87 KB
/
table.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import { border, IBorder } from "./border.ts";
import { Cell, Direction } from "./cell.ts";
import { TableLayout } from "./layout.ts";
import { IDataRow, IRow, Row } from "./row.ts";
/** Border characters settings. */
export type IBorderOptions = Partial<IBorder>;
/** Table options. */
export interface ITableOptions {
indent?: number;
border?: boolean;
align?: Direction;
maxColWidth?: number | number[];
minColWidth?: number | number[];
padding?: number | number[];
chars?: IBorderOptions;
}
/** Table settings. */
export interface ITableSettings extends Required<Omit<ITableOptions, "align">> {
chars: IBorder;
align?: Direction;
}
/** Table type. */
export type ITable<T extends IRow = IRow> = T[] | Table<T>;
/** Table representation. */
export class Table<T extends IRow = IRow> extends Array<T> {
protected static _chars: IBorder = { ...border };
protected options: ITableSettings = {
indent: 0,
border: false,
maxColWidth: Infinity,
minColWidth: 0,
padding: 1,
chars: { ...Table._chars },
};
private headerRow?: Row;
/**
* Create a new table. If rows is a table, all rows and options of the table
* will be copied to the new table.
* @param rows
*/
public static from<T extends IRow>(rows: ITable<T>): Table<T> {
const table = new this(...rows);
if (rows instanceof Table) {
table.options = { ...rows.options };
table.headerRow = rows.headerRow ? Row.from(rows.headerRow) : undefined;
}
return table;
}
/**
* Create a new table from an array of json objects. An object represents a
* row and each property a column.
* @param rows Array of objects.
*/
public static fromJson(rows: IDataRow[]): Table {
return new this().fromJson(rows);
}
/**
* Set global default border characters.
* @param chars Border options.
*/
public static chars(chars: IBorderOptions): typeof Table {
Object.assign(this._chars, chars);
return this;
}
/**
* Write table or rows to stdout.
* @param rows Table or rows.
*/
public static render<T extends IRow>(rows: ITable<T>): void {
Table.from(rows).render();
}
/**
* Read data from an array of json objects. An object represents a
* row and each property a column.
* @param rows Array of objects.
*/
public fromJson(rows: IDataRow[]): this {
this.header(Object.keys(rows[0]));
this.body(rows.map((row) => Object.values(row) as T));
return this;
}
/**
* Set table header.
* @param header Header row or cells.
*/
public header(header: IRow): this {
this.headerRow = header instanceof Row ? header : Row.from(header);
return this;
}
/**
* Set table body.
* @param rows Table rows.
*/
public body(rows: T[]): this {
this.length = 0;
this.push(...rows);
return this;
}
/** Clone table recursively with header and options. */
public clone(): Table {
const table = new Table(
...this.map((row: T) =>
row instanceof Row ? row.clone() : Row.from(row).clone()
),
);
table.options = { ...this.options };
table.headerRow = this.headerRow?.clone();
return table;
}
/** Generate table string. */
public toString(): string {
return new TableLayout(this, this.options).toString();
}
/** Write table to stdout. */
public render(): this {
console.log(this.toString());
return this;
}
/**
* Set max col with.
* @param width Max col width.
* @param override Override existing value.
*/
public maxColWidth(width: number | number[], override = true): this {
if (override || typeof this.options.maxColWidth === "undefined") {
this.options.maxColWidth = width;
}
return this;
}
/**
* Set min col width.
* @param width Min col width.
* @param override Override existing value.
*/
public minColWidth(width: number | number[], override = true): this {
if (override || typeof this.options.minColWidth === "undefined") {
this.options.minColWidth = width;
}
return this;
}
/**
* Set table indentation.
* @param width Indent width.
* @param override Override existing value.
*/
public indent(width: number, override = true): this {
if (override || typeof this.options.indent === "undefined") {
this.options.indent = width;
}
return this;
}
/**
* Set cell padding.
* @param padding Cell padding.
* @param override Override existing value.
*/
public padding(padding: number | number[], override = true): this {
if (override || typeof this.options.padding === "undefined") {
this.options.padding = padding;
}
return this;
}
/**
* Enable/disable cell border.
* @param enable Enable/disable cell border.
* @param override Override existing value.
*/
public border(enable: boolean, override = true): this {
if (override || typeof this.options.border === "undefined") {
this.options.border = enable;
}
return this;
}
/**
* Align table content.
* @param direction Align direction.
* @param override Override existing value.
*/
public align(direction: Direction, override = true): this {
if (override || typeof this.options.align === "undefined") {
this.options.align = direction;
}
return this;
}
/**
* Set border characters.
* @param chars Border options.
*/
public chars(chars: IBorderOptions): this {
Object.assign(this.options.chars, chars);
return this;
}
/** Get table header. */
public getHeader(): Row | undefined {
return this.headerRow;
}
/** Get table body. */
public getBody(): T[] {
return [...this];
}
/** Get mac col widrth. */
public getMaxColWidth(): number | number[] {
return this.options.maxColWidth;
}
/** Get min col width. */
public getMinColWidth(): number | number[] {
return this.options.minColWidth;
}
/** Get table indentation. */
public getIndent(): number {
return this.options.indent;
}
/** Get cell padding. */
public getPadding(): number | number[] {
return this.options.padding;
}
/** Check if table has border. */
public getBorder(): boolean {
return this.options.border === true;
}
/** Check if header row has border. */
public hasHeaderBorder(): boolean {
const hasBorder = this.headerRow?.hasBorder();
return hasBorder === true || (this.getBorder() && hasBorder !== false);
}
/** Check if table bordy has border. */
public hasBodyBorder(): boolean {
return this.getBorder() ||
this.some((row) =>
row instanceof Row
? row.hasBorder()
: row.some((cell) => cell instanceof Cell ? cell.getBorder : false)
);
}
/** Check if table header or body has border. */
public hasBorder(): boolean {
return this.hasHeaderBorder() || this.hasBodyBorder();
}
/** Get table alignment. */
public getAlign(): Direction {
return this.options.align ?? "left";
}
}