• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const stringWidth = require('string-width');
3const chalk = require('chalk');
4const widestLine = require('widest-line');
5const cliBoxes = require('cli-boxes');
6const camelCase = require('camelcase');
7const ansiAlign = require('ansi-align');
8const termSize = require('term-size');
9
10const getObject = detail => {
11	let obj;
12
13	if (typeof detail === 'number') {
14		obj = {
15			top: detail,
16			right: detail * 3,
17			bottom: detail,
18			left: detail * 3
19		};
20	} else {
21		obj = Object.assign({
22			top: 0,
23			right: 0,
24			bottom: 0,
25			left: 0
26		}, detail);
27	}
28
29	return obj;
30};
31
32const getBorderChars = borderStyle => {
33	const sides = [
34		'topLeft',
35		'topRight',
36		'bottomRight',
37		'bottomLeft',
38		'vertical',
39		'horizontal'
40	];
41
42	let chars;
43
44	if (typeof borderStyle === 'string') {
45		chars = cliBoxes[borderStyle];
46
47		if (!chars) {
48			throw new TypeError(`Invalid border style: ${borderStyle}`);
49		}
50	} else {
51		sides.forEach(key => {
52			if (!borderStyle[key] || typeof borderStyle[key] !== 'string') {
53				throw new TypeError(`Invalid border style: ${key}`);
54			}
55		});
56
57		chars = borderStyle;
58	}
59
60	return chars;
61};
62
63const getBackgroundColorName = x => camelCase('bg', x);
64
65module.exports = (text, opts) => {
66	opts = Object.assign({
67		padding: 0,
68		borderStyle: 'single',
69		dimBorder: false,
70		align: 'left',
71		float: 'left'
72	}, opts);
73
74	if (opts.backgroundColor) {
75		opts.backgroundColor = getBackgroundColorName(opts.backgroundColor);
76	}
77
78	if (opts.borderColor && !chalk[opts.borderColor]) {
79		throw new Error(`${opts.borderColor} is not a valid borderColor`);
80	}
81
82	if (opts.backgroundColor && !chalk[opts.backgroundColor]) {
83		throw new Error(`${opts.backgroundColor} is not a valid backgroundColor`);
84	}
85
86	const chars = getBorderChars(opts.borderStyle);
87	const padding = getObject(opts.padding);
88	const margin = getObject(opts.margin);
89
90	const colorizeBorder = x => {
91		const ret = opts.borderColor ? chalk[opts.borderColor](x) : x;
92		return opts.dimBorder ? chalk.dim(ret) : ret;
93	};
94
95	const colorizeContent = x => opts.backgroundColor ? chalk[opts.backgroundColor](x) : x;
96
97	text = ansiAlign(text, {align: opts.align});
98
99	const NL = '\n';
100	const PAD = ' ';
101
102	let lines = text.split(NL);
103
104	if (padding.top > 0) {
105		lines = Array(padding.top).fill('').concat(lines);
106	}
107
108	if (padding.bottom > 0) {
109		lines = lines.concat(Array(padding.bottom).fill(''));
110	}
111
112	const contentWidth = widestLine(text) + padding.left + padding.right;
113	const paddingLeft = PAD.repeat(padding.left);
114	const columns = termSize().columns;
115	let marginLeft = PAD.repeat(margin.left);
116
117	if (opts.float === 'center') {
118		const padWidth = Math.max((columns - contentWidth) / 2, 0);
119		marginLeft = PAD.repeat(padWidth);
120	} else if (opts.float === 'right') {
121		const padWidth = Math.max(columns - contentWidth - margin.right - 2, 0);
122		marginLeft = PAD.repeat(padWidth);
123	}
124
125	const horizontal = chars.horizontal.repeat(contentWidth);
126	const top = colorizeBorder(NL.repeat(margin.top) + marginLeft + chars.topLeft + horizontal + chars.topRight);
127	const bottom = colorizeBorder(marginLeft + chars.bottomLeft + horizontal + chars.bottomRight + NL.repeat(margin.bottom));
128	const side = colorizeBorder(chars.vertical);
129
130	const middle = lines.map(line => {
131		const paddingRight = PAD.repeat(contentWidth - stringWidth(line) - padding.left);
132		return marginLeft + side + colorizeContent(paddingLeft + line + paddingRight) + side;
133	}).join(NL);
134
135	return top + NL + middle + NL + bottom;
136};
137
138module.exports._borderStyles = cliBoxes;
139