Comparing version 5.0.1 to 5.1.0
@@ -124,4 +124,46 @@ import {LiteralUnion} from 'type-fest'; | ||
@default 'left' | ||
@deprecated Use `textAlignment` instead. | ||
*/ | ||
readonly align?: 'left' | 'right' | 'center'; | ||
/** | ||
Align the text in the box based on the widest line. | ||
@default 'left' | ||
*/ | ||
readonly textAlignment?: 'left' | 'right' | 'center'; | ||
/** | ||
Display a title at the top of the box. | ||
If needed, the box will horizontally expand to fit the title. | ||
@example | ||
``` | ||
console.log(boxen('foo bar', {title: 'example'})); | ||
// ┌ example ┐ | ||
// │foo bar │ | ||
// └─────────┘ | ||
``` | ||
*/ | ||
readonly title?: string; | ||
/** | ||
Align the title in the top bar. | ||
@default 'left' | ||
@example | ||
``` | ||
console.log(boxen('foo bar foo bar', {title: 'example', textAlignmentTitle: 'center'})); | ||
// ┌─── example ───┐ | ||
// │foo bar foo bar│ | ||
// └───────────────┘ | ||
console.log(boxen('foo bar foo bar', {title: 'example', textAlignmentTitle: 'right'})); | ||
// ┌────── example ┐ | ||
// │foo bar foo bar│ | ||
// └───────────────┘ | ||
``` | ||
*/ | ||
readonly titleAlignment?: 'left' | 'right' | 'center'; | ||
} | ||
@@ -128,0 +170,0 @@ } |
190
index.js
@@ -10,2 +10,5 @@ 'use strict'; | ||
const NL = '\n'; | ||
const PAD = ' '; | ||
const terminalColumns = () => { | ||
@@ -75,2 +78,104 @@ const {env, stdout, stderr} = process; | ||
const makeTitle = (text, horizontal, alignement) => { | ||
let title = ''; | ||
const textWidth = stringWidth(text); | ||
switch (alignement) { | ||
case 'left': | ||
title = text + horizontal.slice(textWidth); | ||
break; | ||
case 'right': | ||
title = horizontal.slice(textWidth) + text; | ||
break; | ||
default: | ||
horizontal = horizontal.slice(textWidth); | ||
if (horizontal.length % 2 === 1) { // This is needed in case the length is odd | ||
horizontal = horizontal.slice(Math.floor(horizontal.length / 2)); | ||
title = horizontal.slice(1) + text + horizontal; // We reduce the left part of one character to avoid the bar to go beyond its limit | ||
} else { | ||
horizontal = horizontal.slice(horizontal.length / 2); | ||
title = horizontal + text + horizontal; | ||
} | ||
break; | ||
} | ||
return title; | ||
}; | ||
const makeContentText = (text, padding, columns, align) => { | ||
text = ansiAlign(text, {align}); | ||
let lines = text.split(NL); | ||
const textWidth = widestLine(text); | ||
const max = columns - padding.left - padding.right; | ||
if (textWidth > max) { | ||
const newLines = []; | ||
for (const line of lines) { | ||
const createdLines = wrapAnsi(line, max, {hard: true}); | ||
const alignedLines = ansiAlign(createdLines, {align}); | ||
const alignedLinesArray = alignedLines.split('\n'); | ||
const longestLength = Math.max(...alignedLinesArray.map(s => stringWidth(s))); | ||
for (const alignedLine of alignedLinesArray) { | ||
let paddedLine; | ||
switch (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 (align === 'center' && textWidth < max) { | ||
lines = lines.map(line => PAD.repeat((max - textWidth) / 2) + line); | ||
} else if (align === 'right' && textWidth < max) { | ||
lines = lines.map(line => PAD.repeat(max - textWidth) + line); | ||
} | ||
const paddingLeft = PAD.repeat(padding.left); | ||
const paddingRight = PAD.repeat(padding.right); | ||
lines = lines.map(line => paddingLeft + line + paddingRight); | ||
lines = lines.map(line => { | ||
if (columns - stringWidth(line) > 0) { | ||
switch (align) { | ||
case 'center': | ||
return line + PAD.repeat(columns - stringWidth(line)); | ||
case 'right': | ||
return line + PAD.repeat(columns - stringWidth(line)); | ||
default: | ||
return line + PAD.repeat(columns - stringWidth(line)); | ||
} | ||
} | ||
return line; | ||
}); | ||
if (padding.top > 0) { | ||
lines = new Array(padding.top).fill(PAD.repeat(columns)).concat(lines); | ||
} | ||
if (padding.bottom > 0) { | ||
lines = lines.concat(new Array(padding.bottom).fill(PAD.repeat(columns))); | ||
} | ||
return lines.join(NL); | ||
}; | ||
const isHex = color => color.match(/^#(?:[0-f]{3}){1,2}$/i); | ||
@@ -86,7 +191,15 @@ const isColorValid = color => typeof color === 'string' && ((chalk[color]) || isHex(color)); | ||
dimBorder: false, | ||
align: 'left', | ||
textAlignment: 'left', | ||
float: 'left', | ||
titleAlignment: 'left', | ||
...options | ||
}; | ||
// This option is deprecated | ||
if (options.align) { | ||
options.textAlignment = options.align; | ||
} | ||
const BORDERS_WIDTH = 2; | ||
if (options.borderColor && !isColorValid(options.borderColor)) { | ||
@@ -111,45 +224,19 @@ throw new Error(`${options.borderColor} is not a valid borderColor`); | ||
const NL = '\n'; | ||
const PAD = ' '; | ||
const columns = terminalColumns(); | ||
text = ansiAlign(text, {align: options.align}); | ||
let contentWidth = widestLine(wrapAnsi(text, columns - BORDERS_WIDTH, {hard: true})) + padding.left + padding.right; | ||
let lines = text.split(NL); | ||
// This prevents the title bar to exceed the console's width | ||
let title = options.title && options.title.slice(0, columns - 4 - margin.left - margin.right); | ||
let contentWidth = widestLine(text) + padding.left + padding.right; | ||
if (title) { | ||
title = ` ${title} `; | ||
} | ||
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 alignedLinesArray = alignedLines.split('\n'); | ||
const longestLength = Math.max(...alignedLinesArray.map(s => stringWidth(s))); | ||
for (const alignedLine of alignedLinesArray) { | ||
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; | ||
// Make the box larger to fit a larger title | ||
if (stringWidth(title) > contentWidth) { | ||
contentWidth = stringWidth(title); | ||
} | ||
if (contentWidth + BORDERS_WIDTH + margin.left + margin.right > columns) { | ||
if ((margin.left && margin.right) && contentWidth + BORDERS_WIDTH + margin.left + margin.right > columns) { | ||
// Let's assume we have margins: left = 3, right = 5, in total = 8 | ||
@@ -160,4 +247,4 @@ const spaceForMargins = columns - contentWidth - BORDERS_WIDTH; | ||
// Here: multiplier = 4/8 = 0.5 | ||
margin.left = Math.floor(margin.left * multiplier); | ||
margin.right = Math.floor(margin.right * multiplier); | ||
margin.left = Math.max(0, Math.floor(margin.left * multiplier)); | ||
margin.right = Math.max(0, Math.floor(margin.right * multiplier)); | ||
// Left: 3 * 0.5 = 1.5 -> 1 | ||
@@ -167,23 +254,19 @@ // Right: 6 * 0.5 = 3 | ||
if (padding.top > 0) { | ||
lines = new Array(padding.top).fill('').concat(lines); | ||
} | ||
// Prevent content from exceeding the console's width | ||
contentWidth = Math.min(contentWidth, columns - BORDERS_WIDTH - margin.left - margin.right); | ||
if (padding.bottom > 0) { | ||
lines = lines.concat(new Array(padding.bottom).fill('')); | ||
} | ||
text = makeContentText(text, padding, contentWidth, options.textAlignment); | ||
const paddingLeft = PAD.repeat(padding.left); | ||
let marginLeft = PAD.repeat(margin.left); | ||
if (options.float === 'center') { | ||
const padWidth = Math.max((columns - contentWidth - BORDERS_WIDTH) / 2, 0); | ||
marginLeft = PAD.repeat(padWidth); | ||
const marginWidth = Math.max((columns - contentWidth - BORDERS_WIDTH) / 2, 0); | ||
marginLeft = PAD.repeat(marginWidth); | ||
} else if (options.float === 'right') { | ||
const padWidth = Math.max(columns - contentWidth - margin.right - BORDERS_WIDTH, 0); | ||
marginLeft = PAD.repeat(padWidth); | ||
const marginWidth = Math.max(columns - contentWidth - margin.right - BORDERS_WIDTH, 0); | ||
marginLeft = PAD.repeat(marginWidth); | ||
} | ||
const horizontal = chars.horizontal.repeat(contentWidth); | ||
const top = colorizeBorder(NL.repeat(margin.top) + marginLeft + chars.topLeft + horizontal + chars.topRight); | ||
const top = colorizeBorder(NL.repeat(margin.top) + marginLeft + chars.topLeft + (title ? makeTitle(title, horizontal, options.titleAlignment) : horizontal) + chars.topRight); | ||
const bottom = colorizeBorder(marginLeft + chars.bottomLeft + horizontal + chars.bottomRight + NL.repeat(margin.bottom)); | ||
@@ -194,5 +277,6 @@ const side = colorizeBorder(chars.vertical); | ||
const lines = text.split(NL); | ||
const middle = lines.map(line => { | ||
const paddingRight = PAD.repeat(contentWidth - stringWidth(line) - padding.left); | ||
return marginLeft + side + colorizeContent(paddingLeft + line + paddingRight) + side; | ||
return marginLeft + side + colorizeContent(line) + side; | ||
}).join(LINE_SEPARATOR); | ||
@@ -199,0 +283,0 @@ |
{ | ||
"name": "boxen", | ||
"version": "5.0.1", | ||
"version": "5.1.0", | ||
"description": "Create boxes in the terminal", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -37,2 +37,9 @@ # boxen | ||
*/ | ||
console.log(boxen('unicorns love rainbows', {title: 'magical', titleAlignment: 'center'})); | ||
/* | ||
┌────── magical ───────┐ | ||
│unicorns love rainbows│ | ||
└──────────────────────┘ | ||
*/ | ||
``` | ||
@@ -131,2 +138,52 @@ | ||
##### title | ||
Type: `string` | ||
Display a title at the top of the box. | ||
If needed, the box will horizontally expand to fit the title. | ||
Example: | ||
```js | ||
console.log(boxen('foo bar', {title: 'example'})); | ||
/* | ||
┌ example ┐ | ||
│foo bar │ | ||
└─────────┘ | ||
*/ | ||
``` | ||
##### titleAlignment | ||
Type: `string`\ | ||
Default: `'left'` | ||
Align the title in the top bar. | ||
Values: | ||
- `'left'` | ||
```js | ||
/* | ||
┌ example ──────┐ | ||
│foo bar foo bar│ | ||
└───────────────┘ | ||
*/ | ||
``` | ||
- `'center'` | ||
```js | ||
/* | ||
┌─── example ───┐ | ||
│foo bar foo bar│ | ||
└───────────────┘ | ||
*/ | ||
``` | ||
- `'right'` | ||
```js | ||
/* | ||
┌────── example ┐ | ||
│foo bar foo bar│ | ||
└───────────────┘ | ||
*/ | ||
``` | ||
##### padding | ||
@@ -165,3 +222,3 @@ | ||
##### align | ||
##### textAlignment | ||
@@ -168,0 +225,0 @@ Type: `string`\ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18406
390
245