F7 + VUE = TODO
by Nikolay Kuznetsov
HTML
<link rel="stylesheet" href="https://framework7.io/dist/css/framework7.ios.colors.min.css">
<link rel="stylesheet" href="https://framework7.io/dist/css/framework7.ios.min.css">
<script src="https://framework7.io/dist/js/framework7.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div class="views">
<div class="view view-main">
<div class="pages">
<div data-page="home" class="page navbar-fixed" id="app">
<div class="navbar">
<div class="navbar-inner">
<div class="left"> </div>
<div class="center">F7 + VUE = ToDo</div>
<div class="right"><a href="" @click="addTodoUI" class="link">Add Task</a></div>
</div>
</div>
<div class="page-content">
<div class="content-block-title">Todo Filters</div>
<div class="content-block buttons-row">
<a href="#" class="button" :class="{ active: visibility == 'all' }" @click="setFilter('all')">All ({{todos.length}})</a>
<a href="#" class="button" :class="{ active: visibility == 'active' }" @click="setFilter('active')">Active ({{remaining}})</a>
<a href="#" class="button" :class="{ active: visibility == 'completed' }" @click="setFilter('completed')">Complete ({{todos.length - remaining}})</a>
</div>
<div class="list-block">
<ul>
<li v-for="(todo,index) in filteredTodos" :key="todo.id" class="swipeout" @deleted="deleteTodo(index)">
<div class="swipeout-content">
<label class="label-checkbox item-content" :class="{completed: todo.done}">
<input type="checkbox" name="ks-checkbox" v-model="todo.done">
<div class="item-media"><i class="icon icon-form-checkbox"></i></div>
<div class="item-inner">
<div class="item-title">{{todo.title}}</div>
</div>
</label>
</div>
<div class="swipeout-actions-right">
<a href="#" class="swipeout-delete">Delete</a>
</div>
</li>
</ul>
<div class="list-block-label">
Swipe left to delete task
</div>
</div>
<div class="content-block buttons-row">
<a href="#" class="button" @click="completeAll()">Complete all</a>
<a href="#" class="button" @click="removeCompleted()">Remove...
CSS
label.completed {
color: #d9d9d9;
text-decoration: line-through;
}
.view-left {
border-right: 1px solid #b2b2b2;
}
.panel-left .page-content {
padding: 0;
}
.panel-left .page {
margin-top: 44px;
padding-bottom: 44px;
}
.panel-left .navbar {
position: relative;
margin-bottom: -44px;
}
body.with-panel-right-reveal .panel-overlay {
margin-left: 0;
}
@media (min-width: 569px) {
.panel.panel-left.panel-cover {
width: 320px;
left: -320px;
}
body.with-panel-left-cover .panel-left {
-webkit-transform: translate3d(320px, 0, 0);
-ms-transform: translate3d(320px, 0, 0);
transform: translate3d(320px, 0, 0);
}
}
@media (min-width: 769px) {
body .panel.panel-left.panel-cover {
left: 0;
width: 320px;
display: block;
}
body.with-panel-left-cover .panel-left {
-webkit-transform: translate3d(0px, 0, 0);
-ms-transform: translate3d(0px, 0, 0);
transform: translate3d(0px, 0, 0);
}
.view-main {
float: right;
width: -webkit-calc(100% - 320px);
width: -moz-calc(100% - 320px);
width: -ms-calc(100% - 320px);
width: calc(100% - 320px);
}
.view-main .navbar .open-panel {
display: none;
}
}
i.tabbar-demo-icon-1 {
width: 30px;
height: 30px;
background-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30' fill='%23929292'><g><circle cx='15.2' cy='8.8' r='1.5'/><polygon points='16.7,12.3 16.7,11.7 16.7,11.3 13.7,11.3 13.7,12.3 14.7,12.3 14.7,20.3 13.7,20.3 13.7,21.3 17.7,21.3 17.7,20.3 16.7,20.3'/><path d='M15.2,2.3C8.3,2.3,2.7,7.9,2.7,14.8s5.6,12.5,12.5,12.5c6.9,0,12.5-5.6,12.5-12.5S22.1,2.3,15.2,2.3z M15.2,26.3 c-6.3,0-11.5-5.2-11.5-11.5S8.8,3.3,15.2,3.3s11.5,5.2,11.5,11.5S21.5,26.3,15.2,26.3z'/></g></svg>");
}
.active i.tabbar-demo-icon-1 {
background-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 30 30' fill='%23007aff'><path...
JavaScript
var myApp = new Framework7({
modalTitle: 'F7 + VUE'
});
var $$ = Dom7;
var mainView = myApp.addView('.view-main', {
dynamicNavbar: true,
animateNavBackIcon: true,
scrollTopOnStatusbarClick: true,
});
var STORAGE_KEY = 'f7-vue-todo';
var todoStorage = {
load: function () {
var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
return todos
},
save: function (todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
}
}
// visibility filters
var filters = {
all: function (todos) {
return todos
},
active: function (todos) {
return todos.filter(function (todo) {
return !todo.done
})
},
completed: function (todos) {
return todos.filter(function (todo) {
return todo.done
})
}
}
var app = new Vue({
// element to mount to
el: '#app',
// initial data
data: {
todos:todoStorage.load(),
visibility: 'all'
},
watch: {
todos: {
handler: function (todos) {
todoStorage.save(todos)
},
deep: true
}
},
computed: {
remaining: function () {
return filters.active(this.todos).length
},
filteredTodos: function () {
return filters[this.visibility](this.todos)
},
},
// methods
methods: {
setFilter: function(filter){
this.visibility = filter;
},
addTodoUI: function(event) {
myApp.prompt('Add task ' , function (value) {
//console.log(app)
if(value){
app.addTodo(value);
}
});
},
addTodo: function (value) {
this.todos.push({"id": Date.now(),"title": value, "done": false });
},
deleteTodo: function (todo) {
this.todos.splice(todo, 1);
},
removeCompleted: function(){
this.todos = filters.active(this.todos);
this.visibility = "all";
},
completeAll: function(){
this.todos.forEach(function (todo) {
todo.done = true;
});
}
}
})