diff --git a/package-lock.json b/package-lock.json index 90e9696..5d1f08a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,6 +118,12 @@ "webpack-sources": "1.4.3" }, "dependencies": { + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, "typescript": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.5.tgz", @@ -195,9 +201,9 @@ } }, "@angular/animations": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.1.1.tgz", - "integrity": "sha512-IvKv8sV0ymbzDEX2ZLW+F6nOTQqDYallHexuzRVT9txvNE8TNHyySvLcyC5dTmX9fj9LA72NZ6nFyhxq0LFvtQ==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.1.12.tgz", + "integrity": "sha512-tphpf9QHnOPoL2Jl7KpR+R5aHNW3oifLEmRUTajJYJGvo1uzdUDE82+V9OGOinxJsYseCth9gYJhN24aYTB9NA==" }, "@angular/cli": { "version": "9.1.1", @@ -242,14 +248,14 @@ } }, "@angular/common": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.1.1.tgz", - "integrity": "sha512-bS13veMs7//YqYjYJ+JI78ylaCyVcdFKZKikd5SZa6+r6fajcyvLnSKqrKypG3O1BeJ8vOG/Pq54P5gWhbR6eA==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.1.12.tgz", + "integrity": "sha512-XSIqkbM6VV1yixF9zuzeE5eqN1VsiXS517K2VU0XgCRSAzhVhLOeKsdYjeLf7PdSu/HgW/Tr81H+isi9A9I0YA==" }, "@angular/compiler": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.1.tgz", - "integrity": "sha512-u1IP6IzUgK6lIzrG1cxp96umXgtThyhuFn/KPoyVt7wPxZ6vVR0ZxjM7zycEcrMGzk0nf0nyOKaksJk9sTXTbg==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.12.tgz", + "integrity": "sha512-suefk0OFkaJpUUKnV+phbL4T8fmVGHvzkereY5eqybQlumOez8NPL1PJcygAylh/E6OIAYm8SWookYwM6ZY9dg==" }, "@angular/compiler-cli": { "version": "9.1.1", @@ -455,14 +461,14 @@ } }, "@angular/core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.1.1.tgz", - "integrity": "sha512-6lDlUlePafr/392hOvvTZZl6xPHT50U6658sHUAVIr0Un4mJ2MHNHKZtO45bpn3hM4gjFcYRQ7Rpd0umW74iTA==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.1.12.tgz", + "integrity": "sha512-WVA/eh3fzjx0apOzkKot4YRRUsGkHj50zFQWrAOMgivGaj1YVrvhf+m3hpglj5fn/BkLiFDl8RT0wAE8z9X+gQ==" }, "@angular/forms": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.1.1.tgz", - "integrity": "sha512-NX+LuK8JFisiq3uHCOK6YoN/yIb2R9Ye5mwiOPkuZA3lZLKCnUXqCHZbM8VHy/WdjIxxeUaFMJc38yV8RVoabg==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.1.12.tgz", + "integrity": "sha512-LhjnZlC4WEsEsAJfOZLte+Lks3WBAFVeRv2lzoQNFVr/IMzBNDVfjEaaSqKF1cei3cjY39Df2nYDMJM7HfqbJA==" }, "@angular/language-service": { "version": "9.1.1", @@ -471,19 +477,19 @@ "dev": true }, "@angular/platform-browser": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.1.tgz", - "integrity": "sha512-tjALKhdAWPErj0euIIdvx/31AHEZ7is7ADsMu+nYn2NY2gcPUuiqq3RCUJVxBYJ2Cclq1nlF0i2rEDKh7TrBKg==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.12.tgz", + "integrity": "sha512-rPa/hJcLfdId6bYB0b6pFUo3QIgjZlvUlmtKMGdrLNLYR8XQxPa2Y/UdN/5YeZ12htGw6GXrX9U8U7nTbUSpkw==" }, "@angular/platform-browser-dynamic": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.1.tgz", - "integrity": "sha512-kEox5UOwkRLjGKXLh5o5SYopoAylpKgrXtRrKRKTCMmZTpYSe1bLlXMjpwMAMZ9ZFSTvWp9iX94aT5bJDpLrRQ==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.12.tgz", + "integrity": "sha512-NmwUZaQeMnA6f+vP9Fp9P+qjL72H8dKlxLS76ujlKHVf75pP5oahWS8wfl7KXel1tKW3FQWMMffmKf5/NHRiSw==" }, "@angular/router": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.1.tgz", - "integrity": "sha512-OQ5Ctd+swF7ZNlgUxrkGKk2B4yBFqJm0QCxkM86kFDCKQV/4OButZ+4HPy1HxeozBIAmm2pbQA+YjsAUs0VZcQ==" + "version": "9.1.12", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.12.tgz", + "integrity": "sha512-+qCaXa9y0nsRhzjAYBqmGoQ2YkrdXgftZwuFDf6t4qEi30EXa0oS97KrlFq0M5GKdLIDGrbUm9PcdHSTOI+ZhA==" }, "@babel/code-frame": { "version": "7.8.3", @@ -11511,9 +11517,9 @@ } }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, "tslint": { "version": "6.1.1", diff --git a/src/app/app.component.html b/src/app/app.component.html index f348a5a..c34ed49 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -4,27 +4,3 @@
- -
-
-
-
- - - - -
-
-
- Valido {{profileForm.valid}} - -
- Touched {{profileForm.touched}} -
-
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b31f041..576314c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { FormGroup, FormControl, Form, Validators, AbstractControl } from '@angular/forms'; @Component({ selector: 'app-root', @@ -8,25 +7,4 @@ import { FormGroup, FormControl, Form, Validators, AbstractControl } from '@angu }) export class AppComponent { title = 'Todo'; - profileForm = new FormGroup({ - firstName: new FormControl('', [Validators.required, Validators.maxLength(10)]), - lastName: new FormControl('', [Validators.required, StartsWithAValidator]), - }) - constructor () { - this.profileForm.valueChanges.subscribe(value => console.log(value)); - } - - onSubmit() { - console.log(this.profileForm.value); - } - initialize() { - this.profileForm.reset() - } } - -export function StartsWithAValidator(control: AbstractControl) { - if (!control.value.startsWith('A')) { - return { startsWithA: true }; - } - return null; -} \ No newline at end of file diff --git a/src/app/model/todo-item.ts b/src/app/model/todo-item.ts index bdb4f8b..1e2a408 100644 --- a/src/app/model/todo-item.ts +++ b/src/app/model/todo-item.ts @@ -2,6 +2,7 @@ export class TodoItem { id: number; description: string; isCompleted: boolean = false; + url:string toggleCompleted() { this.isCompleted = !this.isCompleted; diff --git a/src/app/stats/stats.component.html b/src/app/stats/stats.component.html index b599e29..f3bb63c 100644 --- a/src/app/stats/stats.component.html +++ b/src/app/stats/stats.component.html @@ -1,6 +1,6 @@ -
-{{completedPercentage()}} % -
+
+
+ {{completedPercentage()}} +
+
\ No newline at end of file diff --git a/src/app/stats/stats.component.scss b/src/app/stats/stats.component.scss index 3543841..03942bb 100644 --- a/src/app/stats/stats.component.scss +++ b/src/app/stats/stats.component.scss @@ -3,4 +3,6 @@ height: 50px; font-size: x-large; transition: width 0.3s; + + } diff --git a/src/app/stats/stats.component.ts b/src/app/stats/stats.component.ts index 1ec80aa..8300f35 100644 --- a/src/app/stats/stats.component.ts +++ b/src/app/stats/stats.component.ts @@ -15,6 +15,8 @@ export class StatsComponent implements OnInit { ngOnInit(): void { } completedPercentage() { - return Math.round(this.service.completedSize() / this.service.list.length * 100) || 0 + let percentaje = Math.round(this.service.completedSize() / this.service.list.length * 100) || 0 + return percentaje + '%' } + } diff --git a/src/app/todo-app/todo-app.component.html b/src/app/todo-app/todo-app.component.html index 786cc57..cbc01f9 100644 --- a/src/app/todo-app/todo-app.component.html +++ b/src/app/todo-app/todo-app.component.html @@ -1,12 +1,16 @@ - - + + + + (itemEdited) = "onItemEditing($event)" + > - - + + + \ No newline at end of file diff --git a/src/app/todo-app/todo-app.component.ts b/src/app/todo-app/todo-app.component.ts index cc42271..9c407ce 100644 --- a/src/app/todo-app/todo-app.component.ts +++ b/src/app/todo-app/todo-app.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit } from '@angular/core'; -import {TodoItem} from '../model/todo-item'; +import { Component, OnInit, EventEmitter, Output } from '@angular/core'; +import { TodoItem } from '../model/todo-item'; import { element } from 'protractor'; import { TodoService } from '../todo.service'; /** */ @@ -8,11 +8,11 @@ import { TodoService } from '../todo.service'; templateUrl: './todo-app.component.html', styleUrls: ['./todo-app.component.scss'], }) -export class TodoAppComponent { +export class TodoAppComponent { + editingTask = undefined; + isEditingTask = false; - constructor( - private service: TodoService - ) {} + constructor(private service: TodoService) {} getList() { return this.service.list; @@ -24,6 +24,14 @@ export class TodoAppComponent { item.toggleCompleted(); } onTodoItemCreated(task) { - this.service.add(task) + this.service.add(task); } + onItemEditing(task){ + this.editingTask = task; + this.isEditingTask = true; + } + onCancelEditingTask(){ + this.isEditingTask = false; + } + } diff --git a/src/app/todo-form/todo-form.component.html b/src/app/todo-form/todo-form.component.html index ec0ad47..4a23bc8 100644 --- a/src/app/todo-form/todo-form.component.html +++ b/src/app/todo-form/todo-form.component.html @@ -1,10 +1,22 @@ +
- +
Task
+
- + + + + + +
+ + +
Optional:
+
+

URL:

+ +
+ +
\ No newline at end of file diff --git a/src/app/todo-form/todo-form.component.scss b/src/app/todo-form/todo-form.component.scss index 213bb36..b1edf77 100644 --- a/src/app/todo-form/todo-form.component.scss +++ b/src/app/todo-form/todo-form.component.scss @@ -1,3 +1,35 @@ +.task-main-form { + background-color: rgb(70, 162, 255); + padding: 20px; + border-radius: 15px; +} + .add-todo { - width: 80% + width: 100%; +} +.main-row{ + display: flex; + justify-content: stretch; +} + +.optional-row{ + display: flex; + justify-content: stretch; +} + +.optional-row p{ + display: inline-block; + padding: 0px 10px; +} + +.optional-row .add-todo{ + width: 100%; +} + +input.ng-invalid{ + border: 2px solid red; } + +.btn{ + margin: 0px 0px 0px 10px; +} \ No newline at end of file diff --git a/src/app/todo-form/todo-form.component.ts b/src/app/todo-form/todo-form.component.ts index 3366020..cc4ed02 100644 --- a/src/app/todo-form/todo-form.component.ts +++ b/src/app/todo-form/todo-form.component.ts @@ -1,24 +1,97 @@ -import { Component, OnInit, Output, EventEmitter} from '@angular/core'; +import { + Component, + Output, + EventEmitter, + Input, + OnChanges, + SimpleChange, +} from '@angular/core'; import { TodoItem } from '../model/todo-item'; +import { FormGroup, FormControl, AbstractControl } from '@angular/forms'; +import { TodoService } from '../todo.service'; @Component({ selector: 'app-todo-form', templateUrl: './todo-form.component.html', - styleUrls: ['./todo-form.component.scss'] + styleUrls: ['./todo-form.component.scss'], }) export class TodoFormComponent { - @Output() add = new EventEmitter(); - save(description){ - if(!description.value || description.value === '') { + @Input() editingTask; + @Input() isEditingTask; + @Output() cancelEditing = new EventEmitter(); + + taskForm = new FormGroup({ + taskName: new FormControl(''), + taskUrl: new FormControl('', [UrlValidator]), + }); + + constructor(private service: TodoService) {} + + save() { + let description = this.taskForm.controls.taskName; + + if (!description.value || description.value === '') { return; } let task = new TodoItem(); task.description = description.value; task.isCompleted = false; + task.url = this.taskForm.controls.taskUrl.value; this.add.emit(task); - description.value = ''; + + this.taskForm.reset(); + } + + ngOnChanges(changes: { [propertyName: string]: SimpleChange }) { + let isEditing = changes.isEditingTask; + if (isEditing !== undefined) { + if (isEditing.currentValue && !isEditing.previousValue) { + this.taskForm.patchValue({ + taskName: this.editingTask.description, + taskUrl: this.editingTask.url, + }); + } + } } + + edit() { + this.editingTask.description = this.taskForm.controls.taskName.value; + this.editingTask.url = this.taskForm.controls.taskUrl.value; + + this.service.edit(this.editingTask); + this.cancelEdit() + } + cancelEdit() { + this.taskForm.reset(); + this.isEditingTask = false; + this.cancelEditing.emit(); + } + } +export function UrlValidator(control: AbstractControl) { + // valid url and empty strings. + if ( + validURL(control.value) || + control.value == '' || + control.value == undefined + ) { + return null; + } + return { error: true }; +} + +function validURL(str) { + var pattern = new RegExp( + '^(https?:\\/\\/)?' + // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d_]*)?$', + 'i' + ); + return pattern.test(str); +} diff --git a/src/app/todo-list/todo-list.component.html b/src/app/todo-list/todo-list.component.html index 127ec76..3f09035 100644 --- a/src/app/todo-list/todo-list.component.html +++ b/src/app/todo-list/todo-list.component.html @@ -1,11 +1,28 @@ - \ No newline at end of file diff --git a/src/app/todo-list/todo-list.component.scss b/src/app/todo-list/todo-list.component.scss index 0096849..c37550b 100644 --- a/src/app/todo-list/todo-list.component.scss +++ b/src/app/todo-list/todo-list.component.scss @@ -1,5 +1,5 @@ .completed { - background-color: greenyellow; + background-color: #4CED69 } .list { padding: 0; @@ -8,8 +8,27 @@ margin: 0; list-style-type: none; font-size: 2rem; - border-bottom: 1px solid lightgrey; - width: 80%; + border-bottom: 1px solid rgb(172, 170, 170); + border-radius: 5px; + width: 100%; +} +.task-main { + padding: 0px 0px 0px 20px; display: flex; justify-content: space-between; + +} +.buttons{ + min-width: 170px; +} + +.task-url{ + font-size: medium; + padding-left: 20px; } + +.task-list{ + background-color: rgb(219, 219, 221); + padding: 0px; + border-radius: 5px; +} \ No newline at end of file diff --git a/src/app/todo-list/todo-list.component.ts b/src/app/todo-list/todo-list.component.ts index fc5d8f0..e9ecb28 100644 --- a/src/app/todo-list/todo-list.component.ts +++ b/src/app/todo-list/todo-list.component.ts @@ -1,26 +1,37 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { TodoItem } from '../model/todo-item'; @Component({ selector: 'app-todo-list', templateUrl: './todo-list.component.html', - styleUrls: ['./todo-list.component.scss'] + styleUrls: ['./todo-list.component.scss'], }) export class TodoListComponent implements OnInit { @Input() list: any[]; @Output() itemRemoved = new EventEmitter(); @Output() itemStateChanged = new EventEmitter(); - constructor() { } + @Output() itemEdited = new EventEmitter(); - ngOnInit() { - } + constructor() {} + ngOnInit() {} removeItem(id) { this.itemRemoved.emit(id); } - completeTask(item:TodoItem) { + completeTask(item: TodoItem) { this.itemStateChanged.emit(item); + } + editTask(item: TodoItem) { + this.itemEdited.emit(item); } + CompleteUrl(url) { + if (url === null || url === undefined) return ''; -} \ No newline at end of file + // completa la URL para que se pueda navegar por click. + if (!url.startsWith('http')) { + url = '//' + url; + } + return url; + } +} diff --git a/src/app/todo.service.ts b/src/app/todo.service.ts index 9bba8cc..c41b6d6 100644 --- a/src/app/todo.service.ts +++ b/src/app/todo.service.ts @@ -2,15 +2,14 @@ import { Injectable } from '@angular/core'; import { LocalStorageService } from './local-storage.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TodoService { - list = []; lastItemId = 0; - constructor(private storage: LocalStorageService) { } - + constructor(private storage: LocalStorageService) {} + add(task) { const id = this.lastItemId; task.id = id; @@ -24,11 +23,14 @@ export class TodoService { } incompletedSize() { - return this.list.filter(item => !item.isCompleted).length; - + return this.list.filter((item) => !item.isCompleted).length; } completedSize() { - return this.list.filter(item => item.isCompleted).length ; + return this.list.filter((item) => item.isCompleted).length; + } + edit(task) { + const index = this.list.findIndex((element) => element.id === task.id); + this.list[index] = task; } getName() { diff --git a/src/index.html b/src/index.html index 62a7817..9ace089 100644 --- a/src/index.html +++ b/src/index.html @@ -6,6 +6,10 @@ + + + +