diff --git a/src/app/components/modal/modals/create-block/create-block.component.html b/src/app/components/modal/modals/create-block/create-block.component.html index 581a0e8..ac8762f 100644 --- a/src/app/components/modal/modals/create-block/create-block.component.html +++ b/src/app/components/modal/modals/create-block/create-block.component.html @@ -7,8 +7,8 @@ - + diff --git a/src/app/components/modal/modals/create-block/create-block.component.ts b/src/app/components/modal/modals/create-block/create-block.component.ts index 1911040..bbf3d84 100644 --- a/src/app/components/modal/modals/create-block/create-block.component.ts +++ b/src/app/components/modal/modals/create-block/create-block.component.ts @@ -14,6 +14,10 @@ export class CreateBlockComponent { constructor(public modalService: ModalService) {} submit() { + if (!this.selected) { + return; + } + this.modalService.submit({ selected: this.selected, description: this.description, diff --git a/src/app/components/pages/page/page.component.html b/src/app/components/pages/page/page.component.html index e2e2798..3283c47 100644 --- a/src/app/components/pages/page/page.component.html +++ b/src/app/components/pages/page/page.component.html @@ -16,7 +16,7 @@ trashcan -
+
* { + max-width: 200px; + box-sizing: content-box; + flex: 0 0 auto; + + &:not(:nth-last-child(1)) { + margin-right: var(--medium-padding); + @media (max-width: $mobile-width) { + margin-right: var(--small-padding); + } + } + } + + position: relative; + + @for $i from 1 to 6 { + & > *:first-child:nth-last-child(#{$i}), + & > *:first-child:nth-last-child(#{$i}) ~ * { + width: calc((100% - (#{$i} - 1) * var(--medium-padding)) / #{$i}); + + @media (max-width: $mobile-width) { + width: calc((100% - (#{$i} - 1) * var(--small-padding)) / #{$i}); + } + } + } } .double-slider-container { @@ -38,16 +66,25 @@ } img { - @include square(47.33333px); - padding: 47.3333px 0; - margin: auto; + @include square(48px); + padding: 16px; - position: relative; + position: absolute; z-index: 1500; + bottom: 8px; + left: 50%; + margin: 0 !important; + + transform: translateX(-50%) scale(0); transition: transform $long-animation-time; + + &.active { + transform: translateX(-50%) scale(1); + } + &:hover { - transform: scale(1.2); + transform: translateX(-50%) scale(1.1); } } } diff --git a/src/app/components/pages/page/page.component.ts b/src/app/components/pages/page/page.component.ts index 4dcafc2..73a52c5 100644 --- a/src/app/components/pages/page/page.component.ts +++ b/src/app/components/pages/page/page.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Page } from '../../../model/page'; import { ModalService } from '../../../services/modal.service'; import { DataService } from '../../../services/data.service'; @@ -20,6 +20,8 @@ export class PageComponent { this.updateDates(); } + @Output() isDragHappening: EventEmitter = new EventEmitter(); + get page(): Page { return this._page; } @@ -59,11 +61,13 @@ export class PageComponent { dropDrag(event: any) { this.page.moveTower(event); this.isDragging = false; + this.isDragHappening.emit(false); } startDrag(id: number) { this.draggedTowerIndex = id; this.isDragging = true; + this.isDragHappening.emit(true); } trashEnter() { 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 521a8cc..ca6ed66 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.html +++ b/src/app/components/pages/page/tower/tasks/tasks.component.html @@ -1,16 +1,14 @@ -
+

- {{ tasks.length == 0 ? 'no' : '' }} {{ tasks.length == 0 ? '' : tasks.length }} - {{ tasks.length == 1 ? 'task' : 'tasks' }} + {{ tasks.length == 0 ? '​' : tasks.length == 1 ? 'task' : 'tasks' }}

-

+
+
+

+
diff --git a/src/app/components/pages/page/tower/tasks/tasks.component.scss b/src/app/components/pages/page/tower/tasks/tasks.component.scss index 0378065..7728456 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.scss +++ b/src/app/components/pages/page/tower/tasks/tasks.component.scss @@ -3,12 +3,20 @@ :host { width: 100%; box-sizing: border-box; + position: relative; + z-index: 100000; .container { @include card(); - box-shadow: $shadow-border; - padding: var(--small-padding); - margin: var(--small-padding); + + cursor: pointer; + transition: box-shadow $long-animation-time; + &.show-hover:hover { + box-shadow: $shadow-border; + } + + padding: calc(var(--small-padding) / 2); + margin: calc(var(--small-padding) / 2); max-height: 30vh; overflow-y: auto; @@ -18,7 +26,6 @@ } p { - width: 100%; font-size: var(--medium-font-size); } @@ -29,23 +36,43 @@ margin-top: var(--small-padding); } - width: 100%; + height: 0; box-sizing: border-box; - + transition: height $long-animation-time; overflow-y: hidden; - height: 0; - transition: height $long-animation-time; - - p { - max-width: 60px; - white-space: nowrap; - text-overflow: ellipsis; - overflow-x: hidden; - text-align: left; + .task-container { + display: flex; + align-items: center; &:hover { - font-weight: bold; + p { + @media (min-width: $mobile-width) { + color: inherit !important; + } + } + } + + div { + flex: 0 0 auto; + margin: 0 calc(var(--small-padding) / 2) 0 0; + @include square(var(--small-padding)); + @media (max-width: $mobile-width) { + @include square(calc(var(--small-padding) / 2)); + } + } + + p { + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; + text-align: left; + + @media (max-width: $mobile-width) { + font-size: var(--small-font-size); + } + + position: relative; } } } diff --git a/src/app/components/pages/page/tower/tasks/tasks.component.ts b/src/app/components/pages/page/tower/tasks/tasks.component.ts index 4364249..de0a612 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.ts +++ b/src/app/components/pages/page/tower/tasks/tasks.component.ts @@ -2,6 +2,7 @@ import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { Block } from '../../../../../model/block'; import { Tower } from '../../../../../model/tower'; import { ModalService } from '../../../../../services/modal.service'; +import { CancelService } from '../../../../../services/cancel.service'; @Component({ selector: 'app-tasks', @@ -12,11 +13,25 @@ export class TasksComponent implements OnInit { @Input() tasks: Block[]; @Input() tower: Tower; - @Input() isOpen = false; + private _isOpen = false; + @Input() set isOpen(value: boolean) { + if (value) { + this.cancelService.cancelAllExcept(this); + } + this._isOpen = value; + } + + get isOpen(): boolean { + return this._isOpen; + } @ViewChild('allTask') allTask: ElementRef; - constructor(private modalService: ModalService) {} + constructor(private modalService: ModalService, private cancelService: CancelService) { + this.cancelService.subscribe(this, () => { + this.isOpen = false; + }); + } ngOnInit() {} @@ -39,4 +54,16 @@ export class TasksComponent implements OnInit { // pass } } + + 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 + } + } } diff --git a/src/app/components/pages/page/tower/tower.component.html b/src/app/components/pages/page/tower/tower.component.html index 5308cde..9daa13d 100644 --- a/src/app/components/pages/page/tower/tower.component.html +++ b/src/app/components/pages/page/tower/tower.component.html @@ -15,7 +15,7 @@ diff --git a/src/app/components/pages/page/tower/tower.component.scss b/src/app/components/pages/page/tower/tower.component.scss index 2437cec..239ae2b 100644 --- a/src/app/components/pages/page/tower/tower.component.scss +++ b/src/app/components/pages/page/tower/tower.component.scss @@ -11,8 +11,17 @@ opacity: 0; } - &.cdk-drag-preview, &:hover { + @media (min-width: $mobile-width) { + div { + .container { + box-shadow: $shadow; + } + } + } + } + + &.cdk-drag-preview { div { .container { box-shadow: $shadow; @@ -39,7 +48,7 @@ display: flex; flex-direction: column; align-items: center; - width: 100%; + max-width: 100%; height: 100%; @include inner-spacing(var(--small-padding)); @@ -52,7 +61,7 @@ position: relative; @include card(); - + overflow: hidden; transition: transform $short-animation-time, box-shadow $long-animation-time; @include inner-spacing(var(--medium-padding)); @@ -65,7 +74,7 @@ pointer-events: none; position: absolute; - z-index: 3; + z-index: 2; left: 0; top: 0; @@ -85,9 +94,9 @@ position: relative; z-index: 2; - height: 56px; + height: 48px; @media (max-width: $mobile-width) { - height: 42px; + height: 32px; } opacity: 0.33; @@ -130,7 +139,7 @@ } input[type='text'] { - font-size: var(--medium-font-size); + font-size: var(--small-font-size); text-align: center; @media (min-width: $mobile-width) { width: 50%; diff --git a/src/app/components/pages/page/tower/tower.component.ts b/src/app/components/pages/page/tower/tower.component.ts index 8c4be1c..124bb43 100644 --- a/src/app/components/pages/page/tower/tower.component.ts +++ b/src/app/components/pages/page/tower/tower.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { Tower } from '../../../../model/tower'; import { ModalService } from '../../../../services/modal.service'; import { Block } from '../../../../model/block'; @@ -40,7 +40,10 @@ export class TowerComponent { public async addBlock() { try { - const { selected: tag, description, isDone } = await this.modalService.showCreateBlock(this.tower.tags); + const { selected: tag, description, isDone } = await this.modalService.showCreateBlock({ + options: this.tower.tags, + isTask: false + }); this.tower.addBlock({ tag, description, isDone }); } catch (e) { // pass diff --git a/src/app/components/pages/pages.component.html b/src/app/components/pages/pages.component.html index 2814cfb..328126b 100644 --- a/src/app/components/pages/pages.component.html +++ b/src/app/components/pages/pages.component.html @@ -11,8 +11,12 @@
- +
- + diff --git a/src/app/components/pages/pages.component.scss b/src/app/components/pages/pages.component.scss index e77e995..0388716 100644 --- a/src/app/components/pages/pages.component.scss +++ b/src/app/components/pages/pages.component.scss @@ -18,4 +18,12 @@ .page-container { flex: 1 0 auto; } + + button { + transition: opacity $long-animation-time; + + &.transparent { + opacity: 0; + } + } } diff --git a/src/app/components/pages/pages.component.ts b/src/app/components/pages/pages.component.ts index bed3181..e8d077d 100644 --- a/src/app/components/pages/pages.component.ts +++ b/src/app/components/pages/pages.component.ts @@ -13,6 +13,8 @@ export class PagesComponent { @ViewChild('page') page: ElementRef; @ViewChild('bottom') bottom: ElementRef; + isDragHappening = false; + constructor(public dataService: DataService, private modalService: ModalService) {} async selectPage(selected: string) { diff --git a/src/app/components/shared/double-slider/double-slider.component.scss b/src/app/components/shared/double-slider/double-slider.component.scss index aea24ec..1240944 100644 --- a/src/app/components/shared/double-slider/double-slider.component.scss +++ b/src/app/components/shared/double-slider/double-slider.component.scss @@ -37,11 +37,12 @@ $slider-size: 40px; transition: box-shadow $long-animation-time, transform $long-animation-time; - &:hover { - box-shadow: $shadow; - transform: translateY(-$slider-size / 2 + $line-height / 2) scale(1.1); + @media (min-width: $mobile-width) { + &:hover { + box-shadow: $shadow; + transform: translateY(-$slider-size / 2 + $line-height / 2) scale(1.1); + } } - cursor: pointer; position: relative; 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 a25901d..d9318c4 100644 --- a/src/app/components/shared/double-slider/double-slider.component.ts +++ b/src/app/components/shared/double-slider/double-slider.component.ts @@ -16,6 +16,12 @@ export class DoubleSliderComponent { this._values = values; this.calculateLabels(); + if (this._oneValue > this._otherValue) { + this._oneValue = this.MAX - 1; + } else { + this._otherValue = this.MAX - 1; + } + this.emitValue(); } diff --git a/src/app/components/shared/select-add/select-add.component.scss b/src/app/components/shared/select-add/select-add.component.scss index 799c636..941da58 100644 --- a/src/app/components/shared/select-add/select-add.component.scss +++ b/src/app/components/shared/select-add/select-add.component.scss @@ -98,8 +98,10 @@ $inner-padding: var(--medium-padding); } &:hover { - .background { - box-shadow: $shadow; + @media (min-width: $mobile-width) { + .background { + box-shadow: $shadow; + } } } 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 8936fc7..d52f4d3 100644 --- a/src/app/components/shared/select-add/select-add.component.ts +++ b/src/app/components/shared/select-add/select-add.component.ts @@ -1,4 +1,5 @@ import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; +import { CancelService } from '../../../services/cancel.service'; @Component({ selector: 'app-select-add', @@ -28,6 +29,12 @@ export class SelectAddComponent { newOption: string; isOpen = false; + constructor(private cancelService: CancelService) { + this.cancelService.subscribe(this, () => { + this.isOpen = false; + }); + } + get otherOptions(): string[] { return this.options.filter(a => a !== this.selected); } diff --git a/src/app/model/block.ts b/src/app/model/block.ts index e1a997b..f4d3cec 100644 --- a/src/app/model/block.ts +++ b/src/app/model/block.ts @@ -4,6 +4,11 @@ import { Color } from './color'; export class Block extends Base implements IBlock { constructor(props: IBlock) { + // TODO: remove + if (props.isDone === undefined) { + props.isDone = true; + } + super(props); if (this.created.constructor.name !== 'Date') { diff --git a/src/app/model/page.ts b/src/app/model/page.ts index 2d4018c..d1cabe2 100644 --- a/src/app/model/page.ts +++ b/src/app/model/page.ts @@ -5,6 +5,11 @@ import { ITower } from '../interfaces/persistance/tower'; export class Page extends Base implements IPage { constructor(props) { + // TODO: remove + if (!props.userData) { + props.userData = {}; + } + super(props); // @ts-ignore to prevent update message this.__towers = this.towers.map(t => this.createTower(t)); @@ -37,7 +42,6 @@ export class Page extends Base implements IPage { do { hue = Math.random() * 360; } while (30 <= hue && hue <= 200); - console.log(hue); this.towers.push( this.createTower({ diff --git a/src/app/model/tower.ts b/src/app/model/tower.ts index 3dc3698..be97bb8 100644 --- a/src/app/model/tower.ts +++ b/src/app/model/tower.ts @@ -3,7 +3,7 @@ import { Color } from './color'; import { Block } from './block'; import { Base } from './base'; import { IBlock } from '../interfaces/persistance/block'; -import { hashCode } from '../utils/hash'; +import { hash } from '../utils/hash'; export class Tower extends Base implements ITower { constructor(props: ITower) { @@ -46,7 +46,7 @@ export class Tower extends Base implements ITower { if (!this.tags.includes(block.tag)) { this.tags.push(block.tag); } - block.color = this.baseColor.lighten(hashCode(block.tag) * 50); + block.color = this.baseColor.lighten(hash(block.tag) * 50); } super.update(); diff --git a/src/app/services/cancel.service.ts b/src/app/services/cancel.service.ts new file mode 100644 index 0000000..b159577 --- /dev/null +++ b/src/app/services/cancel.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; + +interface Subscriber { + callback: () => void; + object: object; +} + +@Injectable({ + providedIn: 'root' +}) +export class CancelService { + private subscribers: Subscriber[] = []; + + constructor() {} + + subscribe(object: object, callback: () => void) { + this.subscribers.push({ + object, + callback + }); + } + + cancelAllExcept(except: object) { + this.subscribers.filter(s => s.object !== except).map(s => s.callback()); + } + + cancelAll() { + this.subscribers.map(s => s.callback()); + } +} diff --git a/src/app/services/modal.service.ts b/src/app/services/modal.service.ts index dcbf171..e7c85fa 100644 --- a/src/app/services/modal.service.ts +++ b/src/app/services/modal.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { Tower } from '../model/tower'; import { top } from '../utils/top'; +import { CancelService } from './cancel.service'; export enum ModalType { createBlock, @@ -25,8 +26,13 @@ interface Modal { export class ModalService { private modalStack: Modal[] = []; - showCreateBlock(options: string[]): Promise<{ selected: string; description: string; isDone: boolean }> { - return this.createPromiseAndPushToStack(options, ModalType.createBlock); + constructor(private cancelService: CancelService) {} + + showCreateBlock(input: { + options: string[]; + isTask: boolean; + }): Promise<{ selected: string; description: string; isDone: boolean }> { + return this.createPromiseAndPushToStack(input, ModalType.createBlock); } showEditBlock(data: { @@ -65,6 +71,8 @@ export class ModalService { } private createPromiseAndPushToStack(input: any, type: ModalType): Promise { + this.cancelService.cancelAll(); + const modal = { input, type, diff --git a/src/app/services/store.service.ts b/src/app/services/store.service.ts index 19d20ea..702ae3b 100644 --- a/src/app/services/store.service.ts +++ b/src/app/services/store.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Page } from '../model/page'; -const LOCAL_STORAGE_KEY = 'life-towers.data.v.2.1'; +const LOCAL_STORAGE_KEY = 'life-towers.data.v.2'; @Injectable({ providedIn: 'root' diff --git a/src/app/utils/hash.ts b/src/app/utils/hash.ts index d7a5392..049f611 100644 --- a/src/app/utils/hash.ts +++ b/src/app/utils/hash.ts @@ -1,14 +1,8 @@ -export const hashCode = (text: string) => { - let hash = 0; - if (text.length == 0) { - return hash; +export const hash = (text: string): number => { + // Return number between 0 and 1. + if (!text) { + return 0; } - for (let i = 0; i < text.length; i++) { - const char = text.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - - hash /= Math.pow(2, 32) - 1; - return hash; + const hash = Array.prototype.reduce.call(text, (hash, char) => (hash << 5) - hash + char.charCodeAt(0), 7); + return hash / (Math.pow(2, 32) - 1); }; diff --git a/src/library/common-variables.scss b/src/library/common-variables.scss index 26cd528..c09ae5c 100644 --- a/src/library/common-variables.scss +++ b/src/library/common-variables.scss @@ -1,5 +1,5 @@ $accent-color: #a2666f; -$text-color: #5d576b; +$text-color: #5d576bff; $light-color: #ffffff; $background-gradient: linear-gradient(90deg, #fff9e077 0, #ffd6d677 100%); diff --git a/src/library/forms.scss b/src/library/forms.scss index b0e96e8..0e730a1 100644 --- a/src/library/forms.scss +++ b/src/library/forms.scss @@ -43,10 +43,35 @@ button { background: transparent; border: 0; - text-decoration: underline; @include medium-text(); - @include jump(); + font-size: var(--large-font-size); + $height: 2px; + cursor: pointer; + border-bottom: solid $height #5d576b55; + position: relative; + + &:disabled { + color: #5d576b55; + border-bottom: solid $height #5d576b33; + cursor: not-allowed; + } + + &:not(:disabled):hover { + &:after { + width: 100%; + } + } + &:after { + content: ''; + width: 0; + height: $height; + position: absolute; + left: 0; + bottom: calc(-1 * #{$height}); + background-color: $text-color; + transition: width 300ms; + } } label { diff --git a/src/library/main.scss b/src/library/main.scss index 12a85ea..47bddb0 100644 --- a/src/library/main.scss +++ b/src/library/main.scss @@ -60,3 +60,8 @@ img { border-radius: var(--border-radius); cursor: pointer; } + +* { + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; +}