Add basic scroll for blocks details
This commit is contained in:
parent
3a1accaae1
commit
fc0d64fce7
29 changed files with 489 additions and 329 deletions
|
|
@ -10,9 +10,15 @@ import { CancelService } from './services/cancel.service';
|
||||||
export class AppComponent implements DoCheck {
|
export class AppComponent implements DoCheck {
|
||||||
title = 'life';
|
title = 'life';
|
||||||
|
|
||||||
constructor(public cancelService: CancelService) {}
|
constructor(public cancelService: CancelService) {
|
||||||
|
window.addEventListener('keydown', (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
this.cancelService.cancelAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngDoCheck() {
|
ngDoCheck() {
|
||||||
console.log('app change detection');
|
// console.log('app change detection');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { PageComponent } from './components/pages/page/page.component';
|
import { PageComponent } from './components/pages/page/page.component';
|
||||||
|
|
@ -13,39 +12,34 @@ import { FormsModule } from '@angular/forms';
|
||||||
import { BlockComponent } from './components/pages/page/tower/block/block.component';
|
import { BlockComponent } from './components/pages/page/tower/block/block.component';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { EditBlockComponent } from './components/modal/modals/edit-block/edit-block.component';
|
|
||||||
import { SettingsComponent } from './components/modal/modals/settings/settings.component';
|
import { SettingsComponent } from './components/modal/modals/settings/settings.component';
|
||||||
import { RemoveTowerComponent } from './components/modal/modals/remove-tower/remove-tower.component';
|
import { RemoveTowerComponent } from './components/modal/modals/remove-tower/remove-tower.component';
|
||||||
import { RemovePageComponent } from './components/modal/modals/remove-page/remove-page.component';
|
import { RemovePageComponent } from './components/modal/modals/remove-page/remove-page.component';
|
||||||
import { GetStartedComponent } from './components/modal/modals/get-started/get-started.component';
|
import { GetStartedComponent } from './components/modal/modals/get-started/get-started.component';
|
||||||
import { CreateBlockComponent } from './components/modal/modals/create-block/create-block.component';
|
|
||||||
import { RemoveBlockComponent } from './components/modal/modals/remove-block/remove-block.component';
|
|
||||||
import { ToggleComponent } from './components/shared/toggle/toggle.component';
|
import { ToggleComponent } from './components/shared/toggle/toggle.component';
|
||||||
import { TasksComponent } from './components/pages/page/tower/tasks/tasks.component';
|
import { TasksComponent } from './components/pages/page/tower/tasks/tasks.component';
|
||||||
import { ColorPipe } from './pipes/color.pipe';
|
import { ColorPipe } from './pipes/color.pipe';
|
||||||
import { Root } from './store/root';
|
import { BlocksComponent } from './components/modal/modals/blocks/blocks.component';
|
||||||
import { InnerNode, InnerNodeState } from './store/inner-node';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
PageComponent,
|
PageComponent,
|
||||||
|
BlockComponent,
|
||||||
TowerComponent,
|
TowerComponent,
|
||||||
DoubleSliderComponent,
|
DoubleSliderComponent,
|
||||||
PagesComponent,
|
PagesComponent,
|
||||||
SelectAddComponent,
|
SelectAddComponent,
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
BlockComponent,
|
BlockComponent,
|
||||||
EditBlockComponent,
|
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
RemoveTowerComponent,
|
RemoveTowerComponent,
|
||||||
RemovePageComponent,
|
RemovePageComponent,
|
||||||
GetStartedComponent,
|
GetStartedComponent,
|
||||||
CreateBlockComponent,
|
|
||||||
RemoveBlockComponent,
|
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
TasksComponent,
|
TasksComponent,
|
||||||
ColorPipe
|
ColorPipe,
|
||||||
|
BlocksComponent
|
||||||
],
|
],
|
||||||
imports: [BrowserModule, AppRoutingModule, FormsModule, BrowserAnimationsModule, DragDropModule],
|
imports: [BrowserModule, AppRoutingModule, FormsModule, BrowserAnimationsModule, DragDropModule],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<section class="{{ modalService.active ? 'active' : '' }}" [ngSwitch]="modalService.active?.type">
|
<section
|
||||||
<app-create-block (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.createBlock"></app-create-block>
|
(click)="modalService.cancel()"
|
||||||
<app-edit-block (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.editBlock"></app-edit-block>
|
class="{{ modalService.active ? 'active' : '' }}"
|
||||||
<app-remove-block (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.removeBlock"></app-remove-block>
|
[ngSwitch]="modalService.active?.type"
|
||||||
|
>
|
||||||
|
<app-blocks (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.blocks"></app-blocks>
|
||||||
<app-remove-tower (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.removeTower"></app-remove-tower>
|
<app-remove-tower (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.removeTower"></app-remove-tower>
|
||||||
<app-settings (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.settings"></app-settings>
|
<app-settings (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.settings"></app-settings>
|
||||||
<app-get-started (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.getStarted"></app-get-started>
|
<app-get-started (click)="$event.stopPropagation()" *ngSwitchCase="ModalType.getStarted"></app-get-started>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ModalService, ModalType } from '../../services/modal.service';
|
import { ModalService, ModalType } from '../../services/modal.service';
|
||||||
|
import { CancelService } from '../../services/cancel.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-modal',
|
selector: 'app-modal',
|
||||||
|
|
@ -9,16 +10,7 @@ import { ModalService, ModalType } from '../../services/modal.service';
|
||||||
export class ModalComponent {
|
export class ModalComponent {
|
||||||
ModalType = ModalType;
|
ModalType = ModalType;
|
||||||
|
|
||||||
constructor(public modalService: ModalService, private changeDetectionRef: ChangeDetectorRef) {
|
constructor(public modalService: ModalService, private cancelService: CancelService) {
|
||||||
window.addEventListener('keydown', (event: KeyboardEvent) => {
|
this.cancelService.subscribe(this, () => this.modalService.cancel());
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.modalService.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*window.addEventListener('resize', (_: UIEvent) => {
|
|
||||||
console.log('resize');
|
|
||||||
this.changeDetectionRef.markForCheck();
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
src/app/components/modal/modals/blocks/blocks.component.html
Normal file
81
src/app/components/modal/modals/blocks/blocks.component.html
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<section #container>
|
||||||
|
<div class="card transparent"></div>
|
||||||
|
<div
|
||||||
|
*ngFor="let i of range({ min: 1, max: blocks.length + 1 })"
|
||||||
|
(click)="$event.stopPropagation(); scrollToChild(i)"
|
||||||
|
class="card {{ i === activeChild ? 'active' : '' }}"
|
||||||
|
>
|
||||||
|
<div class="mask"></div>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<div class="exit" (click)="modalService.cancel()"></div>
|
||||||
|
<h1>View item</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="select-add-container">
|
||||||
|
<app-select-add
|
||||||
|
class="select"
|
||||||
|
[options]="tower.tags"
|
||||||
|
[default]="blocks[i - 1].tag"
|
||||||
|
[alwaysDropShadow]="true"
|
||||||
|
[onlyShadowBorder]="true"
|
||||||
|
[placeholder]="'Tag this item…'"
|
||||||
|
(value)="blocks[i - 1].changeKeys({ tag: $event })"
|
||||||
|
></app-select-add>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
placeholder="Write a description here…"
|
||||||
|
[value]="blocks[i - 1].description"
|
||||||
|
(change)="blocks[i - 1].changeKeys({ description: $event.target.value })"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<app-toggle
|
||||||
|
[beforeText]="'This task hasn\'t been finished yet'"
|
||||||
|
[afterText]="'Goal already accomplished'"
|
||||||
|
[default]="blocks[i - 1].isDone"
|
||||||
|
(value)="blocks[i - 1].changeKeys({ isDone: $event })"
|
||||||
|
></app-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit {{ editMode ? 'active' : '' }}" (click)="editMode = !editMode">
|
||||||
|
<img src="assets/pen.svg" alt="edit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div (click)="$event.stopPropagation()" class="card {{ false ? 'active' : '' }}">
|
||||||
|
<div class="mask"></div>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<div class="exit" (click)="modalService.cancel()"></div>
|
||||||
|
<h1>Create an item</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="select-add-container">
|
||||||
|
<app-select-add
|
||||||
|
class="select"
|
||||||
|
[options]="tower.tags"
|
||||||
|
[default]="tower.tags.length ? tower.tags[0] : null"
|
||||||
|
[alwaysDropShadow]="true"
|
||||||
|
[onlyShadowBorder]="true"
|
||||||
|
[placeholder]="'Tag this item…'"
|
||||||
|
(value)="editedValues.tag = $event"
|
||||||
|
></app-select-add>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea placeholder="Write a description here…" [(ngModel)]="editedValues.description"></textarea>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<app-toggle
|
||||||
|
[beforeText]="'This task hasn\'t been finished yet'"
|
||||||
|
[afterText]="'Goal already accomplished'"
|
||||||
|
[default]="onlyDone"
|
||||||
|
(value)="editedValues.isDone = $event"
|
||||||
|
></app-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button (click)="submitAdd()" [disabled]="!editedValues.tag">Create</button>
|
||||||
|
</div>
|
||||||
|
<div class="card transparent"></div>
|
||||||
|
</section>
|
||||||
134
src/app/components/modal/modals/blocks/blocks.component.scss
Normal file
134
src/app/components/modal/modals/blocks/blocks.component.scss
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
@import '../../../../../styles';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
@include center-child();
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
@include card();
|
||||||
|
box-shadow: $shadow;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
transform-origin: center center;
|
||||||
|
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 66vw;
|
||||||
|
max-width: 400px;
|
||||||
|
@media (max-width: $mobile-width) {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: var(--large-padding);
|
||||||
|
margin: calc(var(--large-padding) / 2);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
@include card();
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: var(--large-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.transparent {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include inner-spacing(var(--large-padding));
|
||||||
|
|
||||||
|
.header {
|
||||||
|
@include center-child();
|
||||||
|
|
||||||
|
.exit {
|
||||||
|
position: absolute;
|
||||||
|
left: var(--large-padding);
|
||||||
|
|
||||||
|
@include exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
opacity: 0.25;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include square(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: opacity $short-animation-time;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(-1 * #{$line-height});
|
||||||
|
left: 0;
|
||||||
|
height: $line-height;
|
||||||
|
background-color: $text-color;
|
||||||
|
width: 0;
|
||||||
|
transition: width $long-animation-time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: $mobile-width) {
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
&:before {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
&:before {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:last-child:after {
|
||||||
|
content: '';
|
||||||
|
height: 1px;
|
||||||
|
width: var(--large-padding);
|
||||||
|
right: calc(-1 * var(--large-padding));
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
154
src/app/components/modal/modals/blocks/blocks.component.ts
Normal file
154
src/app/components/modal/modals/blocks/blocks.component.ts
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { ChangeDetectorRef, Component, ElementRef, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { ModalService } from '../../../../services/modal.service';
|
||||||
|
import { Tower } from '../../../../model/tower';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { Block } from '../../../../model/block';
|
||||||
|
import { IBlock } from '../../../../interfaces/persistance/block';
|
||||||
|
import { CancelService } from '../../../../services/cancel.service';
|
||||||
|
import { range } from 'src/app/utils/range';
|
||||||
|
import { el } from '@angular/platform-browser/testing/src/browser_util';
|
||||||
|
|
||||||
|
const SWIPE_LIMIT = 0.35;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-blocks',
|
||||||
|
templateUrl: './blocks.component.html',
|
||||||
|
styleUrls: ['./blocks.component.scss']
|
||||||
|
})
|
||||||
|
export class BlocksComponent implements OnInit, OnDestroy {
|
||||||
|
readonly range = range;
|
||||||
|
|
||||||
|
tower: Tower;
|
||||||
|
|
||||||
|
editedValues: Partial<IBlock>;
|
||||||
|
|
||||||
|
endOfScrollToken = 0;
|
||||||
|
editMode = false;
|
||||||
|
activeChild: number;
|
||||||
|
scrollMayEnd = true;
|
||||||
|
|
||||||
|
@ViewChild('container') container: ElementRef;
|
||||||
|
|
||||||
|
private subscription;
|
||||||
|
private onlyDone: boolean;
|
||||||
|
|
||||||
|
@HostListener('click') cancel() {
|
||||||
|
this.cancelService.cancelAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('touchstart') fingerDown() {
|
||||||
|
this.scrollMayEnd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('touchend') fingerUp() {
|
||||||
|
this.scrollMayEnd = true;
|
||||||
|
this.onScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('scroll') onScroll() {
|
||||||
|
this.animateScroll();
|
||||||
|
const newToken = ++this.endOfScrollToken;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (newToken === this.endOfScrollToken && this.scrollMayEnd) {
|
||||||
|
this.adjustPosition();
|
||||||
|
}
|
||||||
|
}, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private modalService: ModalService,
|
||||||
|
private cancelService: CancelService,
|
||||||
|
private changeDetector: ChangeDetectorRef,
|
||||||
|
private component: ElementRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get blocks(): Array<Block> {
|
||||||
|
return this.tower.blocks.filter(b => b.isDone === this.onlyDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
const {
|
||||||
|
tower$,
|
||||||
|
onlyDone,
|
||||||
|
startBlock
|
||||||
|
}: { tower$: Observable<Tower>; onlyDone: boolean; startBlock: Block } = this.modalService.active.input;
|
||||||
|
|
||||||
|
this.onlyDone = onlyDone;
|
||||||
|
this.subscription = tower$.subscribe(value => {
|
||||||
|
this.tower = value;
|
||||||
|
setTimeout(() => this.scrollToChild(this.blocks.indexOf(startBlock) + 1, true));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editedValues = {
|
||||||
|
isDone: onlyDone,
|
||||||
|
description: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
animateScroll() {
|
||||||
|
if (!this.container || !this.component) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const c = this.component.nativeElement;
|
||||||
|
|
||||||
|
[...this.container.nativeElement.children]
|
||||||
|
.slice(1, -1)
|
||||||
|
.forEach(element =>
|
||||||
|
this.animate(
|
||||||
|
element.style,
|
||||||
|
element.querySelector('.mask').style,
|
||||||
|
Math.abs(element.offsetLeft - c.scrollLeft + element.clientWidth / 2 - window.innerWidth / 2) /
|
||||||
|
element.clientWidth
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate(cardStyle, maskStyle, t: number) {
|
||||||
|
t = Math.min(1, Math.max(0, t));
|
||||||
|
maskStyle.opacity = Math.pow(t, 0.5).toString();
|
||||||
|
maskStyle.display = t === 0 ? 'none' : 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustPosition() {
|
||||||
|
if (!this.container || !this.component) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const c = this.component.nativeElement;
|
||||||
|
|
||||||
|
const middle =
|
||||||
|
[...this.container.nativeElement.children]
|
||||||
|
.slice(1, -1)
|
||||||
|
.map(element => Math.abs(element.offsetLeft - c.scrollLeft + element.clientWidth / 2 - window.innerWidth / 2))
|
||||||
|
.map((value, index) => (Math.abs(index + 1 - this.activeChild) === 1 ? value / 1.5 : value))
|
||||||
|
.reduce(
|
||||||
|
(middleIndex, current, currentIndex, list) => (list[middleIndex] < current ? middleIndex : currentIndex),
|
||||||
|
0
|
||||||
|
) + 1;
|
||||||
|
|
||||||
|
this.scrollToChild(middle);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToChild(index: number, instantly?: boolean) {
|
||||||
|
this.activeChild = index;
|
||||||
|
const element = this.container.nativeElement.children[index];
|
||||||
|
this.component.nativeElement.scrollTo({
|
||||||
|
left: element.offsetLeft - (window.innerWidth / 2 - element.clientWidth / 2),
|
||||||
|
behavior: instantly ? 'auto' : 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submitAdd() {
|
||||||
|
this.editedValues.created = new Date();
|
||||||
|
this.tower.addBlock(this.editedValues as IBlock);
|
||||||
|
this.modalService.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.subscription) {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<div class="header">
|
|
||||||
<div class="exit" (click)="modalService.cancel()"></div>
|
|
||||||
<h1>Create an item</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="select-add-container">
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
<app-select-add
|
|
||||||
class="select"
|
|
||||||
[options]="modalService.active.input.options"
|
|
||||||
[default]="modalService.active.input.options[0]"
|
|
||||||
[alwaysDropShadow]="true"
|
|
||||||
[onlyShadowBorder]="true"
|
|
||||||
[placeholder]="'Tag this item…'"
|
|
||||||
(value)="selected = $event"
|
|
||||||
></app-select-add>
|
|
||||||
</div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
|
|
||||||
<textarea placeholder="Write a description here…" [(ngModel)]="description"></textarea>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
<app-toggle
|
|
||||||
[beforeText]="'This task hasn\'t been finished yet'"
|
|
||||||
[afterText]="'Goal already accomplished'"
|
|
||||||
[default]="isDone"
|
|
||||||
(value)="isDone = $event"
|
|
||||||
></app-toggle>
|
|
||||||
</div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
|
|
||||||
<button (click)="submit()" [disabled]="!selected">Create</button>
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
@import '../../../../../styles';
|
|
||||||
|
|
||||||
:host {
|
|
||||||
@include card();
|
|
||||||
|
|
||||||
width: 66vw;
|
|
||||||
max-width: 400px;
|
|
||||||
@media (max-width: $mobile-width) {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: var(--large-padding);
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
box-shadow: $shadow;
|
|
||||||
|
|
||||||
@include inner-spacing(var(--large-padding));
|
|
||||||
|
|
||||||
.header {
|
|
||||||
@include center-child();
|
|
||||||
|
|
||||||
.exit {
|
|
||||||
position: absolute;
|
|
||||||
left: var(--large-padding);
|
|
||||||
|
|
||||||
@include exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { ModalService } from '../../../../services/modal.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-create-block',
|
|
||||||
templateUrl: './create-block.component.html',
|
|
||||||
styleUrls: ['./create-block.component.scss']
|
|
||||||
})
|
|
||||||
export class CreateBlockComponent {
|
|
||||||
selected: string;
|
|
||||||
description: string = null;
|
|
||||||
isDone: boolean = !this.modalService.active.input.isTask;
|
|
||||||
|
|
||||||
constructor(public modalService: ModalService) {}
|
|
||||||
|
|
||||||
submit() {
|
|
||||||
if (!this.selected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modalService.submit({
|
|
||||||
selected: this.selected,
|
|
||||||
description: this.description,
|
|
||||||
isDone: this.isDone
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<div class="header">
|
|
||||||
<div class="exit" (click)="modalService.cancel()"></div>
|
|
||||||
<h1>View item</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="select-add-container">
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
<app-select-add
|
|
||||||
class="select"
|
|
||||||
[options]="modalService.active.input.options"
|
|
||||||
[default]="modalService.active.input.default"
|
|
||||||
[alwaysDropShadow]="true"
|
|
||||||
[onlyShadowBorder]="true"
|
|
||||||
[placeholder]="'Tag this item…'"
|
|
||||||
(value)="selected = $event"
|
|
||||||
></app-select-add>
|
|
||||||
</div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
|
|
||||||
<textarea placeholder="Write a description here…" [(ngModel)]="modalService.active.input.description"></textarea>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
<app-toggle
|
|
||||||
[beforeText]="'This task hasn\'t been finished yet'"
|
|
||||||
[afterText]="'Goal already accomplished'"
|
|
||||||
[default]="modalService.active.input.isDone"
|
|
||||||
(value)="modalService.active.input.isDone = $event"
|
|
||||||
></app-toggle>
|
|
||||||
</div>
|
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
|
|
||||||
<button (click)="submit()">Modify</button>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
@import '../../../../../styles';
|
|
||||||
|
|
||||||
:host {
|
|
||||||
@include card();
|
|
||||||
box-shadow: $shadow;
|
|
||||||
|
|
||||||
width: 66vw;
|
|
||||||
max-width: 400px;
|
|
||||||
@media (max-width: $mobile-width) {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: var(--large-padding);
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
@include inner-spacing(var(--large-padding));
|
|
||||||
|
|
||||||
.header {
|
|
||||||
@include center-child();
|
|
||||||
|
|
||||||
.exit {
|
|
||||||
position: absolute;
|
|
||||||
left: var(--large-padding);
|
|
||||||
|
|
||||||
@include exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { ModalService } from '../../../../services/modal.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-edit-block',
|
|
||||||
templateUrl: './edit-block.component.html',
|
|
||||||
styleUrls: ['./edit-block.component.scss']
|
|
||||||
})
|
|
||||||
export class EditBlockComponent {
|
|
||||||
selected: string;
|
|
||||||
|
|
||||||
constructor(public modalService: ModalService) {}
|
|
||||||
|
|
||||||
submit() {
|
|
||||||
this.modalService.submit({
|
|
||||||
selected: this.selected,
|
|
||||||
description: this.modalService.active.input.description,
|
|
||||||
isDone: this.modalService.active.input.isDone
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<p>
|
|
||||||
remove-block works!
|
|
||||||
</p>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-remove-block',
|
|
||||||
templateUrl: './remove-block.component.html',
|
|
||||||
styleUrls: ['./remove-block.component.scss']
|
|
||||||
})
|
|
||||||
export class RemoveBlockComponent implements OnInit {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
<app-toggle
|
<app-toggle
|
||||||
[beforeText]="'Hide create tower button'"
|
[beforeText]="'Hide create tower button'"
|
||||||
[afterText]="'Show create tower button'"
|
[afterText]="'Show create tower button'"
|
||||||
|
|
@ -12,8 +11,7 @@
|
||||||
(value)="page.setHideCreateTowerButton(!$event)"
|
(value)="page.setHideCreateTowerButton(!$event)"
|
||||||
></app-toggle>
|
></app-toggle>
|
||||||
</div>
|
</div>
|
||||||
<!-- wrapper for easier styling -->
|
|
||||||
|
|
||||||
<p *ngIf="page.towers.length == 5">There can be a maximum of <strong>5</strong> towers on each page.</p>
|
<p *ngIf="page.towers.length == 5">There can be a maximum of <strong>5</strong> towers on each page.</p>
|
||||||
|
|
||||||
<button (click)="deletePage()">Delete current page</button>
|
<button (click)="$event.stopPropagation() || deletePage()">Delete current page</button>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
<div [ngStyle]="{ 'background-color': block.color | color }" (click)="handleClick()"></div>
|
<div [ngStyle]="{ 'background-color': block.color | color }" (click)="$event.stopPropagation() || handleClick()"></div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, Input } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input } from '@angular/core';
|
||||||
import { Block, BlockState } from '../../../../../model/block';
|
|
||||||
import { ModalService } from '../../../../../services/modal.service';
|
import { ModalService } from '../../../../../services/modal.service';
|
||||||
import { ColoredBlock, Tower } from '../../../../../model/tower';
|
import { ColoredBlock, Tower } from '../../../../../model/tower';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-block',
|
selector: 'app-block',
|
||||||
|
|
@ -10,25 +10,21 @@ import { ColoredBlock, Tower } from '../../../../../model/tower';
|
||||||
})
|
})
|
||||||
export class BlockComponent {
|
export class BlockComponent {
|
||||||
@Input() block: ColoredBlock;
|
@Input() block: ColoredBlock;
|
||||||
@Input() tower: Tower;
|
@Input() tower$: Observable<Tower>;
|
||||||
|
|
||||||
constructor(private modalService: ModalService) {}
|
constructor(private modalService: ModalService, private changeDetection: ChangeDetectorRef) {}
|
||||||
|
|
||||||
async handleClick() {
|
async handleClick() {
|
||||||
try {
|
try {
|
||||||
const { selected, description, isDone } = await this.modalService.showEditBlock({
|
await this.modalService.showBlocks({
|
||||||
options: this.tower.tags,
|
tower$: this.tower$,
|
||||||
default: this.block.tag,
|
startBlock: this.block,
|
||||||
description: this.block.description,
|
onlyDone: true
|
||||||
isDone: this.block.isDone
|
|
||||||
});
|
|
||||||
this.block.changeKeys({
|
|
||||||
tag: selected,
|
|
||||||
description,
|
|
||||||
isDone
|
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// pass
|
// pass
|
||||||
|
} finally {
|
||||||
|
this.changeDetection.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<div *ngIf="tasks" class="container {{ tasks.length > 0 ? 'show-hover' : '' }}">
|
<div *ngIf="tasks" class="container {{ tasks.length > 0 ? 'show-hover' : '' }}" (click)="$event.stopPropagation()">
|
||||||
<p class="header" (click)="isOpen = !isOpen">
|
<p class="header" (click)="isOpen = !isOpen">
|
||||||
<strong>
|
<strong>
|
||||||
{{ tasks.length == 0 ? '' : tasks.length }}
|
{{ tasks.length == 0 ? '' : tasks.length }}
|
||||||
</strong>
|
</strong>
|
||||||
|
<!-- ​ is the zero width space -->
|
||||||
{{ tasks.length == 0 ? '​' : tasks.length == 1 ? 'task' : 'tasks' }}
|
{{ tasks.length == 0 ? '​' : tasks.length == 1 ? 'task' : 'tasks' }}
|
||||||
</p>
|
</p>
|
||||||
<div class="all-task" #allTask [ngStyle]="{ height: (isOpen ? allTask?.scrollHeight : 0) + 'px' }">
|
<div class="all-task" #allTask [ngStyle]="{ height: (isOpen ? allTask?.scrollHeight : 0) + 'px' }">
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, ElementRef, Input, ViewChild } from '@angular/core';
|
||||||
import { Block, BlockState } from '../../../../../model/block';
|
import { Block } from '../../../../../model/block';
|
||||||
import { Tower } from '../../../../../model/tower';
|
import { Tower } from '../../../../../model/tower';
|
||||||
import { ModalService } from '../../../../../services/modal.service';
|
import { ModalService } from '../../../../../services/modal.service';
|
||||||
import { CancelService } from '../../../../../services/cancel.service';
|
import { CancelService } from '../../../../../services/cancel.service';
|
||||||
import { IColor } from '../../../../../interfaces/color';
|
import { IColor } from '../../../../../interfaces/color';
|
||||||
import { IBlock } from '../../../../../interfaces/persistance/block';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tasks',
|
selector: 'app-tasks',
|
||||||
templateUrl: './tasks.component.html',
|
templateUrl: './tasks.component.html',
|
||||||
styleUrls: ['./tasks.component.scss']
|
styleUrls: ['./tasks.component.scss']
|
||||||
})
|
})
|
||||||
export class TasksComponent implements OnInit {
|
export class TasksComponent {
|
||||||
@Input() tasks: Array<Block & { color: IColor }>;
|
@Input() tasks: Array<Block & { color: IColor }>;
|
||||||
@Input() tower: Tower;
|
@Input() tower$: Observable<Tower>;
|
||||||
|
|
||||||
private _isOpen = false;
|
private _isOpen = false;
|
||||||
@Input() set isOpen(value: boolean) {
|
@Input() set isOpen(value: boolean) {
|
||||||
|
|
@ -29,47 +29,27 @@ export class TasksComponent implements OnInit {
|
||||||
|
|
||||||
@ViewChild('allTask') allTask: ElementRef;
|
@ViewChild('allTask') allTask: ElementRef;
|
||||||
|
|
||||||
constructor(private modalService: ModalService, private cancelService: CancelService) {
|
constructor(
|
||||||
|
private modalService: ModalService,
|
||||||
|
private cancelService: CancelService,
|
||||||
|
private changeDetection: ChangeDetectorRef
|
||||||
|
) {
|
||||||
this.cancelService.subscribe(this, () => {
|
this.cancelService.subscribe(this, () => {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
|
|
||||||
async handleClick(block: Block) {
|
async handleClick(block: Block) {
|
||||||
try {
|
try {
|
||||||
const { selected, description, isDone } = await this.modalService.showEditBlock({
|
await this.modalService.showBlocks({
|
||||||
options: this.tower.tags,
|
tower$: this.tower$,
|
||||||
default: block.tag,
|
startBlock: block,
|
||||||
description: block.description,
|
onlyDone: false
|
||||||
isDone: block.isDone
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const change: Partial<IBlock> = {
|
|
||||||
tag: selected,
|
|
||||||
description,
|
|
||||||
isDone
|
|
||||||
};
|
|
||||||
if (!block.isDone && isDone) {
|
|
||||||
change.created = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
block.changeKeys(change);
|
|
||||||
} catch {
|
} catch {
|
||||||
// pass
|
// pass
|
||||||
}
|
} finally {
|
||||||
}
|
this.changeDetection.markForCheck();
|
||||||
|
|
||||||
public async addTask() {
|
|
||||||
try {
|
|
||||||
const { selected: tag, description, isDone } = await this.modalService.showCreateBlock({
|
|
||||||
options: this.tower.tags,
|
|
||||||
isTask: true
|
|
||||||
});
|
|
||||||
this.tower.addBlock({ tag, description, isDone });
|
|
||||||
} catch (e) {
|
|
||||||
// pass
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
<div class="tower" *ngIf="tower$ | async">
|
<div class="tower" *ngIf="tower$ | async">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="tasks-container">
|
<div class="tasks-container">
|
||||||
<app-tasks [tasks]="tasks" [tower]="tower$ | async"></app-tasks>
|
<app-tasks [tasks]="tasks" [tower$]="tower$"></app-tasks>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="assets/plus-sign.svg" alt="add item" (click)="addBlock()" />
|
<img src="assets/plus-sign.svg" alt="add item" (click)="$event.stopPropagation() || addBlock()" />
|
||||||
|
|
||||||
<div class="block-container-container">
|
<div class="block-container-container">
|
||||||
<div class="block-container" *ngIf="blocks.length > 0">
|
<div class="block-container" *ngIf="styledBlocks.length > 0">
|
||||||
<app-block
|
<app-block
|
||||||
|
*ngFor="let block of drawableBlocks"
|
||||||
[ngClass]="block.cssClass"
|
[ngClass]="block.cssClass"
|
||||||
[ngStyle]="block.style"
|
[ngStyle]="block.style"
|
||||||
*ngFor="let block of drawableBlocks"
|
|
||||||
[block]="block"
|
[block]="block"
|
||||||
[tower]="tower$ | async"
|
[tower$]="tower$"
|
||||||
></app-block>
|
></app-block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||||
import { ColoredBlock, Tower } from '../../../../model/tower';
|
import { ColoredBlock, Tower } from '../../../../model/tower';
|
||||||
import { ModalService } from '../../../../services/modal.service';
|
import { ModalService } from '../../../../services/modal.service';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
@ -14,9 +14,9 @@ type StyledBlock = ColoredBlock & { style: { [p: string]: string }; shouldDraw:
|
||||||
})
|
})
|
||||||
export class TowerComponent implements OnInit {
|
export class TowerComponent implements OnInit {
|
||||||
@Input() dateRange$: Observable<Range<Date>>;
|
@Input() dateRange$: Observable<Range<Date>>;
|
||||||
private dateRange: Range<Date>;
|
|
||||||
|
|
||||||
@Input() tower$: Observable<Tower>;
|
@Input() tower$: Observable<Tower>;
|
||||||
|
|
||||||
|
private dateRange: Range<Date>;
|
||||||
private tower: Tower;
|
private tower: Tower;
|
||||||
|
|
||||||
get towerName(): string {
|
get towerName(): string {
|
||||||
|
|
@ -28,18 +28,19 @@ export class TowerComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks: Array<ColoredBlock>;
|
tasks: Array<ColoredBlock>;
|
||||||
blocks: Array<StyledBlock> = [];
|
|
||||||
|
styledBlocks: Array<StyledBlock> = [];
|
||||||
|
|
||||||
get drawableBlocks(): Array<StyledBlock> {
|
get drawableBlocks(): Array<StyledBlock> {
|
||||||
return this.blocks.filter(b => b.shouldDraw);
|
return this.styledBlocks.filter(b => b.shouldDraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(private modalService: ModalService) {}
|
public constructor(private modalService: ModalService, private changeDetection: ChangeDetectorRef) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.tower$.subscribe(value => {
|
this.tower$.subscribe(value => {
|
||||||
if (value) {
|
if (value) {
|
||||||
console.log('update');
|
this.styledBlocks = value.coloredBlocks
|
||||||
this.blocks = value.coloredBlocks
|
|
||||||
.filter(b => b.isDone)
|
.filter(b => b.isDone)
|
||||||
.map(b => {
|
.map(b => {
|
||||||
const classedBlock = b as StyledBlock;
|
const classedBlock = b as StyledBlock;
|
||||||
|
|
@ -59,11 +60,10 @@ export class TowerComponent implements OnInit {
|
||||||
(this.tower.blocks.length === value.blocks.length &&
|
(this.tower.blocks.length === value.blocks.length &&
|
||||||
this.tower.blocks.filter(b => b.isDone).length + 1 === value.blocks.filter(b => b.isDone).length)
|
this.tower.blocks.filter(b => b.isDone).length + 1 === value.blocks.filter(b => b.isDone).length)
|
||||||
) {
|
) {
|
||||||
const lastBlock = top(this.blocks);
|
const lastBlock = top(this.styledBlocks);
|
||||||
if (lastBlock) {
|
if (lastBlock) {
|
||||||
lastBlock.style = { transform: 'translateY(500%)', opacity: '0' };
|
lastBlock.style = { transform: 'translateY(500%)', opacity: '0' };
|
||||||
lastBlock.cssClass = 'descend';
|
setTimeout(() => this.makeBlockDescend(lastBlock), 0);
|
||||||
setTimeout(() => (lastBlock.style = { transform: 'translateY(0)', opacity: '1' }), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,30 +79,39 @@ export class TowerComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeBlockDescend(block: StyledBlock) {
|
||||||
|
block.cssClass = 'descend';
|
||||||
|
block.style = { transform: 'translateY(0)', opacity: '1' };
|
||||||
|
}
|
||||||
|
|
||||||
|
makeBlockAscend(block: StyledBlock) {
|
||||||
|
block.cssClass = 'ascend';
|
||||||
|
block.style = { transform: 'translateY(500%)', opacity: '0' };
|
||||||
|
}
|
||||||
|
|
||||||
initData(newDateRange: Range<Date>) {
|
initData(newDateRange: Range<Date>) {
|
||||||
for (const block of this.blocks) {
|
for (const block of this.styledBlocks) {
|
||||||
block.shouldDraw = newDateRange.from <= block.created;
|
block.shouldDraw = newDateRange.from <= block.created;
|
||||||
|
|
||||||
if ((block.cssClass === '' || block.cssClass === 'descend') && newDateRange.to < block.created) {
|
if (newDateRange.to < block.created) {
|
||||||
block.cssClass = 'ascend';
|
this.makeBlockAscend(block);
|
||||||
block.style = { transform: 'translateY(500%)', opacity: '0' };
|
|
||||||
}
|
}
|
||||||
if (block.shouldDraw && block.cssClass === 'ascend' && block.created <= newDateRange.to) {
|
if (block.shouldDraw && block.created <= newDateRange.to) {
|
||||||
block.cssClass = 'descend';
|
this.makeBlockDescend(block);
|
||||||
block.style = { transform: 'translateY(0)', opacity: '1' };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addBlock() {
|
public async addBlock() {
|
||||||
try {
|
try {
|
||||||
const { selected: tag, description, isDone } = await this.modalService.showCreateBlock({
|
await this.modalService.showBlocks({
|
||||||
options: this.tower.tags,
|
tower$: this.tower$,
|
||||||
isTask: false
|
onlyDone: true
|
||||||
});
|
});
|
||||||
this.tower.addBlock({ tag, description, isDone });
|
} catch {
|
||||||
} catch (e) {
|
|
||||||
// pass
|
// pass
|
||||||
|
} finally {
|
||||||
|
this.changeDetection.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,6 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- wrapper for easier styling -->
|
<!-- wrapper for easier styling -->
|
||||||
|
|
||||||
<button [ngClass]="isDragHappening ? 'transparent' : ''" (click)="openSettings()">Settings</button>
|
<button [ngClass]="isDragHappening ? 'transparent' : ''" (click)="$event.stopPropagation(); openSettings()">
|
||||||
|
Settings
|
||||||
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
import { Page } from '../../model/page';
|
import { Page } from '../../model/page';
|
||||||
import { DataService } from '../../services/data.service';
|
import { DataService } from '../../services/data.service';
|
||||||
import { ModalService } from '../../services/modal.service';
|
import { ModalService } from '../../services/modal.service';
|
||||||
|
|
@ -32,7 +32,11 @@ export class PagesComponent implements OnInit {
|
||||||
private readonly _selectedPage: BehaviorSubject<Page> = new BehaviorSubject(null);
|
private readonly _selectedPage: BehaviorSubject<Page> = new BehaviorSubject(null);
|
||||||
readonly selectedPage$: Observable<Page> = this._selectedPage.asObservable();
|
readonly selectedPage$: Observable<Page> = this._selectedPage.asObservable();
|
||||||
|
|
||||||
constructor(public dataService: DataService, private modalService: ModalService) {
|
constructor(
|
||||||
|
public dataService: DataService,
|
||||||
|
private modalService: ModalService,
|
||||||
|
private changeDetection: ChangeDetectorRef
|
||||||
|
) {
|
||||||
const userData = JSON.parse(window.localStorage.getItem(USER_DATA_KEY));
|
const userData = JSON.parse(window.localStorage.getItem(USER_DATA_KEY));
|
||||||
if (userData !== null) {
|
if (userData !== null) {
|
||||||
this.selectedPageName = userData.selectedPage;
|
this.selectedPageName = userData.selectedPage;
|
||||||
|
|
@ -42,7 +46,7 @@ export class PagesComponent implements OnInit {
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataService.children$.subscribe(pages => {
|
this.dataService.children$.subscribe(pages => {
|
||||||
if (pages) {
|
if (pages) {
|
||||||
if (this.pages && this.pages.length - 1 === pages.length) {
|
if (this.pages && !pages.includes(this._selectedPage.getValue().latestVersion)) {
|
||||||
this.selectedPageName = null;
|
this.selectedPageName = null;
|
||||||
}
|
}
|
||||||
this.pages = pages;
|
this.pages = pages;
|
||||||
|
|
@ -54,10 +58,10 @@ export class PagesComponent implements OnInit {
|
||||||
changeName({ from, to }: { from: string; to: string }) {
|
changeName({ from, to }: { from: string; to: string }) {
|
||||||
const page = this.pages.find(p => p.name === from);
|
const page = this.pages.find(p => p.name === from);
|
||||||
if (page) {
|
if (page) {
|
||||||
page.changeName(to);
|
|
||||||
if (from === this.selectedPageName) {
|
if (from === this.selectedPageName) {
|
||||||
this.selectPage(to);
|
this.selectedPageName = to;
|
||||||
}
|
}
|
||||||
|
page.changeName(to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +98,8 @@ export class PagesComponent implements OnInit {
|
||||||
await this.modalService.showSettings(this.selectedPage$);
|
await this.modalService.showSettings(this.selectedPage$);
|
||||||
} catch {
|
} catch {
|
||||||
// pass
|
// pass
|
||||||
|
} finally {
|
||||||
|
this.changeDetection.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';
|
||||||
import { CancelService } from '../../../services/cancel.service';
|
import { CancelService } from '../../../services/cancel.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -43,10 +43,11 @@ export class SelectAddComponent {
|
||||||
newOption: string;
|
newOption: string;
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
|
|
||||||
constructor(private cancelService: CancelService) {
|
constructor(private cancelService: CancelService, private changeDetection: ChangeDetectorRef) {
|
||||||
this.cancelService.subscribe(this, () => {
|
this.cancelService.subscribe(this, () => {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
this.editMode = false;
|
this.editMode = false;
|
||||||
|
this.changeDetection.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ import { top } from '../utils/top';
|
||||||
import { CancelService } from './cancel.service';
|
import { CancelService } from './cancel.service';
|
||||||
import { Page } from '../model/page';
|
import { Page } from '../model/page';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { Block } from '../model/block';
|
||||||
|
|
||||||
export enum ModalType {
|
export enum ModalType {
|
||||||
createBlock,
|
blocks,
|
||||||
editBlock,
|
|
||||||
removeBlock,
|
|
||||||
settings,
|
settings,
|
||||||
removeTower,
|
removeTower,
|
||||||
removePage,
|
removePage,
|
||||||
|
|
@ -30,20 +29,8 @@ export class ModalService {
|
||||||
|
|
||||||
constructor(private cancelService: CancelService) {}
|
constructor(private cancelService: CancelService) {}
|
||||||
|
|
||||||
showCreateBlock(input: {
|
showBlocks(input: { tower$: Observable<Tower>; onlyDone: boolean; startBlock?: Block }): Promise<void> {
|
||||||
options: string[];
|
return this.createPromiseAndPushToStack(input, ModalType.blocks);
|
||||||
isTask: boolean;
|
|
||||||
}): Promise<{ selected: string; description: string; isDone: boolean }> {
|
|
||||||
return this.createPromiseAndPushToStack(input, ModalType.createBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
showEditBlock(data: {
|
|
||||||
default: string;
|
|
||||||
options: string[];
|
|
||||||
description: string;
|
|
||||||
isDone: boolean;
|
|
||||||
}): Promise<{ selected: string; description: string; isDone: boolean }> {
|
|
||||||
return this.createPromiseAndPushToStack(data, ModalType.editBlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showSettings(selectedPage: Observable<Page>): Promise<void> {
|
showSettings(selectedPage: Observable<Page>): Promise<void> {
|
||||||
|
|
@ -63,13 +50,17 @@ export class ModalService {
|
||||||
}
|
}
|
||||||
|
|
||||||
submit(output?: any) {
|
submit(output?: any) {
|
||||||
const { resolve } = this.modalStack.pop();
|
const modal = this.modalStack.pop();
|
||||||
resolve(output);
|
if (modal) {
|
||||||
|
modal.resolve(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
const { reject } = this.modalStack.pop();
|
const modal = this.modalStack.pop();
|
||||||
reject();
|
if (modal) {
|
||||||
|
modal.reject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPromiseAndPushToStack(input: any, type: ModalType): Promise<any> {
|
private createPromiseAndPushToStack(input: any, type: ModalType): Promise<any> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export const range = ({ min = 0, max = Infinity, step = 1 }: { min?: number; max?: number; step?: number }) => {
|
export const range = ({ min = 0, max = Infinity, step = 1 }: { min?: number; max?: number; step?: number }) => {
|
||||||
return {
|
return {
|
||||||
*[Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
for (let i = min; i < max; yield i, i += step);
|
for (let i = min; i < max; yield i, i += step) {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ img {
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue