Criando todo-list vue 3 usando cookie
Setup
Nodejs
Você pode está instalando através do primeiro site pesquisando por nodejs.
Editor de código
você pode está utilizando um editor de sua preferência mas vou utilizar o neovim.
screenshot
so projeto vamos está utlizando o vite então digitamos o seguinte comando no terminal do seu vscode:
$ create vite@latest -- --template vue
Após isso de um cd no seu projeto e digite:
$ npm install
após isso vocês instalaram todas as dependências do projeto então já pode digitar "npm run dev" para rodar o projeto.
CSS
como esse post é focado em javascript então vou disponibilizar o css então acrescente dentro da tag style dentro do arquivo App.vue
:root {
--primary: #EA40A4;
--business: #3A82EE;
--personal: #cc0000;
--light: #EEE;
--grey: #888;
--dark: #313154;
--danger: #ff5b57;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
--business-glow: 0px 0px 4px rgba(58, 130, 238, 0.75);
--personal-glow: 0px 0px 4px rgba(234, 64, 164, 0.75);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'montserrat', sans-serif;
}
input:not([type="radio"]):not([type="checkbox"]), button {
appearance: none;
border: none;
outline: none;
background: none;
cursor: initial;
}
body {
background: var(--light);
color: var(--dark);
display: flex;
align-items: center;
justify-content: center;
}
section {
margin-top: 2rem;
margin-bottom: 2rem;
padding-left: 1.5rem;
padding-right: 1.5em;
}
h3 {
color: var(--dark);
font-size: 1rem;
font-weight: 400;
margin-bottom: 0.5rem;
}
h4 {
color: var(--grey);
font-size: 0.875rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.greeting .title {
display: flex;
}
.greeting .title input {
margin-left: 0.5rem;
flex: 1 1 0%;
min-width: 0;
}
.greeting .title,
.greeting .title input {
color: var(--dark);
font-size: 1.5rem;
font-weight: 700;
}
.create-todo input[type="text"] {
display: block;
width: 100%;
font-size: 1.125rem;
padding: 1rem 1.5rem;
color: var(--dark);
background-color: #FFF;
border-radius: 0.5rem;
box-shadow: var(--shadow);
margin-bottom: 1.5rem;
}
.create-todo .options {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 1rem;
margin-bottom: 1.5rem;
}
.create-todo .options label {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1.5rem;
background-color: #FFF;
border-radius: 0.5rem;
box-shadow: var(--shadow);
cursor: pointer;
}
input[type="radio"],
input[type="checkbox"] {
display: none;
}
.bubble {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid var(--business);
box-shadow: var(--business-glow);
}
.bubble.personal {
border-color: var(--personal);
box-shadow: var(--personal-glow);
}
.bubble::after {
content: "";
display: block;
opacity: 0;
width: 0px;
height: 0px;
background-color: var(--business);
box-shadow: var(--business-glow);
border-radius: 50%;
transition: 0.2s ease-in-out;
}
.bubble.personal::after {
background-color: var(--personal);
box-shadow: var(--personal-glow);
}
input:checked ~ .bubble::after {
width: 10px;
height: 10px;
opacity: 1;
}
.create-todo .options label div {
color: var(--dark);
font-size: 1.125rem;
margin-top: 1rem;
}
.create-todo input[type="submit"] {
display: block;
width: 100%;
font-size: 1.125rem;
padding: 1rem 1.5rem;
color: #FFF;
background-color: var(--primary);
border-radius: 0.5rem;
box-shadow: var(--personal-glow);
cursor: pointer;
transition: 0.2s ease-in-out;
}
.create-todo input[type="submit"]:hover {
opacity: 0.75;
}
.todo-list .list {
margin: 1rem 0;
}
.todo-list .todo-item {
display: flex;
align-items: center;
background-color: #FFF;
padding: 1rem;
border-radius: 0.5rem;
box-shadow: var(--shadow);
margin-bottom: 1rem;
}
.todo-item label {
display: block;
margin-right: 1rem;
cursor: pointer;
}
.todo-item .todo-content {
flex: 1 1 0%;
}
.todo-item .todo-content input {
color: var(--dark);
font-size: 1.125rem;
}
.todo-item .actions {
display: flex;
align-items: center;
}
.todo-item .actions button {
display: block;
padding: 0.5rem;
border-radius: 0.25rem;
color: #FFF;
cursor: pointer;
transition: 0.2s ease-in-out;
}
.todo-item .actions button:hover {
opacity: 0.75;
}
.todo-item .actions .edit {
margin-right: 0.5rem;
background-color: var(--primary);
}
.todo-item .actions .delete {
background-color: var(--danger);
}
.todo-item.done .todo-content input {
text-decoration: line-through;
color: var(--grey);
}
html
para vamos partir para o html
<main class="app">
<section class="greeting">
<h2 class="title">
What's up, <input type="text" id="name" placeholder="Name here" v-model="name">
</h2>
</section>
<section class="create-todo">
<h3>CREATE A TODO</h3>
<form id="new-todo-form" @submit.prevent="addTodo">
<h4>What's on your todo list?</h4>
<input
type="text"
name="content"
id="content"
placeholder="e.g. make a video"
v-model="input_content" />
<h4>Pick a category</h4>
<div class="options">
<label>
<input
type="radio"
name="category"
id="category1"
value="business"
v-model="input_category" />
<span class="bubble business"></span>
<div>Business</div>
</label>
<label>
<input
type="radio"
name="category"
id="category2"
value="personal"
v-model="input_category" />
<span class="bubble personal"></span>
<div>Personal</div>
</label>
</div>
<input type="submit" value="Add todo" />
</form>
</section>
<section class="todo-list">
<h3>TODO LIST</h3>
<div class="list" id="todo-list">
<div v-for="todo in todos_asc" :class="`todo-item ${todo.done && 'done'}`">
<label>
<input type="checkbox" v-model="todo.done" />
<span :class="`bubble ${
todo.category == 'business'
? 'business'
: 'personal'
}`"></span>
</label>
<div class="todo-content">
<input type="text" v-model="todo.content" />
</div>
<div class="actions">
<button class="delete" @click="removeTodo(todo)">Delete</button>
</div>
</div>
</div>
</section>
</main>
Javascript
agora vamos fazer a lógica do nosso projeto
// vamos importar alguns metodos do vue.js
import { ref, onMounted, computed, watch } from 'vue'
// variavel na qual vai está todos os todos que criarmos
const todos = ref([])
// variavel que vai guardar o nome do usuario
const name = ref('')
// variavel que vai obter a task ddo todo que o usuario vai digitar
const input_content = ref('')
// vairavel que vai guardar a caregoria da task se vai ser personal ou business
const input_category = ref(null)
// função que vai colocar em ordem do mais novo para o mais velhos os todos criados
const todos_asc = computed(() => todos.value.sort((a,b) =>{
return a.createdAt - b.createdAt
}))
// metodo do vue.js que vai obserbar a variavel name
// para sempre que ela mudasse ela fosse atualiazada na variavel e que fosse atualizada no cookie
watch(name, (newVal) => {
localStorage.setItem('name', newVal)
})
// metodo do vue.js que vai guardar observar se deletou editou ou criou uma nova task
// então vai atualizar no cookie, podendo atualizar a pagina e não mudaria
watch(todos, (newVal) => {
localStorage.setItem('todos', JSON.stringify(newVal))
}, {
deep: true
})
// verificar se o input de criação da task está vazia
const addTodo = () => {
if (input_content.value.trim() === '' || input_category.value === null) {
return
}
todos.value.push({
content: input_content.value,
category: input_category.value,
done: false,
editable: false,
createdAt: new Date().getTime()
})
}
// função que remove a task
const removeTodo = (todo) => {
todos.value = todos.value.filter((t) => t !== todo)
}
// metodo do vue.js que guarda o nome e as tasks dentro do cookie
onMounted(() => {
name.value = localStorage.getItem('name') || ''
todos.value = JSON.parse(localStorage.getItem('todos')) || []
})
Tasks para vocês leitores
- Deixar a Página responsiva
- Adicionar mais tags além de personal e business
- Criar a opção de prioridade e com isso sublinhar a linha com a cor laranja