Skip to content

Commit

Permalink
Support text wrapping (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaszmn committed May 6, 2020
1 parent 5da7c20 commit 14fd5c4
Show file tree
Hide file tree
Showing 4 changed files with 372 additions and 14 deletions.
10 changes: 10 additions & 0 deletions example.js
Expand Up @@ -44,3 +44,13 @@ console.log('\n\n' + boxen(chalk.black('unicorn'), {
vertical: '|'
}
}) + '\n');

const sentences = 'Unbreakable_text_because_it_has_no_spaces '.repeat(5);
console.log('\n\n' + boxen(sentences, {align: 'left'}) + '\n');

console.log('\n\n' + boxen(sentences, {align: 'center'}) + '\n');

console.log('\n\n' + boxen(sentences, {align: 'right', padding: {left: 1, right: 1, top: 0, bottom: 0}}) + '\n');

const longWord = 'x'.repeat(process.stdout.columns + 20);
console.log('\n\n' + boxen(longWord, {align: 'center'}) + '\n');
64 changes: 56 additions & 8 deletions index.js
Expand Up @@ -6,6 +6,7 @@ const cliBoxes = require('cli-boxes');
const camelCase = require('camelcase');
const ansiAlign = require('ansi-align');
const termSize = require('term-size');
const wrapAnsi = require('wrap-ansi');

const getObject = detail => {
let object;
Expand Down Expand Up @@ -95,13 +96,60 @@ module.exports = (text, options) => {

const colorizeContent = content => options.backgroundColor ? getBGColorFn(options.backgroundColor)(content) : content;

text = ansiAlign(text, {align: options.align});

const NL = '\n';
const PAD = ' ';
const {columns} = termSize();

text = ansiAlign(text, {align: options.align});

let lines = text.split(NL);

let contentWidth = widestLine(text) + padding.left + padding.right;

const BORDERS_WIDTH = 2;
if (contentWidth + BORDERS_WIDTH > columns) {
contentWidth = columns - BORDERS_WIDTH;
const max = contentWidth - padding.left - padding.right;
const newLines = [];
for (const line of lines) {
const createdLines = wrapAnsi(line, max, {hard: true});
const alignedLines = ansiAlign(createdLines, {align: options.align});
const alignedLinesArr = alignedLines.split('\n');
const longestLength = Math.max(...alignedLinesArr.map(s => s.length));

for (const alignedLine of alignedLinesArr) {
let paddedLine;
switch (options.align) {
case 'center':
paddedLine = PAD.repeat((max - longestLength) / 2) + alignedLine;
break;
case 'right':
paddedLine = PAD.repeat(max - longestLength) + alignedLine;
break;
default:
paddedLine = alignedLine;
break;
}

newLines.push(paddedLine);
}
}

lines = newLines;
}

if (contentWidth + BORDERS_WIDTH + margin.left + margin.right > columns) {
// Let's assume we have margins: left = 3, right = 5, in total = 8
const spaceForMargins = columns - contentWidth - BORDERS_WIDTH;
// Let's assume we have space = 4
const multiplier = spaceForMargins / (margin.left + margin.right);
// Here: multiplier = 4/8 = 0.5
margin.left = Math.floor(margin.left * multiplier);
margin.right = Math.floor(margin.right * multiplier);
// Left: 3 * 0.5 = 1.5 -> 1
// Right: 6 * 0.5 = 3
}

if (padding.top > 0) {
lines = new Array(padding.top).fill('').concat(lines);
}
Expand All @@ -110,16 +158,14 @@ module.exports = (text, options) => {
lines = lines.concat(new Array(padding.bottom).fill(''));
}

const contentWidth = widestLine(text) + padding.left + padding.right;
const paddingLeft = PAD.repeat(padding.left);
const {columns} = termSize();
let marginLeft = PAD.repeat(margin.left);

if (options.float === 'center') {
const padWidth = Math.max((columns - contentWidth) / 2, 0);
const padWidth = Math.max((columns - contentWidth - BORDERS_WIDTH) / 2, 0);
marginLeft = PAD.repeat(padWidth);
} else if (options.float === 'right') {
const padWidth = Math.max(columns - contentWidth - margin.right - 2, 0);
const padWidth = Math.max(columns - contentWidth - margin.right - BORDERS_WIDTH, 0);
marginLeft = PAD.repeat(padWidth);
}

Expand All @@ -128,12 +174,14 @@ module.exports = (text, options) => {
const bottom = colorizeBorder(marginLeft + chars.bottomLeft + horizontal + chars.bottomRight + NL.repeat(margin.bottom));
const side = colorizeBorder(chars.vertical);

const LINE_SEPARATOR = (contentWidth + BORDERS_WIDTH + margin.left >= columns) ? '' : NL;

const middle = lines.map(line => {
const paddingRight = PAD.repeat(contentWidth - stringWidth(line) - padding.left);
return marginLeft + side + colorizeContent(paddingLeft + line + paddingRight) + side;
}).join(NL);
}).join(LINE_SEPARATOR);

return top + NL + middle + NL + bottom;
return top + LINE_SEPARATOR + middle + LINE_SEPARATOR + bottom;
};

module.exports._borderStyles = cliBoxes;
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -40,7 +40,8 @@
"string-width": "^4.1.0",
"term-size": "^2.1.0",
"type-fest": "^0.8.1",
"widest-line": "^3.1.0"
"widest-line": "^3.1.0",
"wrap-ansi": "^6.2.0"
},
"devDependencies": {
"ava": "^2.1.0",
Expand Down

0 comments on commit 14fd5c4

Please sign in to comment.