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  }