Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/cassdeckard/tviewyaml/config"
"github.com/cassdeckard/tviewyaml/template"
Expand Down Expand Up @@ -385,6 +386,24 @@ func (b *Builder) buildTable(table *tview.Table, cfg *config.PageConfig, bc *Bui

table.SetBorder(true)
table.SetSelectable(true, false)

if cfg.OnDone != "" {
onDoneExpr := cfg.OnDone
table.SetDoneFunc(func(key tcell.Key) {
if key != tcell.KeyEnter && key != tcell.KeyEscape {
return
}
doneKey := "Enter"
if key == tcell.KeyEscape {
doneKey = "Escape"
}
b.context.SetStateDirect("__doneKey", doneKey)
if cb, err := b.executor.ExecuteCallback(onDoneExpr); err == nil {
cb()
}
})
}

return table, nil
}

Expand Down
52 changes: 50 additions & 2 deletions builder/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,24 @@ func (pm *PropertyMapper) applyTextViewProperties(tv *tview.TextView, prim *conf
}
if prim.Regions {
tv.SetRegions(true)

// Add region navigation handlers
}
if prim.OnDone != "" && pm.executor != nil {
onDoneExpr := prim.OnDone
tv.SetDoneFunc(func(key tcell.Key) {
if key != tcell.KeyEnter && key != tcell.KeyEscape {
return
}
doneKey := "Enter"
if key == tcell.KeyEscape {
doneKey = "Escape"
}
pm.context.SetStateDirect("__doneKey", doneKey)
if cb, err := pm.executor.ExecuteCallback(onDoneExpr); err == nil {
cb()
}
})
} else if prim.Regions {
// Add region navigation handlers (only when onDone is not set)
tv.SetDoneFunc(func(key tcell.Key) {
currentSelection := tv.GetHighlights()
if key == tcell.KeyEnter {
Expand Down Expand Up @@ -144,6 +160,22 @@ func (pm *PropertyMapper) applyInputFieldProperties(input *tview.InputField, pri
if prim.Text != "" {
input.SetText(prim.Text)
}
if prim.OnDone != "" && pm.executor != nil {
onDoneExpr := prim.OnDone
input.SetDoneFunc(func(key tcell.Key) {
if key != tcell.KeyEnter && key != tcell.KeyEscape {
return
}
doneKey := "Enter"
if key == tcell.KeyEscape {
doneKey = "Escape"
}
pm.context.SetStateDirect("__doneKey", doneKey)
if cb, err := pm.executor.ExecuteCallback(onDoneExpr); err == nil {
cb()
}
})
}
return nil
}

Expand Down Expand Up @@ -175,6 +207,22 @@ func (pm *PropertyMapper) applyDropDownProperties(dd *tview.DropDown, prim *conf

func (pm *PropertyMapper) applyTableProperties(table *tview.Table, prim *config.Primitive) error {
// Table borders, fixed rows/columns, and data are all handled in populateTableData
if prim.OnDone != "" && pm.executor != nil {
onDoneExpr := prim.OnDone
table.SetDoneFunc(func(key tcell.Key) {
if key != tcell.KeyEnter && key != tcell.KeyEscape {
return
}
doneKey := "Enter"
if key == tcell.KeyEscape {
doneKey = "Escape"
}
pm.context.SetStateDirect("__doneKey", doneKey)
if cb, err := pm.executor.ExecuteCallback(onDoneExpr); err == nil {
cb()
}
})
}
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type PageConfig struct {
FormItems []FormItem `yaml:"formItems,omitempty"`
OnSubmit string `yaml:"onSubmit,omitempty"` // Template expression for runFormSubmit (e.g. Submit button)
OnCancel string `yaml:"onCancel,omitempty"` // Template expression when form is cancelled (Escape); if unset and OnSubmit set, Escape runs OnSubmit
OnDone string `yaml:"onDone,omitempty"` // Template expression when user presses Enter/Escape (e.g. page-level table)
TableData *TableData `yaml:"tableData,omitempty"`
// TreeView-specific (for page-level type: treeView)
OnNodeSelected string `yaml:"onNodeSelected,omitempty"` // Template expression when a node is selected (state: __selectedNodeText)
Expand Down Expand Up @@ -92,6 +93,8 @@ type Primitive struct {
FormItems []FormItem `yaml:"formItems,omitempty"`
OnSubmit string `yaml:"onSubmit,omitempty"` // Template expression for runFormSubmit (nested form)
OnCancel string `yaml:"onCancel,omitempty"` // Template expression when form is cancelled (Escape); if unset and OnSubmit set, Escape runs OnSubmit
// onDone: Template expression when user presses Enter/Escape (TextView, InputField, Table)
OnDone string `yaml:"onDone,omitempty"`
// Table-specific properties
OnCellSelected string `yaml:"onCellSelected,omitempty"` // Template expression when a cell is selected (state: __selectedCellText, __selectedRow, __selectedCol)
Borders bool `yaml:"borders,omitempty"` // Show borders between cells
Expand Down
80 changes: 80 additions & 0 deletions docs/issue-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Issue Dependencies and Presentation Demo

```mermaid
flowchart LR
subgraph demo [Presentation Demo Slides]
cover[Cover]
intro[Introduction]
helloworld[HelloWorld]
inputfield[InputField]
form[Form]
textview1[TextView1]
textview2[TextView2]
table[Table]
treeview[TreeView]
flex[Flex]
grid[Grid]
colors[Colors]
endSlide[End]
end

subgraph core [Core Navigation - Info Bar]
i17["#17 presentation nav model"]
i5["#5 onDone / SetDoneFunc"]
i6["#6 SetHighlightedFunc"]
i16["#16 region count config"]
end

subgraph textview [TextView Slides]
i7["#7 SetScrollable / SetChangedFunc"]
end

subgraph tableIssues [Table Slide]
i8["#8 SetSeparator"]
i9["#9 SetSelectable row/col"]
i10["#10 SetExpansion / NotSelectable"]
i11["#11 SetBorderPadding"]
i15["#15 cross-primitive callbacks"]
end

subgraph tree [TreeView Slide]
i14["#14 SetAlign / SetTopLevel / SetGraphics / SetPrefixes"]
end

subgraph layout [Layout Slides]
i12["#12 responsive Grid"]
i13["#13 SetInputCapture per primitive"]
end

subgraph other [Other Features]
i4["#4 maskCharacter"]
i2["#2 Frame"]
i3["#3 Image"]
end

i17 -->|depends on| i5
i17 -->|depends on| i6
i16 -->|enhances| i6

i5 -->|enables| textview1
i5 -->|enables| inputfield
i5 -->|enables| table
i5 -->|enables| textview2
i6 -->|enables| i17
i17 -->|enables| cover
i17 -->|enables| intro
i16 -->|enables| textview2

i7 -->|enables| textview1
i8 -->|enables| table
i9 -->|enables| table
i10 -->|enables| table
i11 -->|enables| table
i15 -->|enables| table

i14 -->|enables| treeview
i12 -->|enables| grid
i13 -->|enables| flex
i13 -->|enables| grid
i4 -->|enables| form
```
63 changes: 63 additions & 0 deletions docs/tview-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# tview Component Coverage in tviewyaml

This table maps [tview](https://pkg.go.dev/github.com/rivo/tview) primitives and features to their implementation status in tviewyaml. Based on the [tview package documentation](https://pkg.go.dev/github.com/rivo/tview).

| tview Component | Implemented | Page-Level | Nested/Child | Primary Example Config | Notes |
|-----------------|-------------|------------|--------------|------------------------|-------|
| **Box** | Yes | No | Yes | [box.yaml](../example/config/box.yaml) | Basic container; base class for most primitives |
| **Button** | Yes | No | Yes (in Form) | [button.yaml](../example/config/button.yaml) | Via form buttons or standalone in flex/grid |
| **Checkbox** | Yes | No | Yes (Form item) | [checkbox.yaml](../example/config/checkbox.yaml) | Form item type `checkbox` |
| **DropDown** | Yes | No | Yes (Form item) | [dropdown.yaml](../example/config/dropdown.yaml) | Form item type `dropdown` |
| **Flex** | Yes | Yes | Yes | [flex.yaml](../example/config/flex.yaml) | Row/column layout; `direction: row` or default column |
| **Form** | Yes | Yes | Yes | [form.yaml](../example/config/form.yaml) | With InputField, Checkbox, Dropdown, Button, TextArea |
| **Frame** | No | No | No | — | Wrapper with header/footer text; not in factory |
| **Grid** | Yes | Yes | Yes | [grid.yaml](../example/config/grid.yaml) | Row/column sizing via `gridRows`, `gridColumns` |
| **Image** | No | No | No | — | Form.AddImage exists in tview; no YAML support |
| **InputField** | Yes | No | Yes (Form item) | [inputfield.yaml](../example/config/inputfield.yaml) | Form item type `inputfield`; supports placeholder, acceptance; `onDone` for Enter/Escape when standalone |
| **List** | Yes | Yes | Yes | [list.yaml](../example/config/list.yaml) | MainText, secondaryText, shortcut, onSelected |
| **Modal** | Yes | Yes | No | [modal.yaml](../example/config/modal.yaml), [modal-about.yaml](../example/config/modal-about.yaml), [modal-confirm.yaml](../example/config/modal-confirm.yaml), [modal-help.yaml](../example/config/modal-help.yaml) | Page-level `type: modal`; text + buttons |
| **Pages** | Yes | Yes (root) | Yes | [app.yaml](../example/config/app.yaml), [nested-pages.yaml](../example/config/nested-pages.yaml) | Tab-like page switching; `ref` to YAML files |
| **Table** | Yes | Yes | Yes | [table.yaml](../example/config/table.yaml) | Headers, rows, borders, fixed rows/columns; `onDone` for Enter/Escape |
| **TextArea** | Yes | No | Yes (Form item) | [form.yaml](../example/config/form.yaml) | Form item type `textarea`; multi-line input |
| **TextView** | Yes | No | Yes | [textview.yaml](../example/config/textview.yaml) | Dynamic colors, regions, scrollable; `onDone` for Enter/Escape |
| **TreeView** | Yes | Yes | Yes | [treeview.yaml](../example/config/treeview.yaml), [treeview-standalone.yaml](../example/config/treeview-standalone.yaml), [treeview-modes.yaml](../example/config/treeview-modes.yaml) | Nodes with children; selectable modes |

## Form Item Types

Form items supported in `formItems`:

| Form Item Type | tview Primitive | Example |
|----------------|-----------------|---------|
| `inputfield` | InputField | [form.yaml](../example/config/form.yaml), [inputfield.yaml](../example/config/inputfield.yaml) |
| `checkbox` | Checkbox | [form.yaml](../example/config/form.yaml), [checkbox.yaml](../example/config/checkbox.yaml) |
| `dropdown` | DropDown | [form.yaml](../example/config/form.yaml), [dropdown.yaml](../example/config/dropdown.yaml) |
| `textarea` | TextArea | [form.yaml](../example/config/form.yaml) |
| `button` | Button | All form configs |

## tview Features Not Yet Implemented

| Feature | tview API | Notes |
|---------|-----------|-------|
| **Frame** | `tview.NewFrame(primitive)` | Header/footer text around a primitive |
| **Image** | `tview.NewImage()`, `Form.AddImage()` | Terminal image display; form image field |
| **PasswordField** | `Form.AddPasswordField()` | Implemented via `passwordMode: true` on inputfield + SetMaskCharacter('*'). **TODO:** Support custom `maskCharacter` (e.g. `•`) with `'*'` as default. |

## Callback Support: onDone

TextView, InputField (standalone), and Table support `onDone`—a template expression that runs when the user presses Enter or Escape. State `__doneKey` is set to `"Enter"` or `"Escape"` before the callback runs, enabling template branching. See [ondone-demo.yaml](../example/config/ondone-demo.yaml).

Note: tview's Table does not call SetDoneFunc for Enter when rows are selectable—Enter is used for selection. Escape still triggers onDone on selectable tables.

## Additional Example Configs (Feature Demos)

| Config | Purpose |
|--------|---------|
| [main.yaml](../example/config/main.yaml) | Demo menu (list of all features) |
| [state-binding.yaml](../example/config/state-binding.yaml) | Template state binding, reactive views |
| [dynamic-pages.yaml](../example/config/dynamic-pages.yaml) | Dynamic page add/remove |
| [modal-yaml.yaml](../example/config/modal-yaml.yaml) | Modals opened from YAML actions |
| [nested-pages-example.yaml](../example/config/nested-pages-example.yaml) | Nested Pages + Form |
| [clock.yaml](../example/config/clock.yaml) | Live-updating clock (template refresh) |
| [ondone-demo.yaml](../example/config/ondone-demo.yaml) | TextView, InputField, Table with `onDone` (Enter/Escape callbacks) |
| [about.yaml](../example/config/about.yaml) | Simple TextView page |
| [help.yaml](../example/config/help.yaml) | Modal help overlay |
1 change: 1 addition & 0 deletions example/acceptance/navigation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var navPages = []struct {
{"u", "", "ButtonPage", "Button Demo", ""},
{"c", "", "CheckboxPage", "Checkbox Demo", ""},
{"i", "", "InputFieldPage", "InputField Demo", ""},
{"o", "", "OnDoneDemoPage", "TextView - Enter or Escape", ""},
{"f", "", "FormPage", "Form Demo", "Enter"}, // Escape triggers onCancel modal, Enter dismisses
{"l", "", "ListPage", "List Demo", ""},
{"t", "", "TablePage", "Table Demo", ""},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
╔═══════════════════════════════════Tview Feature Demos - Select a feature to explore══════════════════════════════════╗
║(i) InputField ║
║ Text input with validation ║
║(o) onDone Demo ║
║ TextView, InputField, Table with Enter/Escape callbacks ║
║(f) Form ║
║ Complete form with all input types ║
║(l) List ║
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
║ Checkboxes with state management ║
║(i) InputField ║
║ Text input with validation ║
║(o) onDone Demo ║
║ TextView, InputField, Table with Enter/Escape callbacks ║
║(f) Form ║
║ Complete form with all input types ║
║(l) List ║
Expand All @@ -25,6 +27,4 @@
║ Compare auto, true, and false selection behaviors ║
║(d) DropDown ║
║ Dropdown menu with options ║
║(m) Modal ║
║ YAML-configured vs programmatic modals ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
Loading