github.com/vugu/vugu@v0.3.6-0.20240430171613-3f6f402e014b/vgform/util.go (about) 1 package vgform 2 3 import ( 4 "sort" 5 "strings" 6 7 "golang.org/x/text/cases" 8 "golang.org/x/text/language" 9 ) 10 11 // KeyLister provides a list keys as a string slice. 12 // Keys are used in the `value` attribute of HTML option tags (with a select). 13 type KeyLister interface { 14 KeyList() []string 15 } 16 17 // KeyListerFunc implements KeyLister as a function. 18 type KeyListerFunc func() []string 19 20 // KeyList implements the KeyLister interface. 21 func (f KeyListerFunc) KeyList() []string { return f() } 22 23 // TextMapper provides mapping from a key to the corresponding text. 24 // Text is used inside the contents of an HTML option tag (with a select). 25 // Text values are always HTML escaped. 26 type TextMapper interface { 27 TextMap(key string) string 28 } 29 30 // TextMapperFunc implements TextMapper as a function. 31 type TextMapperFunc func(key string) string 32 33 // TextMap implements the TextMapper interface. 34 func (f TextMapperFunc) TextMap(key string) string { return f(key) } 35 36 // SimpleTitle implements TextMapper by replacing '-' and '_' with a space and calling strings.Title. 37 var SimpleTitle = TextMapperFunc(func(key string) string { 38 c := cases.Title(language.Und) 39 return c.String(strings.NewReplacer("-", " ", "_", " ").Replace(key)) 40 }) 41 42 // Options is an interface with KeyList and TextMap. 43 // It is used to express the options for a select element. 44 // It intentionally does not support option groups or other 45 // advanced behavior as that can be accomplished using slots (TO BE IMPLEMENTED). 46 // Options is provided to make it easy for the common case of 47 // adapting a slice or map to be used as select options. 48 type Options interface { 49 KeyLister 50 TextMapper 51 } 52 53 // MapOptions implements the Options interface on a map[string]string. 54 // The keys will be returned in alphanumeric sequence (using sort.Strings), 55 // or you can call SortFunc to assign a custom sort function. 56 type MapOptions map[string]string 57 58 // KeyList implements KeyLister by returning the map keys sorted with sort.Strings(). 59 func (m MapOptions) KeyList() []string { 60 s := make([]string, 0, len(m)) 61 for k := range m { 62 s = append(s, k) 63 } 64 sort.Strings(s) 65 return s 66 } 67 68 // TextMap implements TextMapper by returning `m[key]`. 69 func (m MapOptions) TextMap(key string) string { return m[key] } 70 71 // SortFunc returns an Options instance that uses this map for 72 // keys and text and sorts according to the order specified by this 73 // function. 74 func (m MapOptions) SortFunc(sf func(i, j int) bool) Options { 75 return customOptions{ 76 KeyLister: KeyListerFunc(func() []string { 77 // build the key list directly, calling m.KeyList would call sort.Strings unnecessarily 78 s := make([]string, 0, len(m)) 79 for k := range m { 80 s = append(s, k) 81 } 82 sort.Slice(s, sf) 83 return s 84 }), 85 TextMapper: m, 86 } 87 } 88 89 // SliceOptions implements the Options interface on a []string. 90 // The slice specifies the sequence and these exact string keys are 91 // also used as the text. You can also call Title() to use the 92 // SimpleTitle mapper or use TextFunc to assign a custom text mapper. 93 type SliceOptions []string 94 95 // Title is shorthand for s.TextFunc(SimpleTitle). 96 func (s SliceOptions) Title() Options { 97 return s.TextFunc(SimpleTitle) 98 } 99 100 // KeyList implements KeyLister with a type conversion ([]string(s)). 101 func (s SliceOptions) KeyList() []string { return []string(s) } 102 103 // TextMap implements TextMapper by returning the key as the text. 104 func (s SliceOptions) TextMap(key string) string { return key } 105 106 // TextFunc returns an Options instance that uses this slice 107 // as the key list and the specified function for text mapping. 108 func (s SliceOptions) TextFunc(tmf TextMapperFunc) Options { 109 return customOptions{ 110 KeyLister: s, 111 TextMapper: tmf, 112 } 113 } 114 115 type customOptions struct { 116 KeyLister 117 TextMapper 118 }