Skip to content

Commit 31d6b79

Browse files
committed
boxes
1 parent 6db4e94 commit 31d6b79

File tree

11 files changed

+201
-37
lines changed

11 files changed

+201
-37
lines changed

auto-imports.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// Generated by 'unplugin-auto-import'
22
// We suggest you to commit this file into source control
33
declare global {
4+
const Box: typeof import('vue-termui')['TuiBox']
45
const Br: typeof import('vue-termui')['TuiNewline']
56
const computed: typeof import('vue-termui')['computed']
67
const createApp: typeof import('vue-termui')['createApp']
78
const customRef: typeof import('vue-termui')['customRef']
89
const defineAsyncComponent: typeof import('vue-termui')['defineAsyncComponent']
910
const defineComponent: typeof import('vue-termui')['defineComponent']
11+
const Div: typeof import('vue-termui')['TuiBox']
1012
const effectScope: typeof import('vue-termui')['effectScope']
1113
const EffectScope: typeof import('vue-termui')['EffectScope']
1214
const getCurrentInstance: typeof import('vue-termui')['getCurrentInstance']
@@ -43,6 +45,7 @@ declare global {
4345
const toRef: typeof import('vue-termui')['toRef']
4446
const toRefs: typeof import('vue-termui')['toRefs']
4547
const triggerRef: typeof import('vue-termui')['triggerRef']
48+
const TuiBox: typeof import('vue-termui')['TuiBox']
4649
const TuiNewline: typeof import('vue-termui')['TuiNewline']
4750
const TuiText: typeof import('vue-termui')['TuiText']
4851
const unref: typeof import('vue-termui')['unref']

src/tui/App.vue

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import { TuiBox as Box } from 'vue-termui'
3+
24
const n = ref(0)
35
onMounted(() => {
46
setInterval(() => {
@@ -8,17 +10,19 @@ onMounted(() => {
810
</script>
911

1012
<template>
11-
<span color="red"
12-
>Counter: <span bold>{{ n }}</span
13-
>.
14-
<br />
15-
<span color="blue"
16-
>is it Odd?
17-
<span :inverse="n % 2 == 0">
18-
{{ n % 2 == 0 ? 'No' : 'Yes' }}
13+
<Box align-items="flex-end" justify="center" width="80" height="50">
14+
<span color="red"
15+
>Counter: <span bold>{{ n }}</span
16+
>.
17+
<br />
18+
<span color="blue"
19+
>is it Odd?
20+
<span :inverse="n % 2 == 0">
21+
{{ n % 2 == 0 ? 'No' : 'Yes' }}
22+
</span>
1923
</span>
24+
<!-- <br /> -->
25+
<!-- <Span inverse color="yellow">Hello</Span> -->
2026
</span>
21-
<!-- <br /> -->
22-
<!-- <Span inverse color="yellow">Hello</Span> -->
23-
</span>
27+
</Box>
2428
</template>

src/tui/demos/Borders.vue

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script lang="ts" setup>
2+
import { TuiBox as Box } from 'vue-termui'
3+
</script>
4+
5+
<template>
6+
<Box flexDirection="column" :padding="2">
7+
<Box>
8+
<Box borderStyle="single" :marginRight="2">
9+
<TuiText>single</TuiText>
10+
</Box>
11+
12+
<Box borderStyle="double" :marginRight="2">
13+
<TuiText>double</TuiText>
14+
</Box>
15+
16+
<Box borderStyle="round" :marginRight="2">
17+
<TuiText>round</TuiText>
18+
</Box>
19+
20+
<Box borderStyle="bold">
21+
<TuiText>bold</TuiText>
22+
</Box>
23+
</Box>
24+
25+
<Box :marginTop="1">
26+
<Box borderStyle="singleDouble" :marginRight="2">
27+
<TuiText>singleDouble</TuiText>
28+
</Box>
29+
30+
<Box borderStyle="doubleSingle" :marginRight="2">
31+
<TuiText>doubleSingle</TuiText>
32+
</Box>
33+
34+
<Box borderStyle="classic">
35+
<TuiText>classic</TuiText>
36+
</Box>
37+
</Box>
38+
</Box>
39+
</template>

src/tui/renderer/components/Box.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { h, FunctionalComponent } from '@vue/runtime-core'
2+
import { Styles } from '../styles'
3+
4+
export interface TuiBoxProps extends Omit<Styles, 'textWrap'> {
5+
/**
6+
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`.
7+
*
8+
* @default 0
9+
*/
10+
margin?: Number
11+
12+
/**
13+
* Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`.
14+
*
15+
* @default 0
16+
*/
17+
marginX?: Number
18+
19+
/**
20+
* Vertical margin. Equivalent to setting `marginTop` and `marginBottom`.
21+
*
22+
* @default 0
23+
*/
24+
marginY?: Number
25+
26+
/**
27+
* Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`.
28+
*
29+
* @default 0
30+
*/
31+
padding?: Number
32+
33+
/**
34+
* Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`.
35+
*
36+
* @default 0
37+
*/
38+
paddingX?: Number
39+
40+
/**
41+
* Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`.
42+
*
43+
* @default 0
44+
*/
45+
paddingY?: Number
46+
}
47+
48+
export const TuiBox: FunctionalComponent<TuiBoxProps> = (
49+
props,
50+
{ attrs, slots }
51+
) => {
52+
return h(
53+
'tui-box',
54+
{
55+
style: {
56+
flexDirection: props.flexDirection ?? 'row',
57+
flexGrow: props.flexGrow ?? 0,
58+
flexShrink: props.flexShrink ?? 1,
59+
60+
...props,
61+
62+
marginLeft: props.marginLeft ?? props.marginX ?? props.margin ?? 0,
63+
marginRight: props.marginRight ?? props.marginX ?? props.margin ?? 0,
64+
marginTop: props.marginTop ?? props.marginY ?? props.margin ?? 0,
65+
marginBottom: props.marginBottom ?? props.marginY ?? props.margin ?? 0,
66+
67+
paddingLeft: props.paddingLeft ?? props.paddingX ?? props.padding ?? 0,
68+
paddingRight:
69+
props.paddingRight ?? props.paddingX ?? props.padding ?? 0,
70+
paddingTop: props.paddingTop ?? props.paddingY ?? props.padding ?? 0,
71+
paddingBottom:
72+
props.paddingBottom ?? props.paddingY ?? props.padding ?? 0,
73+
},
74+
},
75+
slots.default?.()
76+
)
77+
}
78+
79+
TuiBox.displayName = 'TuiBox'

src/tui/renderer/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { TuiText } from './Text'
22
export { TuiNewline } from './Newline'
33
export { TuiApp } from './App'
4+
export { TuiBox } from './Box'

src/tui/renderer/dom.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ export class TuiNode {
1515
}
1616
}
1717

18-
export type DOMElementName = 'tui-text' | 'tui-virtual-text' | 'tui-root'
18+
export type DOMElementName =
19+
| 'tui-text'
20+
| 'tui-virtual-text'
21+
| 'tui-root'
22+
| 'tui-box'
1923
export type NodeName = DOMElementName | '#text' | '#comment'
2024

2125
export class DOMElement extends TuiNode {

src/tui/renderer/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from './dom'
1212
import { logSymbol, rootNodeSymbol, stdoutSymbol } from './injectionSymbols'
1313
import { TuiText, TuiNewline, TuiApp as RootApp } from './components'
14+
import { applyStyles } from './styles'
1415

1516
function removeNode(node: DOMNode) {
1617
// recurse for children
@@ -46,6 +47,9 @@ const { render, createApp: baseCreateApp } = createRenderer<
4647
// console.log('TODO: patchProp', { el, key, nextValue })
4748
if (key === 'style') {
4849
el.style = nextValue
50+
if (el.yogaNode) {
51+
applyStyles(el.yogaNode, nextValue)
52+
}
4953
} else if (key === 'internal_transform') {
5054
el.internal_transform = nextValue
5155
}

src/tui/renderer/renderBorders.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import cliBoxes from 'cli-boxes'
2+
import { colorize } from './textColor'
3+
import { DOMNode } from './dom'
4+
import { Output } from './output'
5+
6+
export function renderBorders(
7+
x: number,
8+
y: number,
9+
node: DOMNode,
10+
output: Output
11+
) {
12+
if (typeof node.style.borderStyle === 'string') {
13+
const width = node.yogaNode!.getComputedWidth()
14+
const height = node.yogaNode!.getComputedHeight()
15+
const color = node.style.borderColor
16+
const box = cliBoxes[node.style.borderStyle]
17+
18+
const topBorder = colorize(
19+
box.topLeft + box.top.repeat(width - 2) + box.topRight,
20+
color,
21+
'foreground'
22+
)
23+
24+
const rightBorder = (
25+
colorize(box.right, color, 'foreground') + '\n'
26+
).repeat(height - 2)
27+
28+
const leftBorder = (colorize(box.left, color, 'foreground') + '\n').repeat(
29+
height - 2
30+
)
31+
32+
const bottomBorder = colorize(
33+
box.bottomLeft + box.bottom.repeat(width - 2) + box.bottomRight,
34+
color,
35+
'foreground'
36+
)
37+
38+
output.write(x, y, topBorder, { transformers: [] })
39+
output.write(x, y + 1, leftBorder, { transformers: [] })
40+
output.write(x + width - 1, y + 1, rightBorder, { transformers: [] })
41+
output.write(x, y + height - 1, bottomBorder, { transformers: [] })
42+
}
43+
}

src/tui/renderer/renderNodeToOutput.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { wrapText } from './text'
66
import { squashTextNodes } from './text'
77
// import renderBorder from './render-border'
88
import { Output, OutputTransformer } from './Output'
9+
import { renderBorders } from './renderBorders'
910

1011
// If parent container is `<Box>`, text nodes will be treated as separate nodes in
1112
// the tree and will have their own coordinates in the layout.
@@ -65,9 +66,8 @@ const renderNodeToOutput = (
6566
newTransformers = [node.internal_transform, ...transformers]
6667
}
6768

68-
if (node.nodeName === '#text') {
69-
// TODO: remove this, only allow text inside Text or span component
70-
let text = node.nodeValue
69+
if (node.nodeName === 'tui-text') {
70+
let text = squashTextNodes(node)
7171

7272
if (text.length > 0) {
7373
const currentWidth = widestLine(text)
@@ -79,29 +79,17 @@ const renderNodeToOutput = (
7979
}
8080

8181
text = applyPaddingToText(node, text)
82-
// console.log('#text', { text, maxWidth })
8382
output.write(x, y, text, { transformers: newTransformers })
8483
}
8584

8685
return
87-
} else if (node.nodeName === 'tui-text') {
88-
let text = squashTextNodes(node)
89-
90-
if (text.length > 0) {
91-
const currentWidth = widestLine(text)
92-
const maxWidth = getMaxWidth(yogaNode)
93-
94-
if (currentWidth > maxWidth) {
95-
const textWrap = node.style.textWrap ?? 'wrap'
96-
text = wrapText(text, maxWidth, textWrap)
97-
}
86+
}
9887

99-
text = applyPaddingToText(node, text)
100-
output.write(x, y, text, { transformers: newTransformers })
101-
}
88+
if (node.nodeName === 'tui-box') {
89+
renderBorders(x, y, node, output)
90+
}
10291

103-
return
104-
} else if (node.nodeName === 'tui-root') {
92+
if (node.nodeName === 'tui-root' || node.nodeName === 'tui-box') {
10593
for (const childNode of node.childNodes) {
10694
renderNodeToOutput(childNode, output, {
10795
offsetX: x,
@@ -111,10 +99,6 @@ const renderNodeToOutput = (
11199
})
112100
}
113101
}
114-
115-
// if (node.nodeName === 'tui-box') {
116-
// renderBorder(x, y, node, output)
117-
// }
118102
}
119103
}
120104

src/tui/renderer/styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ const applyBorderStyles = (node: YogaNode, style: Styles): void => {
347347
}
348348
}
349349

350-
export default (node: YogaNode, style: Styles = {}): void => {
350+
export function applyStyles(node: YogaNode, style: Styles = {}) {
351351
applyPositionStyles(node, style)
352352
applyMarginStyles(node, style)
353353
applyPaddingStyles(node, style)

0 commit comments

Comments
 (0)