package input import ( "fmt" "strconv" "github.com/vugu/vugu" ) // Field is a Any text or number based input component. // The HTML input type is determined by the bound data type. type Field struct { AttrMap vugu.AttrMap Bind FieldBinder DefaultSlot vugu.Builder err error // Current error caused by any invalid input. } func (c *Field) content() string { if c.Bind != nil { return c.Bind.String() } return "" } func (c *Field) inputType() string { if c.Bind != nil { return c.Bind.HTMLInputType() } return "" } func (c *Field) handleChange(event vugu.DOMEvent) { val := event.PropString("target", "value") if c.Bind != nil { c.err = c.Bind.SetString(val) } } type FieldBinder interface { String() string // String returns the bound variable formatted in the users/current locale. SetString(string) error // SetString parses the string value in the users/current locale and sets the value of the bound variable. HTMLInputType() string // HTMLInputType returns the to be used type parameter for the HTML input element. } // FieldBindAny implements the FieldBinder interface for all basic types. type FieldBindAny struct{ Value any } func (f FieldBindAny) String() string { switch v := f.Value.(type) { case *string: return *v case *int: return strconv.FormatInt(int64(*v), 10) case *int8: return strconv.FormatInt(int64(*v), 10) case *int16: return strconv.FormatInt(int64(*v), 10) case *int32: return strconv.FormatInt(int64(*v), 10) case *int64: return strconv.FormatInt(int64(*v), 10) case *uint: return strconv.FormatUint(uint64(*v), 10) case *uint8: return strconv.FormatUint(uint64(*v), 10) case *uint16: return strconv.FormatUint(uint64(*v), 10) case *uint32: return strconv.FormatUint(uint64(*v), 10) case *uint64: return strconv.FormatUint(uint64(*v), 10) case *float32: return strconv.FormatFloat(float64(*v), 'f', -1, 32) // TODO: Format number in current user's locale case *float64: return strconv.FormatFloat(float64(*v), 'f', -1, 64) } return "" } func (f FieldBindAny) SetString(value string) error { switch v := f.Value.(type) { case *string: *v = value case *int: val, err := strconv.ParseInt(value, 10, 0) if err != nil { return err } *v = int(val) case *int8: val, err := strconv.ParseInt(value, 10, 8) if err != nil { return err } *v = int8(val) case *int16: val, err := strconv.ParseInt(value, 10, 16) if err != nil { return err } *v = int16(val) case *int32: val, err := strconv.ParseInt(value, 10, 32) if err != nil { return err } *v = int32(val) case *int64: val, err := strconv.ParseInt(value, 10, 64) if err != nil { return err } *v = int64(val) case *uint: val, err := strconv.ParseUint(value, 10, 0) if err != nil { return err } *v = uint(val) case *uint8: val, err := strconv.ParseUint(value, 10, 8) if err != nil { return err } *v = uint8(val) case *uint16: val, err := strconv.ParseUint(value, 10, 16) if err != nil { return err } *v = uint16(val) case *uint32: val, err := strconv.ParseUint(value, 10, 32) if err != nil { return err } *v = uint32(val) case *uint64: val, err := strconv.ParseUint(value, 10, 64) if err != nil { return err } *v = uint64(val) case *float32: val, err := strconv.ParseFloat(value, 32) if err != nil { return err } *v = float32(val) case *float64: val, err := strconv.ParseFloat(value, 64) if err != nil { return err } *v = float64(val) default: return fmt.Errorf("bound type %T is not supported", f.Value) } return nil } func (f FieldBindAny) HTMLInputType() string { switch f.Value.(type) { case *string: return "text" case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64: return "number" case *float32, *float64: return "number" } return "text" }