├── README.md ├── app.js ├── app.ls ├── index.html ├── index.jade ├── vue-extended.js └── vue-extended.ls /README.md: -------------------------------------------------------------------------------- 1 | # [TodoMVC](http://farzher.github.io/vue-livescript-todomvc/) using *[Vue.js](https://github.com/yyx990803/vue)* + *[Livescript](https://github.com/gkz/LiveScript)* 2 | 3 | ## Demo 4 | http://farzher.github.io/vue-livescript-todomvc/ 5 | 6 | ## Possibly the smallest todomvc implementation 7 | * **~40 lines of code** 8 | * Only **17 lines of JS** 9 | * Only 2 source files: [app.ls](https://github.com/farzher/vue-livescript-todomvc/blob/gh-pages/app.ls) + [index.jade](https://github.com/farzher/vue-livescript-todomvc/blob/gh-pages/index.jade) 10 | 11 | 12 | ### Source with syntax highlighting 13 | ![](http://i.imgur.com/VYi5bWR.png) 14 | ![](http://i.imgur.com/PoAKtvB.png) 15 | 16 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.2.0 2 | var app; 3 | app = new Vue({ 4 | el: '#todoapp', 5 | data: { 6 | name: '', 7 | activeFilter: 'all', 8 | all: JSON.parse(localStorage.getItem('v')) || [] 9 | }, 10 | ready: function(){ 11 | return this.$watch('all', function(it){ 12 | return localStorage.v = JSON.stringify(it); 13 | }, { 14 | deep: true 15 | }); 16 | }, 17 | computed: { 18 | completed: function(){ 19 | return _.filter(function(it){ 20 | return it.completed; 21 | })( 22 | this.all); 23 | }, 24 | active: function(){ 25 | return _.reject(function(it){ 26 | return it.completed; 27 | })( 28 | this.all); 29 | }, 30 | allDone: { 31 | get: function(){ 32 | return this.active.length === 0; 33 | }, 34 | set: function(v){ 35 | return _.each(function(it){ 36 | return it.completed = v; 37 | })( 38 | this.all); 39 | } 40 | } 41 | }, 42 | methods: { 43 | create: function(){ 44 | if (this.name) { 45 | this.all.push({ 46 | name: this.name, 47 | completed: false, 48 | oldName: false 49 | }); 50 | return this.name = ''; 51 | } 52 | }, 53 | cancel: function(it){ 54 | it.name = it.oldName; 55 | return it.oldName = ''; 56 | }, 57 | save: function(it){ 58 | it.oldName = ''; 59 | if (!it.name.trim()) { 60 | return this.all.$remove(it); 61 | } 62 | }, 63 | removeCompleted: function(){ 64 | return this.all = _.reject(function(it){ 65 | return it.completed; 66 | })(this.all); 67 | } 68 | } 69 | }); 70 | Router({ 71 | '*': function(it){ 72 | return app.activeFilter = it; 73 | } 74 | }).init('/all'); -------------------------------------------------------------------------------- /app.ls: -------------------------------------------------------------------------------- 1 | app = new Vue do 2 | el:'#todoapp' 3 | data:{name:'', activeFilter:'all', all:JSON.parse localStorage.getItem \v or []} 4 | ready:-> @$watch 'all' (-> localStorage.v = JSON.stringify it), {+deep} 5 | computed: 6 | completed:-> @all |> _.filter (.completed) 7 | active:-> @all |> _.reject (.completed) 8 | allDone: 9 | get:-> @active.length is 0 10 | set:(v)-> @all |> _.each (.completed = v) 11 | methods: 12 | create:-> if @name => @all.push {@name, -completed, -oldName}; @name = '' 13 | cancel:-> it.name = it.oldName; it.oldName = '' 14 | save:-> it.oldName = ''; if !it.name.trim! => @all.$remove it 15 | removeCompleted:-> @all |>= _.reject (.completed) 16 | 17 | Router {'*':-> app.activeFilter = it} .init '/all' 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /index.jade: -------------------------------------------------------------------------------- 1 | #todoapp 2 | #header 3 | h1 todos 4 | input#new-todo(autofocus placeholder="What needs to be done?" v-model="name" @keyup.enter="create") 5 | #main(v-show="all.length") 6 | input#toggle-all(type="checkbox" v-model="allDone") 7 | ul#todo-list 8 | li(v-for="it in this[activeFilter]" ,:class="{todo:true, completed:it.completed, editing:it.oldName}") 9 | .view 10 | input.toggle(type="checkbox" v-model="it.completed") 11 | label(@dblclick="it.oldName = it.name") {{it.name}} 12 | button.destroy(@click="all.$remove(it)") 13 | input.edit(v-model="it.name" v-select="it.oldName" @blur="save(it)" @keyup.enter="save(it)" @keyup.esc="cancel(it)") 14 | #footer(v-show="all.length") 15 | span#todo-count: #[strong {{active.length}}] {{active.length | pluralize 'item'}} left 16 | ul#filters 17 | li: a(href="#/all" ,:class="{selected:activeFilter=='all'}") All 18 | li: a(href="#/active" ,:class="{selected:activeFilter=='active'}") Active 19 | li: a(href="#/completed" ,:class="{selected:activeFilter=='completed'}") Completed 20 | button#clear-completed(@click="removeCompleted" v-show="all.length>active.length") Clear completed 21 | 22 | 23 | | 27 | | 28 | | 29 | | 30 | | 31 | | 32 | | 33 | | 34 | -------------------------------------------------------------------------------- /vue-extended.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.2.0 2 | Vue.directive('select', function(v){ 3 | if (v) { 4 | this.el.select(); 5 | } 6 | }); -------------------------------------------------------------------------------- /vue-extended.ls: -------------------------------------------------------------------------------- 1 | Vue.directive 'select', (v) !-> @el.select! if v 2 | --------------------------------------------------------------------------------