This commit is contained in:
Andras Schmelczer 2026-05-30 14:33:39 +01:00
parent e2a60e71a3
commit 003f38ea60
36 changed files with 1543 additions and 1287 deletions

View file

@ -15,7 +15,6 @@ import {
} from '@angular/core';
import { Block, HslColor } from '../../models';
import { SelectAddComponent } from '../shared/select-add/select-add.component';
import { ToggleComponent } from '../shared/toggle/toggle.component';
import { getColorOfTag } from '../../utils/color';
export interface BlockEditSave {
@ -35,9 +34,13 @@ interface EditedValue {
@Component({
selector: 'lt-block-edit',
standalone: true,
imports: [SelectAddComponent, ToggleComponent],
imports: [SelectAddComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@if (viewTitle()) {
<h2 class="view-title">{{ viewTitle() }}</h2>
}
<section
#container
class="carousel"
@ -61,7 +64,7 @@ interface EditedValue {
class="block-dot"
[style.background-color]="colorOfTagForBlock(b.id)"
></div>
<h1>{{ formatDate(b.created_at) }}</h1>
<h1>{{ formatDate(b.created_at, true) }}</h1>
</div>
<div class="select-add-container">
@ -83,14 +86,14 @@ interface EditedValue {
(blur)="flushExisting(b.id)"
></textarea>
<div>
<lt-toggle
<label class="done-checkbox">
<input
type="checkbox"
[checked]="editedFor(b.id).is_done"
(checkedChange)="updateDone(b.id, $event)"
offLabel="This task hasn't been finished yet"
onLabel="Goal already accomplished"
(change)="updateDone(b.id, $any($event.target).checked)"
/>
</div>
<span>Already done</span>
</label>
<div class="bottom">
<button (click)="onDelete(b.id); $event.stopPropagation()">Delete</button>
@ -133,14 +136,14 @@ interface EditedValue {
(input)="updateNewDescription($any($event.target).value)"
></textarea>
<div>
<lt-toggle
<label class="done-checkbox">
<input
type="checkbox"
[checked]="newValue().is_done"
(checkedChange)="updateNewDone($event)"
offLabel="This task hasn't been finished yet"
onLabel="Goal already accomplished"
(change)="updateNewDone($any($event.target).checked)"
/>
</div>
<span>Already done</span>
</label>
<div class="bottom">
<button
@ -166,21 +169,50 @@ interface EditedValue {
right: 0;
bottom: 0;
z-index: 10001; // above modal backdrop (10000)
@media (max-height: $min-height) {
align-items: flex-start;
overflow-y: auto;
}
}
.view-title {
position: fixed;
top: var(--large-padding);
left: var(--large-padding);
right: var(--large-padding);
z-index: 10002;
margin: 0;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
}
.carousel {
--title-clearance: calc((var(--large-padding) * 2) + var(--larger-font-size));
width: 100%;
height: 100%;
display: flex;
align-items: center;
box-sizing: border-box;
padding: var(--title-clearance) 0 var(--large-padding);
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
scroll-snap-type: x mandatory;
@media (max-width: $mobile-width) {
padding: 0 7.5vw;
padding: var(--title-clearance) var(--medium-padding) var(--medium-padding);
}
@media (max-height: $min-height) {
min-height: max-content;
align-items: flex-start;
padding-top: var(--title-clearance);
padding-bottom: var(--medium-padding);
overflow-y: visible;
}
&::-webkit-scrollbar {
@ -199,10 +231,10 @@ interface EditedValue {
max-width: 400px;
scroll-snap-align: center;
@media (max-width: $mobile-width) {
width: 85vw;
max-width: 85vw;
width: min(88vw, 360px);
max-width: calc(100vw - (2 * var(--medium-padding)));
padding: var(--medium-padding);
margin: calc(var(--medium-padding) / 2);
margin: 0 calc(var(--small-padding) / 2);
opacity: 1 !important;
}
box-sizing: border-box;
@ -256,8 +288,9 @@ interface EditedValue {
width: 60vw;
max-width: 60vw;
@media (max-width: $mobile-width) {
width: 7.5vw;
max-width: 7.5vw;
width: var(--medium-padding);
max-width: var(--medium-padding);
min-width: var(--medium-padding);
}
box-shadow: none;
background: transparent;
@ -266,6 +299,12 @@ interface EditedValue {
.header {
@include center-child();
position: relative;
gap: var(--small-padding);
h1 {
min-width: 0;
overflow-wrap: anywhere;
}
.exit {
position: absolute;
@ -289,6 +328,70 @@ interface EditedValue {
}
}
.done-checkbox {
@include medium-text();
display: flex;
align-items: center;
justify-content: center;
gap: var(--small-padding);
width: max-content;
max-width: 100%;
margin: 0 auto var(--medium-padding);
cursor: pointer;
input[type='checkbox'] {
-webkit-appearance: none;
appearance: none;
@include square(22px);
flex: 0 0 auto;
position: relative;
box-sizing: border-box;
margin: 0;
border: 0;
border-radius: 4px;
background: $light-color;
box-shadow: $shadow-border;
cursor: pointer;
transition: background-color $short-animation-time, box-shadow $long-animation-time, transform $short-animation-time;
&::after {
content: '';
position: absolute;
left: 7px;
top: 3px;
width: 6px;
height: 12px;
border: solid $light-color;
border-width: 0 2px 2px 0;
opacity: 0;
transform: rotate(45deg) scale(0.8);
transition: opacity $short-animation-time, transform $short-animation-time;
}
&:checked {
background: $text-color;
&::after {
opacity: 1;
transform: rotate(45deg) scale(1);
}
}
&:hover,
&:focus-visible {
box-shadow: $shadow;
}
&:active {
transform: scale(0.95);
}
}
span {
line-height: 1.3;
}
}
.bottom {
height: 32px;
@media (max-width: $mobile-width) {
@ -306,15 +409,27 @@ interface EditedValue {
}
@media (max-width: $mobile-width) {
lt-select-add, lt-toggle {
lt-select-add,
.done-checkbox {
max-width: 100%;
width: 100%;
}
.bottom {
min-height: 42px;
button {
width: max-content;
max-width: 100%;
min-height: 42px;
}
}
}
}
`,
})
export class BlockEditComponent implements AfterViewInit {
readonly viewTitle = input<string>('');
readonly blocks = input.required<Block[]>();
readonly activeBlockId = input<string | null>(null);
readonly tags = input<string[]>([]);
@ -407,8 +522,16 @@ export class BlockEditComponent implements AfterViewInit {
return t ? getColorOfTag(t, this.baseColor()) : 'transparent';
});
formatDate(ts: number): string {
formatDate(ts: number, compact = false): string {
const d = new Date(ts * 1000);
if (compact) {
return d.toLocaleString(undefined, {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}
// e.g. "May 28, 2026, 14:32"
return d.toLocaleString(undefined, {
year: 'numeric',