mirror of
https://github.com/LucasVbr/todo-list.git
synced 2026-05-13 17:22:04 +00:00
Remove components and center the card
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"node_modules/font-awesome/scss/font-awesome.scss",
|
||||||
"node_modules/bulma/bulma.sass",
|
"node_modules/bulma/bulma.sass",
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
],
|
],
|
||||||
|
|||||||
Generated
+14
@@ -17,6 +17,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "^14.1.0",
|
"@angular/platform-browser-dynamic": "^14.1.0",
|
||||||
"@angular/router": "^14.1.0",
|
"@angular/router": "^14.1.0",
|
||||||
"bulma": "^0.9.4",
|
"bulma": "^0.9.4",
|
||||||
|
"font-awesome": "^4.7.0",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
@@ -5822,6 +5823,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/font-awesome": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -15989,6 +15998,11 @@
|
|||||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"font-awesome": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg=="
|
||||||
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "^14.1.0",
|
"@angular/platform-browser-dynamic": "^14.1.0",
|
||||||
"@angular/router": "^14.1.0",
|
"@angular/router": "^14.1.0",
|
||||||
"bulma": "^0.9.4",
|
"bulma": "^0.9.4",
|
||||||
|
"font-awesome": "^4.7.0",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
|
|||||||
@@ -1,10 +1,40 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="card m-6">
|
<div class="card p-6 mt-6">
|
||||||
<div class="content p-6">
|
<h1 class="title has-text-centered">{{ title }}</h1>
|
||||||
<h1 class="title has-text-centered">Todo List</h1>
|
|
||||||
<app-todo-list></app-todo-list>
|
<!-- Todolist -->
|
||||||
<app-todo-input></app-todo-input>
|
<div class="m-5">
|
||||||
|
<div *ngIf="list.length == 0">
|
||||||
|
<p class="empty-list subtitle has-text-centered">Empty list</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns" *ngFor="let item of list">
|
||||||
|
<input type="checkbox" [(ngModel)]="item.checked" name="{{ item.text }}" id="{{ item.text }}">
|
||||||
|
<label class="checkbox pl-2" for="{{ item.text }}">
|
||||||
|
{{ item.text }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Input -->
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<div class="column">
|
||||||
|
<input class="input" type="text" [(ngModel)]="inputValue" (keydown.enter)="addItem()">
|
||||||
|
</div>
|
||||||
|
<div class="column is-2">
|
||||||
|
<button class="button is-primary" (click)="addItem()">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="column is-2">
|
||||||
|
<button class="button is-danger" (click)="removeCheckedItems()">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
input[type=checkbox] + label {
|
||||||
|
transition: all 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:checked + label {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-list {
|
||||||
|
color: gray;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,56 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import TodoItem from "../models/todo-item.model";
|
||||||
|
import LocalService from "../services/local.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit {
|
||||||
title = 'todo-list';
|
public title = 'Todo List App';
|
||||||
|
public list: TodoItem[] = [];
|
||||||
|
|
||||||
|
private LOCAL_STORAGE_KEY = "todoList";
|
||||||
|
inputValue: any;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const savedData: null | TodoItem[] = LocalService.getData(this.LOCAL_STORAGE_KEY)
|
||||||
|
if (savedData) this.list = savedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new item if input is not empty and the item doesn't exist already
|
||||||
|
*/
|
||||||
|
addItem(): void {
|
||||||
|
const listOfTodoText = this.list.map(item => item.text);
|
||||||
|
|
||||||
|
if (this.inputValue && !listOfTodoText.includes(this.inputValue)) {
|
||||||
|
this.list.push(new TodoItem(this.inputValue))
|
||||||
|
this.inputValue = "";
|
||||||
|
this.saveList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a specific item of the list
|
||||||
|
* @param toRemove TodoItem to remove
|
||||||
|
*/
|
||||||
|
removeItem(toRemove: TodoItem): void {
|
||||||
|
this.list = this.list.filter(item => item !== toRemove);
|
||||||
|
this.saveList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all checked items of the list
|
||||||
|
*/
|
||||||
|
removeCheckedItems(): void {
|
||||||
|
this.list = this.list.filter(item => !item.checked)
|
||||||
|
this.saveList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveList(): void {
|
||||||
|
LocalService.saveData(this.LOCAL_STORAGE_KEY, this.list);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,11 @@ import { NgModule } from '@angular/core';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { TodoListComponent } from './todo-list/todo-list.component';
|
|
||||||
import { TodoItemComponent } from './todo-item/todo-item.component';
|
|
||||||
import { TodoInputComponent } from './todo-input/todo-input.component';
|
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from "@angular/forms";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent
|
||||||
TodoListComponent,
|
|
||||||
TodoItemComponent,
|
|
||||||
TodoInputComponent
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
<div class="mt-5">
|
|
||||||
<input class="input" type="text" [(ngModel)]="textInput" (keyup.enter)="createTodoItem()">
|
|
||||||
<input class="button is-primary mt-2 is-fullwidth" type="button" value="Create" (click)="createTodoItem()">
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { TodoInputComponent } from './todo-input.component';
|
|
||||||
|
|
||||||
describe('TodoInputComponent', () => {
|
|
||||||
let component: TodoInputComponent;
|
|
||||||
let fixture: ComponentFixture<TodoInputComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [ TodoInputComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(TodoInputComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import {TodoItemService} from "../../services/todo-item.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-todo-input',
|
|
||||||
templateUrl: './todo-input.component.html',
|
|
||||||
styleUrls: ['./todo-input.component.scss']
|
|
||||||
})
|
|
||||||
export class TodoInputComponent implements OnInit {
|
|
||||||
textInput: string = "";
|
|
||||||
|
|
||||||
constructor(private todoItemService: TodoItemService) { }
|
|
||||||
ngOnInit(): void {}
|
|
||||||
|
|
||||||
createTodoItem(): void {
|
|
||||||
if (this.textInput) {
|
|
||||||
this.todoItemService.newItem(this.textInput);
|
|
||||||
this.textInput = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<li>
|
|
||||||
<input type="checkbox" [checked]="todoItem.checked" (change)="check()" name="checkbox"
|
|
||||||
id="checkbox-{{ todoItem.id }}">
|
|
||||||
<label class="checkbox ml-2" for="checkbox-{{ todoItem.id }}">{{ todoItem.text }}</label>
|
|
||||||
<!-- Todo add delete button -->
|
|
||||||
</li>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
input:checked + label {
|
|
||||||
color: gray;
|
|
||||||
&::after { width: 100%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
label::after {
|
|
||||||
content: '';
|
|
||||||
width: 0; // Before animation
|
|
||||||
height: 2px;
|
|
||||||
background-color: gray;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: 50%; left: 0;
|
|
||||||
|
|
||||||
transition: width 300ms ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { TodoItemComponent } from './todo-item.component';
|
|
||||||
|
|
||||||
describe('TodoItemComponent', () => {
|
|
||||||
let component: TodoItemComponent;
|
|
||||||
let fixture: ComponentFixture<TodoItemComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [ TodoItemComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(TodoItemComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
|
||||||
import {TodoItem} from "../../models/todo-item.model";
|
|
||||||
import {TodoItemService} from "../../services/todo-item.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-todo-item',
|
|
||||||
templateUrl: './todo-item.component.html',
|
|
||||||
styleUrls: ['./todo-item.component.scss']
|
|
||||||
})
|
|
||||||
export class TodoItemComponent implements OnInit {
|
|
||||||
@Input() todoItem!: TodoItem
|
|
||||||
|
|
||||||
constructor(private todoItemService: TodoItemService) {}
|
|
||||||
ngOnInit(): void {}
|
|
||||||
|
|
||||||
check() {
|
|
||||||
this.todoItem.checked = !this.todoItem.checked;
|
|
||||||
this.todoItemService.checkById(this.todoItem.id, this.todoItem.checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<ul>
|
|
||||||
<app-todo-item *ngFor="let todoItem of todoItems" [todoItem]="todoItem"></app-todo-item>
|
|
||||||
</ul>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { TodoListComponent } from './todo-list.component';
|
|
||||||
|
|
||||||
describe('TodoListComponent', () => {
|
|
||||||
let component: TodoListComponent;
|
|
||||||
let fixture: ComponentFixture<TodoListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [ TodoListComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(TodoListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
|
||||||
import {TodoItem} from "../../models/todo-item.model";
|
|
||||||
import {TodoItemService} from "../../services/todo-item.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-todo-list',
|
|
||||||
templateUrl: './todo-list.component.html',
|
|
||||||
styleUrls: ['./todo-list.component.scss']
|
|
||||||
})
|
|
||||||
export class TodoListComponent implements OnInit {
|
|
||||||
todoItems!: TodoItem[];
|
|
||||||
|
|
||||||
constructor(private todoItemService: TodoItemService) {}
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.todoItemService.loadItems();
|
|
||||||
this.todoItems = this.todoItemService.todoItems;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export class TodoItem {
|
export default class TodoItem {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public id: number,
|
|
||||||
public text: string,
|
public text: string,
|
||||||
public checked: boolean = false
|
public checked: boolean = false
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import TodoItem from "../models/todo-item.model";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export default class LocalService {
|
||||||
|
|
||||||
|
static saveData(key: string, data: TodoItem[]) {
|
||||||
|
localStorage.setItem(key, JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getData(key: string): TodoItem[] | null {
|
||||||
|
const data = localStorage.getItem(key);
|
||||||
|
if (!data) return null
|
||||||
|
|
||||||
|
return JSON.parse(data) as TodoItem[];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import {Injectable} from "@angular/core";
|
|
||||||
import {TodoItem} from "../models/todo-item.model";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class TodoItemService {
|
|
||||||
todoItems: TodoItem[] = [];
|
|
||||||
|
|
||||||
get nextId(): number {
|
|
||||||
return this.todoItems.length + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(id: number): TodoItem {
|
|
||||||
const todoItem = this.todoItems.find(item => item.id === id);
|
|
||||||
if (!todoItem) throw new Error('Todo item not found!');
|
|
||||||
return todoItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
newItem(text: string): void {
|
|
||||||
const newTodoItem: TodoItem = new TodoItem(this.nextId, text);
|
|
||||||
this.todoItems.push(newTodoItem);
|
|
||||||
|
|
||||||
this.saveItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
checkById(id: number, checked: boolean) {
|
|
||||||
this.todoItems[id - 1].checked = checked;
|
|
||||||
this.saveItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
loadItems() {
|
|
||||||
const saveDataRaw: any = localStorage.getItem("todoList");
|
|
||||||
|
|
||||||
if (saveDataRaw as string) this.todoItems = JSON.parse(saveDataRaw);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveItems() {
|
|
||||||
localStorage.setItem("todoList", JSON.stringify(this.todoItems))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +1,9 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
body {
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user