refactor: optimize and add comments to TaskComponent
This commit is contained in:
parent
b75fb18e42
commit
c02cf4d7a3
3 changed files with 182 additions and 137 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
<section>
|
<section>
|
||||||
@if (task.id) {
|
<!-- If the task exists, display it -->
|
||||||
|
@if (task && task.id) {
|
||||||
<div (click)="handleMenuButtonClick($event, task.id)">
|
<div (click)="handleMenuButtonClick($event, task.id)">
|
||||||
<div
|
<div
|
||||||
class="content"
|
class="task"
|
||||||
[draggable]="disableDrag"
|
[draggable]="disableDrag"
|
||||||
(dragstart)="
|
(dragstart)="
|
||||||
disableDrag ? dragDropService.startDragging($event, task) : null
|
disableDrag ? dragDropService.startDragging($event, task) : null
|
||||||
|
|
@ -13,8 +14,10 @@
|
||||||
class="category"
|
class="category"
|
||||||
[style.background-color]="categoryColors.get(task.category)"
|
[style.background-color]="categoryColors.get(task.category)"
|
||||||
>
|
>
|
||||||
{{ task.category }}
|
<p>{{ task.category }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- If the user is on a mobile device, show the menu button -->
|
||||||
@if (pageViewMedia$ | async) {
|
@if (pageViewMedia$ | async) {
|
||||||
<div class="menu-btn" (click)="handleMenuButtonClick($event, task.id)">
|
<div class="menu-btn" (click)="handleMenuButtonClick($event, task.id)">
|
||||||
<img
|
<img
|
||||||
|
|
@ -25,10 +28,13 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="headline">{{ task.title }}</div>
|
|
||||||
|
<div class="title">{{ task.title }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{ task.description }}
|
{{ task.description }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- If the task has subtasks, display them -->
|
||||||
@if(task.subtasks.length > 0) {
|
@if(task.subtasks.length > 0) {
|
||||||
<div class="subtask">
|
<div class="subtask">
|
||||||
<div class="subtask-line">
|
<div class="subtask-line">
|
||||||
|
|
@ -37,46 +43,67 @@
|
||||||
[style.width.%]="completedSubtasksPercent()"
|
[style.width.%]="completedSubtasksPercent()"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="subtask-text">
|
<p>{{ completedSubtasks() }} / {{ task.subtasks.length }} Subtasks</p>
|
||||||
{{ completedSubtasks() }} / {{ task.subtasks.length }} Subtasks
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="footer">
|
|
||||||
<div class="footer-badge">
|
<div class="assignees">
|
||||||
@for (user of task.userData; track user) { @if (task.creator ===
|
<div class="assigned">
|
||||||
user.id) {
|
<!-- Loop through user data -->
|
||||||
<span
|
@for (user of task.userData; track user) {
|
||||||
class="footer-badged"
|
|
||||||
|
<!-- If this user is the creator of the task, display the badge -->
|
||||||
|
@if (task.creator === user.id) {
|
||||||
|
<div
|
||||||
|
class="assigned-badged"
|
||||||
(mousemove)="openDialog(task.creator, $event)"
|
(mousemove)="openDialog(task.creator, $event)"
|
||||||
(mouseleave)="closeDialog()"
|
(mouseleave)="closeDialog()"
|
||||||
[style.background-color]="user.color"
|
[style.background-color]="user.color"
|
||||||
>{{ user.initials }}</span
|
>
|
||||||
>} } @for (user of task.userData; track user) { @for (assigned of
|
<span>{{ user.initials }}</span>
|
||||||
task.assignees; track assigned) { @if (assigned.userId === user.id) {
|
</div>
|
||||||
<span
|
} }
|
||||||
class="footer-badged"
|
|
||||||
|
<!-- Loop through user data -->
|
||||||
|
@for (user of task.userData; track user) {
|
||||||
|
|
||||||
|
<!-- Loop through task assignees -->
|
||||||
|
@for (assigned of task.assignees; track assigned) {
|
||||||
|
|
||||||
|
<!-- If this user is assigned to the task, display the badge -->
|
||||||
|
@if (assigned.userId === user.id) {
|
||||||
|
<div
|
||||||
|
class="assigned-badged"
|
||||||
(mousemove)="openDialog(user.id, $event)"
|
(mousemove)="openDialog(user.id, $event)"
|
||||||
(mouseleave)="closeDialog()"
|
(mouseleave)="closeDialog()"
|
||||||
[style.background-color]="user.color"
|
[style.background-color]="user.color"
|
||||||
>{{ user.initials }}</span
|
|
||||||
>
|
>
|
||||||
|
<span>{{ user.initials }}</span>
|
||||||
|
</div>
|
||||||
} } }
|
} } }
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-priority prio-{{ task.priority }}"></div>
|
<div class="priority priority-{{ task.priority }}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (menuOpen){
|
|
||||||
|
<!-- If the task menu is open, display it -->
|
||||||
|
@if (mobileMenuOpen){
|
||||||
<app-task-menu
|
<app-task-menu
|
||||||
[task]="task"
|
[task]="task"
|
||||||
[boardTaskStatus]="task.status"
|
[boardTaskStatus]="task.status"
|
||||||
(updateStatusEmitter)="onStatusUpdate($event)"
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
></app-task-menu>
|
></app-task-menu>
|
||||||
} @if (AssignedDialogId) {
|
}
|
||||||
|
|
||||||
|
<!-- If the assigned dialog is open, display it -->
|
||||||
|
@if (assignedDialogId) {
|
||||||
<div class="dialog" [style.left.px]="dialogX" [style.top.px]="dialogY">
|
<div class="dialog" [style.left.px]="dialogX" [style.top.px]="dialogY">
|
||||||
@for (user of task.userData; track user) { @if (user.id ===
|
<!-- Loop through user data -->
|
||||||
AssignedDialogId) {
|
@for (user of task.userData; track user) {
|
||||||
|
|
||||||
|
<!-- If the user id matches the assigned dialog id, display the user details -->
|
||||||
|
@if (user.id === assignedDialogId) {
|
||||||
<p>
|
<p>
|
||||||
{{ user.firstName }}
|
{{ user.firstName }}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ section {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.task {
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -13,27 +12,12 @@ section {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.08);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
|
||||||
box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
&:hover {
|
||||||
position: fixed;
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
|
||||||
width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border-radius: 0px 20px 20px 20px;
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
z-index: 2;
|
|
||||||
background-color: var(--white);
|
|
||||||
p,
|
|
||||||
span {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,23 +25,31 @@ section {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
font-size: 16px;
|
|
||||||
padding: 4px 16px;
|
padding: 4px 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
text-shadow: 1px 1px 2px var(--black);
|
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
text-shadow: 1px 1px 2px var(--black);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-btn {
|
.menu-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
img {
|
img {
|
||||||
filter: brightness(0) saturate(100%) invert(56%) sepia(64%)
|
filter: brightness(0) saturate(100%) invert(56%) sepia(64%)
|
||||||
|
|
@ -67,9 +59,9 @@ section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline {
|
.title {
|
||||||
color: var(--dark-blue);
|
color: var(--dark-blue);
|
||||||
font-size: 16px;
|
font-size: 18px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
@ -83,11 +75,18 @@ section {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subtasks
|
||||||
|
|
||||||
.subtask {
|
.subtask {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtask-line {
|
.subtask-line {
|
||||||
|
|
@ -100,78 +99,101 @@ section {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: var(--very-light-gray);
|
background-color: var(--very-light-gray);
|
||||||
|
|
||||||
|
.filler-full {
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: var(--light-blue);
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filler-full {
|
// Assignees
|
||||||
border-radius: 16px;
|
|
||||||
background-color: var(--light-blue);
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtask-text {
|
.assignees {
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 24px;
|
margin-top: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
.assigned {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
width: 170px;
|
||||||
|
height: 45px;
|
||||||
|
gap: 6px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-badge {
|
.assigned-badged {
|
||||||
display: flex;
|
|
||||||
width: 170px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-badged {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 32px;
|
width: 32px;
|
||||||
min-height: 32px;
|
height: 32px;
|
||||||
border-radius: 45px;
|
border-radius: 100%;
|
||||||
border: 1px solid var(--white);
|
border: 1px solid var(--black);
|
||||||
color: var(--white);
|
position: relative;
|
||||||
font-size: 12px;
|
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
||||||
font-weight: 400;
|
margin-left: 3px;
|
||||||
|
|
||||||
|
&:nth-child(n + 2) {
|
||||||
|
margin-left: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-shadow: 1px 1px 2px var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px) scale(1.02);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-badge span {
|
// Priority
|
||||||
margin-right: 4px;
|
|
||||||
text-shadow: 1px 1px 2px var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-badge span:first-child {
|
.priority {
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-priority {
|
|
||||||
background-size: 32px;
|
background-size: 32px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
.priority {
|
|
||||||
background-size: 24px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prio-urgent {
|
.priority-urgent {
|
||||||
background-image: url(./../../../../assets/img/urgent.svg);
|
background-image: url(./../../../../assets/img/urgent.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prio-medium {
|
.priority-medium {
|
||||||
background-image: url(./../../../../assets/img/medium.svg);
|
background-image: url(./../../../../assets/img/medium.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prio-low {
|
.priority-low {
|
||||||
background-image: url(./../../../../assets/img/low.svg);
|
background-image: url(./../../../../assets/img/low.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dialog
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
position: fixed;
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 0px 20px 20px 20px;
|
||||||
|
border: 1px solid var(--black);
|
||||||
|
z-index: 3;
|
||||||
|
background-color: var(--white);
|
||||||
|
|
||||||
|
p,
|
||||||
|
span {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,22 +33,20 @@ export class TaskComponent {
|
||||||
readonly DIALOG_OFFSET_X = 25;
|
readonly DIALOG_OFFSET_X = 25;
|
||||||
readonly DIALOG_OFFSET_Y = 10;
|
readonly DIALOG_OFFSET_Y = 10;
|
||||||
|
|
||||||
|
pageViewMedia$ = this.resizeService.pageViewMedia$;
|
||||||
|
|
||||||
|
assignees: Assignee[] = [];
|
||||||
categoryColors = new Map<string, string>([
|
categoryColors = new Map<string, string>([
|
||||||
['User Story', '#0038ff'],
|
['User Story', '#0038ff'],
|
||||||
['Technical Task', '#20d7c2'],
|
['Technical Task', '#20d7c2'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pageViewMedia$ = this.resizeService.pageViewMedia$;
|
mobileMenuOpen = false;
|
||||||
|
|
||||||
menuOpen = false;
|
|
||||||
disableDrag = false;
|
disableDrag = false;
|
||||||
AssignedDialogId = '';
|
assignedDialogId = '';
|
||||||
dialogX = 0;
|
dialogX = 0;
|
||||||
dialogY = 0;
|
dialogY = 0;
|
||||||
|
|
||||||
creator = '';
|
|
||||||
assignees: Assignee[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dragDropService: DragDropService,
|
public dragDropService: DragDropService,
|
||||||
public overlayService: OverlayService,
|
public overlayService: OverlayService,
|
||||||
|
|
@ -78,20 +76,20 @@ export class TaskComponent {
|
||||||
* If the event target is a menu button or menu img, toggle the task menu
|
* If the event target is a menu button or menu img, toggle the task menu
|
||||||
* If the event target is anything else, open the task details overlay
|
* If the event target is anything else, open the task details overlay
|
||||||
*/
|
*/
|
||||||
handleMenuButtonClick(event: MouseEvent, taskId: string) {
|
handleMenuButtonClick(event: MouseEvent, taskId: string): void {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const targetElement = event.target as HTMLElement;
|
const targetElement = event.target as HTMLElement;
|
||||||
targetElement.classList.contains('menu-btn') ||
|
targetElement.classList.contains('menu-btn') ||
|
||||||
targetElement.classList.contains('menu-img')
|
targetElement.classList.contains('menu-img')
|
||||||
? this.toggleTaskMenu()
|
? this.toggleMobileMenu()
|
||||||
: this.openTaskDetailsOverlay(taskId);
|
: this.openTaskDetailsOverlay(taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the task menu for the current task
|
* Toggle the task menu for the current task
|
||||||
*/
|
*/
|
||||||
toggleTaskMenu() {
|
toggleMobileMenu(): void {
|
||||||
this.menuOpen = !this.menuOpen;
|
this.mobileMenuOpen = !this.mobileMenuOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,9 +100,9 @@ export class TaskComponent {
|
||||||
* If the page is in the media view, navigate to the task details page
|
* If the page is in the media view, navigate to the task details page
|
||||||
* If the page is not in the media view, open the task overlay
|
* If the page is not in the media view, open the task overlay
|
||||||
*/
|
*/
|
||||||
openTaskDetailsOverlay(taskId: string) {
|
openTaskDetailsOverlay(taskId: string): void {
|
||||||
if (this.menuOpen) {
|
if (this.mobileMenuOpen) {
|
||||||
this.toggleTaskMenu();
|
this.toggleMobileMenu();
|
||||||
}
|
}
|
||||||
this.pageViewMedia$.pipe(take(1)).subscribe((isPageViewMedia) => {
|
this.pageViewMedia$.pipe(take(1)).subscribe((isPageViewMedia) => {
|
||||||
isPageViewMedia
|
isPageViewMedia
|
||||||
|
|
@ -113,33 +111,13 @@ export class TaskComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
|
||||||
/**
|
|
||||||
* If the target element of the event is not a task menu button,
|
|
||||||
* task menu img, or the task menu itself, close the task menu
|
|
||||||
* @param event the MouseEvent
|
|
||||||
*/
|
|
||||||
checkToggleTaskMenu(event: MouseEvent) {
|
|
||||||
const targetElement = event.target as HTMLElement;
|
|
||||||
const menuSelectors = ['.menu-btn', '.menu-img', 'app-task-menu'];
|
|
||||||
const isMenuClicked = menuSelectors.some((selector) =>
|
|
||||||
targetElement.closest(selector)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isMenuClicked) {
|
|
||||||
this.menuOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User Dialog
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the user dialog for the given user id at the position of the mouse click
|
* Opens the user dialog for the given user id at the position of the mouse click
|
||||||
* @param userId the id of the user
|
* @param userId the id of the user
|
||||||
* @param event the MouseEvent that triggered the dialog
|
* @param event the MouseEvent that triggered the dialog
|
||||||
*/
|
*/
|
||||||
openDialog(userId: any, event: MouseEvent) {
|
openDialog(userId: string, event: MouseEvent): void {
|
||||||
this.AssignedDialogId = userId;
|
this.assignedDialogId = userId;
|
||||||
this.updateDialogPosition(event);
|
this.updateDialogPosition(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +125,7 @@ export class TaskComponent {
|
||||||
* Updates the position of the user dialog based on the MouseEvent
|
* Updates the position of the user dialog based on the MouseEvent
|
||||||
* @param event the MouseEvent that triggered the dialog
|
* @param event the MouseEvent that triggered the dialog
|
||||||
*/
|
*/
|
||||||
updateDialogPosition(event: MouseEvent) {
|
updateDialogPosition(event: MouseEvent): void {
|
||||||
this.dialogX = event.clientX + this.DIALOG_OFFSET_X;
|
this.dialogX = event.clientX + this.DIALOG_OFFSET_X;
|
||||||
this.dialogY = event.clientY + this.DIALOG_OFFSET_Y;
|
this.dialogY = event.clientY + this.DIALOG_OFFSET_Y;
|
||||||
}
|
}
|
||||||
|
|
@ -155,12 +133,10 @@ export class TaskComponent {
|
||||||
/**
|
/**
|
||||||
* Closes the user dialog
|
* Closes the user dialog
|
||||||
*/
|
*/
|
||||||
closeDialog() {
|
closeDialog(): void {
|
||||||
this.AssignedDialogId = '';
|
this.assignedDialogId = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtasks
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of completed subtasks of the task
|
* Returns the number of completed subtasks of the task
|
||||||
* @returns the number of completed subtasks
|
* @returns the number of completed subtasks
|
||||||
|
|
@ -178,26 +154,46 @@ export class TaskComponent {
|
||||||
(subtask) => subtask.status
|
(subtask) => subtask.status
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
return (completedSubtasksCount / this.task.subtasks.length) * 100;
|
return this.task.subtasks.length > 0
|
||||||
|
? (completedSubtasksCount / this.task.subtasks.length) * 100
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits an event to update the status of a task.
|
* Emits an event to update the status of a task.
|
||||||
* @param event The TaskMoveEvent containing the task and the new status to move to.
|
* @param event The TaskMoveEvent containing the task and the new status to move to.
|
||||||
*/
|
*/
|
||||||
onStatusUpdate(event: TaskMoveEvent) {
|
onStatusUpdate(event: TaskMoveEvent): void {
|
||||||
this.updateStatusEmitter.emit({
|
this.updateStatusEmitter.emit({
|
||||||
task: event.task,
|
task: event.task,
|
||||||
moveTo: event.moveTo,
|
moveTo: event.moveTo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('document:click', ['$event'])
|
||||||
|
/**
|
||||||
|
* If the target element of the event is not a task menu button,
|
||||||
|
* task menu img, or the task menu itself, close the task menu
|
||||||
|
* @param event the MouseEvent
|
||||||
|
*/
|
||||||
|
checkToggleTaskMenu(event: MouseEvent): void {
|
||||||
|
const targetElement = event.target as HTMLElement;
|
||||||
|
const menuSelectors = ['.menu-btn', '.menu-img', 'app-task-menu'];
|
||||||
|
const isMenuClicked = menuSelectors.some((selector) =>
|
||||||
|
targetElement.closest(selector)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isMenuClicked) {
|
||||||
|
this.mobileMenuOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('window:resize')
|
@HostListener('window:resize')
|
||||||
/**
|
/**
|
||||||
* Called when the window is resized.
|
* Called when the window is resized.
|
||||||
* Updates the disableDragStatus so that tasks are not draggable on mobile devices.
|
* Updates the disableDragStatus so that tasks are not draggable on mobile devices.
|
||||||
*/
|
*/
|
||||||
onResize() {
|
onResize(): void {
|
||||||
this.updateDisableDragStatus();
|
this.updateDisableDragStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue