range.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package app
  2. import (
  3. "context"
  4. "io"
  5. "reflect"
  6. "sort"
  7. "github.com/maxence-charriere/go-app/v9/pkg/errors"
  8. )
  9. // RangeLoop represents a control structure that iterates within a slice, an
  10. // array or a map.
  11. type RangeLoop interface {
  12. UI
  13. // Slice sets the loop content by repeating the given function for the
  14. // number of elements in the source.
  15. //
  16. // It panics if the range source is not a slice or an array.
  17. Slice(f func(int) UI) RangeLoop
  18. // Map sets the loop content by repeating the given function for the number
  19. // of elements in the source. Elements are ordered by keys.
  20. //
  21. // It panics if the range source is not a map or if map keys are not strings.
  22. Map(f func(string) UI) RangeLoop
  23. }
  24. // Range returns a range loop that iterates within the given source. Source must
  25. // be a slice, an array or a map with strings as keys.
  26. func Range(src any) RangeLoop {
  27. return rangeLoop{source: src}
  28. }
  29. type rangeLoop struct {
  30. body []UI
  31. source any
  32. }
  33. func (r rangeLoop) Slice(f func(int) UI) RangeLoop {
  34. src := reflect.ValueOf(r.source)
  35. if src.Kind() != reflect.Slice && src.Kind() != reflect.Array {
  36. panic(errors.New("range loop source is not a slice or array").
  37. Tag("src-type", src.Type),
  38. )
  39. }
  40. body := make([]UI, 0, src.Len())
  41. for i := 0; i < src.Len(); i++ {
  42. body = append(body, FilterUIElems(f(i))...)
  43. }
  44. r.body = body
  45. return r
  46. }
  47. func (r rangeLoop) Map(f func(string) UI) RangeLoop {
  48. src := reflect.ValueOf(r.source)
  49. if src.Kind() != reflect.Map {
  50. panic(errors.New("range loop source is not a map").
  51. Tag("src-type", src.Type),
  52. )
  53. }
  54. if keyType := src.Type().Key(); keyType.Kind() != reflect.String {
  55. panic(errors.New("range loop source keys are not strings").
  56. Tag("src-type", src.Type).
  57. Tag("key-type", keyType),
  58. )
  59. }
  60. body := make([]UI, 0, src.Len())
  61. keys := make([]string, 0, src.Len())
  62. for _, k := range src.MapKeys() {
  63. keys = append(keys, k.String())
  64. }
  65. sort.Strings(keys)
  66. for _, k := range keys {
  67. body = append(body, FilterUIElems(f(k))...)
  68. }
  69. r.body = body
  70. return r
  71. }
  72. func (r rangeLoop) Kind() Kind {
  73. return Selector
  74. }
  75. func (r rangeLoop) JSValue() Value {
  76. return nil
  77. }
  78. func (r rangeLoop) Mounted() bool {
  79. return false
  80. }
  81. func (r rangeLoop) name() string {
  82. return "range"
  83. }
  84. func (r rangeLoop) self() UI {
  85. return r
  86. }
  87. func (r rangeLoop) setSelf(UI) {
  88. }
  89. func (r rangeLoop) getContext() context.Context {
  90. return nil
  91. }
  92. func (r rangeLoop) getDispatcher() Dispatcher {
  93. return nil
  94. }
  95. func (r rangeLoop) getAttributes() attributes {
  96. return nil
  97. }
  98. func (r rangeLoop) getEventHandlers() eventHandlers {
  99. return nil
  100. }
  101. func (r rangeLoop) getParent() UI {
  102. return nil
  103. }
  104. func (r rangeLoop) setParent(UI) {
  105. }
  106. func (r rangeLoop) getChildren() []UI {
  107. return r.body
  108. }
  109. func (r rangeLoop) mount(Dispatcher) error {
  110. return errors.New("range loop is not mountable").
  111. Tag("name", r.name()).
  112. Tag("kind", r.Kind())
  113. }
  114. func (r rangeLoop) dismount() {
  115. }
  116. func (r rangeLoop) canUpdateWith(UI) bool {
  117. return false
  118. }
  119. func (r rangeLoop) updateWith(UI) error {
  120. return errors.New("range loop cannot be updated").
  121. Tag("name", r.name()).
  122. Tag("kind", r.Kind())
  123. }
  124. func (r rangeLoop) preRender(Page) {
  125. }
  126. func (r rangeLoop) onComponentEvent(any) {
  127. }
  128. func (r rangeLoop) html(w io.Writer) {
  129. panic("should not be called")
  130. }
  131. func (r rangeLoop) htmlWithIndent(w io.Writer, indent int) {
  132. panic("should not be called")
  133. }