todomvc.html 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <script src="../../dist/vue-vapor.global.js"></script>
  2. <link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css">
  3. <div id="app"></div>
  4. <script>
  5. const { createVaporApp, defineComponent, reactive, computed, watchEffect, onMounted, onUnmounted, nextTick } = VueVapor
  6. const STORAGE_KEY = 'todos-vuejs-3.x'
  7. const todoStorage = {
  8. fetch () {
  9. const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
  10. todos.forEach((todo, index) => {
  11. todo.id = index
  12. })
  13. todoStorage.uid = todos.length
  14. return todos
  15. },
  16. save (todos) {
  17. localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
  18. }
  19. }
  20. const filters = {
  21. all (todos) {
  22. return todos
  23. },
  24. active (todos) {
  25. return todos.filter((todo) => {
  26. return !todo.completed
  27. })
  28. },
  29. completed (todos) {
  30. return todos.filter(function (todo) {
  31. return todo.completed
  32. })
  33. }
  34. }
  35. function pluralize (n) {
  36. return n === 1 ? 'item' : 'items'
  37. }
  38. const _sfc_main = defineComponent({
  39. setup() {
  40. const state = reactive({
  41. todos: todoStorage.fetch(),
  42. editedTodo: null,
  43. newTodo: '',
  44. beforeEditCache: '',
  45. visibility: 'all',
  46. remaining: computed(() => {
  47. return filters.active(state.todos).length
  48. }),
  49. remainingText: computed(() => {
  50. return ` ${pluralize(state.remaining)} left`
  51. }),
  52. filteredTodos: computed(() => {
  53. return filters[state.visibility](state.todos)
  54. }),
  55. allDone: computed({
  56. get: function () {
  57. return state.remaining === 0
  58. },
  59. set: function (value) {
  60. state.todos.forEach(todo => {
  61. todo.completed = value
  62. })
  63. },
  64. }),
  65. })
  66. watchEffect(() => {
  67. todoStorage.save(state.todos)
  68. })
  69. onMounted(() => {
  70. window.addEventListener('hashchange', onHashChange)
  71. onHashChange()
  72. })
  73. onUnmounted(() => {
  74. window.removeEventListener('hashchange', onHashChange)
  75. })
  76. function onHashChange() {
  77. const visibility = window.location.hash.replace(/#\/?/, '')
  78. if (filters[visibility]) {
  79. state.visibility = visibility
  80. } else {
  81. window.location.hash = ''
  82. state.visibility = 'all'
  83. }
  84. }
  85. function addTodo() {
  86. const value = state.newTodo && state.newTodo.trim()
  87. if (!value) {
  88. return
  89. }
  90. state.todos.push({
  91. id: todoStorage.uid++,
  92. title: value,
  93. completed: false,
  94. })
  95. state.newTodo = ''
  96. }
  97. function removeTodo(todo) {
  98. state.todos.splice(state.todos.indexOf(todo), 1)
  99. }
  100. async function editTodo(todo) {
  101. state.beforeEditCache = todo.title
  102. state.editedTodo = todo
  103. await nextTick()
  104. document.getElementById(`todo-${todo.id}-input`).focus()
  105. }
  106. function doneEdit(todo) {
  107. if (!state.editedTodo) {
  108. return
  109. }
  110. state.editedTodo = null
  111. todo.title = todo.title.trim()
  112. if (!todo.title) {
  113. removeTodo(todo)
  114. }
  115. }
  116. function cancelEdit(todo) {
  117. state.editedTodo = null
  118. todo.title = state.beforeEditCache
  119. }
  120. function removeCompleted() {
  121. state.todos = filters.active(state.todos)
  122. }
  123. return {
  124. state,
  125. addTodo,
  126. removeTodo,
  127. editTodo,
  128. doneEdit,
  129. cancelEdit,
  130. removeCompleted,
  131. }
  132. },
  133. })
  134. const { children: _children, vModelText: _vModelText, withDirectives: _withDirectives, vShow: _vShow, next: _next, delegate: _delegate, on: _on, setDynamicProp: _setDynamicProp, setText: _setText, setClass: _setClass, renderEffect: _renderEffect, createFor: _createFor, insert: _insert, delegateEvents: _delegateEvents, template: _template } = VueVapor
  135. const t0 = _template("<li><div class=\"view\"><input class=\"toggle\" type=\"checkbox\"><label></label><button class=\"destroy\"></button></div><input class=\"edit\" type=\"text\"></li>")
  136. const t1 = _template("<section class=\"todoapp\"><header class=\"header\"><h1>todos</h1><input class=\"new-todo\" autofocus autocomplete=\"off\" placeholder=\"What needs to be done?\"></header><section class=\"main\"><input id=\"toggle-all\" class=\"toggle-all\" type=\"checkbox\"><label for=\"toggle-all\">Mark all as complete</label><ul class=\"todo-list\"></ul></section><footer class=\"footer\"><span class=\"todo-count\"><strong></strong><span></span></span><ul class=\"filters\"><li><a href=\"#/all\">All</a></li><li><a href=\"#/active\">Active</a></li><li><a href=\"#/completed\">Completed</a></li></ul><button class=\"clear-completed\"> Clear completed </button></footer></section>")
  137. _delegateEvents("keyup", "dblclick", "click")
  138. function _sfc_render(_ctx) {
  139. const n18 = t1()
  140. const n0 = _children(n18, 0, 1)
  141. _withDirectives(n0, [[_vModelText, () => _ctx.state.newTodo]])
  142. const n10 = _children(n18, 1)
  143. _withDirectives(n10, [[_vShow, () => _ctx.state.todos.length]])
  144. const n1 = n10.firstChild
  145. const n9 = _next(n1, 2)
  146. const n17 = n10.nextSibling
  147. _withDirectives(n17, [[_vShow, () => _ctx.state.todos.length]])
  148. const n11 = _children(n17, 0, 0)
  149. const n12 = n11.nextSibling
  150. const n13 = _children(n17, 1, 0, 0)
  151. const n14 = _children(n17, 1, 1, 0)
  152. const n15 = _children(n17, 1, 2, 0)
  153. const n16 = _children(n17, 2)
  154. _withDirectives(n16, [[_vShow, () => _ctx.state.todos.length > _ctx.state.remaining]])
  155. _delegate(n0, "update:modelValue", () => $event => (_ctx.state.newTodo = $event))
  156. _delegate(n0, "keyup", () => _ctx.addTodo, {
  157. keys: ["enter"]
  158. })
  159. _on(n1, "change", () => $event => (_ctx.state.allDone = $event.target.checked))
  160. const n2 = _createFor(() => (_ctx.state.filteredTodos), (_block) => {
  161. const n8 = t0()
  162. const n4 = _children(n8, 0, 0)
  163. const n5 = n4.nextSibling
  164. const n6 = n5.nextSibling
  165. const n7 = _children(n8, 1)
  166. _on(n4, "change", () => $event => (_block.s[0].completed = $event.target.checked))
  167. _delegate(n5, "dblclick", () => $event => (_ctx.editTodo(_block.s[0])))
  168. _delegate(n6, "click", () => $event => (_ctx.removeTodo(_block.s[0])))
  169. _on(n7, "input", () => $event => (_block.s[0].title = $event.target.value))
  170. _on(n7, "blur", () => $event => (_ctx.doneEdit(_block.s[0])))
  171. _delegate(n7, "keyup", () => $event => (_ctx.doneEdit(_block.s[0])), {
  172. keys: ["enter"]
  173. })
  174. _delegate(n7, "keyup", () => $event => (_ctx.cancelEdit(_block.s[0])), {
  175. keys: ["escape"]
  176. })
  177. const _updateEffect = () => {
  178. const [todo] = _block.s
  179. _setDynamicProp(n4, "checked", todo.completed)
  180. _setText(n5, todo.title)
  181. _setDynamicProp(n7, "value", todo.title)
  182. _setDynamicProp(n7, "id", `todo-${todo.id}-input`)
  183. _setClass(n8, ["todo", {
  184. completed: todo.completed,
  185. editing: todo === _ctx.state.editedTodo,
  186. }])
  187. }
  188. _renderEffect(_updateEffect)
  189. return [n8, _updateEffect]
  190. }, (todo) => (todo.id))
  191. _insert(n2, n9)
  192. _delegate(n16, "click", () => _ctx.removeCompleted)
  193. _renderEffect(() => _setDynamicProp(n1, "checked", _ctx.state.allDone))
  194. _renderEffect(() => _setText(n11, _ctx.state.remaining))
  195. _renderEffect(() => _setText(n12, _ctx.state.remainingText))
  196. _renderEffect(() => _setClass(n13, { selected: _ctx.state.visibility === 'all' }))
  197. _renderEffect(() => _setClass(n14, { selected: _ctx.state.visibility === 'active' }))
  198. _renderEffect(() => _setClass(n15, { selected: _ctx.state.visibility === 'completed' }))
  199. return n18
  200. }
  201. _sfc_main.render = _sfc_render
  202. const app = createVaporApp(_sfc_main, {})
  203. app.mount('#app')
  204. </script>