Skip to content

Commit

Permalink
Control visibility of individual border sides (#568)
Browse files Browse the repository at this point in the history
  • Loading branch information
vadimdemedes committed Mar 26, 2023
1 parent ba9afd9 commit 3e0aeab
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 21 deletions.
28 changes: 28 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,34 @@ Accepts the same values as [`color`](#color) in `<Text>` component.

<img src="media/box-borderColor.jpg" width="228">

#### borderTop

Type: `boolean`\
Default: `true`

Determines whether top border is visible.

#### borderRight

Type: `boolean`\
Default: `true`

Determines whether right border is visible.

#### borderBottom

Type: `boolean`\
Default: `true`

Determines whether bottom border is visible.

#### borderLeft

Type: `boolean`\
Default: `true`

Determines whether left border is visible.

### `<Newline>`

Adds one or more newline (`\n`) characters.
Expand Down
75 changes: 58 additions & 17 deletions src/render-border.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,67 @@ const renderBorder = (
const color = node.style.borderColor;
const box = cliBoxes[node.style.borderStyle];

const topBorder = colorize(
box.topLeft + box.top.repeat(width - 2) + box.topRight,
color,
'foreground'
);
const showTopBorder = node.style.borderTop !== false;
const showBottomBorder = node.style.borderBottom !== false;
const showLeftBorder = node.style.borderLeft !== false;
const showRightBorder = node.style.borderRight !== false;

const contentWidth =
width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0);

const topBorder = showTopBorder
? colorize(
(showLeftBorder ? box.topLeft : '') +
box.top.repeat(contentWidth) +
(showRightBorder ? box.topRight : ''),
color,
'foreground'
)
: undefined;

let verticalBorderHeight = height;

if (showTopBorder) {
verticalBorderHeight -= 1;
}

if (showBottomBorder) {
verticalBorderHeight -= 1;
}

const verticalBorder = (
colorize(box.left, color, 'foreground') + '\n'
).repeat(height - 2);

const bottomBorder = colorize(
box.bottomLeft + box.bottom.repeat(width - 2) + box.bottomRight,
color,
'foreground'
);

output.write(x, y, topBorder, {transformers: []});
output.write(x, y + 1, verticalBorder, {transformers: []});
output.write(x + width - 1, y + 1, verticalBorder, {transformers: []});
output.write(x, y + height - 1, bottomBorder, {transformers: []});
).repeat(verticalBorderHeight);

const bottomBorder = showBottomBorder
? colorize(
(showLeftBorder ? box.bottomLeft : '') +
box.bottom.repeat(contentWidth) +
(showRightBorder ? box.bottomRight : ''),
color,
'foreground'
)
: undefined;

const offsetY = showTopBorder ? 1 : 0;

if (topBorder) {
output.write(x, y, topBorder, {transformers: []});
}

if (showLeftBorder) {
output.write(x, y + offsetY, verticalBorder, {transformers: []});
}

if (showRightBorder) {
output.write(x + width - 1, y + offsetY, verticalBorder, {
transformers: []
});
}

if (bottomBorder) {
output.write(x, y + height - 1, bottomBorder, {transformers: []});
}
}
};

Expand Down
47 changes: 43 additions & 4 deletions src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,34 @@ export type Styles = {
*/
readonly borderStyle?: keyof Boxes;

/**
* Determines whether top border is visible.
*
* @default true
*/
readonly borderTop?: boolean;

/**
* Determines whether bottom border is visible.
*
* @default true
*/
readonly borderBottom?: boolean;

/**
* Determines whether left border is visible.
*
* @default true
*/
readonly borderLeft?: boolean;

/**
* Determines whether right border is visible.
*
* @default true
*/
readonly borderRight?: boolean;

/**
* Change border color.
* Accepts the same values as `color` in <Text> component.
Expand Down Expand Up @@ -449,10 +477,21 @@ const applyBorderStyles = (node: YogaNode, style: Styles): void => {
if ('borderStyle' in style) {
const borderWidth = typeof style.borderStyle === 'string' ? 1 : 0;

node.setBorder(Yoga.EDGE_TOP, borderWidth);
node.setBorder(Yoga.EDGE_BOTTOM, borderWidth);
node.setBorder(Yoga.EDGE_LEFT, borderWidth);
node.setBorder(Yoga.EDGE_RIGHT, borderWidth);
if (style.borderTop !== false) {
node.setBorder(Yoga.EDGE_TOP, borderWidth);
}

if (style.borderBottom !== false) {
node.setBorder(Yoga.EDGE_BOTTOM, borderWidth);
}

if (style.borderLeft !== false) {
node.setBorder(Yoga.EDGE_LEFT, borderWidth);
}

if (style.borderRight !== false) {
node.setBorder(Yoga.EDGE_RIGHT, borderWidth);
}
}
};

Expand Down
159 changes: 159 additions & 0 deletions test/borders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import boxen, {type Options} from 'boxen';
import indentString from 'indent-string';
import delay from 'delay';
import widestLine from 'widest-line';
import cliBoxes from 'cli-boxes';
import {render, Box, Text} from '../src/index.js';
import {renderToString} from './helpers/render-to-string.js';
import createStdout from './helpers/create-stdout.js';
Expand Down Expand Up @@ -452,3 +453,161 @@ test('render border after update', t => {
})
);
});

test('hide top border', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderTop={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
`${cliBoxes.round.left}Content${cliBoxes.round.right}`,
`${cliBoxes.round.bottomLeft}${cliBoxes.round.bottom.repeat(7)}${
cliBoxes.round.bottomRight
}`,
'Below'
].join('\n')
);
});

test('hide bottom border', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderBottom={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
`${cliBoxes.round.topLeft}${cliBoxes.round.top.repeat(7)}${
cliBoxes.round.topRight
}`,
`${cliBoxes.round.left}Content${cliBoxes.round.right}`,
'Below'
].join('\n')
);
});

test('hide top and bottom borders', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderTop={false} borderBottom={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
`${cliBoxes.round.left}Content${cliBoxes.round.right}`,
'Below'
].join('\n')
);
});

test('hide left border', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderLeft={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
`${cliBoxes.round.top.repeat(7)}${cliBoxes.round.topRight}`,
`Content${cliBoxes.round.right}`,
`${cliBoxes.round.bottom.repeat(7)}${cliBoxes.round.bottomRight}`,
'Below'
].join('\n')
);
});

test('hide right border', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderRight={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
`${cliBoxes.round.topLeft}${cliBoxes.round.top.repeat(7)}`,
`${cliBoxes.round.left}Content`,
`${cliBoxes.round.bottomLeft}${cliBoxes.round.bottom.repeat(7)}`,
'Below'
].join('\n')
);
});

test('hide left and right border', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box borderStyle="round" borderLeft={false} borderRight={false}>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(
output,
[
'Above',
cliBoxes.round.top.repeat(7),
'Content',
cliBoxes.round.bottom.repeat(7),
'Below'
].join('\n')
);
});

test('hide all borders', t => {
const output = renderToString(
<Box flexDirection="column" alignItems="flex-start">
<Text>Above</Text>
<Box
borderStyle="round"
borderTop={false}
borderBottom={false}
borderLeft={false}
borderRight={false}
>
<Text>Content</Text>
</Box>
<Text>Below</Text>
</Box>
);

t.is(output, ['Above', 'Content', 'Below'].join('\n'));
});

0 comments on commit 3e0aeab

Please sign in to comment.