Make scrolling blocks usable
This commit is contained in:
parent
fc0d64fce7
commit
32704c5561
10 changed files with 185 additions and 99 deletions
|
|
@ -1,50 +1,63 @@
|
||||||
<section #container>
|
<section #container>
|
||||||
<div class="card transparent"></div>
|
<div class="card placeholder"></div>
|
||||||
<div
|
<div
|
||||||
*ngFor="let i of range({ min: 1, max: blocks.length + 1 })"
|
*ngFor="let i of range({ max: blocks.length })"
|
||||||
(click)="$event.stopPropagation(); scrollToChild(i)"
|
(click)="$event.stopPropagation(); scrollToChild(i + 1)"
|
||||||
class="card {{ i === activeChild ? 'active' : '' }}"
|
class="card {{ i + 1 === activeChild ? 'active' : '' }} {{
|
||||||
|
i + 2 === activeChild || i === activeChild ? 'near-active' : ''
|
||||||
|
}}"
|
||||||
>
|
>
|
||||||
<div class="mask"></div>
|
<div class="mask"></div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="exit" (click)="modalService.cancel()"></div>
|
<div class="exit" (click)="modalService.cancel()"></div>
|
||||||
|
<div class="block" [ngStyle]="{ 'background-color': tower.getColorOfBlock(editedValues[i]) | color }"></div>
|
||||||
<h1>View item</h1>
|
<h1>View item</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="select-add-container">
|
<div class="select-add-container">
|
||||||
<app-select-add
|
<app-select-add
|
||||||
class="select"
|
class="select"
|
||||||
|
[disabled]="!editMode"
|
||||||
[options]="tower.tags"
|
[options]="tower.tags"
|
||||||
[default]="blocks[i - 1].tag"
|
[default]="editedValues[i].tag"
|
||||||
[alwaysDropShadow]="true"
|
[alwaysDropShadow]="true"
|
||||||
[onlyShadowBorder]="true"
|
[onlyShadowBorder]="true"
|
||||||
[placeholder]="'Tag this item…'"
|
[placeholder]="'Tag this item…'"
|
||||||
(value)="blocks[i - 1].changeKeys({ tag: $event })"
|
(value)="editedValues[i].tag = $event"
|
||||||
></app-select-add>
|
></app-select-add>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
|
[disabled]="!editMode"
|
||||||
placeholder="Write a description here…"
|
placeholder="Write a description here…"
|
||||||
[value]="blocks[i - 1].description"
|
[(ngModel)]="editedValues[i].description"
|
||||||
(change)="blocks[i - 1].changeKeys({ description: $event.target.value })"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<app-toggle
|
<app-toggle
|
||||||
[beforeText]="'This task hasn\'t been finished yet'"
|
[beforeText]="'This task hasn\'t been finished yet'"
|
||||||
[afterText]="'Goal already accomplished'"
|
[afterText]="'Goal already accomplished'"
|
||||||
[default]="blocks[i - 1].isDone"
|
[default]="blocks[i].isDone"
|
||||||
(value)="blocks[i - 1].changeKeys({ isDone: $event })"
|
[disabled]="!editMode"
|
||||||
|
(value)="editedValues[i].isDone = $event"
|
||||||
></app-toggle>
|
></app-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="edit {{ editMode ? 'active' : '' }}" (click)="editMode = !editMode">
|
<div class="bottom">
|
||||||
<img src="assets/pen.svg" alt="edit" />
|
<button class="{{ editMode && i + 1 === activeChild ? '' : 'hidden' }}" (click)="submitChange()"></button>
|
||||||
|
<div class="edit {{ editMode && i + 1 === activeChild ? 'active' : '' }}" (click)="editMode = !editMode">
|
||||||
|
<img src="assets/pen.svg" alt="edit" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div (click)="$event.stopPropagation()" class="card {{ false ? 'active' : '' }}">
|
<div
|
||||||
|
(click)="$event.stopPropagation(); scrollToChild(blocks.length + 1)"
|
||||||
|
class="card {{ blocks.length + 1 === activeChild ? 'active' : '' }} {{
|
||||||
|
blocks.length === activeChild ? 'near-active' : ''
|
||||||
|
}} "
|
||||||
|
>
|
||||||
<div class="mask"></div>
|
<div class="mask"></div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
|
@ -60,22 +73,24 @@
|
||||||
[alwaysDropShadow]="true"
|
[alwaysDropShadow]="true"
|
||||||
[onlyShadowBorder]="true"
|
[onlyShadowBorder]="true"
|
||||||
[placeholder]="'Tag this item…'"
|
[placeholder]="'Tag this item…'"
|
||||||
(value)="editedValues.tag = $event"
|
(value)="top(editedValues).tag = $event"
|
||||||
></app-select-add>
|
></app-select-add>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea placeholder="Write a description here…" [(ngModel)]="editedValues.description"></textarea>
|
<textarea placeholder="Write a description here…" [(ngModel)]="top(editedValues).description"></textarea>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<app-toggle
|
<app-toggle
|
||||||
[beforeText]="'This task hasn\'t been finished yet'"
|
[beforeText]="'This task hasn\'t been finished yet'"
|
||||||
[afterText]="'Goal already accomplished'"
|
[afterText]="'Goal already accomplished'"
|
||||||
[default]="onlyDone"
|
[default]="onlyDone"
|
||||||
(value)="editedValues.isDone = $event"
|
(value)="top(editedValues).isDone = $event"
|
||||||
></app-toggle>
|
></app-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button (click)="submitAdd()" [disabled]="!editedValues.tag">Create</button>
|
<div class="bottom">
|
||||||
|
<button (click)="submitAdd()" [disabled]="!top(editedValues).tag">Create and exit</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card transparent"></div>
|
<div class="card placeholder"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: $mobile-width) {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
@ -44,13 +45,17 @@
|
||||||
margin: calc(var(--large-padding) / 2);
|
margin: calc(var(--large-padding) / 2);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&.near-active {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.mask {
|
.mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 100;
|
z-index: 10000;
|
||||||
|
|
||||||
@include card();
|
@include card();
|
||||||
}
|
}
|
||||||
|
|
@ -59,68 +64,100 @@
|
||||||
margin-left: var(--large-padding);
|
margin-left: var(--large-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.transparent {
|
&.placeholder {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
width: 1000px;
|
||||||
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include inner-spacing(var(--large-padding));
|
@include inner-spacing(var(--large-padding));
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@include center-child();
|
@include center-child();
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.exit {
|
.exit {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: var(--large-padding);
|
left: 0;
|
||||||
|
|
||||||
@include exit();
|
@include exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
@include square(12px);
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit {
|
.bottom {
|
||||||
|
height: 32px;
|
||||||
|
@media (max-width: $mobile-width) {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
|
||||||
float: right;
|
|
||||||
opacity: 0.25;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
img {
|
button {
|
||||||
@include square(16px);
|
margin: 0;
|
||||||
}
|
|
||||||
|
|
||||||
transition: opacity $short-animation-time;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: calc(-1 * #{$line-height});
|
left: 50%;
|
||||||
left: 0;
|
top: 50%;
|
||||||
height: $line-height;
|
transform: translateY(-50%) translateX(-50%);
|
||||||
background-color: $text-color;
|
|
||||||
width: 0;
|
transition: opacity $short-animation-time;
|
||||||
transition: width $long-animation-time;
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $mobile-width) {
|
.edit {
|
||||||
&:hover {
|
position: absolute;
|
||||||
opacity: 0.5;
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
opacity: 0.25;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include square(16px);
|
||||||
}
|
}
|
||||||
&:hover {
|
|
||||||
|
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 {
|
&:before {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
&:before {
|
opacity: 1;
|
||||||
width: 100% !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ import { Block } from '../../../../model/block';
|
||||||
import { IBlock } from '../../../../interfaces/persistance/block';
|
import { IBlock } from '../../../../interfaces/persistance/block';
|
||||||
import { CancelService } from '../../../../services/cancel.service';
|
import { CancelService } from '../../../../services/cancel.service';
|
||||||
import { range } from 'src/app/utils/range';
|
import { range } from 'src/app/utils/range';
|
||||||
import { el } from '@angular/platform-browser/testing/src/browser_util';
|
import { top } from 'src/app/utils/top';
|
||||||
|
|
||||||
const SWIPE_LIMIT = 0.35;
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-blocks',
|
selector: 'app-blocks',
|
||||||
|
|
@ -17,10 +15,11 @@ const SWIPE_LIMIT = 0.35;
|
||||||
})
|
})
|
||||||
export class BlocksComponent implements OnInit, OnDestroy {
|
export class BlocksComponent implements OnInit, OnDestroy {
|
||||||
readonly range = range;
|
readonly range = range;
|
||||||
|
readonly top = top;
|
||||||
|
|
||||||
tower: Tower;
|
tower: Tower;
|
||||||
|
|
||||||
editedValues: Partial<IBlock>;
|
editedValues: Array<Partial<IBlock>>;
|
||||||
|
|
||||||
endOfScrollToken = 0;
|
endOfScrollToken = 0;
|
||||||
editMode = false;
|
editMode = false;
|
||||||
|
|
@ -76,13 +75,16 @@ export class BlocksComponent implements OnInit, OnDestroy {
|
||||||
this.onlyDone = onlyDone;
|
this.onlyDone = onlyDone;
|
||||||
this.subscription = tower$.subscribe(value => {
|
this.subscription = tower$.subscribe(value => {
|
||||||
this.tower = value;
|
this.tower = value;
|
||||||
setTimeout(() => this.scrollToChild(this.blocks.indexOf(startBlock) + 1, true));
|
this.editedValues = this.blocks.map(({ isDone, description, tag }) => ({ isDone, description, tag }));
|
||||||
});
|
this.editedValues.push({
|
||||||
|
isDone: this.onlyDone,
|
||||||
|
description: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.editedValues = {
|
setTimeout(() =>
|
||||||
isDone: onlyDone,
|
this.scrollToChild(startBlock ? this.blocks.indexOf(startBlock) + 1 : this.blocks.length + 1, true)
|
||||||
description: ''
|
);
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
animateScroll() {
|
animateScroll() {
|
||||||
|
|
@ -106,8 +108,9 @@ export class BlocksComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
animate(cardStyle, maskStyle, t: number) {
|
animate(cardStyle, maskStyle, t: number) {
|
||||||
t = Math.min(1, Math.max(0, t));
|
t = Math.min(1, Math.max(0, t));
|
||||||
|
cardStyle.opacity = (1 - t / 1.5).toString();
|
||||||
maskStyle.opacity = Math.pow(t, 0.5).toString();
|
maskStyle.opacity = Math.pow(t, 0.5).toString();
|
||||||
maskStyle.display = t === 0 ? 'none' : 'block';
|
maskStyle.display = t <= 0.1 ? 'none' : 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustPosition() {
|
adjustPosition() {
|
||||||
|
|
@ -141,8 +144,13 @@ export class BlocksComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
submitAdd() {
|
submitAdd() {
|
||||||
this.editedValues.created = new Date();
|
top(this.editedValues).created = new Date();
|
||||||
this.tower.addBlock(this.editedValues as IBlock);
|
this.tower.addBlock(top(this.editedValues) as IBlock);
|
||||||
|
this.modalService.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
submitChange() {
|
||||||
|
this.blocks[this.activeChild - 1].changeKeys(this.editedValues[this.activeChild - 1]);
|
||||||
this.modalService.submit();
|
this.modalService.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
<div class="select-add {{ onlyShadowBorder ? 'shadow-border' : '' }}" (click)="$event.stopPropagation()">
|
<div
|
||||||
<div #top class="top" (click)="!editMode && toggle()">
|
class="select-add {{ onlyShadowBorder ? 'shadow-border' : '' }} {{ disabled ? 'disabled' : '' }}"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
<div #top class="top" (click)="!editMode && !disabled && toggle()">
|
||||||
<p [innerHTML]="selected ? selected : placeholder" *ngIf="!editMode || !selected; else editableSelected"></p>
|
<p [innerHTML]="selected ? selected : placeholder" *ngIf="!editMode || !selected; else editableSelected"></p>
|
||||||
<ng-template #editableSelected>
|
<ng-template #editableSelected>
|
||||||
<input type="text" [value]="selected" (change)="changeOption(selected, $event)" />
|
<input type="text" [value]="selected" (change)="changeOption(selected, $event)" />
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@ $inner-padding: var(--medium-padding);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
.top {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.top,
|
.top,
|
||||||
.bottom {
|
.bottom {
|
||||||
padding: $inner-padding;
|
padding: $inner-padding;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export class SelectAddComponent {
|
||||||
@Input() alwaysDropShadow = false;
|
@Input() alwaysDropShadow = false;
|
||||||
@Input() onlyShadowBorder = false;
|
@Input() onlyShadowBorder = false;
|
||||||
@Input() editable = false;
|
@Input() editable = false;
|
||||||
|
@Input() disabled = false;
|
||||||
|
|
||||||
@Input() set default(value: string) {
|
@Input() set default(value: string) {
|
||||||
this.selected = value;
|
this.selected = value;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<span [className]="!on ? 'active' : ''" (click)="on = false" [innerText]="beforeText"></span>
|
<span [className]="!on ? 'active' : ''" (click)="on = false" [innerText]="beforeText"></span>
|
||||||
|
|
||||||
<input type="checkbox" [(ngModel)]="on" [className]="on ? 'on' : ''" />
|
<label class="{{ disabled ? 'disabled' : '' }}">
|
||||||
|
<input type="checkbox" [disabled]="disabled" [(ngModel)]="on" [className]="on ? 'on' : ''" />
|
||||||
|
</label>
|
||||||
|
|
||||||
<span [className]="on ? 'active' : ''" (click)="on = true" [innerText]="afterText"></span>
|
<span [className]="on ? 'active' : ''" (click)="on = true" [innerText]="afterText"></span>
|
||||||
|
|
|
||||||
|
|
@ -24,46 +24,54 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='checkbox'] {
|
label {
|
||||||
-webkit-appearance: none;
|
display: block;
|
||||||
-moz-appearance: none;
|
|
||||||
|
|
||||||
width: 2 * $size;
|
input[type='checkbox'] {
|
||||||
height: $size;
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
|
||||||
border-radius: 1000px;
|
width: 2 * $size;
|
||||||
box-shadow: $shadow-border;
|
height: $size;
|
||||||
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
@include square($size);
|
|
||||||
|
|
||||||
border-radius: 1000px;
|
border-radius: 1000px;
|
||||||
background-color: $text-color;
|
box-shadow: $shadow-border;
|
||||||
|
|
||||||
transition: box-shadow $long-animation-time, left $long-animation-time, transform $long-animation-time;
|
position: relative;
|
||||||
}
|
cursor: pointer;
|
||||||
|
|
||||||
@media (min-width: $mobile-width) {
|
&:after {
|
||||||
&:hover:after {
|
content: '';
|
||||||
box-shadow: $shadow;
|
position: absolute;
|
||||||
transform: translateX(2px);
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
@include square($size);
|
||||||
|
|
||||||
|
border-radius: 1000px;
|
||||||
|
background-color: $text-color;
|
||||||
|
|
||||||
|
transition: box-shadow $long-animation-time, left $long-animation-time, transform $long-animation-time;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.on:hover:after {
|
&.on:after {
|
||||||
transform: translateX(-2px);
|
left: $size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.on:after {
|
&:not(.disabled) {
|
||||||
left: $size;
|
input[type='checkbox'] {
|
||||||
|
@media (min-width: $mobile-width) {
|
||||||
|
&:hover:after {
|
||||||
|
box-shadow: $shadow;
|
||||||
|
transform: translateX(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.on:hover:after {
|
||||||
|
transform: translateX(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ export class ToggleComponent {
|
||||||
@Input() beforeText: string;
|
@Input() beforeText: string;
|
||||||
@Input() afterText: string;
|
@Input() afterText: string;
|
||||||
|
|
||||||
|
@Input() disabled = false;
|
||||||
|
|
||||||
@Output() value: EventEmitter<boolean> = new EventEmitter();
|
@Output() value: EventEmitter<boolean> = new EventEmitter();
|
||||||
|
|
||||||
@Input() set default(value: boolean) {
|
@Input() set default(value: boolean) {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ export class Tower extends Serializable implements ITower, TowerState {
|
||||||
this.changeKeys({ name });
|
this.changeKeys({ name });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getColorOfBlock(block: Block): IColor {
|
||||||
|
return lighten((hash(block.tag) - 0.5) * 50, this.baseColor);
|
||||||
|
}
|
||||||
|
|
||||||
protected onAfterClone() {
|
protected onAfterClone() {
|
||||||
this.blocks.sort((a, b) => {
|
this.blocks.sort((a, b) => {
|
||||||
return a.created.getTime() - b.created.getTime();
|
return a.created.getTime() - b.created.getTime();
|
||||||
|
|
@ -55,7 +59,7 @@ export class Tower extends Serializable implements ITower, TowerState {
|
||||||
|
|
||||||
this.coloredBlocks = this.blocks.map(b => {
|
this.coloredBlocks = this.blocks.map(b => {
|
||||||
const coloredBlock = b as ColoredBlock;
|
const coloredBlock = b as ColoredBlock;
|
||||||
coloredBlock.color = lighten((hash(coloredBlock.tag) - 0.5) * 50, this.baseColor);
|
coloredBlock.color = this.getColorOfBlock(b);
|
||||||
return coloredBlock;
|
return coloredBlock;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue