diff --git a/angular.json b/angular.json index 3a667d1..b8ad48d 100644 --- a/angular.json +++ b/angular.json @@ -28,6 +28,7 @@ "src/assets" ], "styles": [ + "node_modules/font-awesome/scss/font-awesome.scss", "node_modules/bulma/bulma.sass", "src/styles.scss" ], diff --git a/package-lock.json b/package-lock.json index 7f69f67..8b691ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser-dynamic": "^14.1.0", "@angular/router": "^14.1.0", "bulma": "^0.9.4", + "font-awesome": "^4.7.0", "rxjs": "~7.5.0", "tslib": "^2.3.0", "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": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -15989,6 +15998,11 @@ "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "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": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", diff --git a/package.json b/package.json index 75f3f78..99dfa24 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@angular/platform-browser-dynamic": "^14.1.0", "@angular/router": "^14.1.0", "bulma": "^0.9.4", + "font-awesome": "^4.7.0", "rxjs": "~7.5.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" diff --git a/src/app/app.component.html b/src/app/app.component.html index 27166ee..2ec8989 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,10 +1,40 @@
-
-
-

Todo List

- - +
+

{{ title }}

+ + +
+
+

Empty list

+
+ +
+ + +
+
+ + +
+
+ +
+
+ +
+
+ +
- diff --git a/src/app/app.component.scss b/src/app/app.component.scss index e69de29..5a0c145 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -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; +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 12d5133..bdc8370 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { - title = 'todo-list'; +export class AppComponent implements OnInit { + 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); + } + } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3053728..f87c0b3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -2,17 +2,11 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; 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"; @NgModule({ declarations: [ - AppComponent, - TodoListComponent, - TodoItemComponent, - TodoInputComponent + AppComponent ], imports: [ BrowserModule, diff --git a/src/app/todo-input/todo-input.component.html b/src/app/todo-input/todo-input.component.html deleted file mode 100644 index 6ba32f8..0000000 --- a/src/app/todo-input/todo-input.component.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/src/app/todo-input/todo-input.component.scss b/src/app/todo-input/todo-input.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/todo-input/todo-input.component.spec.ts b/src/app/todo-input/todo-input.component.spec.ts deleted file mode 100644 index a006708..0000000 --- a/src/app/todo-input/todo-input.component.spec.ts +++ /dev/null @@ -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; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ TodoInputComponent ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(TodoInputComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/todo-input/todo-input.component.ts b/src/app/todo-input/todo-input.component.ts deleted file mode 100644 index a5586c0..0000000 --- a/src/app/todo-input/todo-input.component.ts +++ /dev/null @@ -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 = ""; - } - } -} diff --git a/src/app/todo-item/todo-item.component.html b/src/app/todo-item/todo-item.component.html deleted file mode 100644 index 4472c4b..0000000 --- a/src/app/todo-item/todo-item.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
  • - - - -
  • diff --git a/src/app/todo-item/todo-item.component.scss b/src/app/todo-item/todo-item.component.scss deleted file mode 100644 index 62c5008..0000000 --- a/src/app/todo-item/todo-item.component.scss +++ /dev/null @@ -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; - } -} diff --git a/src/app/todo-item/todo-item.component.spec.ts b/src/app/todo-item/todo-item.component.spec.ts deleted file mode 100644 index f7580ef..0000000 --- a/src/app/todo-item/todo-item.component.spec.ts +++ /dev/null @@ -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; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ TodoItemComponent ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(TodoItemComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/todo-item/todo-item.component.ts b/src/app/todo-item/todo-item.component.ts deleted file mode 100644 index c8bdf12..0000000 --- a/src/app/todo-item/todo-item.component.ts +++ /dev/null @@ -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) - } - -} diff --git a/src/app/todo-list/todo-list.component.html b/src/app/todo-list/todo-list.component.html deleted file mode 100644 index 1535950..0000000 --- a/src/app/todo-list/todo-list.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
      - -
    diff --git a/src/app/todo-list/todo-list.component.scss b/src/app/todo-list/todo-list.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/todo-list/todo-list.component.spec.ts b/src/app/todo-list/todo-list.component.spec.ts deleted file mode 100644 index cbb54a9..0000000 --- a/src/app/todo-list/todo-list.component.spec.ts +++ /dev/null @@ -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; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ TodoListComponent ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(TodoListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/todo-list/todo-list.component.ts b/src/app/todo-list/todo-list.component.ts deleted file mode 100644 index eff02e0..0000000 --- a/src/app/todo-list/todo-list.component.ts +++ /dev/null @@ -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; - } -} diff --git a/src/models/todo-item.model.ts b/src/models/todo-item.model.ts index c52b187..cfd7fc8 100644 --- a/src/models/todo-item.model.ts +++ b/src/models/todo-item.model.ts @@ -1,6 +1,6 @@ -export class TodoItem { +export default class TodoItem { + constructor( - public id: number, public text: string, public checked: boolean = false ) {} diff --git a/src/services/local.service.ts b/src/services/local.service.ts new file mode 100644 index 0000000..6ea0762 --- /dev/null +++ b/src/services/local.service.ts @@ -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[]; + } +} diff --git a/src/services/todo-item.service.ts b/src/services/todo-item.service.ts deleted file mode 100644 index 1489c67..0000000 --- a/src/services/todo-item.service.ts +++ /dev/null @@ -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)) - } -} diff --git a/src/styles.scss b/src/styles.scss index 90d4ee0..3b5bd06 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,9 @@ /* 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; +}