package rui import ( "strconv" "strings" ) // DropDownEvent is the constant for "drop-down-event" property tag. // // Used by `DropDownList`. // Occur when a list item becomes selected. // // General listener format: // `func(list rui.DropDownList, index int)`. // // where: // list - Interface of a drop down list which generated this event, // index - Index of a newly selected item. // // Allowed listener formats: const DropDownEvent PropertyName = "drop-down-event" // DropDownList represent a DropDownList view type DropDownList interface { View } type dropDownListData struct { viewData } // NewDropDownList create new DropDownList object and return it func NewDropDownList(session Session, params Params) DropDownList { view := new(dropDownListData) view.init(session) setInitParams(view, params) return view } func newDropDownList(session Session) View { return new(dropDownListData) } func (list *dropDownListData) init(session Session) { list.viewData.init(session) list.tag = "DropDownList" list.hasHtmlDisabled = true list.normalize = normalizeDropDownListTag list.set = list.setFunc list.changed = list.propertyChanged } func (list *dropDownListData) Focusable() bool { return true } func normalizeDropDownListTag(tag PropertyName) PropertyName { tag = defaultNormalize(tag) if tag == "separators" { return ItemSeparators } return tag } func (list *dropDownListData) setFunc(tag PropertyName, value any) []PropertyName { switch tag { case Items: if items, ok := anyToStringArray(value, ""); ok { return setArrayPropertyValue(list, tag, items) } notCompatibleType(Items, value) return nil case DisabledItems, ItemSeparators: if items, ok := parseIndicesArray(value); ok { return setArrayPropertyValue(list, tag, items) } notCompatibleType(tag, value) return nil case DropDownEvent: return setTwoArgEventListener[DropDownList, int](list, tag, value) case Current: list.setRaw("old-current", GetCurrent(list)) return setIntProperty(list, Current, value) } return list.viewData.setFunc(tag, value) } func (list *dropDownListData) propertyChanged(tag PropertyName) { switch tag { case Items, DisabledItems, ItemSeparators: updateInnerHTML(list.htmlID(), list.Session()) case Current: current := GetCurrent(list) list.Session().callFunc("selectDropDownListItem", list.htmlID(), current) oldCurrent, _ := intProperty(list, "old-current", list.Session(), -1) for _, listener := range GetDropDownListeners(list) { listener(list, current, oldCurrent) } default: list.viewData.propertyChanged(tag) } } func intArrayToStringArray[T int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](array []T) []string { items := make([]string, len(array)) for i, val := range array { items[i] = strconv.Itoa(int(val)) } return items } func parseIndicesArray(value any) ([]any, bool) { switch value := value.(type) { case int: return []any{value}, true case []int: items := make([]any, len(value)) for i, n := range value { items[i] = n } return items, true case []any: items := make([]any, 0, len(value)) for _, val := range value { if val != nil { switch val := val.(type) { case string: if isConstantName(val) { items = append(items, val) } else if n, err := strconv.Atoi(val); err == nil { items = append(items, n) } else { return nil, false } default: if n, ok := isInt(val); ok { items = append(items, n) } else { return nil, false } } } } return items, true case []string: items := make([]any, 0, len(value)) for _, str := range value { if str = strings.Trim(str, " \t"); str != "" { if isConstantName(str) { items = append(items, str) } else if n, err := strconv.Atoi(str); err == nil { items = append(items, n) } else { return nil, false } } } return items, true case string: return parseIndicesArray(strings.Split(value, ",")) case []DataValue: items := make([]string, 0, len(value)) for _, val := range value { if !val.IsObject() { items = append(items, val.Value()) } } return parseIndicesArray(items) } return nil, false } func (list *dropDownListData) htmlTag() string { return "select" } func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) { if items := GetDropDownItems(list); len(items) > 0 { current := GetCurrent(list) notTranslate := GetNotTranslate(list) disabledItems := GetDropDownDisabledItems(list) separators := GetDropDownItemSeparators(list) for i, item := range items { disabled := false for _, index := range disabledItems { if i == index { disabled = true break } } if disabled { buffer.WriteString("") for _, index := range separators { if i == index { buffer.WriteString("