raw.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package app
  2. import (
  3. "context"
  4. "io"
  5. "strings"
  6. "github.com/maxence-charriere/go-app/v9/pkg/errors"
  7. )
  8. // Raw returns a ui element from the given raw value. HTML raw value must have a
  9. // single root.
  10. //
  11. // It is not recommended to use this kind of node since there is no check on the
  12. // raw string content.
  13. func Raw(v string) UI {
  14. v = strings.TrimSpace(v)
  15. tag := rawRootTagName(v)
  16. if tag == "" {
  17. v = "<div></div>"
  18. }
  19. return &raw{
  20. value: v,
  21. tag: tag,
  22. }
  23. }
  24. type raw struct {
  25. disp Dispatcher
  26. jsvalue Value
  27. parentElem UI
  28. tag string
  29. value string
  30. }
  31. func (r *raw) Kind() Kind {
  32. return RawHTML
  33. }
  34. func (r *raw) JSValue() Value {
  35. return r.jsvalue
  36. }
  37. func (r *raw) Mounted() bool {
  38. return r.jsvalue != nil && r.getDispatcher() != nil
  39. }
  40. func (r *raw) name() string {
  41. return "raw." + r.tag
  42. }
  43. func (r *raw) self() UI {
  44. return r
  45. }
  46. func (r *raw) setSelf(UI) {
  47. }
  48. func (r *raw) getContext() context.Context {
  49. return nil
  50. }
  51. func (r *raw) getDispatcher() Dispatcher {
  52. return r.disp
  53. }
  54. func (r *raw) getAttributes() attributes {
  55. return nil
  56. }
  57. func (r *raw) getEventHandlers() eventHandlers {
  58. return nil
  59. }
  60. func (r *raw) getParent() UI {
  61. return r.parentElem
  62. }
  63. func (r *raw) setParent(p UI) {
  64. r.parentElem = p
  65. }
  66. func (r *raw) getChildren() []UI {
  67. return nil
  68. }
  69. func (r *raw) mount(d Dispatcher) error {
  70. if r.Mounted() {
  71. return errors.New("mounting raw html element failed").
  72. Tag("reason", "already mounted").
  73. Tag("name", r.name()).
  74. Tag("kind", r.Kind())
  75. }
  76. r.disp = d
  77. wrapper, err := Window().createElement("div", "")
  78. if err != nil {
  79. return errors.New("creating raw node wrapper failed").Wrap(err)
  80. }
  81. if IsServer {
  82. r.jsvalue = wrapper
  83. return nil
  84. }
  85. wrapper.setInnerHTML(r.value)
  86. value := wrapper.firstChild()
  87. if !value.Truthy() {
  88. return errors.New("mounting raw html element failed").
  89. Tag("reason", "converting raw html to html elements returned nil").
  90. Tag("name", r.name()).
  91. Tag("kind", r.Kind()).
  92. Tag("raw-html", r.value)
  93. }
  94. wrapper.removeChild(value)
  95. r.jsvalue = value
  96. return nil
  97. }
  98. func (r *raw) dismount() {
  99. r.jsvalue = nil
  100. }
  101. func (r *raw) canUpdateWith(n UI) bool {
  102. if n, ok := n.(*raw); ok {
  103. return r.value == n.value
  104. }
  105. return false
  106. }
  107. func (r *raw) updateWith(n UI) error {
  108. return nil
  109. }
  110. func (r *raw) preRender(Page) {
  111. }
  112. func (r *raw) onComponentEvent(any) {
  113. }
  114. func (r *raw) html(w io.Writer) {
  115. w.Write([]byte(r.value))
  116. }
  117. func (r *raw) htmlWithIndent(w io.Writer, indent int) {
  118. writeIndent(w, indent)
  119. w.Write([]byte(r.value))
  120. }
  121. func rawRootTagName(raw string) string {
  122. raw = strings.TrimSpace(raw)
  123. if strings.HasPrefix(raw, "</") || !strings.HasPrefix(raw, "<") {
  124. return ""
  125. }
  126. end := -1
  127. for i := 1; i < len(raw); i++ {
  128. if raw[i] == ' ' ||
  129. raw[i] == '\t' ||
  130. raw[i] == '\n' ||
  131. raw[i] == '>' {
  132. end = i
  133. break
  134. }
  135. }
  136. if end <= 0 {
  137. return ""
  138. }
  139. return raw[1:end]
  140. }