@chat21/chat21-ionic 3.4.32-rc8 → 3.4.32-rc9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +14 -11
- package/src/app/components/sidebar-user-details/sidebar-user-details.component.scss +23 -21
- package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +65 -19
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
### **Copyrigth**:
|
|
9
9
|
*Tiledesk SRL*
|
|
10
10
|
|
|
11
|
+
# 3.4.32-rc9
|
|
12
|
+
- **added**: sidebar-user-details — MutationObserver to close dropdowns when user details panel is hidden (watches #user-details class); cleanup in ngOnDestroy to prevent memory leaks.
|
|
13
|
+
- **changed**: sidebar-user-details — refined closeDropdowns for better dropdown management when panel closes.
|
|
14
|
+
- **added**: sidebar-user-details — hover to open status dropdown; improved status dropdown positioning logic.
|
|
15
|
+
- **changed**: sidebar-user-details — flexbox layout (justify-content: space-between) for improved project item spacing.
|
|
16
|
+
- **changed**: sidebar-user-details — HTML structure for conditional rendering of teammate status images and titles; cleaned up unused SCSS.
|
|
17
|
+
|
|
11
18
|
# 3.4.32-rc8
|
|
12
19
|
- **bug-fixed**: sidebar-user-details — status dropdown not visible when clicking first/last project; moved outside #user-details container to avoid overflow clipping.
|
|
13
20
|
- **bug-fixed**: sidebar-user-details — replaced `transform` on #user-details with `left` animation to fix `position: fixed` containing block (dropdown positioning).
|
package/package.json
CHANGED
|
@@ -96,23 +96,24 @@
|
|
|
96
96
|
<button class="btn projects-dropdown-toggle" (click)="toggleProjectsDropdown()">
|
|
97
97
|
<span class="projects-dropdown-toggle-label">
|
|
98
98
|
{{ project?.name || 'Progetti' }}
|
|
99
|
-
<img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="project?.teammateStatus?.avatar" />
|
|
99
|
+
<img *ngIf="project?.teammateStatus" style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="project?.teammateStatus?.avatar" />
|
|
100
100
|
</span>
|
|
101
101
|
<i class="material-icons" style="margin-left: 4px; font-size: 18px;">arrow_drop_down</i>
|
|
102
102
|
</button>
|
|
103
|
-
<div class="ripple-container"></div>
|
|
104
103
|
<div id="projects_dropdown" class="dropdown-menu" [ngClass]="{'open': openDropdownProjects}">
|
|
105
|
-
<li *ngFor="let prjct of projects?.slice() | slice:0:
|
|
106
|
-
|
|
104
|
+
<li *ngFor="let prjct of projects?.slice() | slice:0:10; let i=index" style="cursor: pointer"
|
|
105
|
+
(mouseenter)="openStatusDropdownOnHover($event, prjct)"
|
|
106
|
+
(mouseleave)="closeStatusDropdownOnLeave()">
|
|
107
|
+
<a [ngClass]="{'li-selected' : prjct?.id_project?._id === openStatusDropdownProjectId }"
|
|
107
108
|
class="project-item-row">
|
|
108
109
|
<span class="project-item-name">{{ prjct?.id_project?.name }}</span>
|
|
109
110
|
<span class="project-item-status project-item-status-wrapper"
|
|
110
|
-
[attr.title]="
|
|
111
|
-
|
|
112
|
-
<img
|
|
111
|
+
[attr.title]="prjct?.teammateStatus?.name">
|
|
112
|
+
<span class="project-item-status-name">{{prjct?.teammateStatus?.name}}</span>
|
|
113
|
+
<img [src]="prjct?.teammateStatus?.avatar" />
|
|
113
114
|
</span>
|
|
114
115
|
</a>
|
|
115
|
-
|
|
116
|
+
</li>
|
|
116
117
|
</div>
|
|
117
118
|
</div>
|
|
118
119
|
</ng-container>
|
|
@@ -171,11 +172,13 @@
|
|
|
171
172
|
</div>
|
|
172
173
|
|
|
173
174
|
<!-- Status dropdown fuori da #user-details per evitare clipping da overflow -->
|
|
174
|
-
<div class="status-dropdown status-dropdown-fixed status-dropdown-outside" *ngIf="openStatusDropdownProjectId && selectedProjectForStatus"
|
|
175
|
-
[style.top.px]="statusDropdownPosition.top" [style.
|
|
175
|
+
<div class="status-dropdown status-dropdown-fixed status-dropdown-outside" *ngIf="openStatusDropdownProjectId && selectedProjectForStatus"
|
|
176
|
+
[style.top.px]="statusDropdownPosition.top" [style.left.px]="statusDropdownPosition.left"
|
|
177
|
+
(mouseenter)="cancelStatusDropdownClose()"
|
|
178
|
+
(mouseleave)="closeStatusDropdownOnLeave()">
|
|
176
179
|
<div class="status-dropdown-option" *ngFor="let status of TEAMMATE_STATUS"
|
|
177
180
|
(click)="$event.stopPropagation(); onChangeProjectStatus(selectedProjectForStatus, status.id)">
|
|
178
181
|
<img style="width: 15px; height: 15px; margin-right: 6px;" [src]="status.avatar" />
|
|
179
|
-
<span>{{
|
|
182
|
+
<span>{{ status.name }}</span>
|
|
180
183
|
</div>
|
|
181
184
|
</div>
|
|
@@ -226,7 +226,6 @@
|
|
|
226
226
|
color: var(--sidebar-user-detail-select-color);
|
|
227
227
|
border: 1px solid var(--sidebar-user-detail-select-background);
|
|
228
228
|
border-radius: 4px;
|
|
229
|
-
cursor: pointer;
|
|
230
229
|
display: flex;
|
|
231
230
|
align-items: center;
|
|
232
231
|
text-align: left;
|
|
@@ -240,7 +239,7 @@
|
|
|
240
239
|
padding: 0 !important;
|
|
241
240
|
min-height: auto;
|
|
242
241
|
flex: 1;
|
|
243
|
-
justify-content:
|
|
242
|
+
justify-content: space-between;
|
|
244
243
|
color: inherit;
|
|
245
244
|
font-size: inherit;
|
|
246
245
|
}
|
|
@@ -317,16 +316,6 @@
|
|
|
317
316
|
background-color: var(--sidebar-user-detail-select-hover-background);
|
|
318
317
|
}
|
|
319
318
|
|
|
320
|
-
&.all-projects,
|
|
321
|
-
&.add-project{
|
|
322
|
-
color: var(--sidebar-user-detail-select-color);
|
|
323
|
-
i{
|
|
324
|
-
padding-right: 5px;
|
|
325
|
-
margin-bottom: 2px;
|
|
326
|
-
font-size: 20px
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
319
|
&.section-title{
|
|
331
320
|
padding-left: 24px;
|
|
332
321
|
font-size: 12px;
|
|
@@ -335,14 +324,6 @@
|
|
|
335
324
|
}
|
|
336
325
|
}
|
|
337
326
|
|
|
338
|
-
.dropdown-menu .divider{
|
|
339
|
-
background-color: rgba(255, 255, 255, 0.2);
|
|
340
|
-
margin: 5px 0;
|
|
341
|
-
|
|
342
|
-
height: 1px;
|
|
343
|
-
overflow: hidden;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
327
|
.dropdown-menu li>a{
|
|
347
328
|
font-size: 13px;
|
|
348
329
|
padding: 10px 20px;
|
|
@@ -356,7 +337,14 @@
|
|
|
356
337
|
color: var(--sidebar-user-detail-select-color);
|
|
357
338
|
white-space: nowrap;
|
|
358
339
|
text-decoration: none;
|
|
359
|
-
cursor:
|
|
340
|
+
cursor: auto;
|
|
341
|
+
|
|
342
|
+
img {
|
|
343
|
+
width: 15px;
|
|
344
|
+
height: 15px;
|
|
345
|
+
position: relative;
|
|
346
|
+
top: 1px;
|
|
347
|
+
}
|
|
360
348
|
|
|
361
349
|
.material-icons {
|
|
362
350
|
vertical-align: middle;
|
|
@@ -383,6 +371,7 @@
|
|
|
383
371
|
}
|
|
384
372
|
|
|
385
373
|
.project-item-status {
|
|
374
|
+
cursor: pointer;
|
|
386
375
|
flex: 0 0 10%;
|
|
387
376
|
display: flex;
|
|
388
377
|
justify-content: flex-end;
|
|
@@ -447,6 +436,19 @@
|
|
|
447
436
|
box-shadow: 0 2px 5px 0 rgb(0, 0, 0, 0.26);
|
|
448
437
|
list-style: none;
|
|
449
438
|
|
|
439
|
+
/* Triangolino in alto a sinistra, fuori dal container, con leggero gap in basso */
|
|
440
|
+
&::before {
|
|
441
|
+
content: '';
|
|
442
|
+
position: absolute;
|
|
443
|
+
left: -7px;
|
|
444
|
+
top: 10px;
|
|
445
|
+
width: 0;
|
|
446
|
+
height: 0;
|
|
447
|
+
border-top: 7px solid transparent;
|
|
448
|
+
border-bottom: 7px solid transparent;
|
|
449
|
+
border-right: 7px solid var(--sidebar-user-detail-select-background);
|
|
450
|
+
}
|
|
451
|
+
|
|
450
452
|
.status-dropdown-option {
|
|
451
453
|
display: flex;
|
|
452
454
|
align-items: center;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnInit, Output } from '@angular/core';
|
|
1
|
+
import { Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnChanges, OnInit, Output } from '@angular/core';
|
|
2
2
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
3
3
|
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
4
4
|
import { TranslateService } from '@ngx-translate/core';
|
|
@@ -24,7 +24,7 @@ import { ProjectUser } from 'src/chat21-core/models/project_user';
|
|
|
24
24
|
templateUrl: './sidebar-user-details.component.html',
|
|
25
25
|
styleUrls: ['./sidebar-user-details.component.scss'],
|
|
26
26
|
})
|
|
27
|
-
export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
27
|
+
export class SidebarUserDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
|
28
28
|
// HAS_CLICKED_OPEN_USER_DETAIL: boolean = false;
|
|
29
29
|
// @Output() onCloseUserDetailsSidebar = new EventEmitter();
|
|
30
30
|
|
|
@@ -61,9 +61,11 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
61
61
|
selectedProjectForStatus: ProjectUser | null = null;
|
|
62
62
|
public openDropdownProjects: boolean = false
|
|
63
63
|
public openStatusDropdownProjectId: string | null = null
|
|
64
|
-
statusDropdownPosition = { top: 0,
|
|
64
|
+
statusDropdownPosition = { top: 0, left: 0 };
|
|
65
65
|
isVisibleMT = false;
|
|
66
66
|
isVisibleMPA = false;
|
|
67
|
+
private userDetailsMutationObserver: MutationObserver | null = null;
|
|
68
|
+
private statusDropdownCloseTimeout: any = null;
|
|
67
69
|
|
|
68
70
|
translationsMap: Map<string, string> = new Map();
|
|
69
71
|
|
|
@@ -91,10 +93,45 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
91
93
|
this.listenToCurrentStoredProject();
|
|
92
94
|
this.listenToUserGoOnline();
|
|
93
95
|
this.getOSCODE();
|
|
96
|
+
this.setupUserDetailsCloseObserver();
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
ngOnChanges() { }
|
|
97
100
|
|
|
101
|
+
ngOnDestroy(): void {
|
|
102
|
+
this.userDetailsMutationObserver?.disconnect();
|
|
103
|
+
this.userDetailsMutationObserver = null;
|
|
104
|
+
this.cancelStatusDropdownClose();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Osserva la rimozione della classe 'active' da #user-details (es. chiusura via click avatar nel sidebar)
|
|
109
|
+
* per chiudere i dropdown aperti
|
|
110
|
+
*/
|
|
111
|
+
private setupUserDetailsCloseObserver(): void {
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
const el = document.getElementById('user-details');
|
|
114
|
+
if (!el) return;
|
|
115
|
+
this.userDetailsMutationObserver = new MutationObserver((mutations) => {
|
|
116
|
+
mutations.forEach((mutation) => {
|
|
117
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
118
|
+
const target = mutation.target as HTMLElement;
|
|
119
|
+
if (!target.classList.contains('active')) {
|
|
120
|
+
this.closeDropdowns();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
this.userDetailsMutationObserver.observe(el, { attributes: true, attributeFilter: ['class'] });
|
|
126
|
+
}, 0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private closeDropdowns(): void {
|
|
130
|
+
this.openDropdownProjects = false;
|
|
131
|
+
this.openStatusDropdownProjectId = null;
|
|
132
|
+
this.selectedProjectForStatus = null;
|
|
133
|
+
}
|
|
134
|
+
|
|
98
135
|
subcribeToAuthStateChanged() {
|
|
99
136
|
this.messagingAuthService.BSAuthStateChanged.subscribe((state) => {
|
|
100
137
|
this.logger.log('[SIDEBAR-USER-DETAILS] BSAuthStateChanged ', state)
|
|
@@ -416,23 +453,32 @@ export class SidebarUserDetailsComponent implements OnInit, OnChanges {
|
|
|
416
453
|
}
|
|
417
454
|
}
|
|
418
455
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
456
|
+
openStatusDropdownOnHover(event: Event, prjct: any) {
|
|
457
|
+
this.cancelStatusDropdownClose();
|
|
458
|
+
const projectId = prjct?.id_project?._id;
|
|
459
|
+
const el = event.currentTarget as HTMLElement;
|
|
460
|
+
const rect = el.getBoundingClientRect();
|
|
461
|
+
this.statusDropdownPosition = {
|
|
462
|
+
top: rect.top,
|
|
463
|
+
left: rect.right + 10
|
|
464
|
+
};
|
|
465
|
+
this.selectedProjectForStatus = prjct;
|
|
466
|
+
this.openStatusDropdownProjectId = projectId;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
closeStatusDropdownOnLeave() {
|
|
470
|
+
this.cancelStatusDropdownClose();
|
|
471
|
+
this.statusDropdownCloseTimeout = setTimeout(() => {
|
|
472
|
+
this.closeDropdowns();
|
|
473
|
+
this.statusDropdownCloseTimeout = null;
|
|
474
|
+
}, 150);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
cancelStatusDropdownClose() {
|
|
478
|
+
if (this.statusDropdownCloseTimeout) {
|
|
479
|
+
clearTimeout(this.statusDropdownCloseTimeout);
|
|
480
|
+
this.statusDropdownCloseTimeout = null;
|
|
434
481
|
}
|
|
435
|
-
this.openStatusDropdownProjectId = this.openStatusDropdownProjectId === projectId ? null : projectId
|
|
436
482
|
}
|
|
437
483
|
|
|
438
484
|
onChangeProjectStatus(projectUser: ProjectUser, selectedStatusID: any) {
|