Calendar: components 421-440
This commit is contained in:
110 files changed
+7335
-23
No files matched your search
@@ -0,0 +1,100 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
)
|
||||
|
||||
// DurationPickerOptions configure a DurationPicker.
|
||||
type DurationPickerOptions struct {
|
||||
// Seconds adds a seconds field.
|
||||
Seconds bool
|
||||
// Min and Max bound the duration; zero Max means 99:59:59.
|
||||
Min, Max time.Duration
|
||||
Disabled bool
|
||||
Label string
|
||||
}
|
||||
|
||||
// DurationPicker edits hours, minutes and seconds within a range.
|
||||
func DurationPicker(c *ui.Context, value *time.Duration, opts DurationPickerOptions) ChoiceResult {
|
||||
hi := opts.Max
|
||||
if hi == 0 {
|
||||
hi = 99*time.Hour + 59*time.Minute + 59*time.Second
|
||||
}
|
||||
unit := time.Minute
|
||||
if opts.Seconds {
|
||||
unit = time.Second
|
||||
}
|
||||
switch {
|
||||
case opts.Min < 0 || hi < opts.Min:
|
||||
panic(fmt.Sprintf("mujica: DurationPicker range %v..%v is not valid", opts.Min, hi))
|
||||
case *value < 0 || *value%unit != 0:
|
||||
panic(fmt.Sprintf("mujica: DurationPicker value %v is negative or finer than the fields", *value))
|
||||
}
|
||||
d := Density(c)
|
||||
label := opts.Label
|
||||
if label == "" {
|
||||
label = msg(c, "duration_picker.label")
|
||||
}
|
||||
col := ui.Column(c).Gap(d.Unit()).Role(ui.RoleGroup).Label(label).Disabled(opts.Disabled)
|
||||
draft := ui.Local(col, "duration-draft", func() [3]float64 { return durationParts(*value) })
|
||||
seen := ui.Local(col, "duration-seen", func() time.Duration { return *value })
|
||||
if *seen != *value {
|
||||
*draft, *seen = durationParts(*value), *value
|
||||
}
|
||||
changed, edited := false, false
|
||||
col.Children(func() {
|
||||
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
||||
fields := []struct {
|
||||
key string
|
||||
max float64
|
||||
}{{"duration_picker.hours", math.Floor(hi.Hours())}, {"duration_picker.minutes", 59}, {"duration_picker.seconds", 59}}
|
||||
for i, f := range fields {
|
||||
if i == 2 && !opts.Seconds {
|
||||
break
|
||||
}
|
||||
ui.Row(c).Gap(d.Unit()).AlignItems(ui.Center).Children(func() {
|
||||
ui.Box(c).Width(d.ControlHeight() * 3.2).Children(func() {
|
||||
r := NumberInput(c, &draft[i], NumberInputOptions{Min: 0, Max: f.max, Label: msg(c, f.key), Disabled: opts.Disabled})
|
||||
edited = edited || r.Changed()
|
||||
})
|
||||
ui.Text(c, msg(c, f.key+"_unit")).TextColor(Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
})
|
||||
total := time.Duration(draft[0])*time.Hour + time.Duration(draft[1])*time.Minute + time.Duration(draft[2])*time.Second
|
||||
problem := ""
|
||||
if total < opts.Min || total > hi {
|
||||
problem = fmt.Sprintf(msg(c, "duration_picker.range"), DurationText(c, opts.Min), DurationText(c, hi))
|
||||
}
|
||||
if edited && problem == "" && total != *value && !col.IsDisabled() {
|
||||
slog.Debug("mujica: duration_picker change", "from", *value, "to", total)
|
||||
*value, *seen, changed = total, total, true
|
||||
}
|
||||
datetimeError(c, problem)
|
||||
})
|
||||
return ChoiceResult{Element: col, changed: changed}
|
||||
}
|
||||
|
||||
func durationParts(v time.Duration) [3]float64 {
|
||||
return [3]float64{float64(v / time.Hour), float64(v % time.Hour / time.Minute), float64(v % time.Minute / time.Second)}
|
||||
}
|
||||
|
||||
// DurationText words a duration, as "1 h 30 min".
|
||||
func DurationText(c *ui.Context, v time.Duration) string {
|
||||
p := durationParts(v)
|
||||
s := ""
|
||||
for i, key := range []string{"duration_picker.hours_unit", "duration_picker.minutes_unit", "duration_picker.seconds_unit"} {
|
||||
if p[i] > 0 || i == 1 && s == "" && p[2] == 0 {
|
||||
if s != "" {
|
||||
s += " "
|
||||
}
|
||||
s += fmt.Sprintf("%d %s", int(p[i]), msg(c, key))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in new issue
Block a user