attribute.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package app
  2. import (
  3. "strconv"
  4. "strings"
  5. )
  6. type attributes map[string]string
  7. func (a attributes) Set(name string, value any) {
  8. switch name {
  9. case "style", "allow":
  10. a[name] += toAttributeValue(value) + ";"
  11. case "class":
  12. s := a[name]
  13. if s != "" {
  14. s += " "
  15. }
  16. s += toAttributeValue(value)
  17. a[name] = s
  18. default:
  19. a[name] = toAttributeValue(value)
  20. }
  21. }
  22. func (a attributes) Mount(jsElement Value, resolveURL attributeURLResolver) {
  23. for name, value := range a {
  24. setJSAttribute(jsElement, name, resolveAttributeURLValue(
  25. name,
  26. value,
  27. resolveURL,
  28. ))
  29. }
  30. }
  31. func (a attributes) Update(jsElement Value, b attributes, resolveURL attributeURLResolver) {
  32. for name := range a {
  33. if _, ok := b[name]; !ok {
  34. deleteJSAttribute(jsElement, name)
  35. delete(a, name)
  36. }
  37. }
  38. for name, value := range b {
  39. if a[name] == value {
  40. continue
  41. }
  42. a[name] = value
  43. setJSAttribute(jsElement, name, resolveAttributeURLValue(
  44. name,
  45. value,
  46. resolveURL,
  47. ))
  48. }
  49. }
  50. type attributeURLResolver func(string) string
  51. func toAttributeValue(v any) string {
  52. return strings.TrimSpace(toString(v))
  53. }
  54. func resolveAttributeURLValue(name, value string, resolve attributeURLResolver) string {
  55. switch name {
  56. case "cite",
  57. "data",
  58. "href",
  59. "src",
  60. "srcset":
  61. return resolve(value)
  62. default:
  63. return value
  64. }
  65. }
  66. func setJSAttribute(jsElement Value, name, value string) {
  67. toBool := func(v string) bool {
  68. b, _ := strconv.ParseBool(v)
  69. return b
  70. }
  71. switch name {
  72. case "value":
  73. jsElement.Set(name, value)
  74. case "class":
  75. jsElement.Set("className", value)
  76. case "contenteditable":
  77. jsElement.Set("contentEditable", value)
  78. case "ismap":
  79. jsElement.Set("isMap", toBool(value))
  80. case "readonly":
  81. jsElement.Set("readOnly", toBool(value))
  82. case "async",
  83. "autofocus",
  84. "autoplay",
  85. "checked",
  86. "default",
  87. "defer",
  88. "disabled",
  89. "hidden",
  90. "loop",
  91. "multiple",
  92. "muted",
  93. "open",
  94. "required",
  95. "reversed",
  96. "selected":
  97. jsElement.Set(name, toBool(value))
  98. default:
  99. jsElement.Call("setAttribute", name, value)
  100. }
  101. }
  102. func deleteJSAttribute(jsElement Value, name string) {
  103. jsElement.Call("removeAttribute", name)
  104. }