summaryrefslogtreecommitdiff
path: root/internal/client/ui/core/networkcreation
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2024-11-25 23:49:40 +0200
committerKyren223 <Kyren223@proton.me>2024-11-25 23:49:40 +0200
commitb05e4fcbcb59bdf20ca7b3e27f8ed15fea594cad (patch)
tree96dbcdf57b6e2958d99a803197f5b3210f0c5bc5 /internal/client/ui/core/networkcreation
parentd12ebef2c5b1fdc17d4151f752d4e4d95f6e56b3 (diff)
Added typing and input validation to network creation popup
Diffstat (limited to 'internal/client/ui/core/networkcreation')
-rw-r--r--internal/client/ui/core/networkcreation/networkcreation.go133
1 files changed, 109 insertions, 24 deletions
diff --git a/internal/client/ui/core/networkcreation/networkcreation.go b/internal/client/ui/core/networkcreation/networkcreation.go
index c911dbb..f21bb73 100644
--- a/internal/client/ui/core/networkcreation/networkcreation.go
+++ b/internal/client/ui/core/networkcreation/networkcreation.go
@@ -1,6 +1,7 @@
package networkcreation
import (
+ "errors"
"strings"
"github.com/charmbracelet/bubbles/textinput"
@@ -8,11 +9,18 @@ import (
"github.com/charmbracelet/lipgloss"
"github.com/kyren223/eko/internal/client/ui/colors"
"github.com/kyren223/eko/internal/client/ui/field"
+ "github.com/kyren223/eko/internal/client/ui/layouts/flex"
"github.com/kyren223/eko/pkg/assert"
)
var (
- style = lipgloss.NewStyle().Border(lipgloss.ThickBorder())
+ width = 48
+
+ style = lipgloss.NewStyle().
+ Border(lipgloss.ThickBorder()).
+ Padding(1, 4).
+ Align(lipgloss.Center, lipgloss.Center)
+
headerStyle = lipgloss.NewStyle().Foreground(colors.Turquoise)
fieldBlurredStyle = lipgloss.NewStyle().
@@ -23,11 +31,7 @@ var (
BorderForeground(colors.Focus).
Border(lipgloss.ThickBorder())
- underlineStyle = func(s string, width int, focus bool) string {
- color := colors.Gray
- if focus {
- color = colors.Focus
- }
+ underlineStyle = func(s string, width int, color lipgloss.Color) string {
underline := lipgloss.NewStyle().Foreground(color).
Render(strings.Repeat(lipgloss.ThickBorder().Bottom, width))
return lipgloss.JoinVertical(lipgloss.Left, s, underline)
@@ -35,6 +39,11 @@ var (
iconHeader = headerStyle.Bold(true).Render(" Icon: ")
colorHeader = headerStyle.Bold(true).Italic(true).Render("# ")
+
+ blurredCreate = lipgloss.NewStyle().
+ Background(colors.Gray).Padding(0, 1).Render("Create Network")
+ focusedCreate = lipgloss.NewStyle().
+ Background(colors.Blue).Padding(0, 1).Render("Create Network")
)
const (
@@ -46,49 +55,73 @@ const (
NameField = iota
IconField
ColorField
+ CreateField
FieldCount
)
type Model struct {
- name field.Model
- Style lipgloss.Style
precomputedIconStyle lipgloss.Style
- icon textinput.Model
- color textinput.Model
- Width int
- Height int
- selected int
+ name field.Model
+ icon textinput.Model
+ color textinput.Model
+ create string
+
+ selected int
+ nameWidth int
}
func New() Model {
- name := field.New(30)
+ name := field.New(width)
name.Header = "Network Name"
name.HeaderStyle = headerStyle
name.FocusedStyle = fieldFocusedStyle
name.BlurredStyle = fieldBlurredStyle
- name.Input.CharLimit = 32
+ name.ErrorStyle = lipgloss.NewStyle().Foreground(colors.Error)
+ name.Input.CharLimit = width
name.Focus()
+ name.Input.Validate = func(s string) error {
+ if strings.TrimSpace(s) == "" {
+ return errors.New("cannot be empty")
+ }
+ if strings.TrimSpace(s) != s {
+ return errors.New("no leading/trailing spaces")
+ }
+ return nil
+ }
+
+ nameWidth := lipgloss.Width(name.View())
icon := textinput.New()
icon.Prompt = ""
icon.CharLimit = MaxIconLength
icon.Placeholder = "ic"
+ icon.Validate = func(s string) error {
+ if len(s) == 0 {
+ return errors.New("err")
+ }
+ return nil
+ }
color := textinput.New()
color.Prompt = ""
color.CharLimit = MaxHexDigits
color.Placeholder = "000000"
+ color.Validate = func(s string) error {
+ if len(s) != 0 && len(s) != MaxHexDigits {
+ return errors.New("err")
+ }
+ return nil
+ }
return Model{
- Width: 50,
- Height: 10,
name: name,
icon: icon,
color: color,
- Style: style,
+ create: blurredCreate,
- precomputedIconStyle: lipgloss.NewStyle().Width(lipgloss.Width(name.View()) / 2),
+ nameWidth: nameWidth,
+ precomputedIconStyle: lipgloss.NewStyle().Width(nameWidth / 2),
}
}
@@ -99,17 +132,31 @@ func (m Model) Init() tea.Cmd {
func (m Model) View() string {
name := m.name.View()
- iconInput := underlineStyle(m.icon.View(), MaxIconLength, m.selected == IconField)
+ color := colors.Gray
+ if m.icon.Err != nil {
+ color = colors.Error
+ } else if m.selected == IconField {
+ color = colors.Focus
+ }
+ iconInput := underlineStyle(m.icon.View(), MaxIconLength, color)
iconText := m.precomputedIconStyle.Render(lipgloss.JoinHorizontal(lipgloss.Top, iconHeader, iconInput))
- colorInput := underlineStyle(m.color.View(), MaxHexDigits, m.selected == ColorField)
+ color = colors.Gray
+ if m.color.Err != nil {
+ color = colors.Error
+ } else if m.selected == ColorField {
+ color = colors.Focus
+ }
+ colorInput := underlineStyle(m.color.View(), MaxHexDigits, color)
colorText := lipgloss.JoinHorizontal(lipgloss.Top, colorHeader, colorInput)
icon := lipgloss.JoinHorizontal(lipgloss.Top, iconText, colorText)
- content := lipgloss.JoinVertical(lipgloss.Left, name, "\n", icon)
- popup := lipgloss.NewStyle().Width(m.Width).Height(m.Height).Render(content)
- return m.Style.Render(popup)
+ // create := lipgloss.NewStyle().Width(m.nameWidth).Align(lipgloss.Center).Render(m.create)
+ create := m.create
+
+ content := flex.NewVertical(name, icon, create).WithGap(1).View()
+ return style.Render(content)
}
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
@@ -130,7 +177,25 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
case IconField:
m.icon, cmd = m.icon.Update(msg)
case ColorField:
+ oldValue := m.color.Value()
+ position := m.color.Position()
m.color, cmd = m.color.Update(msg)
+ newValue := m.color.Value()
+
+ hex := "0123456789abcdefABCDEF"
+ invalid := false
+ for _, c := range newValue {
+ if !strings.ContainsRune(hex, c) {
+ invalid = true
+ break
+ }
+ }
+
+ if invalid {
+ m.color.SetValue(oldValue)
+ m.color.SetCursor(position)
+ }
+
}
return m, cmd
}
@@ -153,6 +218,7 @@ func (m *Model) updateFocus() tea.Cmd {
m.name.Blur()
m.icon.Blur()
m.color.Blur()
+ m.create = blurredCreate
switch m.selected {
case NameField:
return m.name.Focus()
@@ -160,8 +226,27 @@ func (m *Model) updateFocus() tea.Cmd {
return m.icon.Focus()
case ColorField:
return m.color.Focus()
+ case CreateField:
+ m.create = focusedCreate
+ return nil
default:
assert.Never("missing switch statement field in update focus", "selected", m.selected)
return nil
}
}
+
+func (m *Model) Select() tea.Cmd {
+ if m.selected != CreateField {
+ return nil
+ }
+
+ m.name.Input.Err = m.name.Input.Validate(m.name.Input.Value())
+ m.icon.Err = m.icon.Validate(m.icon.Value())
+ m.color.Err = m.color.Validate(m.color.Value())
+ if m.name.Input.Err != nil || m.icon.Err != nil || m.color.Err != nil {
+ return nil
+ }
+
+ // TODO: send api request for creating the server
+ return nil
+}