rui_orig/timePicker.go

425 lines
12 KiB
Go
Raw Normal View History

2021-09-07 17:36:50 +03:00
package rui
import (
"strconv"
"strings"
"time"
)
// Constants for [TimePicker] specific properties and events.
2021-09-07 17:36:50 +03:00
const (
// TimeChangedEvent is the constant for "time-changed" property tag.
//
// Used by `TimePicker`.
// Occur when current time of the time picker has been changed.
//
// General listener format:
// `func(picker rui.TimePicker, newTime, oldTime time.Time)`.
//
// where:
// picker - Interface of a time picker which generated this event,
// newTime - New time value,
// oldTime - Old time value.
//
// Allowed listener formats:
// `func(picker rui.TimePicker, newTime time.Time)`,
// `func(newTime, oldTime time.Time)`,
// `func(newTime time.Time)`,
// `func(picker rui.TimePicker)`,
// `func()`.
2024-11-13 12:56:39 +03:00
TimeChangedEvent PropertyName = "time-changed"
// TimePickerMin is the constant for "time-picker-min" property tag.
//
// Used by `TimePicker`.
// The minimum value of the time.
//
// Supported types: `time.Time`, `string`.
//
// Internal type is `time.Time`, other types converted to it during assignment.
//
// Conversion rules:
// `string` - values of this type parsed and converted to `time.Time`. The following formats are supported:
// "HH:MM:SS" - "08:15:00".
// "HH:MM:SS PM" - "08:15:00 AM".
// "HH:MM" - "08:15".
// "HH:MM PM" - "08:15 AM".
2024-11-13 12:56:39 +03:00
TimePickerMin PropertyName = "time-picker-min"
// TimePickerMax is the constant for "time-picker-max" property tag.
//
// Used by `TimePicker`.
// The maximum value of the time.
//
// Supported types: `time.Time`, `string`.
//
// Internal type is `time.Time`, other types converted to it during assignment.
//
// Conversion rules:
// `string` - values of this type parsed and converted to `time.Time`. The following formats are supported:
// "HH:MM:SS" - "08:15:00".
// "HH:MM:SS PM" - "08:15:00 AM".
// "HH:MM" - "08:15".
// "HH:MM PM" - "08:15 AM".
2024-11-13 12:56:39 +03:00
TimePickerMax PropertyName = "time-picker-max"
// TimePickerStep is the constant for "time-picker-step" property tag.
//
// Used by `TimePicker`.
// Time step in seconds.
//
// Supported types: `int`, `string`.
//
// Values:
// >= `0` or >= "0" - Step value in seconds used to increment or decrement time.
2024-11-13 12:56:39 +03:00
TimePickerStep PropertyName = "time-picker-step"
// TimePickerValue is the constant for "time-picker-value" property tag.
//
// Used by `TimePicker`.
// Current value.
//
// Supported types: `time.Time`, `string`.
//
// Internal type is `time.Time`, other types converted to it during assignment.
//
// Conversion rules:
// `string` - values of this type parsed and converted to `time.Time`. The following formats are supported:
// "HH:MM:SS" - "08:15:00".
// "HH:MM:SS PM" - "08:15:00 AM".
// "HH:MM" - "08:15".
// "HH:MM PM" - "08:15 AM".
2024-11-13 12:56:39 +03:00
TimePickerValue PropertyName = "time-picker-value"
timeFormat = "15:04:05"
2021-09-07 17:36:50 +03:00
)
// TimePicker represents a TimePicker view
2021-09-07 17:36:50 +03:00
type TimePicker interface {
View
}
type timePickerData struct {
viewData
}
// NewTimePicker create new TimePicker object and return it
func NewTimePicker(session Session, params Params) TimePicker {
view := new(timePickerData)
view.init(session)
2021-09-07 17:36:50 +03:00
setInitParams(view, params)
return view
}
func newTimePicker(session Session) View {
2024-11-13 12:56:39 +03:00
return new(timePickerData)
2021-09-07 17:36:50 +03:00
}
func (picker *timePickerData) init(session Session) {
picker.viewData.init(session)
2021-09-07 17:36:50 +03:00
picker.tag = "TimePicker"
2024-04-23 18:24:51 +03:00
picker.hasHtmlDisabled = true
2024-11-13 12:56:39 +03:00
picker.normalize = normalizeTimePickerTag
picker.set = picker.setFunc
picker.changed = picker.propertyChanged
2022-05-22 12:54:02 +03:00
}
func (picker *timePickerData) Focusable() bool {
return true
}
2024-11-13 12:56:39 +03:00
func normalizeTimePickerTag(tag PropertyName) PropertyName {
tag = defaultNormalize(tag)
2021-09-07 17:36:50 +03:00
switch tag {
case Type, Min, Max, Step, Value:
return "time-picker-" + tag
}
2024-11-13 12:56:39 +03:00
return normalizeDataListTag(tag)
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
func stringToTime(value string) (time.Time, bool) {
lowText := strings.ToUpper(value)
pm := strings.HasSuffix(lowText, "PM") || strings.HasSuffix(lowText, "AM")
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
var format string
switch len(strings.Split(value, ":")) {
case 2:
if pm {
format = "3:04 PM"
} else {
2024-11-13 12:56:39 +03:00
format = "15:04"
2024-05-18 18:57:41 +03:00
}
2021-09-07 17:36:50 +03:00
default:
2024-11-13 12:56:39 +03:00
if pm {
format = "03:04:05 PM"
} else {
format = "15:04:05"
}
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
result, err := time.Parse(format, value)
if err != nil {
ErrorLog(err.Error())
return time.Now(), false
}
return result, true
2021-09-07 17:36:50 +03:00
}
func (picker *timePickerData) setFunc(tag PropertyName, value any) []PropertyName {
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
setTimeValue := func(tag PropertyName) []PropertyName {
2021-09-07 17:36:50 +03:00
switch value := value.(type) {
case time.Time:
picker.setRaw(tag, value)
2024-11-13 12:56:39 +03:00
return []PropertyName{tag}
2021-09-07 17:36:50 +03:00
case string:
2024-11-13 12:56:39 +03:00
if isConstantName(value) {
picker.setRaw(tag, value)
2024-11-13 12:56:39 +03:00
return []PropertyName{tag}
}
2022-06-09 13:08:39 +03:00
2024-11-13 12:56:39 +03:00
if time, ok := stringToTime(value); ok {
picker.setRaw(tag, time)
2024-11-13 12:56:39 +03:00
return []PropertyName{tag}
2021-09-07 17:36:50 +03:00
}
}
notCompatibleType(tag, value)
2024-11-13 12:56:39 +03:00
return nil
2021-09-07 17:36:50 +03:00
}
switch tag {
case TimePickerMin:
2024-11-13 12:56:39 +03:00
return setTimeValue(TimePickerMin)
2021-09-07 17:36:50 +03:00
case TimePickerMax:
2024-11-13 12:56:39 +03:00
return setTimeValue(TimePickerMax)
2021-09-07 17:36:50 +03:00
case TimePickerStep:
return setIntProperty(picker, TimePickerStep, value)
2021-09-07 17:36:50 +03:00
case TimePickerValue:
picker.setRaw("old-time", GetTimePickerValue(picker))
2024-11-13 12:56:39 +03:00
return setTimeValue(tag)
2021-09-07 17:36:50 +03:00
case TimeChangedEvent:
return setTwoArgEventListener[TimePicker, time.Time](picker, tag, value)
2021-09-07 17:36:50 +03:00
2024-05-18 18:57:41 +03:00
case DataList:
return setDataList(picker, value, timeFormat)
2021-09-07 17:36:50 +03:00
}
return picker.viewData.setFunc(tag, value)
2021-09-07 17:36:50 +03:00
}
func (picker *timePickerData) propertyChanged(tag PropertyName) {
2024-11-13 12:56:39 +03:00
session := picker.Session()
2024-11-13 12:56:39 +03:00
2021-09-07 17:36:50 +03:00
switch tag {
2024-11-13 12:56:39 +03:00
case TimePickerMin:
if time, ok := GetTimePickerMin(picker); ok {
session.updateProperty(picker.htmlID(), "min", time.Format(timeFormat))
2024-11-13 12:56:39 +03:00
} else {
session.removeProperty(picker.htmlID(), "min")
2024-11-13 12:56:39 +03:00
}
case TimePickerMax:
if time, ok := GetTimePickerMax(picker); ok {
session.updateProperty(picker.htmlID(), "max", time.Format(timeFormat))
2024-11-13 12:56:39 +03:00
} else {
session.removeProperty(picker.htmlID(), "max")
2024-11-13 12:56:39 +03:00
}
case TimePickerStep:
if step := GetTimePickerStep(picker); step > 0 {
session.updateProperty(picker.htmlID(), "step", strconv.Itoa(step))
2024-11-13 12:56:39 +03:00
} else {
session.removeProperty(picker.htmlID(), "step")
2024-11-13 12:56:39 +03:00
}
case TimePickerValue:
value := GetTimePickerValue(picker)
session.callFunc("setInputValue", picker.htmlID(), value.Format(timeFormat))
2024-11-13 12:56:39 +03:00
if listeners := GetTimeChangedListeners(picker); len(listeners) > 0 {
2024-11-13 12:56:39 +03:00
oldTime := time.Now()
if val := picker.getRaw("old-time"); val != nil {
2024-11-13 12:56:39 +03:00
if time, ok := val.(time.Time); ok {
oldTime = time
}
}
for _, listener := range listeners {
listener(picker, value, oldTime)
2024-11-13 12:56:39 +03:00
}
}
2024-05-18 18:57:41 +03:00
2021-09-07 17:36:50 +03:00
default:
picker.viewData.propertyChanged(tag)
2021-09-07 17:36:50 +03:00
}
}
func (picker *timePickerData) htmlTag() string {
return "input"
}
2024-05-18 18:57:41 +03:00
func (picker *timePickerData) htmlSubviews(self View, buffer *strings.Builder) {
2024-11-13 12:56:39 +03:00
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
text, _ = session.resolveConstants(text)
if time, ok := stringToTime(text); ok {
return time.Format(timeFormat)
}
return text
})
2024-05-18 18:57:41 +03:00
}
2021-09-07 17:36:50 +03:00
func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder) {
picker.viewData.htmlProperties(self, buffer)
buffer.WriteString(` type="time"`)
if min, ok := getTimeProperty(picker, TimePickerMin, Min); ok {
buffer.WriteString(` min="`)
buffer.WriteString(min.Format(timeFormat))
buffer.WriteByte('"')
}
if max, ok := getTimeProperty(picker, TimePickerMax, Max); ok {
buffer.WriteString(` max="`)
buffer.WriteString(max.Format(timeFormat))
buffer.WriteByte('"')
}
if step, ok := intProperty(picker, TimePickerStep, picker.Session(), 0); ok && step > 0 {
buffer.WriteString(` step="`)
buffer.WriteString(strconv.Itoa(step))
buffer.WriteByte('"')
}
buffer.WriteString(` value="`)
buffer.WriteString(GetTimePickerValue(picker).Format(timeFormat))
2021-09-07 17:36:50 +03:00
buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
2022-04-15 15:41:44 +03:00
if picker.getRaw(ClickEvent) == nil {
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
}
2024-05-18 18:57:41 +03:00
2024-11-13 12:56:39 +03:00
dataListHtmlProperties(picker, buffer)
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
func (picker *timePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
2021-09-07 17:36:50 +03:00
switch command {
case "textChanged":
if text, ok := data.PropertyValue("text"); ok {
if value, err := time.Parse(timeFormat, text); err == nil {
oldValue := GetTimePickerValue(picker)
2021-09-07 17:36:50 +03:00
picker.properties[TimePickerValue] = value
if value != oldValue {
2024-11-13 12:56:39 +03:00
for _, listener := range GetTimeChangedListeners(picker) {
2023-04-23 18:54:53 +03:00
listener(picker, value, oldValue)
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
if listener, ok := picker.changeListener[TimePickerValue]; ok {
listener(picker, TimePickerValue)
}
2021-09-07 17:36:50 +03:00
}
}
}
return true
}
return picker.viewData.handleCommand(self, command, data)
}
2024-11-13 12:56:39 +03:00
func getTimeProperty(view View, mainTag, shortTag PropertyName) (time.Time, bool) {
2022-07-26 18:36:00 +03:00
valueToTime := func(value any) (time.Time, bool) {
2021-09-07 17:36:50 +03:00
if value != nil {
switch value := value.(type) {
case time.Time:
return value, true
case string:
if text, ok := view.Session().resolveConstants(value); ok {
2024-11-13 12:56:39 +03:00
if result, ok := stringToTime(text); ok {
2021-09-07 17:36:50 +03:00
return result, true
}
}
}
}
return time.Now(), false
}
if view != nil {
if result, ok := valueToTime(view.getRaw(mainTag)); ok {
return result, true
}
2024-11-13 12:56:39 +03:00
for _, tag := range []PropertyName{mainTag, shortTag} {
if value := valueFromStyle(view, tag); value != nil {
if result, ok := valueToTime(value); ok {
return result, true
}
2021-09-07 17:36:50 +03:00
}
}
}
return time.Now(), false
}
// GetTimePickerMin returns the min time of TimePicker subview and "true" as the second value if the min time is set,
// "false" as the second value otherwise.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
2021-09-07 17:36:50 +03:00
}
if view != nil {
return getTimeProperty(view, TimePickerMin, Min)
}
return time.Now(), false
}
// GetTimePickerMax returns the max time of TimePicker subview and "true" as the second value if the min time is set,
// "false" as the second value otherwise.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
2021-09-07 17:36:50 +03:00
}
if view != nil {
return getTimeProperty(view, TimePickerMax, Max)
}
return time.Now(), false
}
// GetTimePickerStep returns the time changing step in seconds of TimePicker subview.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTimePickerStep(view View, subviewID ...string) int {
2022-07-28 12:11:27 +03:00
return intStyledProperty(view, subviewID, TimePickerStep, 60)
2021-09-07 17:36:50 +03:00
}
// GetTimePickerValue returns the time of TimePicker subview.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTimePickerValue(view View, subviewID ...string) time.Time {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
2021-09-07 17:36:50 +03:00
}
if view == nil {
return time.Now()
}
time, _ := getTimeProperty(view, TimePickerValue, Value)
return time
}
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
// If there are no listeners then the empty list is returned
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
2023-04-23 18:54:53 +03:00
func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time, time.Time) {
return getTwoArgEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
2021-09-07 17:36:50 +03:00
}