todo.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // +build js
  2. package views
  3. import (
  4. "fmt"
  5. "github.com/cryptix/exp/humbleRpcTodo/types"
  6. "github.com/soroushjp/humble"
  7. "github.com/soroushjp/humble/view"
  8. "honnef.co/go/js/dom"
  9. )
  10. type Todo struct {
  11. humble.Identifier
  12. Model *types.Todo
  13. Parent *App
  14. }
  15. func (t *Todo) RenderHTML() string {
  16. return fmt.Sprintf(`<li class="todo-list-item %s">
  17. <input class="toggle" type="checkbox" %s>
  18. <label class="todo-label">%s</label>
  19. <button class="destroy"></button>
  20. <input class="edit" onfocus="this.value = this.value;" value="%s">
  21. </li>`,
  22. t.Model.CompletedStr(), t.Model.CheckedStr(), t.Model.Title, t.Model.Title)
  23. }
  24. func (t *Todo) OnLoad() error {
  25. if err := view.AddListener(t, "button.destroy", "click", t.deleteButtonClicked); err != nil {
  26. return err
  27. }
  28. if err := view.AddListener(t, "label.todo-label", "dblclick", t.todoDoubleClick); err != nil {
  29. return err
  30. }
  31. if err := view.AddListener(t, "input.edit", "keyup", t.todoEditKeyUp); err != nil {
  32. return err
  33. }
  34. if err := view.AddListener(t, "input.toggle", "click", t.checkboxClicked); err != nil {
  35. return err
  36. }
  37. if err := view.AddListener(t, "input.edit", "blur", t.todoEditBlurred); err != nil {
  38. return err
  39. }
  40. return nil
  41. }
  42. func (t *Todo) OuterTag() string {
  43. return "div"
  44. }
  45. func (t *Todo) todoEditKeyUp(event dom.Event) {
  46. // If key is not Enter or Escape, we keep label and input.edit in sync but otherwise just return
  47. key := event.(*dom.KeyboardEvent).KeyCode
  48. // Grab contents of input.edit
  49. inputEdit, err := view.QuerySelector(t, "input.edit")
  50. if err != nil {
  51. panic(err)
  52. }
  53. title := inputEdit.Underlying().Get("value").String()
  54. if key != EnterKey && key != EscapeKey {
  55. // Change everything in label to match input.edit
  56. label, err := view.QuerySelector(t, "label.todo-label")
  57. if err != nil {
  58. panic(err)
  59. }
  60. label.SetInnerHTML(title)
  61. return
  62. }
  63. // If Escape or Enter key is entered, we want to get out of input.edit field
  64. if key == EscapeKey || key == EnterKey {
  65. t.removeEditTodo()
  66. }
  67. // If Enter key is pressed, we want to save to model
  68. if key == EnterKey {
  69. t.Model.Title = title
  70. if err := t.Parent.Client.Call("TodoService.Save", t.Model, t.Model); err != nil {
  71. panic(err)
  72. }
  73. }
  74. }
  75. func (t *Todo) todoEditBlurred(dom.Event) {
  76. t.removeEditTodo()
  77. }
  78. func (t *Todo) removeEditTodo() {
  79. label, err := view.QuerySelector(t, "label.todo-label")
  80. if err != nil {
  81. panic(err)
  82. }
  83. todoItem, err := view.QuerySelector(t, "li.todo-list-item")
  84. if err != nil {
  85. panic(err)
  86. }
  87. //Remove 'editing' class to todoItem to make it disappear
  88. todoItem.Class().Remove("editing")
  89. // Show our label while input.edit is open
  90. label.(*dom.HTMLLabelElement).Style().SetProperty("display", "block", "important")
  91. }
  92. func (t *Todo) todoDoubleClick(dom.Event) {
  93. // Get elements
  94. label, err := view.QuerySelector(t, "label.todo-label")
  95. if err != nil {
  96. panic(err)
  97. }
  98. todoItem, err := view.QuerySelector(t, "li.todo-list-item")
  99. if err != nil {
  100. panic(err)
  101. }
  102. inputEdit, err := view.QuerySelector(t, "input.edit")
  103. if err != nil {
  104. panic(err)
  105. }
  106. // Hide our label while input.edit is open
  107. label.(*dom.HTMLLabelElement).Style().SetProperty("display", "none", "important")
  108. // Append 'editing' class to todoItem to make it an editable input field
  109. todoItem.Class().Add("editing")
  110. // Set focus to input.edit field
  111. inputEdit.(dom.HTMLElement).Focus()
  112. }
  113. func (t *Todo) deleteButtonClicked(dom.Event) {
  114. t.remove()
  115. }
  116. func (t *Todo) remove() {
  117. if err := view.Remove(t); err != nil {
  118. panic(err)
  119. }
  120. if err := t.Parent.Client.Call("TodoService.Delete", t.Model.Id, nil); err != nil {
  121. panic(err)
  122. }
  123. t.Parent.removeChild(t)
  124. }
  125. func (t *Todo) checkboxClicked(event dom.Event) {
  126. isChecked := event.Target().(*dom.HTMLInputElement).Checked
  127. t.setComplete(isChecked)
  128. if err := view.Update(t.Parent.Footer); err != nil {
  129. panic(err)
  130. }
  131. if t.Parent.CurrentFilter == FilterActive {
  132. switch isChecked {
  133. case true:
  134. // If we are only showing active todos and we just completed this one,
  135. // hide it
  136. if err := view.Hide(t); err != nil {
  137. panic(err)
  138. }
  139. case false:
  140. // If we are only showing active todos and we just uncompleted this on,
  141. // show it.
  142. if err := view.Show(t); err != nil {
  143. panic(err)
  144. }
  145. }
  146. } else if t.Parent.CurrentFilter == FilterCompleted {
  147. switch isChecked {
  148. case true:
  149. // If we are only showing completed todos and we just completed this on,
  150. // show it.
  151. if err := view.Show(t); err != nil {
  152. panic(err)
  153. }
  154. case false:
  155. // If we are only showing completed todos and we just uncompleted this on,
  156. // hide it.
  157. if err := view.Hide(t); err != nil {
  158. panic(err)
  159. }
  160. }
  161. }
  162. }
  163. func (t *Todo) setComplete(isCompleted bool) {
  164. t.Model.IsCompleted = isCompleted
  165. if err := t.Parent.Client.Call("TodoService.Save", t.Model, &t.Model); err != nil {
  166. panic(err)
  167. }
  168. view.Update(t)
  169. }