node.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package app
  2. import (
  3. "context"
  4. "io"
  5. "reflect"
  6. "strings"
  7. )
  8. // UI is the interface that describes a user interface element such as
  9. // components and HTML elements.
  10. type UI interface {
  11. // Kind represents the specific kind of a UI element.
  12. Kind() Kind
  13. // JSValue returns the javascript value linked to the element.
  14. JSValue() Value
  15. // Reports whether the element is mounted.
  16. Mounted() bool
  17. name() string
  18. self() UI
  19. setSelf(UI)
  20. getContext() context.Context
  21. getDispatcher() Dispatcher
  22. getAttributes() attributes
  23. getEventHandlers() eventHandlers
  24. getParent() UI
  25. setParent(UI)
  26. getChildren() []UI
  27. mount(Dispatcher) error
  28. dismount()
  29. canUpdateWith(UI) bool
  30. updateWith(UI) error
  31. preRender(Page)
  32. onComponentEvent(any)
  33. html(w io.Writer)
  34. htmlWithIndent(w io.Writer, indent int)
  35. }
  36. // Kind represents the specific kind of a user interface element.
  37. type Kind uint
  38. func (k Kind) String() string {
  39. switch k {
  40. case SimpleText:
  41. return "text"
  42. case HTML:
  43. return "html"
  44. case Component:
  45. return "component"
  46. case Selector:
  47. return "selector"
  48. case RawHTML:
  49. return "raw"
  50. default:
  51. return "undefined"
  52. }
  53. }
  54. const (
  55. // UndefinedElem represents an undefined UI element.
  56. UndefinedElem Kind = iota
  57. // SimpleText represents a simple text element.
  58. SimpleText
  59. // HTML represents an HTML element.
  60. HTML
  61. // Component represents a customized, independent and reusable UI element.
  62. Component
  63. // Selector represents an element that is used to select a subset of
  64. // elements within a given list.
  65. Selector
  66. // RawHTML represents an HTML element obtained from a raw HTML code snippet.
  67. RawHTML
  68. )
  69. // FilterUIElems returns a filtered version of the given UI elements where
  70. // selector elements such as If and Range are interpreted and removed. It also
  71. // remove nil elements.
  72. //
  73. // It should be used only when implementing components that can accept content
  74. // with variadic arguments like HTML elements Body method.
  75. func FilterUIElems(v ...UI) []UI {
  76. if len(v) == 0 {
  77. return nil
  78. }
  79. remove := func(i int) {
  80. copy(v[i:], v[i+1:])
  81. v[len(v)-1] = nil
  82. v = v[:len(v)-1]
  83. }
  84. var b []UI
  85. replaceAt := func(i int, s ...UI) {
  86. b = append(b, v[i+1:]...)
  87. v = append(v[:i], s...)
  88. v = append(v, b...)
  89. b = b[:0]
  90. }
  91. for i := len(v) - 1; i >= 0; i-- {
  92. e := v[i]
  93. if ev := reflect.ValueOf(e); e == nil || ev.Kind() == reflect.Pointer && ev.IsNil() {
  94. remove(i)
  95. continue
  96. }
  97. switch e.Kind() {
  98. case SimpleText, HTML, Component, RawHTML:
  99. case Selector:
  100. replaceAt(i, e.getChildren()...)
  101. default:
  102. remove(i)
  103. }
  104. }
  105. return v
  106. }
  107. func mount(d Dispatcher, n UI) error {
  108. n.setSelf(n)
  109. return n.mount(d)
  110. }
  111. func dismount(n UI) {
  112. n.dismount()
  113. n.setSelf(nil)
  114. }
  115. func canUpdate(a, b UI) bool {
  116. a.setSelf(a)
  117. b.setSelf(b)
  118. return a.canUpdateWith(b)
  119. }
  120. func update(a, b UI) error {
  121. a.setSelf(a)
  122. b.setSelf(b)
  123. return a.updateWith(b)
  124. }
  125. // HTMLString return an HTML string representation of the given UI element.
  126. func HTMLString(ui UI) string {
  127. var w strings.Builder
  128. PrintHTML(&w, ui)
  129. return w.String()
  130. }
  131. // HTMLStringWithIndent return an indented HTML string representation of the
  132. // given UI element.
  133. func HTMLStringWithIndent(ui UI) string {
  134. var w strings.Builder
  135. PrintHTMLWithIndent(&w, ui)
  136. return w.String()
  137. }
  138. // PrintHTML writes an HTML representation of the UI element into the given
  139. // writer.
  140. func PrintHTML(w io.Writer, ui UI) {
  141. if !ui.Mounted() {
  142. ui.setSelf(ui)
  143. }
  144. ui.html(w)
  145. }
  146. // PrintHTMLWithIndent writes an idented HTML representation of the UI element
  147. // into the given writer.
  148. func PrintHTMLWithIndent(w io.Writer, ui UI) {
  149. if !ui.Mounted() {
  150. ui.setSelf(ui)
  151. }
  152. ui.htmlWithIndent(w, 0)
  153. }