Skip to content

Commit c2dceac

Browse files
committed
Vue Lab - Added list functionallity
1 parent 5319626 commit c2dceac

4 files changed

Lines changed: 264 additions & 21 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<template>
2+
<div class="lists-container">
3+
<div class="lists-header">
4+
<h2>Lists</h2>
5+
<button @click="showNewListInput = true" class="add-list-button">
6+
<span class="plus">+</span>
7+
</button>
8+
</div>
9+
10+
<div v-if="showNewListInput" class="new-list-input">
11+
<input v-model="newListName" @keyup.enter="createList" @keyup.esc="cancelNewList" placeholder="List name..."
12+
ref="newListInput" autofocus />
13+
<div class="new-list-actions">
14+
<button @click="createList" class="save-button">Save</button>
15+
<button @click="cancelNewList" class="cancel-button">Cancel</button>
16+
</div>
17+
</div>
18+
19+
<ul class="lists">
20+
<li v-for="list in store.lists" :key="list.id" :class="{ active: list.id === store.activeListId }"
21+
@click="store.setActiveList(list.id)">
22+
{{ list.name }}
23+
<span class="todo-count">({{ list.todos.length }})</span>
24+
<button v-if="store.lists.length > 1" @click.stop="deleteList(list.id)" class="delete-list">
25+
×
26+
</button>
27+
</li>
28+
</ul>
29+
</div>
30+
</template>
31+
32+
<script setup lang="ts">
33+
import { ref } from 'vue'
34+
import { useTodoStore } from '~/stores/todo-store'
35+
36+
const store = useTodoStore()
37+
const showNewListInput = ref(false)
38+
const newListName = ref('')
39+
40+
const createList = () => {
41+
if (newListName.value.trim()) {
42+
store.addList(newListName.value.trim())
43+
newListName.value = ''
44+
showNewListInput.value = false
45+
}
46+
}
47+
48+
const cancelNewList = () => {
49+
showNewListInput.value = false
50+
newListName.value = ''
51+
}
52+
53+
const deleteList = (id: number) => {
54+
if (confirm('Are you sure you want to delete this list?')) {
55+
store.deleteList(id)
56+
}
57+
}
58+
</script>
59+
60+
<style scoped>
61+
.lists-container {
62+
padding: 1rem;
63+
border-right: 1px solid #eee;
64+
background: white;
65+
}
66+
67+
.lists-header {
68+
display: flex;
69+
justify-content: space-between;
70+
align-items: center;
71+
margin-bottom: 1rem;
72+
}
73+
74+
.add-list-button {
75+
padding: 0.25rem 0.5rem;
76+
border-radius: 4px;
77+
border: none;
78+
background: #4CAF50;
79+
color: white;
80+
cursor: pointer;
81+
font-size: 1.2rem;
82+
}
83+
84+
.lists {
85+
list-style: none;
86+
padding: 0;
87+
margin: 0;
88+
}
89+
90+
.lists li {
91+
padding: 0.5rem;
92+
cursor: pointer;
93+
display: flex;
94+
align-items: center;
95+
justify-content: space-between;
96+
border-radius: 4px;
97+
margin-bottom: 0.25rem;
98+
}
99+
100+
.lists li.active {
101+
background: #e8f5e9;
102+
color: #2e7d32;
103+
}
104+
105+
.lists li:hover {
106+
background: #f5f5f5;
107+
}
108+
109+
.todo-count {
110+
color: #666;
111+
font-size: 0.9em;
112+
}
113+
114+
.delete-list {
115+
padding: 0.25rem 0.5rem;
116+
border: none;
117+
background: none;
118+
color: #ff4444;
119+
cursor: pointer;
120+
font-size: 1.2rem;
121+
}
122+
123+
.new-list-input {
124+
margin-bottom: 1rem;
125+
}
126+
127+
.new-list-input input {
128+
width: 100%;
129+
padding: 0.5rem;
130+
margin-bottom: 0.5rem;
131+
border: 1px solid #ddd;
132+
border-radius: 4px;
133+
}
134+
135+
.new-list-actions {
136+
display: flex;
137+
gap: 0.5rem;
138+
}
139+
140+
@media (max-width: 768px) {
141+
.lists-container {
142+
border-right: none;
143+
border-bottom: 1px solid #eee;
144+
}
145+
}
146+
</style>
Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
<template>
2-
<div class="container">
3-
<h1>Todo App</h1>
4-
<TodoInput @add-todo="addTodo" />
5-
<SearchBar />
6-
<TodoFilter />
7-
<TodoList :todos="store.filteredTodos" @toggle="toggleTodo" @delete="deleteTodo" />
8-
<TodoActions />
2+
<div class="page-container">
3+
<div class="sidebar" :class="{ 'show-mobile': showSidebar }">
4+
<TodoLists />
5+
</div>
6+
<div class="main-content">
7+
<button class="mobile-menu" @click="showSidebar = !showSidebar">
8+
{{ showSidebar ? '×' : '☰' }}
9+
</button>
10+
<h1>{{ store.activeList?.name || 'Todo App' }}</h1>
11+
<TodoInput @add-todo="addTodo" />
12+
<SearchBar />
13+
<TodoFilter />
14+
<TodoList :todos="store.filteredTodos" @toggle="toggleTodo" @delete="deleteTodo" />
15+
<TodoActions />
16+
</div>
917
</div>
1018
</template>
1119

1220
<script setup lang="ts">
21+
import { ref } from 'vue'
1322
import { useTodoStore } from '~/stores/todo-store'
1423
1524
const store = useTodoStore()
25+
const showSidebar = ref(false)
1626
1727
const addTodo = (title: string) => {
1828
store.addTodo(title)
@@ -28,14 +38,61 @@ const deleteTodo = (id: number) => {
2838
</script>
2939

3040
<style scoped>
31-
.container {
32-
max-width: 600px;
33-
margin: 2rem auto;
34-
padding: 0 1rem;
41+
.page-container {
42+
display: flex;
43+
max-width: 1200px;
44+
margin: 0 auto;
45+
min-height: 100vh;
3546
}
3647
37-
h1 {
38-
text-align: center;
39-
color: #2c3e50;
48+
.sidebar {
49+
width: 250px;
50+
flex-shrink: 0;
51+
}
52+
53+
.main-content {
54+
flex: 1;
55+
padding: 2rem;
56+
position: relative;
57+
}
58+
59+
.mobile-menu {
60+
display: none;
61+
}
62+
63+
@media (max-width: 768px) {
64+
.page-container {
65+
display: block;
66+
}
67+
68+
.sidebar {
69+
position: fixed;
70+
left: 0;
71+
top: 0;
72+
bottom: 0;
73+
background: white;
74+
z-index: 100;
75+
transform: translateX(-100%);
76+
transition: transform 0.3s ease;
77+
}
78+
79+
.sidebar.show-mobile {
80+
transform: translateX(0);
81+
}
82+
83+
.mobile-menu {
84+
display: block;
85+
position: absolute;
86+
top: 1rem;
87+
left: 1rem;
88+
background: none;
89+
border: none;
90+
font-size: 1.5rem;
91+
cursor: pointer;
92+
}
93+
94+
.main-content {
95+
padding-top: 4rem;
96+
}
4097
}
4198
</style>

4 - Frameworks/Ejercicios/VUE/vue-lab/todo-app/stores/todo-store.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
import { defineStore } from "pinia";
22
import { ref, computed } from "vue";
3-
import type { Todo, FilterType } from "~/types/index";
3+
import type { Todo, FilterType, TodoList } from "~/types/index";
44

55
export const useTodoStore = defineStore(
66
"todos",
77
() => {
8+
const lists = ref<TodoList[]>([{ id: 1, name: "Default List", todos: [] }]);
89
const todos = ref<Todo[]>([]);
910
const editingId = ref<number | null>(null);
1011
const filter = ref<FilterType>("all");
1112
const searchQuery = ref("");
13+
const activeListId = ref<number | null>(1);
14+
15+
const activeList = computed(() =>
16+
lists.value.find((list) => list.id === activeListId.value)
17+
);
18+
19+
const addList = (name: string) => {
20+
lists.value.push({
21+
id: Date.now(),
22+
name,
23+
todos: [],
24+
});
25+
};
26+
27+
const deleteList = (id: number) => {
28+
lists.value = lists.value.filter((list) => list.id !== id);
29+
if (activeListId.value === id) {
30+
activeListId.value = lists.value[0]?.id;
31+
}
32+
};
33+
34+
const setActiveList = (id: number) => {
35+
activeListId.value = id;
36+
};
1237

1338
const filteredTodos = computed(() => {
1439
let filtered = todos.value;
@@ -30,12 +55,15 @@ export const useTodoStore = defineStore(
3055
});
3156

3257
const addTodo = (title: string) => {
33-
todos.value.push({
34-
id: Date.now(),
35-
title,
36-
completed: false,
37-
createdAt: new Date(),
38-
});
58+
const list = lists.value.find((l) => l.id === activeListId.value);
59+
if (list) {
60+
list.todos.push({
61+
id: Date.now(),
62+
title,
63+
completed: false,
64+
createdAt: new Date(),
65+
});
66+
}
3967
};
4068

4169
const toggleTodo = (id: number) => {
@@ -108,6 +136,12 @@ export const useTodoStore = defineStore(
108136
deleteAllTodos,
109137
completeAllTodos,
110138
uncompleteAllTodos,
139+
addList,
140+
deleteList,
141+
setActiveList,
142+
lists,
143+
activeList,
144+
activeListId,
111145
};
112146
},
113147
{

4 - Frameworks/Ejercicios/VUE/vue-lab/todo-app/types/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@ export interface Todo {
55
createdAt: Date;
66
}
77

8+
export interface TodoList {
9+
id: number;
10+
name: string;
11+
todos: Todo[];
12+
}
13+
814
export type FilterType = "all" | "active" | "completed";

0 commit comments

Comments
 (0)