From db6a31dd85aead5563a70ffdfa684fef68f83985 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Sun, 1 Sep 2019 18:13:21 +0200 Subject: [PATCH] Add falling animation --- angular.json | 6 +- .../remove-tower/remove-tower.component.html | 4 +- .../remove-tower/remove-tower.component.ts | 3 + .../modals/settings/settings.component.html | 4 +- .../modals/settings/settings.component.ts | 8 +- .../components/pages/page/page.component.html | 19 ++-- .../components/pages/page/page.component.scss | 22 ++++- .../components/pages/page/page.component.ts | 68 +++++++------ .../page/tower/tasks/tasks.component.html | 2 +- .../pages/page/tower/tower.component.html | 16 ++- .../pages/page/tower/tower.component.scss | 22 ++--- .../pages/page/tower/tower.component.ts | 98 ++++++++++++++----- src/app/components/pages/pages.component.html | 6 +- src/app/components/pages/pages.component.ts | 36 ++----- .../double-slider/double-slider.component.ts | 22 +++-- .../shared/select-add/select-add.component.ts | 2 +- src/app/interfaces/persistance/page.ts | 6 +- src/app/interfaces/range.ts | 4 + src/app/services/data.service.ts | 11 ++- src/app/services/store.service.ts | 4 +- 20 files changed, 211 insertions(+), 152 deletions(-) create mode 100644 src/app/interfaces/range.ts diff --git a/angular.json b/angular.json index 408a516..1779d21 100644 --- a/angular.json +++ b/angular.json @@ -35,7 +35,7 @@ "with": "src/environments/environment.prod.ts" } ], - "optimization": false, + "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, @@ -47,8 +47,8 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "20mb", + "maximumError": "50mb" } ] } diff --git a/src/app/components/modal/modals/remove-tower/remove-tower.component.html b/src/app/components/modal/modals/remove-tower/remove-tower.component.html index 639b547..83fdc83 100644 --- a/src/app/components/modal/modals/remove-tower/remove-tower.component.html +++ b/src/app/components/modal/modals/remove-tower/remove-tower.component.html @@ -6,9 +6,7 @@

You are trying to remove - {{ - modalService.active.input.name ? modalService.active.input.name : 'an unnamed tower' - }}{{ tower.name ? tower.name : 'an unnamed tower' }}.

diff --git a/src/app/components/modal/modals/remove-tower/remove-tower.component.ts b/src/app/components/modal/modals/remove-tower/remove-tower.component.ts index 45a3113..5444702 100644 --- a/src/app/components/modal/modals/remove-tower/remove-tower.component.ts +++ b/src/app/components/modal/modals/remove-tower/remove-tower.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { ModalService } from '../../../../services/modal.service'; +import { Tower } from '../../../../model/tower'; @Component({ selector: 'app-remove-tower', @@ -8,4 +9,6 @@ import { ModalService } from '../../../../services/modal.service'; }) export class RemoveTowerComponent { constructor(public modalService: ModalService) {} + + tower: Tower = this.modalService.active.input; } diff --git a/src/app/components/modal/modals/settings/settings.component.html b/src/app/components/modal/modals/settings/settings.component.html index e3714aa..3d893ae 100644 --- a/src/app/components/modal/modals/settings/settings.component.html +++ b/src/app/components/modal/modals/settings/settings.component.html @@ -9,11 +9,11 @@ [beforeText]="'Hide create tower button'" [afterText]="'Show create tower button'" [default]="!page.userData.hideCreateTowerButton" - (value)="page?.setHideCreateTowerButton(!$event)" + (value)="page.setHideCreateTowerButton(!$event)" > -

There can be a maximum of 5 towers on each page.

+

There can be a maximum of 5 towers on each page.

diff --git a/src/app/components/modal/modals/settings/settings.component.ts b/src/app/components/modal/modals/settings/settings.component.ts index 5fcfe8c..09e470c 100644 --- a/src/app/components/modal/modals/settings/settings.component.ts +++ b/src/app/components/modal/modals/settings/settings.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ModalService } from '../../../../services/modal.service'; import { DataService } from '../../../../services/data.service'; import { Page } from '../../../../model/page'; @@ -8,8 +8,10 @@ import { Page } from '../../../../model/page'; templateUrl: './settings.component.html', styleUrls: ['./settings.component.scss'] }) -export class SettingsComponent { - constructor(public modalService: ModalService, public dataService: DataService) { +export class SettingsComponent implements OnInit { + constructor(public modalService: ModalService, public dataService: DataService) {} + + ngOnInit() { this.modalService.active.input.subscribe(p => (this.page = p)); } diff --git a/src/app/components/pages/page/page.component.html b/src/app/components/pages/page/page.component.html index 0a94a08..1697354 100644 --- a/src/app/components/pages/page/page.component.html +++ b/src/app/components/pages/page/page.component.html @@ -1,21 +1,21 @@ - -
+
+ add tower +
trashcan diff --git a/src/app/components/pages/page/page.component.scss b/src/app/components/pages/page/page.component.scss index 58b5052..1fc1a77 100644 --- a/src/app/components/pages/page/page.component.scss +++ b/src/app/components/pages/page/page.component.scss @@ -24,13 +24,31 @@ transition: box-shadow $short-animation-time; + max-width: 800px; + &.cdk-drop-list-dragging { *:not(.cdk-drag-placeholder) { transition: transform $long-animation-time cubic-bezier(0, 0, 0.2, 1); } } - max-width: 800px; + div { + @include center-child(); + img.add-tower { + height: 48px; + @media (max-width: $mobile-width) { + height: 32px; + } + + opacity: 0.33; + transition: opacity $long-animation-time; + cursor: pointer; + + &:hover { + opacity: 1; + } + } + } & > * { max-width: 200px; @@ -65,7 +83,7 @@ } } - img { + img.trash { @include square(48px); padding: 16px; diff --git a/src/app/components/pages/page/page.component.ts b/src/app/components/pages/page/page.component.ts index f8de9c7..e9467dd 100644 --- a/src/app/components/pages/page/page.component.ts +++ b/src/app/components/pages/page/page.component.ts @@ -1,58 +1,62 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Page } from '../../../model/page'; import { ModalService } from '../../../services/modal.service'; import { DataService } from '../../../services/data.service'; +import { Observable } from 'rxjs/internal/Observable'; +import { Range } from '../../../interfaces/range'; +import { Subject } from 'rxjs/internal/Subject'; +import { Tower } from '../../../model/tower'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; @Component({ selector: 'app-page', templateUrl: './page.component.html', styleUrls: ['./page.component.scss'] }) -export class PageComponent { - private _page: Page; - @Input() set page(value: Page) { - if (!value) { - return; - } +export class PageComponent implements OnInit { + @Input() page$: Observable; + private page: Page; - this._page = value; - this.updateDates(); - } + towers: Array> = []; @Output() isDragHappening: EventEmitter = new EventEmitter(); - get page(): Page { - return this._page; - } - - readonly MIN_BLOCK_COUNT_BEFORE_SHOWING_SLIDER = 3; + readonly MIN_BLOCK_COUNT_BEFORE_SHOWING_SLIDER = 6; isDragging = false; draggedTowerIndex: number; nearTrashcan = false; dates: Date[] = []; - startDate: Date; - endDate: Date; + dateRange: Subject> = new Subject>(); get dateLabels(): string[] { return this.dates.map(d => d.toLocaleDateString()); } - get dateRange(): { from: Date; to: Date } { - return this.dates.length >= this.MIN_BLOCK_COUNT_BEFORE_SHOWING_SLIDER - ? { - from: this.startDate, - to: this.endDate - } - : { - from: new Date(0, 0), - to: new Date(10000, 0) - }; - } - constructor(private modalService: ModalService, public dataService: DataService) {} + ngOnInit(): void { + this.page$.subscribe(value => { + if (value) { + this.towers = value.towers.map((t, index) => { + if (index < this.towers.length) { + if (this.towers[index].getValue() !== t) { + this.towers[index].next(t); + } + return this.towers[index]; + } + return new BehaviorSubject(t); + }); + + this.page = value; + this.dates = value.towers + .reduce((all, t) => [...t.blocks.map(b => b.created), ...all], []) + .sort((d1, d2) => d1.getTime() - d2.getTime()); + } + }); + } + dropDrag(event: any) { this.page.moveTower(event); this.isDragging = false; @@ -88,10 +92,4 @@ export class PageComponent { // pass } } - - private updateDates() { - this.dates = this.page.towers - .reduce((all, t) => [...t.blocks.map(b => b.created), ...all], []) - .sort((d1, d2) => d1.getTime() - d2.getTime()); - } } diff --git a/src/app/components/pages/page/tower/tasks/tasks.component.html b/src/app/components/pages/page/tower/tasks/tasks.component.html index afe2543..4355c5f 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.html +++ b/src/app/components/pages/page/tower/tasks/tasks.component.html @@ -1,4 +1,4 @@ -
+

{{ tasks.length == 0 ? '' : tasks.length }} diff --git a/src/app/components/pages/page/tower/tower.component.html b/src/app/components/pages/page/tower/tower.component.html index de6bcaf..3bcad7e 100644 --- a/src/app/components/pages/page/tower/tower.component.html +++ b/src/app/components/pages/page/tower/tower.component.html @@ -1,14 +1,20 @@ -

+
- +
add item
-
- +
+
@@ -19,6 +25,6 @@ type="text" placeholder="name…" [(ngModel)]="towerName" - [ngStyle]="{ color: tower?.baseColor | color }" + [ngStyle]="{ color: (tower$ | async)?.baseColor | color }" />
diff --git a/src/app/components/pages/page/tower/tower.component.scss b/src/app/components/pages/page/tower/tower.component.scss index 931af89..9d333fc 100644 --- a/src/app/components/pages/page/tower/tower.component.scss +++ b/src/app/components/pages/page/tower/tower.component.scss @@ -121,23 +121,17 @@ bottom: 0; width: 100%; transform: scaleY(-1); - &.falling > *:last-child { - animation: falling 1.5s cubic-bezier(0.5, 0, 1, 0) forwards; - @keyframes falling { - 0% { - opacity: 0; - transform: translateY(500%); - } + * { + transform: translateY(500%); + } - 50% { - opacity: 1; - } + .descend { + transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), opacity 500ms cubic-bezier(0.5, 0, 1, 0); + } - 100% { - transform: translateY(0); - } - } + .ascend { + transition: transform 1.5s cubic-bezier(0.5, 0, 1, 0), opacity 500ms cubic-bezier(0.5, 0, 1, 0) 1s; } } } diff --git a/src/app/components/pages/page/tower/tower.component.ts b/src/app/components/pages/page/tower/tower.component.ts index 0f463c2..6f01947 100644 --- a/src/app/components/pages/page/tower/tower.component.ts +++ b/src/app/components/pages/page/tower/tower.component.ts @@ -1,52 +1,98 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { ColoredBlock, Tower } from '../../../../model/tower'; import { ModalService } from '../../../../services/modal.service'; +import { Observable } from 'rxjs/internal/Observable'; +import { Range } from '../../../../interfaces/range'; +import { top } from '../../../../utils/top'; + +type StyledBlock = ColoredBlock & { style: { [p: string]: string }; shouldDraw: boolean; cssClass: string }; @Component({ selector: 'app-tower', templateUrl: './tower.component.html', styleUrls: ['./tower.component.scss'] }) -export class TowerComponent { +export class TowerComponent implements OnInit { + @Input() dateRange$: Observable>; + private dateRange: Range; + + @Input() tower$: Observable; + private tower: Tower; + get towerName(): string { - return this.tower.name; + return this.tower ? this.tower.name : 'Loading…'; } set towerName(value: string) { this.tower.changeName(value); } - @Input() set dateRange(value: { from: Date; to: Date }) { - if (this.dateRange !== undefined && this.dateRange.from === value.from && this.dateRange.to === value.to) { - return; - } - this._dateRange = value; - } - - get dateRange(): { from: Date; to: Date } { - return this._dateRange; + tasks: Array; + blocks: Array = []; + get drawableBlocks(): Array { + return this.blocks.filter(b => b.shouldDraw); } public constructor(private modalService: ModalService) {} - get drawableBlocks(): Array { - return this.tower.coloredBlocks.filter( - block => this.dateRange.from <= block.created && block.created <= this.dateRange.to && block.isDone - ); + ngOnInit() { + this.tower$.subscribe(value => { + if (value) { + this.blocks = value.coloredBlocks + .filter(b => b.isDone) + .map(b => { + let classedBlock = b as StyledBlock; + classedBlock.shouldDraw = true; + classedBlock.style = { transform: 'translateY(0)', opacity: '1' }; + classedBlock.cssClass = ''; + return classedBlock; + }); + + if (this.tower) { + let difference = this.tower.blocks.map((b, index) => { + return b === value.blocks[index]; + }); + + if ( + (difference.every(i => i) && this.tower.blocks.length < value.blocks.length) || + this.tower.blocks.filter(b => b.isDone).length + 1 === value.blocks.filter(b => b.isDone).length + ) { + const lastBlock = top(this.blocks); + if (lastBlock) { + lastBlock.style = { opacity: '0' }; + lastBlock.cssClass = 'descend'; + setTimeout(() => (lastBlock.style = { transform: 'translateY(0)', opacity: '1' }), 0); + } + } + } + + this.tasks = value.coloredBlocks.filter(block => !block.isDone); + + this.tower = value; + } + }); + + this.dateRange$.subscribe(dateRange => { + this.initData(dateRange); + this.dateRange = dateRange; + }); } - get tasks(): Array { - return this.tower.coloredBlocks.filter( - block => this.dateRange.from <= block.created && block.created <= this.dateRange.to && !block.isDone - ); + initData(newDateRange: Range) { + for (const block of this.blocks) { + block.shouldDraw = newDateRange.from <= block.created; + + if ((block.cssClass === '' || block.cssClass === 'descend') && newDateRange.to < block.created) { + block.cssClass = 'ascend'; + block.style = { transform: 'translateY(500%)', opacity: '0' }; + } + if (block.shouldDraw && block.cssClass === 'ascend' && block.created < newDateRange.to) { + block.cssClass = 'descend'; + block.style = { transform: 'translateY(0)', opacity: '1' }; + } + } } - @Input() tower: Tower; - - _dateRange: { from: Date; to: Date }; - - isFalling = true; - public async addBlock() { try { const { selected: tag, description, isDone } = await this.modalService.showCreateBlock({ diff --git a/src/app/components/pages/pages.component.html b/src/app/components/pages/pages.component.html index d6c427f..4fcaef9 100644 --- a/src/app/components/pages/pages.component.html +++ b/src/app/components/pages/pages.component.html @@ -2,8 +2,8 @@
@@ -11,7 +11,7 @@
- +
diff --git a/src/app/components/pages/pages.component.ts b/src/app/components/pages/pages.component.ts index c006b5a..8e6a9af 100644 --- a/src/app/components/pages/pages.component.ts +++ b/src/app/components/pages/pages.component.ts @@ -27,19 +27,6 @@ export class PagesComponent { return []; } - get selectedPage(): Page { - try { - return this.pages[this.pageNames.indexOf(this.selectedPageName)]; - } catch { - return null; - } - } - - private _selectedPageName: string; - get selectedPageName(): string { - return this._selectedPageName; - } - set selectedPageName(value: string) { window.localStorage.setItem( USER_DATA_KEY, @@ -47,7 +34,8 @@ export class PagesComponent { selectedPage: value }) ); - this._selectedPageName = value; + const index = this.pageNames.indexOf(value); + this._selectedPage.next(index >= 0 ? this.pages[0] : null); } private readonly _selectedPage: BehaviorSubject = new BehaviorSubject(null); @@ -56,28 +44,22 @@ export class PagesComponent { constructor(public dataService: DataService, private modalService: ModalService) { const userData = JSON.parse(window.localStorage.getItem(USER_DATA_KEY)); if (userData !== null && userData.selectedPage !== undefined) { - this._selectedPageName = userData.selectedPage; + this.selectedPageName = userData.selectedPage; } - this.dataService.safeChildren$.subscribe(pages => { + this.dataService.children$.subscribe(pages => { if (pages) { this.pages = pages; - if (!this.selectedPage) { - this.selectedPageName = this.pages.length > 0 ? this.pages[0].name : null; + if (!this._selectedPage.getValue() && this.pages.length > 0) { + this.selectedPageName = this.pages[0].name; + } else if (this._selectedPage.getValue()) { + // To trigger update on new page. + this.selectedPageName = this._selectedPage.getValue().name; } - this._selectedPage.next(this.selectedPage); } }); } - async selectPage(selected: string) { - if (!this.pageNames.includes(selected)) { - this.dataService.addPage(selected); - } - this.selectedPageName = selected; - this._selectedPage.next(this.selectedPage); - } - async openSettings() { try { await this.modalService.showSettings(this.selectedPage$); diff --git a/src/app/components/shared/double-slider/double-slider.component.ts b/src/app/components/shared/double-slider/double-slider.component.ts index d9318c4..9a0278c 100644 --- a/src/app/components/shared/double-slider/double-slider.component.ts +++ b/src/app/components/shared/double-slider/double-slider.component.ts @@ -1,5 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { range } from '../../../utils/range'; +import { Range } from '../../../interfaces/range'; @Component({ selector: 'app-double-slider', @@ -49,8 +50,7 @@ export class DoubleSliderComponent { private _values: any[]; - @Output() lowerBound: EventEmitter = new EventEmitter(); - @Output() upperBound: EventEmitter = new EventEmitter(); + @Output() range: EventEmitter> = new EventEmitter(); drawnLabels: string[]; @@ -93,12 +93,16 @@ export class DoubleSliderComponent { } private emitValue() { - if (this.oneValue < this.otherValue) { - this.lowerBound.emit(this.values[this.indexFromValue(this.oneValue)]); - this.upperBound.emit(this.values[this.indexFromValue(this.otherValue)]); - } else { - this.lowerBound.emit(this.values[this.indexFromValue(this.otherValue)]); - this.upperBound.emit(this.values[this.indexFromValue(this.oneValue)]); - } + const range = { + from: + this.oneValue < this.otherValue + ? this.values[this.indexFromValue(this.oneValue)] + : this.values[this.indexFromValue(this.otherValue)], + to: + this.oneValue < this.otherValue + ? this.values[this.indexFromValue(this.otherValue)] + : this.values[this.indexFromValue(this.oneValue)] + }; + this.range.emit(range); } } diff --git a/src/app/components/shared/select-add/select-add.component.ts b/src/app/components/shared/select-add/select-add.component.ts index d52f4d3..5cc5af7 100644 --- a/src/app/components/shared/select-add/select-add.component.ts +++ b/src/app/components/shared/select-add/select-add.component.ts @@ -59,12 +59,12 @@ export class SelectAddComponent { this.select(this.newOption); this.newOption = ''; } + this.toggle(); } select(option: string) { this.selected = option; this.value.emit(this.selected); - this.toggle(); } toggle() { diff --git a/src/app/interfaces/persistance/page.ts b/src/app/interfaces/persistance/page.ts index 43dd10a..75d9bf7 100644 --- a/src/app/interfaces/persistance/page.ts +++ b/src/app/interfaces/persistance/page.ts @@ -1,4 +1,5 @@ import { ITower } from './tower'; +import { Range } from '../range'; export interface IPage { name: string; @@ -6,9 +7,6 @@ export interface IPage { userData: { hideCreateTowerButton?: boolean; - defaultDateRange?: { - from: Date; - to: Date; - }; + defaultDateRange?: Range; }; } diff --git a/src/app/interfaces/range.ts b/src/app/interfaces/range.ts new file mode 100644 index 0000000..ecbb43d --- /dev/null +++ b/src/app/interfaces/range.ts @@ -0,0 +1,4 @@ +export interface Range { + from: T; + to: T; +} diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index e939076..0bd0771 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -41,11 +41,17 @@ export class DataService extends Root { childrenConstructor: null } }; + for (let page of pages) { new Page(this, page); } + setTimeout(() => { + this.children$.subscribe(value => { + this.log(); + }); + }, 0); + this.children$.subscribe(value => { - this.log(); this._safeChildren.next(value); this.save(0); }); @@ -60,11 +66,12 @@ export class DataService extends Root { } addPage(name: string) { - new Page(this, { + const page = new Page(this, { name, userData: {}, towers: [] }); + page.addTower(); } removePage(page: Page) { diff --git a/src/app/services/store.service.ts b/src/app/services/store.service.ts index 2a029a8..b574e0c 100644 --- a/src/app/services/store.service.ts +++ b/src/app/services/store.service.ts @@ -49,13 +49,13 @@ export class StoreService { isDone: false }, { - created: new Date(2020, 2, 15), + created: new Date(2019, 3, 15), tag: 'go to school', description: 'done it', isDone: true }, { - created: new Date(2021, 2, 15), + created: new Date(2019, 3, 15, 19), tag: 'go to school', isDone: false }