Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add title and titleAlignment options #59

Merged
merged 24 commits into from
Sep 11, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
65f9cfd
Refactored the code into sub-functions
Caesarovich Aug 27, 2021
a19c2bd
Fix margin error shrink on specific cases
Caesarovich Aug 30, 2021
6da1b52
Remove outdated tests that aren't relevant anymore
Caesarovich Aug 30, 2021
39693c0
Code reduction
Caesarovich Sep 6, 2021
cf55cc3
Reforming corretly last commit
Caesarovich Sep 6, 2021
b93b3cc
Fix margin shrink error on negative values
Caesarovich Sep 6, 2021
e750e40
Added tests for box overflows
Caesarovich Sep 6, 2021
c3c1335
Re-adapted unit tests
Caesarovich Sep 6, 2021
64ff8c5
Added title feature with tests
Caesarovich Sep 6, 2021
51ef182
Improved code readability
Caesarovich Sep 6, 2021
0f53317
Merge branch 'sindresorhus:main' into main
Caesarovich Sep 6, 2021
363bdb0
Added title feature with tests
Caesarovich Sep 6, 2021
5402523
Cleaning left-overs
Caesarovich Sep 6, 2021
0dcdcc4
Merge branch 'feature-title' of https://github.com/Caesarovich/boxen …
Caesarovich Sep 6, 2021
f0eeb05
Update index.d.ts
Caesarovich Sep 7, 2021
01fba09
Document box enlargement
Caesarovich Sep 7, 2021
2027380
Place the constant variables back on top
Caesarovich Sep 7, 2021
fe8e663
Documented the new functionnalities
Caesarovich Sep 7, 2021
969a78f
Corrected documentations
Caesarovich Sep 9, 2021
d72cac4
Replace titleAlign by titleAlignement
Caesarovich Sep 9, 2021
82921d6
Change align to textAlignement
Caesarovich Sep 9, 2021
31923c6
Deprecate the align option
Caesarovich Sep 9, 2021
0650ba1
Fix 'alignment' typo
Caesarovich Sep 11, 2021
e7b88c7
Sync readme and index.d.ts
Caesarovich Sep 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,8 @@ console.log('\n\n' + boxen(sentences, {align: 'right', padding: {left: 1, right:

const longWord = 'x'.repeat(process.stdout.columns + 20);
console.log('\n\n' + boxen(longWord, {align: 'center'}) + '\n');

const title = 'Beautiful title';
console.log('\n\n' + boxen('This box has a nice title', {title}) + '\n');

console.log('\n\n' + boxen('This box has a centered title', {title, titleAlign: 'center'}) + '\n');
28 changes: 28 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,34 @@ declare namespace boxen {
@default 'left'
*/
readonly align?: 'left' | 'right' | 'center';

/**
Display a title on the top of the box.

@example
```
console.log(boxen('foo bar foo bar', {title: 'title'}));
// ┌title──────────┐
// │foo bar foo bar│
// └───────────────┘
```
*/
readonly title?: string;

/**
Align the title on the top bar.

@default 'left'

@example
```
console.log(boxen('foo bar foo bar', {title: 'title', alignTitle: 'right'}));
// ┌────────title┐
// │foo bar foo │
// └─────────────┘
```
*/
readonly titleAlign?: 'left' | 'right' | 'center';
}
}

Expand Down
55 changes: 50 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ const camelCase = require('camelcase');
const ansiAlign = require('ansi-align');
const wrapAnsi = require('wrap-ansi');

const NL = '\n';
const PAD = ' ';
const BORDERS_WIDTH = 2;

const terminalColumns = () => {
const {env, stdout, stderr} = process;

Expand Down Expand Up @@ -75,7 +71,39 @@ const getBorderChars = borderStyle => {
return chararacters;
};

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) => {
const NL = '\n';
const PAD = ' ';

text = ansiAlign(text, {align});
let lines = text.split(NL);
const textWidth = widestLine(text);
Expand Down Expand Up @@ -160,9 +188,14 @@ module.exports = (text, options) => {
dimBorder: false,
align: 'left',
float: 'left',
titleAlign: 'left',
...options
};

const BORDERS_WIDTH = 2;
const NL = '\n';
const PAD = ' ';

if (options.borderColor && !isColorValid(options.borderColor)) {
throw new Error(`${options.borderColor} is not a valid borderColor`);
}
Expand All @@ -186,6 +219,18 @@ module.exports = (text, options) => {

let contentWidth = widestLine(wrapAnsi(text, columns - BORDERS_WIDTH, {hard: true})) + padding.left + padding.right;

// 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);

if (title) {
title = ` ${title} `;
}

// Make the box larger to fit a larger title
if (stringWidth(title) > contentWidth) {
contentWidth = stringWidth(title);
}

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
const spaceForMargins = columns - contentWidth - BORDERS_WIDTH;
Expand Down Expand Up @@ -214,7 +259,7 @@ module.exports = (text, options) => {
}

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.titleAlign) : horizontal) + chars.topRight);
const bottom = colorizeBorder(marginLeft + chars.bottomLeft + horizontal + chars.bottomRight + NL.repeat(margin.bottom));
const side = colorizeBorder(chars.vertical);

Expand Down
2 changes: 2 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const spacing: Spacing = {
};

expectType<string>(boxen('unicorns'));
expectType<string>(boxen('unicorns', {title: 'title'}));
expectType<string>(boxen('unicorns', {title: 'title', titleAlign: 'center'}));
expectType<string>(boxen('unicorns', {borderColor: 'green'}));
expectType<string>(boxen('unicorns', {borderColor: '#ff0000'}));
expectType<string>(boxen('unicorns', {borderStyle: 'double'}));
Expand Down
95 changes: 95 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,101 @@ test('creates a box', t => {
`);
});

test('title option', t => {
compare(t, boxen('foo', {title: 'title'}), `
┌ title ┐
│foo │
└───────┘
`);
});

test('title align center option', t => {
compare(t, boxen('foo bar foo bar', {
title: 'title',
titleAlign: 'center'
}), `
┌──── title ────┐
│foo bar foo bar│
└───────────────┘
`);
});

test('title align right option', t => {
compare(t, boxen('foo bar foo bar', {
title: 'title',
titleAlign: 'right'
}), `
┌──────── title ┐
│foo bar foo bar│
└───────────────┘
`);
});

test('title and text align', t => {
compare(t, boxen('Hello !\nThis text is on the right\nAmazing!', {
title: 'title',
align: 'right'
}), `
┌ title ──────────────────┐
│ Hello !│
│This text is on the right│
│ Amazing!│
└─────────────────────────┘
`);
});

test('box size adapts to title length', t => {
compare(t, boxen('Hello !\nThis text is on the center\nAmazing!\nIt stretched to the title length.', {
title: 'This is a very large title with many words in it',
align: 'center'
}), `
┌ This is a very large title with many words in it ┐
│ Hello ! │
│ This text is on the center │
│ Amazing! │
│ It stretched to the title length. │
└──────────────────────────────────────────────────┘
`);
});

test('title with align and text padding', t => {
compare(t, boxen('Hello !\nAll the text here is on the right\nEven the title.\nAmazing padding too ;)', {
title: 'This is a title',
titleAlign: 'right',
align: 'right',
padding: 2
}), `
┌──────────────────────────── This is a title ┐
│ │
│ │
│ Hello ! │
│ All the text here is on the right │
│ Even the title. │
│ Amazing padding too ;) │
│ │
│ │
└─────────────────────────────────────────────┘
`);
});

test('title with align, padding and margin', t => {
compare(t, boxen('Hello !\nThis text has padding and margin.\nCentered too !', {
title: 'This is a title',
titleAlign: 'center',
align: 'center',
margin: 1,
padding: 1
}), `
┌─────────── This is a title ───────────┐
│ │
│ Hello ! │
│ This text has padding and margin. │
│ Centered too ! │
│ │
└───────────────────────────────────────┘
`);
});

test('padding option', t => {
compare(t, boxen('foo', {padding: 2}), `
┌───────────────┐
Expand Down