384 lines
6.2 KiB
Markdown
384 lines
6.2 KiB
Markdown
# Container Components
|
|
|
|
Components for grouping and organizing content in OpenTUI.
|
|
|
|
## Box Component
|
|
|
|
The primary container component with borders, backgrounds, and layout capabilities.
|
|
|
|
### Basic Usage
|
|
|
|
```tsx
|
|
// React/Solid
|
|
<box>
|
|
<text>Content inside box</text>
|
|
</box>
|
|
|
|
// Core
|
|
const box = new BoxRenderable(renderer, {
|
|
id: "container",
|
|
})
|
|
box.add(child)
|
|
```
|
|
|
|
### Borders
|
|
|
|
```tsx
|
|
<box border>
|
|
Simple border
|
|
</box>
|
|
|
|
<box
|
|
border
|
|
borderStyle="single" // single | double | rounded | bold | none
|
|
borderColor="#FFFFFF"
|
|
>
|
|
Styled border
|
|
</box>
|
|
|
|
// Individual borders
|
|
<box
|
|
borderTop
|
|
borderBottom
|
|
borderLeft={false}
|
|
borderRight={false}
|
|
>
|
|
Top and bottom only
|
|
</box>
|
|
```
|
|
|
|
**Border Styles:**
|
|
|
|
| Style | Appearance |
|
|
|-------|------------|
|
|
| `single` | `┌─┐│ │└─┘` |
|
|
| `double` | `╔═╗║ ║╚═╝` |
|
|
| `rounded` | `╭─╮│ │╰─╯` |
|
|
| `bold` | `┏━┓┃ ┃┗━┛` |
|
|
|
|
### Title
|
|
|
|
```tsx
|
|
<box
|
|
border
|
|
title="Settings"
|
|
titleAlignment="center" // left | center | right
|
|
>
|
|
Panel content
|
|
</box>
|
|
```
|
|
|
|
### Background
|
|
|
|
```tsx
|
|
<box backgroundColor="#1a1a2e">
|
|
Dark background
|
|
</box>
|
|
|
|
<box backgroundColor="transparent">
|
|
No background
|
|
</box>
|
|
```
|
|
|
|
### Layout
|
|
|
|
Boxes are flex containers by default:
|
|
|
|
```tsx
|
|
<box
|
|
flexDirection="row" // row | column | row-reverse | column-reverse
|
|
justifyContent="center" // flex-start | flex-end | center | space-between | space-around
|
|
alignItems="center" // flex-start | flex-end | center | stretch | baseline
|
|
gap={2} // Space between children
|
|
>
|
|
<text>Item 1</text>
|
|
<text>Item 2</text>
|
|
</box>
|
|
```
|
|
|
|
### Spacing
|
|
|
|
```tsx
|
|
<box
|
|
padding={2} // All sides
|
|
paddingTop={1}
|
|
paddingRight={2}
|
|
paddingBottom={1}
|
|
paddingLeft={2}
|
|
margin={1}
|
|
marginTop={1}
|
|
>
|
|
Spaced content
|
|
</box>
|
|
```
|
|
|
|
### Dimensions
|
|
|
|
```tsx
|
|
<box
|
|
width={40} // Fixed width
|
|
height={10} // Fixed height
|
|
width="50%" // Percentage of parent
|
|
minWidth={20} // Minimum width
|
|
maxWidth={80} // Maximum width
|
|
flexGrow={1} // Grow to fill space
|
|
>
|
|
Sized box
|
|
</box>
|
|
```
|
|
|
|
### Mouse Events
|
|
|
|
```tsx
|
|
<box
|
|
onMouseDown={(event) => {
|
|
console.log("Clicked at:", event.x, event.y)
|
|
}}
|
|
onMouseUp={(event) => {}}
|
|
onMouseMove={(event) => {}}
|
|
>
|
|
Clickable box
|
|
</box>
|
|
```
|
|
|
|
## ScrollBox Component
|
|
|
|
A scrollable container for content that exceeds the viewport.
|
|
|
|
### Basic Usage
|
|
|
|
```tsx
|
|
// React
|
|
<scrollbox height={10}>
|
|
{items.map((item, i) => (
|
|
<text key={i}>{item}</text>
|
|
))}
|
|
</scrollbox>
|
|
|
|
// Solid
|
|
<scrollbox height={10}>
|
|
<For each={items()}>
|
|
{(item) => <text>{item}</text>}
|
|
</For>
|
|
</scrollbox>
|
|
|
|
// Core
|
|
const scrollbox = new ScrollBoxRenderable(renderer, {
|
|
id: "list",
|
|
height: 10,
|
|
})
|
|
items.forEach(item => {
|
|
scrollbox.add(new TextRenderable(renderer, { content: item }))
|
|
})
|
|
```
|
|
|
|
### Focus for Keyboard Scrolling
|
|
|
|
```tsx
|
|
<scrollbox focused height={20}>
|
|
{/* Use arrow keys to scroll */}
|
|
</scrollbox>
|
|
```
|
|
|
|
### Scrollbar Styling
|
|
|
|
```tsx
|
|
// React
|
|
<scrollbox
|
|
style={{
|
|
rootOptions: {
|
|
backgroundColor: "#24283b",
|
|
},
|
|
wrapperOptions: {
|
|
backgroundColor: "#1f2335",
|
|
},
|
|
viewportOptions: {
|
|
backgroundColor: "#1a1b26",
|
|
},
|
|
contentOptions: {
|
|
backgroundColor: "#16161e",
|
|
},
|
|
scrollbarOptions: {
|
|
showArrows: true,
|
|
trackOptions: {
|
|
foregroundColor: "#7aa2f7",
|
|
backgroundColor: "#414868",
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
{content}
|
|
</scrollbox>
|
|
```
|
|
|
|
### Scroll Position (Core)
|
|
|
|
```typescript
|
|
const scrollbox = new ScrollBoxRenderable(renderer, {
|
|
id: "list",
|
|
height: 20,
|
|
})
|
|
|
|
// Scroll programmatically
|
|
scrollbox.scrollTo(0) // Scroll to top
|
|
scrollbox.scrollTo(100) // Scroll to position
|
|
scrollbox.scrollBy(10) // Scroll relative
|
|
scrollbox.scrollToBottom() // Scroll to end
|
|
```
|
|
|
|
## Composition Patterns
|
|
|
|
### Card Component
|
|
|
|
```tsx
|
|
function Card({ title, children }) {
|
|
return (
|
|
<box
|
|
border
|
|
borderStyle="rounded"
|
|
padding={2}
|
|
marginBottom={1}
|
|
>
|
|
{title && (
|
|
<text fg="#00FFFF" bold>
|
|
{title}
|
|
</text>
|
|
)}
|
|
<box marginTop={title ? 1 : 0}>
|
|
{children}
|
|
</box>
|
|
</box>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Panel Component
|
|
|
|
```tsx
|
|
function Panel({ title, children, width = 40 }) {
|
|
return (
|
|
<box
|
|
border
|
|
borderStyle="double"
|
|
width={width}
|
|
backgroundColor="#1a1a2e"
|
|
>
|
|
{title && (
|
|
<box
|
|
borderBottom
|
|
padding={1}
|
|
backgroundColor="#2a2a4e"
|
|
>
|
|
<text bold>{title}</text>
|
|
</box>
|
|
)}
|
|
<box padding={2}>
|
|
{children}
|
|
</box>
|
|
</box>
|
|
)
|
|
}
|
|
```
|
|
|
|
### List Container
|
|
|
|
```tsx
|
|
function List({ items, renderItem }) {
|
|
return (
|
|
<scrollbox height={15} focused>
|
|
{items.map((item, i) => (
|
|
<box
|
|
key={i}
|
|
padding={1}
|
|
backgroundColor={i % 2 === 0 ? "#222" : "#333"}
|
|
>
|
|
{renderItem(item, i)}
|
|
</box>
|
|
))}
|
|
</scrollbox>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Nesting Containers
|
|
|
|
```tsx
|
|
<box flexDirection="column" height="100%">
|
|
{/* Header */}
|
|
<box height={3} border>
|
|
<text>Header</text>
|
|
</box>
|
|
|
|
{/* Main area with sidebar */}
|
|
<box flexDirection="row" flexGrow={1}>
|
|
<box width={20} border>
|
|
<text>Sidebar</text>
|
|
</box>
|
|
<box flexGrow={1}>
|
|
<scrollbox height="100%">
|
|
{/* Scrollable content */}
|
|
</scrollbox>
|
|
</box>
|
|
</box>
|
|
|
|
{/* Footer */}
|
|
<box height={1}>
|
|
<text>Footer</text>
|
|
</box>
|
|
</box>
|
|
```
|
|
|
|
## Gotchas
|
|
|
|
### Percentage Dimensions Need Parent Size
|
|
|
|
```tsx
|
|
// WRONG - parent has no explicit size
|
|
<box>
|
|
<box width="50%">Won't work</box>
|
|
</box>
|
|
|
|
// CORRECT
|
|
<box width="100%">
|
|
<box width="50%">Works</box>
|
|
</box>
|
|
```
|
|
|
|
### FlexGrow Needs Sized Parent
|
|
|
|
```tsx
|
|
// WRONG
|
|
<box>
|
|
<box flexGrow={1}>Won't grow</box>
|
|
</box>
|
|
|
|
// CORRECT
|
|
<box height="100%">
|
|
<box flexGrow={1}>Will grow</box>
|
|
</box>
|
|
```
|
|
|
|
### ScrollBox Needs Height
|
|
|
|
```tsx
|
|
// WRONG - no height constraint
|
|
<scrollbox>
|
|
{items}
|
|
</scrollbox>
|
|
|
|
// CORRECT
|
|
<scrollbox height={20}>
|
|
{items}
|
|
</scrollbox>
|
|
```
|
|
|
|
### Borders Add to Size
|
|
|
|
Borders take up space inside the box:
|
|
|
|
```tsx
|
|
<box width={10} border>
|
|
{/* Inner content area is 8 chars (10 - 2 for borders) */}
|
|
</box>
|
|
```
|