Commit a4994b4e authored by Denys Mishunov's avatar Denys Mishunov
Browse files

Initial commit

parent ba5649d4
node_modules
# Frankenstein TodoMVC
# todomvc
The collection of TodoMVC projects available from http://todomvc.com/ but suited
and tuned to be suitable for Frankenstein approach demos. Make sure to check
branches for a partcular framework's implementation.
\ No newline at end of file
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/app'
]
};
This diff is collapsed.
{
"name": "todomvc",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^2.6.5",
"todomvc-app-css": "^2.2.0",
"todomvc-common": "^1.0.5",
"vue": "^2.6.10",
"vue-router": "^3.0.6"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.6.0",
"@vue/cli-plugin-eslint": "^3.6.0",
"@vue/cli-service": "^3.6.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.5.21"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>todomvc</title>
<style>html,body {height: 100%}</style>
</head>
<body>
<noscript>
<strong>We're sorry but todomvc doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<header class="header">
<h1 :class="$style.TodoHeader">Vue.todos</h1>
<input
class="new-todo"
:class="$style.NewTodo"
autofocus
autocomplete="off"
placeholder="What needs to be done?"
v-model="newTodo"
@keyup.enter="addTodo"
/>
</header>
</template>
<script>
import todoStorage from "./storage";
export default {
name: "todoapp-header",
// app initial state
data: () => {
return {
todos: todoStorage.fetch(),
newTodo: "",
editedTodo: null,
visibility: "all"
};
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
addTodo: function() {
var value = this.newTodo && this.newTodo.trim();
if (!value) {
return;
}
this.todos.unshift({
id: todoStorage.generateid(5),
text: value,
completed: false
});
this.newTodo = "";
todoStorage.save(this.todos);
},
updateTodos: function(e) {
if (this.todos !== e.detail.todos) {
this.todos = e.detail.todos;
}
}
},
created: function() {
document.addEventListener("store-update", this.updateTodos);
},
destroyed: function() {
document.removeEventListener("store-update", this.updateTodos);
}
};
</script>
<style module>
.TodoHeader {
text-indent: 100%;
overflow-x: hidden;
background-image: url(../public/logo.png) !important;
height: 100px;
background-size: auto 100px;
background-position: center center;
background-repeat: no-repeat;
top: -200px !important;
position: absolute;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.NewTodo::placeholder {
color: #41b883 !important;
font-style: italic;
}
</style>
<template>
<section class="main" v-show="todos.length">
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list" ref="root">
<li class="todo" v-for="todo in filteredTodos" :key="todo.id" :class="{completed: todo.completed, editing: todo === editedTodo}">
<div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed">
<label @dblclick="editTodo(todo)">{{todo.text}}</label>
<button class="destroy" @click="removeTodo(todo)"></button>
</div>
<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo === editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">
</li>
</ul>
<footer class="footer" v-show="todos.length">
<span class="todo-count">
<strong v-text="remaining"></strong> {{pluralize('item', remaining)}} left
</span>
<ul class="filters">
<li><a href="#/all" @click="updateVisibility('all')" :class="{selected: visibility === 'all'}">All</a></li>
<li><a href="#/active" @click="updateVisibility('active')" :class="{selected: visibility === 'active'}">Active</a></li>
<li><a href="#/completed" @click="updateVisibility('completed')" :class="{selected: visibility === 'completed'}">Completed</a></li>
</ul>
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
Clear completed
</button>
</footer>
</section>
</template>
<script>
import Vue from "vue";
import todoStorage from "./storage";
const filters = {
all: function (todos) {
return todos;
},
active: function (todos) {
return todos.filter(function (todo) {
return !todo.completed;
});
},
completed: function (todos) {
return todos.filter(function (todo) {
return todo.completed;
});
}
};
export default {
name: 'todoapp-listing',
// app initial state
data: () => {
return {
todos: todoStorage.fetch(),
newTodo: '',
editedTodo: null,
visibility: 'all'
}
},
// watch todos change for localStorage persistence
watch: {
todos: {
handler: todoStorage.save,
deep: true,
}
},
// computed properties
// http://vuejs.org/guide/computed.html
computed: {
filteredTodos: function () {
return filters[this.visibility](this.todos);
},
remaining: function () {
return filters.active(this.todos).length;
},
allDone: {
get: function () {
return this.remaining === 0;
},
set: function (value) {
this.todos.forEach(function (todo) {
todo.completed = value;
});
}
}
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
updateVisibility: function(type) {
this.visibility = type;
},
pluralize: function (word, count) {
return word + (count === 1 ? '' : 's');
},
removeTodo: function (todo) {
var index = this.todos.indexOf(todo);
this.todos.splice(index, 1);
},
editTodo: function (todo) {
this.beforeEditCache = todo.text;
this.editedTodo = todo;
},
doneEdit: function (todo) {
if (!this.editedTodo) {
return;
}
this.editedTodo = null;
todo.text = todo.text.trim();
if (!todo.text) {
this.removeTodo(todo);
}
},
cancelEdit: function (todo) {
this.editedTodo = null;
todo.text = this.beforeEditCache;
},
removeCompleted: function () {
this.todos = filters.active(this.todos);
},
updateTodos: function(e) {
if(this.todos !== e.detail.todos) {
this.todos = e.detail.todos;
}
Vue.nextTick(() => {
const root = this.$refs.root
root.classList.add('rubberBand');
setTimeout(() => {
root.classList.remove('rubberBand');
}, 1000);
}, this);
}
},
created: function() {
document.addEventListener('store-update', this.updateTodos);
},
destroyed: function() {
document.removeEventListener('store-update', this.updateTodos);
},
// a custom directive to wait for the DOM to be updated
// before focusing on the input field.
// http://vuejs.org/guide/custom-directive.html
directives: {
"todo-focus": function(el, binding) {
if (binding.value) {
el.focus();
}
}
}
}
</script>
<style>
@import "./listing-styles.css";
</style>
\ No newline at end of file
<template>
<div id="app">
<section class="todoapp">
<todo-header></todo-header>
<todo-listing></todo-listing>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://evanyou.me">Evan You</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</template>
<script>
import TodoHeader from './Header';
import TodoListing from './Listing';
import "todomvc-common/base.css";
import "todomvc-app-css/index.css";
// import "./styles.css";
export default {
name: 'todoapp',
components: {
TodoHeader,
TodoListing
},
}
</script>
h1 {
text-indent: 100%;
overflow-x: hidden;
background-image: url(../public/logo.png) !important;
height: 100px;
background-size: auto 100px;
background-position: center center;
background-repeat: no-repeat;
top: -200px !important;
}
input::placeholder {
color: #41b883 !important;
}
\ No newline at end of file
.filters li a.selected {
background: #41b883 !important;
color: White;
font-weight: bold;
}
@-webkit-keyframes rubberBand {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(0.95, 1.05, 1);
transform: scale3d(0.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, 0.95, 1);
transform: scale3d(1.05, 0.95, 1);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
@keyframes rubberBand {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(0.95, 1.05, 1);
transform: scale3d(0.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, 0.95, 1);
transform: scale3d(1.05, 0.95, 1);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
.rubberBand {
-webkit-animation-name: rubberBand;
animation-name: rubberBand;
-webkit-animation-duration: 1s;
animation-duration: 1s;
}
\ No newline at end of file
import Vue from 'vue';
import TodoApp from './TodoApp.vue';
Vue.config.productionTip = false;
new Vue({
render: h => h(TodoApp),
}).$mount('#app');
var STORAGE_KEY = "frankenstein";
export default {
fetch: function() {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
},
save: function(todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
var event = new CustomEvent("store-update", { detail: { todos } });
document.dispatchEvent(event);
},
generateid: function(length) {
var result = "";
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
};
/* Override bootstrap's styling*/
*, ::after, ::before {
box-sizing: content-box;
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment