package rui import ( "fmt" "strconv" "strings" "time" ) const ( // DataList is the constant for "data-list" property tag. // // Used by `ColorPicker`, `DatePicker`, `EditView`, `NumberPicker`, `TimePicker`. // // Usage in `ColorPicker`: // List of pre-defined colors. // // Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing // elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` … // `uint64`. // // Internal type is `[]string`, other types converted to it during assignment. // // Conversion rules: // `string` - contain single item. // `[]string` - an array of items. // `[]fmt.Stringer` - an array of objects convertible to a string. // `[]Color` - An array of color values which will be converted to a string array. // `[]SizeUnit` - an array of size unit values which will be converted to a string array. // `[]any` - this array must contain only types which were listed in Types section. // // Usage in `DatePicker`: // List of predefined dates. If we set this property, date picker may have a drop-down menu with a list of these values. // Some browsers may ignore this property, such as Safari for macOS. The value of this property must be an array of // strings in the format "YYYY-MM-DD". // // Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing // elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` … // `uint64`. // // Internal type is `[]string`, other types converted to it during assignment. // // Conversion rules: // `string` - contain single item. // `[]string` - an array of items. // `[]fmt.Stringer` - an array of objects convertible to a string. // `[]Color` - An array of color values which will be converted to a string array. // `[]SizeUnit` - an array of size unit values which will be converted to a string array. // `[]any` - this array must contain only types which were listed in Types section. // // Usage in `EditView`: // Array of recommended values. // // Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing // elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` … // `uint64`. // // Internal type is `[]string`, other types converted to it during assignment. // // Conversion rules: // `string` - contain single item. // `[]string` - an array of items. // `[]fmt.Stringer` - an array of objects convertible to a string. // `[]Color` - An array of color values which will be converted to a string array. // `[]SizeUnit` - an array of size unit values which will be converted to a string array. // `[]any` - this array must contain only types which were listed in Types section. // // Usage in `NumberPicker`: // Specify an array of recommended values. // // Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]float`, `[]int`, // `[]bool`, `[]any` containing elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` // … `int64`, `uint`, `uint8` … `uint64`. // // Internal type is `[]string`, other types converted to it during assignment. // // Conversion rules: // `string` - must contain integer or floating point number, converted to `[]string`. // `[]string` - an array of strings which must contain integer or floating point numbers, stored as is. // `[]fmt.Stringer` - object which implement this interface must contain integer or floating point numbers, converted to a `[]string`. // `[]Color` - an array of color values, converted to `[]string`. // `[]SizeUnit` - an array of size unit, converted to `[]string`. // `[]AngleUnit` - an array of angle unit, converted to `[]string`. // `[]float` - converted to `[]string`. // `[]int` - converted to `[]string`. // `[]bool` - converted to `[]string`. // `[]any` - an array which may contain types listed in Types section above, each value will be converted to a `string` and wrapped to array. // // Usage in `TimePicker`: // An array of recommended values. The value of this property must be an array of strings in the format "HH:MM:SS" or // "HH:MM". // // Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing // elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` … // `uint64`. // // Internal type is `[]string`, other types converted to it during assignment. // // Conversion rules: // `string` - contain single item. // `[]string` - an array of items. // `[]fmt.Stringer` - an array of objects convertible to a string. // `[]Color` - An array of color values which will be converted to a string array. // `[]SizeUnit` - an array of size unit values which will be converted to a string array. // `[]any` - this array must contain only types which were listed in Types section. DataList PropertyName = "data-list" ) func dataListID(view View) string { return view.htmlID() + "-datalist" } func normalizeDataListTag(tag PropertyName) PropertyName { switch tag { case "datalist": return DataList } return tag } func setDataList(properties Properties, value any, dateTimeFormat string) []PropertyName { if items, ok := anyToStringArray(value, dateTimeFormat); ok { properties.setRaw(DataList, items) return []PropertyName{DataList} } notCompatibleType(DataList, value) return nil } func anyToStringArray(value any, dateTimeFormat string) ([]string, bool) { switch value := value.(type) { case string: return []string{value}, true case []string: return value, true case []DataValue: items := make([]string, 0, len(value)) for _, val := range value { if !val.IsObject() { items = append(items, val.Value()) } } return items, true case []fmt.Stringer: items := make([]string, len(value)) for i, str := range value { items[i] = str.String() } return items, true case []Color: items := make([]string, len(value)) for i, str := range value { items[i] = str.String() } return items, true case []SizeUnit: items := make([]string, len(value)) for i, str := range value { items[i] = str.String() } return items, true case []AngleUnit: items := make([]string, len(value)) for i, str := range value { items[i] = str.String() } return items, true case []float32: items := make([]string, len(value)) for i, val := range value { items[i] = fmt.Sprintf("%g", float64(val)) } return items, true case []float64: items := make([]string, len(value)) for i, val := range value { items[i] = fmt.Sprintf("%g", val) } return items, true case []int: return intArrayToStringArray(value), true case []uint: return intArrayToStringArray(value), true case []int8: return intArrayToStringArray(value), true case []uint8: return intArrayToStringArray(value), true case []int16: return intArrayToStringArray(value), true case []uint16: return intArrayToStringArray(value), true case []int32: return intArrayToStringArray(value), true case []uint32: return intArrayToStringArray(value), true case []int64: return intArrayToStringArray(value), true case []uint64: return intArrayToStringArray(value), true case []bool: items := make([]string, len(value)) for i, val := range value { if val { items[i] = "true" } else { items[i] = "false" } } return items, true case []time.Time: if dateTimeFormat == "" { dateTimeFormat = dateFormat + " " + timeFormat } items := make([]string, len(value)) for i, val := range value { items[i] = val.Format(dateTimeFormat) } return items, true case []any: items := make([]string, 0, len(value)) for _, v := range value { switch val := v.(type) { case string: items = append(items, val) case fmt.Stringer: items = append(items, val.String()) case bool: if val { items = append(items, "true") } else { items = append(items, "false") } case float32: items = append(items, fmt.Sprintf("%g", float64(val))) case float64: items = append(items, fmt.Sprintf("%g", val)) case rune: items = append(items, string(val)) default: if n, ok := isInt(v); ok { items = append(items, strconv.Itoa(n)) } else { return []string{}, false } } } return items, true } return []string{}, false } func getDataListProperty(properties Properties) []string { if value := properties.getRaw(DataList); value != nil { if items, ok := value.([]string); ok { return items } } return nil } func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func(text string, session Session) string) { if items := getDataListProperty(view); len(items) > 0 { session := view.Session() buffer.WriteString(``) for _, text := range items { text = normalizeItem(text, session) if strings.ContainsRune(text, '"') { text = strings.ReplaceAll(text, `"`, `"`) } if strings.ContainsRune(text, '\n') { text = strings.ReplaceAll(text, "\n", `\n`) } buffer.WriteString(``) } buffer.WriteString(``) } } func dataListHtmlProperties(view View, buffer *strings.Builder) { if len(getDataListProperty(view)) > 0 { buffer.WriteString(` list="`) buffer.WriteString(dataListID(view)) buffer.WriteString(`"`) } } /* func (list *dataList) setDataList(view View, value any, created bool) bool { items, ok := anyToStringArray(value) if !ok { notCompatibleType(DataList, value) return false } list.dataList = items if created { session := view.Session() dataListID := dataListID(view) buffer := allocStringBuilder() defer freeStringBuilder(buffer) if list.dataListHtml { list.dataListItemsHtml(buffer) session.updateInnerHTML(dataListID, buffer.String()) } else { list.dataListHtmlCode(view, buffer) session.appendToInnerHTML(view.parentHTMLID(), buffer.String()) list.dataListHtml = true session.updateProperty(view.htmlID(), "list", dataListID) } } return true } func (list *dataList) dataListHtmlSubviews(view View, buffer *strings.Builder) { if len(list.dataList) > 0 { list.dataListHtmlCode(view, buffer) list.dataListHtml = true } else { list.dataListHtml = false } } func (list *dataList) dataListHtmlCode(view View, buffer *strings.Builder) { buffer.WriteString(``) list.dataListItemsHtml(buffer) buffer.WriteString(``) } func (list *dataList) dataListItemsHtml(buffer *strings.Builder) { for _, text := range list.dataList { if strings.ContainsRune(text, '"') { text = strings.ReplaceAll(text, `"`, `"`) } if strings.ContainsRune(text, '\n') { text = strings.ReplaceAll(text, "\n", `\n`) } buffer.WriteString(``) } } func (list *dataList) dataListHtmlProperties(view View, buffer *strings.Builder) { if len(list.dataList) > 0 { buffer.WriteString(` list="`) buffer.WriteString(dataListID(view)) buffer.WriteString(`"`) } } */ // GetDataList returns the data list of an editor. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetDataList(view View, subviewID ...string) []string { if len(subviewID) > 0 && subviewID[0] != "" { view = ViewByID(view, subviewID[0]) } if view != nil { return getDataListProperty(view) } return []string{} }