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 18 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');
29 changes: 29 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,35 @@ declare namespace boxen {
@default 'left'
*/
readonly align?: 'left' | 'right' | 'center';

/**
Display a title on the top of the box.
If needed the box will enlarge to fit the title.

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

/**
The alignment of the box title.

@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
47 changes: 45 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ 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,6 +74,35 @@ 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) => {
text = ansiAlign(text, {align});
let lines = text.split(NL);
Expand Down Expand Up @@ -160,9 +188,12 @@ module.exports = (text, options) => {
dimBorder: false,
align: 'left',
float: 'left',
titleAlign: 'left',
...options
};

const BORDERS_WIDTH = 2;

if (options.borderColor && !isColorValid(options.borderColor)) {
throw new Error(`${options.borderColor} is not a valid borderColor`);
}
Expand All @@ -186,6 +217,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 +257,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
56 changes: 56 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ console.log(boxen('unicorn', {padding: 1, margin: 1, borderStyle: 'double'}));
╚═════════════╝

*/

console.log(boxen('unicorns love rainbows', {title: 'rainbow', titleAlign: 'center'}));
/*
┌────── rainbow ───────┐
│unicorns love rainbows│
└──────────────────────┘
*/
```

## API
Expand Down Expand Up @@ -127,6 +134,55 @@ Default: `false`

Reduce opacity of the border.

##### title

Type: `string`

Add a title to the box. If needed the box wil enlarge to fit the title.

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

##### titleAlign

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

Type: `number | object`\
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