@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 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-ionic",
3
3
  "author": "Tiledesk SRL",
4
- "version": "3.4.32-rc8",
4
+ "version": "3.4.32-rc9",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -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:5; let i=index" style="cursor: pointer">
106
- <a [ngClass]="{'li-selected' : prjct?.id_project?._id === project?.id_project?.id }"
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]="translationsMap?.get(prjct?.teammateStatus?.label) || prjct?.teammateStatus?.name"
111
- (click)="toggleStatusDropdown($event, prjct)">
112
- <img style="width: 15px; height: 15px; position: relative; top: 1px; cursor: pointer;" height="15" width="15" [src]="prjct?.teammateStatus?.avatar" />
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
- </li>
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" (click)="$event.stopPropagation()"
175
- [style.top.px]="statusDropdownPosition.top" [style.right.px]="statusDropdownPosition.right">
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>{{ translationsMap?.get(status.label) || status.name }}</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: flex-start;
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: pointer;
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, right: 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
- toggleStatusDropdown(event: Event, prjct: any) {
420
- event.stopPropagation()
421
- event.preventDefault()
422
- const projectId = prjct?.id_project?._id
423
- const isOpening = this.openStatusDropdownProjectId !== projectId
424
- if (isOpening) {
425
- const el = event.currentTarget as HTMLElement
426
- const rect = el.getBoundingClientRect()
427
- this.statusDropdownPosition = {
428
- top: rect.top + rect.height / 2,
429
- right: window.innerWidth - rect.left + 4
430
- }
431
- this.selectedProjectForStatus = prjct
432
- } else {
433
- this.selectedProjectForStatus = null
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) {