Skip to content

Commit

Permalink
date-time component
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Oct 22, 2024
1 parent ca88f14 commit d0e4e58
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 26 deletions.
47 changes: 28 additions & 19 deletions cmd/slackdump/internal/ui/bubbles/btime/btime.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type KeyMap struct {
Quit key.Binding
}

type TimeModel struct {
type Model struct {
Time time.Time
entry [6]int
maxnum [3]int
Expand Down Expand Up @@ -68,8 +68,8 @@ func DefaultStyles() Styles {
}
}

func NewTime(t time.Time) *TimeModel {
tm := &TimeModel{
func New(t time.Time) *Model {
tm := &Model{
Time: t,
entry: [6]int{0, 0, 0, 0, 0, 0},
maxnum: [3]int{23, 59, 59},
Expand All @@ -83,17 +83,21 @@ func NewTime(t time.Time) *TimeModel {
return tm
}

func (m *TimeModel) Focus() {
func (m *Model) Focus() {
m.Focused = true
}

func (m *TimeModel) Init() tea.Cmd {
func (m *Model) Blur() {
m.Focused = false
}

func (m *Model) Init() tea.Cmd {
return nil
}

var digitsRe = regexp.MustCompile(`\d`)

func (m *TimeModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd) {
if !m.Focused {
return m, nil
}
Expand Down Expand Up @@ -178,21 +182,26 @@ func (m *TimeModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}

func (m *TimeModel) whatIf(digit int, hasVal int) int {
func (m *Model) whatIf(digit int, hasVal int) int {
whatIf := make([]int, len(m.entry))
copy(whatIf, m.entry[:])
whatIf[digit] = hasVal
return tupleVal(whatIf, m.cursor/2)
}

func (m *TimeModel) updateTime() {
func (m *Model) updateTime() {
hour := tupleVal(m.entry[:], 0)
minute := tupleVal(m.entry[:], 1)
second := tupleVal(m.entry[:], 2)
m.Time = time.Date(m.Time.Year(), m.Time.Month(), m.Time.Day(), hour, minute, second, 0, time.UTC)
}

func (m *TimeModel) toEntry() {
func (m *Model) Value() time.Time {
m.updateTime()
return m.Time
}

func (m *Model) toEntry() {
hour := m.Time.Hour()
minute := m.Time.Minute()
second := m.Time.Second()
Expand All @@ -211,7 +220,7 @@ func tupleVal(entry []int, tuple int) int {
return entry[tuple*2]*10 + entry[tuple*2+1]
}

func (m *TimeModel) View() string {
func (m *Model) View() string {
if m.finishing {
return ""
}
Expand All @@ -231,9 +240,9 @@ func (m *TimeModel) View() string {
)

var buf strings.Builder
buf.WriteString(cursor(m.cursor, 2, '↑') + "\n")
buf.WriteString(drawCursor(m.cursor, 2, '↑', 3) + "\n")
buf.WriteString(r(0) + r(1) + sep + r(2) + r(3) + sep + r(4) + r(5) + "\n")
buf.WriteString(cursor(m.cursor, 2, '↓'))
buf.WriteString(drawCursor(m.cursor, 2, '↓', 3))
if m.ShowHelp {
buf.WriteString("\n\n" + m.Styles.Help.Render(
"↓/↑ change, tab jump, backspace zero, delete clear, enter to finish",
Expand All @@ -243,14 +252,14 @@ func (m *TimeModel) View() string {
return buf.String()
}

func cursor(pos int, tupleSz int, char rune) string {
// numTuples is the size of the field in tuples.
func drawCursor(pos int, tupleSz int, char rune, numTuples int) string {
var buf strings.Builder
numTuples := pos / tupleSz
offset := pos % tupleSz
const fill = " "

for i := 0; i < numTuples; i++ {
buf.WriteString(strings.Repeat(" ", tupleSz) + " ")
}
buf.WriteString(strings.Repeat(" ", offset) + string(char))
before := pos + (pos / tupleSz)
after := numTuples*tupleSz - before + 1

buf.WriteString(strings.Repeat(fill, before) + string(char) + strings.Repeat(fill, after))
return buf.String()
}
55 changes: 51 additions & 4 deletions cmd/slackdump/internal/ui/cfgui/updaters/date.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ import (
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
datepicker "github.com/ethanefung/bubble-datepicker"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/bubbles/btime"
)

type DateModel struct {
Value *time.Time
dm datepicker.Model
tm *btime.Model
finishing bool
timeEnabled bool
state state
}

func NewDTTM(ptrTime *time.Time) DateModel {
m := datepicker.New(*ptrTime)
t := btime.New(m.Time)
m.SelectDate()
return DateModel{
Value: ptrTime,
dm: m,
tm: t,
timeEnabled: true,
}
}
Expand All @@ -29,6 +35,13 @@ func (m DateModel) Init() tea.Cmd {
return m.dm.Init()
}

type state int

const (
scalendar state = iota
stime
)

func (m DateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
Expand All @@ -39,23 +52,57 @@ func (m DateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "esc", "ctrl+c":
return m, OnClose
case "enter":
*m.Value = m.dm.Time
d := m.dm.Time
t := m.tm.Value()
*m.Value = time.Date(d.Year(), d.Month(), d.Day(), t.Hour(), t.Minute(), t.Second(), 0, m.Value.Location())
m.finishing = true
return m, OnClose
case "tab":
switch m.state {
case scalendar:
if !m.timeEnabled || m.dm.Focused != datepicker.FocusCalendar {
break
}
m.state = stime
m.tm.Focus()
return m, nil
case stime:
// ignore tab in time mode.
return m, nil
}
case "shift+tab":
switch m.state {
case scalendar:
break
case stime:
m.state = scalendar
m.tm.Blur()
return m, nil
}
}
}

m.dm, cmd = m.dm.Update(msg)
switch m.state {
case scalendar:
m.dm, cmd = m.dm.Update(msg)
case stime:
m.tm, cmd = m.tm.Update(msg)
}
cmds = append(cmds, cmd)

return m, tea.Batch(cmds...)
}

func (m DateModel) View() string {
if m.finishing {
return ""
}

var b strings.Builder
b.WriteString(m.dm.View())
if m.timeEnabled {
b.WriteString("\n\nTime: " + m.Value.Format("15:04:05") + " (UTC)")
b.WriteString(lipgloss.JoinVertical(lipgloss.Center, m.dm.View(), m.tm.View()))
} else {
b.WriteString(m.dm.View())
}
b.WriteString("\n\n" + m.dm.Styles.Text.Render("Use arrow keys to navigate, tab/shift+tab to switch between fields, and enter to select."))
return b.String()
Expand Down
16 changes: 14 additions & 2 deletions cmd/slackdump/internal/ui/cfgui/updaters/examples/time/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ func main() {
defer logf.Close()
log.SetOutput(logf)

m := btime.NewTime(time.Now())
m := btime.New(time.Now())
m.Focused = true
m.ShowHelp = true
p, err := tea.NewProgram(m).Run()
p, err := tea.NewProgram(timeModel{m}).Run()
if err != nil {
log.Fatal(err)
}
_ = p
}

type timeModel struct {
*btime.Model
}

// Update wraps the Update method of the embedded btime.Model, to satisfy
// the tea.Model interface.
func (m timeModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
mod, cmd := m.Model.Update(msg)
m.Model = mod
return m, cmd
}
1 change: 0 additions & 1 deletion cmd/slackdump/internal/ui/cfgui/updaters/filepick.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ func (m FileModel) Init() tea.Cmd {
}

func (m FileModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// cfg.Log.Printf("fileUpdateModel.Update: %[1]T %[1]v", msg)
var cmd tea.Cmd
var cmds []tea.Cmd

Expand Down

0 comments on commit d0e4e58

Please sign in to comment.