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