<h1>Things to do</h1>
<h2></h2>
<div id="app">
<form>
<input
autocomplete="off"
type="text"
name="item"
class="item"
placeholder="Add new item...">
<button type="submit">+ Add</button>
</form>
<ul></ul>
</div>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
html, body {
background: #CB356B;
background: -webkit-linear-gradient(to right, #BD3F32, #CB356B);
background: linear-gradient(to right, #BD3F32, #CB356B);
}
body {
padding: 20px;
-webkit-font-smoothing: antialiased;
}
body,
button,
input {
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
line-height: 1.2;
}
#app {
margin: 0 auto;
max-width: 440px;
position: relative;
}
#app:before {
background: #fff;
content: ' ';
display: block;
height: 10px;
opacity: .5;
width: 420px;
top: -10px;
left: 10px;
position: absolute;
}
#app:after {
background: #fff;
content: ' ';
display: block;
height: 10px;
opacity: .3;
width: 400px;
top: -20px;
left: 20px;
position: absolute;
}
h1,
h2 {
color: #fff;
font-size: 24px;
font-weight: 300;
margin: 0 auto;
max-width: 420px;
opacity: .5;
text-align: center;
}
h2 {
font-size: 32px;
opacity: 1;
padding: 0 0 40px;
}
form {
background: transparent;
position: ;
transition: all linear .1s;
}
form input {
background: rgba(255,255,255,.7);
border: 0;
box-sizing: border-box;
display: block;
font-size: 18px;
line-height: 50px;
height: 50px;
padding: 0 10px;
position: relative;
transition: all linear .1s;
width: 440px;
}
form.focus {
background: #3F1E24;
padding: 10px 0;
}
form.focus input {
background: #fff;
margin-left: -10px;
width: 460px;
}
form.focus button {
top: 25px;
right: 5px;
}
form input:focus,
form button:focus,
li button:focus {
outline: none;
}
form button {
background: transparent;
border: 0;
color: #000;
cursor: pointer;
font-weight: 600;
font-size: 18px;
opacity: .3;
position: absolute;
transition: all linear .1s;
top: 15px;
right: 15px;
}
form.valid button {
color: #BD3F32;
opacity: 1;
}
li {
background: #FFFFFF;
border-top: 1px solid #CB356B;
height: 50px;
line-height: 50px;
padding: 0 15px 0 50px;
position: relative;
}
li button {
background: #FF4954;
bottom: 0;
border: none;
border-radius: 100%;
color: #FF4954;
cursor: pointer;
font-size: 14px;
font-weight: 400;
line-height: 10px;
opacity: 0;
position: absolute;
transition: all ease-in .2s;
right: 13px;
top: 13px;
transform-origin: 0 0;
width: 24px;
height: 24px;
}
li button:before {
content: ' ';
display: block;
position: absolute;
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
width: 12px;
opacity: 1;
border-top: solid white 2px;
top: 11px;
left: 6px;
}
li button:after {
content: ' ';
display: block;
position: absolute;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
width: 12px;
opacity: 1;
border-top: solid white 2px;
top: 11px;
left: 6px;
}
li:hover button {
opacity: 1;
}
li input[type='checkbox'] {
cursor: pointer;
width: 20px;
height: 20px;
position: absolute;
left: 15px;
top: 15px;
}
li input[type='checkbox']:before {
border: 2px solid #9d9d9d;
border-radius: 100%;
content: '';
margin-right: 10px;
display: inline-block;
vertical-align: text-top;
width: 20px;
height: 20px;
left: -2px;
position: absolute;
top: -2px;
background: white;
}
li.done input[type='checkbox']:before {
border-color: #005500;
}
li.done input[type='checkbox']:after {
content: '';
position: absolute;
left: 5px;
top: 10px;
background: #005500;
width: 2px;
height: 2px;
-webkit-box-shadow: 2px 0 0 #005500, 4px 0 0 #005500, 4px -2px 0 #005500, 4px -4px 0 #005500, 4px -6px 0 #005500, 4px -8px 0 #005500;
box-shadow: 2px 0 0 #005500, 4px 0 0 #005500, 4px -2px 0 #005500, 4px -4px 0 #005500, 4px -6px 0 #005500, 4px -8px 0 #005500;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.done {
border-top: 1px solid #97A79B;
background: #D7E7DB;
color: #005500;
text-decoration: line-through;
}
@media (max-width: 560px) {
#app {
max-width: 340px;
}
#app:before {
width: 320px;
}
#app:after {
width: 300px;
}
body {
padding: 20px;
}
form input {
width: 340px;
}
form.focus input {
width: 360px;
}
}
@media (max-width: 390px) {
#app {
max-width: 300px;
}
#app:before {
width: 280px;
}
#app:after {
width: 260px;
}
form input {
width: 300px;
}
form.focus input {
width: 320px;
}
}
@media (max-width: 340px) {
#app {
max-width: 260px;
}
#app:before {
width: 240px;
}
#app:after {
width: 220px;
}
form input {
width: 260px;
}
form.focus input {
width: 280px;
}
}
;(function(window){
'use strict';
class Store {
constructor(name) {
let isSupported;
try {
window.localStorage.setItem('test', 'test');
window.localStorage.removeItem('test');
isSupported = true;
} catch(e) {
isSupported = false;
}
this.name = name;
this.isSupported = isSupported;
}
save(items) {
const json = JSON.stringify(items);
window.localStorage.setItem(this.name, json)
}
load() {
const items = window.localStorage.getItem(this.name);
return (items && items.length) ? JSON.parse(items) : [];
}
}
window.Store = Store;
})(window);
;(function(window, Store){
'use strict';
class State {
constructor(name) {
this.itemId = 1;
this.date = this.setDate();
this.form = {
focus: false,
valid: false
};
this.items = [];
this.store = new Store(name);
if (this.store.isSupported) {
this.items = this.store.load();
}
if (this.items.length) {
for (const item of this.items) {
if (item.id >= this.itemId) {
this.itemId = item.id + 1;
}
}
}
this.orderItems();
}
saveItems() {
if (this.store.isSupported) {
this.store.save(this.items);
}
}
addItem(item) {
this.items.unshift({
id: this.itemId++,
status: 0,
value: item
});
this.saveItems();
}
orderItems() {
const todo = this.items.filter((item) => {
return item.status === 0;
});
const done = this.items.filter((item) => {
return item.status === 1;
});
this.items = todo.concat(done);
}
findItemIndex(id) {
return this.items.findIndex((item) => {
return item.id === id;
});
}
deleteItem(id) {
const itemIndex = this.findItemIndex(id);
this.items.splice(itemIndex, 1);
this.saveItems();
}
toggleItemStatus(id) {
const itemIndex = this.findItemIndex(id);
this.items[itemIndex].status = this.items[itemIndex].status ? 0 : 1;
this.orderItems();
this.saveItems();
}
setDate() {
const date = new Date();
const monthNames = [
'January', 'February', 'March',
'April', 'May', 'June', 'July',
'August', 'September', 'October',
'November', 'December'
];
let day = date.getDate();
let monthIndex = date.getMonth();
let year = date.getFullYear();
return day + ' ' + monthNames[monthIndex] + ' ' + year;
}
}
window.State = State;
})(window, window.Store);
;(function(State){
'use strict';
class App {
constructor(name) {
this.name = name;
this.state = new State(name);
this.date = document.querySelector('h2');
this.list = document.querySelector('ul');
this.button = document.querySelector('button');
this.form = document.querySelector('form');
this.input = document.querySelector('input');
this.bindEvents();
this.render();
}
bindEvents() {
this.list.addEventListener('click', this.handleClick.bind(this));
this.form.addEventListener('submit', this.submitForm.bind(this));
this.input.addEventListener('keyup', this.keyUp.bind(this));
this.input.addEventListener('focus', this.focus.bind(this));
this.input.addEventListener('blur', this.blur.bind(this));
this.button.addEventListener('focus', this.focus.bind(this));
this.button.addEventListener('blur', this.blur.bind(this));
}
submitForm(event) {
event.preventDefault();
if (!this.input.value.length) {
return;
}
this.state.addItem(this.input.value);
this.input.value = '';
this.render();
}
handleClick(event) {
const e = event;
if (e && e.target) {
const element = e.target.type;
if (element == 'submit' || element == 'checkbox') {
const id = parseInt(e.target.parentNode.getAttribute('data-id'));
if (element == 'submit') {
this.state.deleteItem(id);
} else {
this.state.toggleItemStatus(id);
}
}
}
this.render();
}
render() {
let listHTML = '';
for (const item of this.state.items) {
const className = item.status ? 'done' : '';
listHTML += '<li class="' + className + '" data-id="' + item.id + '">';
listHTML += '<input type="checkbox"' + (item.status ? ' checked' : '') + '>';
listHTML += item.value + '<button>x</button></li>';
}
this.date.innerHTML = this.state.date;
this.form.classList.toggle('focus', this.state.form.focus);
this.form.classList.toggle('valid', this.state.form.valid);
this.list.innerHTML = listHTML;
}
keyUp() {
this.state.form.valid = (this.input.value.length) ? 1 : 0;
this.render();
}
blur() {
this.state.form.focus = 0;
this.render();
}
focus() {
this.state.form.focus = 1;
this.render();
}
}
const app = new App('todo-list-state-example');
})(window.State);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.