Vue.js: Create a new note with a button
by Peter Jang
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Notes</title>
<link rel="stylesheet" href="css/notes.css"></link>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="js/notes.js" defer></script>
</head>
<body>
<div id="app">
<div class="toolbar">
<button class="toolbar-button" v-on:click="createNote()">New</button>
<button class="toolbar-button">Delete</button>
<input class="toolbar-search" type="text" placeholder="Search...">
</div>
<div class="note-container">
<div class="note-selectors">
<div class="note-selector"
v-for="note in transformedNotes"
v-on:click="selectNote(note)"
v-bind:class="{active: note === selectedNote}"
>
<p class="note-selector-title">{{ note.body | formatTitle }}</p>
<p class="note-selector-timestamp">{{ note.timestamp | formatTimestamp }}</p>
</div>
</div>
<div class="note-editor">
<p class="note-editor-info">{{ selectedNote.timestamp | formatTimestamp }}</p>
<textarea class="note-editor-input" v-model="selectedNote.body"></textarea>
</div>
</div>
</div>
</body>
</html>
CSS
/* RESET */
* {
margin: 0;
padding: 0;
border: 0;
outline: none;
box-sizing: border-box;
}
/* LAYOUT */
#app {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.toolbar {
padding: 0.5em;
}
.toolbar-button, .toolbar-search {
padding: inherit;
border-radius: 0.3em;
}
.toolbar-search {
float: right;
}
.note-container {
display: flex;
flex: 1;
}
.note-selectors {
flex: 0 0 13em;
}
.note-selector {
padding: 1em;
}
.note-selector p {
margin: 0;
}
.note-editor {
display: flex;
flex: 1;
flex-direction: column;
}
.note-editor-info {
padding: 0.5em;
text-align: center;
}
.note-editor-input {
display: flex;
flex: 1;
width: 100%;
padding: 0 2em 0 2em;
}
/* COLORS */
* {
color: #454545;
background-color: #FAFAF8;
}
.toolbar {
background-color: #DCDADC;
}
.toolbar-button {
background-color: #FFFFFF;
}
.toolbar-button:active {
background-color: #AAAAAA;
}
.note-selectors {
border-right: 1px solid #DCDADC;
}
.note-selector {
border-bottom: 1px solid #DCDADC;
}
.note-selector.active {
background-color: #FCE18D;
}
.note-selector-title {
background-color: inherit;
}
.note-selector-timestamp {
color: #626262;
background-color: inherit;
}
.note-editor-info {
color: #DCDADC;
}
/* TYPOGRAPHY */
body {
font-family: sans-serif;
}
.note-selector-title {
font-weight: bold;
}
.note-selector-timestamp {
font-size: 0.7em;
}
.note-editor, .note-editor-input {
font-size: 0.9em;
}
JavaScript
var app = new Vue({
el: '#app',
data: {
notes: [],
selectedNote: {}
},
mounted: function() {
this.notes = [
{id: 1, body: "This is a first test", timestamp: Date.now()},
{id: 2, body: "This is a second test", timestamp: Date.now()},
{id: 3, body: "This is a third test", timestamp: Date.now()}
];
this.selectedNote = this.notes[0];
},
computed: {
transformedNotes: function() {
return this.notes.slice().sort(function(a, b) {
return b.timestamp - a.timestamp;
});
}
},
filters: {
formatTitle: function(body) {
var maxLength = 20;
if (body.length > maxLength) {
return body.substring(0, maxLength - 3) + '...';
} else if (body.length === 0) {
return "New note";
} else {
return body;
}
},
formatTimestamp: function(timestamp) {
return new Date(timestamp).toUTCString();
}
},
methods: {
selectNote: function(note) {
this.selectedNote = note;
},
createNote: function() {
var newNote = {
id: Date.now(),
body: "",
timestamp: Date.now()
};
this.notes.push(newNote);
this.selectedNote = newNote;
}
},
watch: {
notes: {
handler: function() {
this.selectedNote.timestamp = Date.now();
},
deep: true
}
}
});