feat: add other tab
There is an error in the checkbox display when scrolling
This commit is contained in:
		| @@ -0,0 +1,13 @@ | ||||
| .header-menu { | ||||
|   position: sticky; | ||||
|   top: 0; | ||||
|   background-color: var(--mat-menu-container-color); | ||||
|   z-index: 1; | ||||
|   padding-top: 5px; | ||||
| } | ||||
|  | ||||
| .button-group { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| <!--suppress CssInvalidPropertyValue --> | ||||
| <button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton">{{ buttonText }}</button> | ||||
|  | ||||
| <mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options"> | ||||
|   <div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px"> | ||||
|     <div class="header-menu"> | ||||
|       <mat-form-field appearance="outline" color="accent" style="display:flex;"> | ||||
|         <input matInput placeholder="Поиск..." [(ngModel)]="searchQuery" [disabled]="data.length === 0"> | ||||
|         <button mat-icon-button matSuffix (click)="clearSearch()" [disabled]="data.length === 0"> | ||||
|           <mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon> | ||||
|         </button> | ||||
|       </mat-form-field> | ||||
|  | ||||
|       <div class="button-group"> | ||||
|         <mat-checkbox (click)="selectData()" [disabled]="data.length === 0" #chooseCheckbox/> | ||||
|         <button mat-button (click)="clearAll()" [disabled]="data.length === 0">Очистить</button> | ||||
|       </div> | ||||
|       <hr/> | ||||
|     </div> | ||||
|     @if (data.length === 0) { | ||||
|       <app-data-spinner style="display: flex; justify-content: center;"/> | ||||
|     } @else { | ||||
|       <mat-selection-list> | ||||
|         @if (filteredData.value.length !== 0) { | ||||
|           <cdk-virtual-scroll-viewport autosize [minBufferPx]="300" [maxBufferPx]="500" style="height: 250px; width: 260px; overflow-x: clip;"> | ||||
|             <div *cdkVirtualFor="let item of filteredData"> | ||||
|               <mat-list-option togglePosition="before" style="height: auto;" [value]="item.id" [selected]="item.selected" | ||||
|                                (click)="selectChange(item.id)"> | ||||
|                 <div style="text-wrap: balance;">{{ item.name }}</div> | ||||
|               </mat-list-option> | ||||
|               <mat-divider style="margin: 10px 0;"/> | ||||
|             </div> | ||||
|           </cdk-virtual-scroll-viewport> | ||||
|         } @else { | ||||
|           <div style="color: var(--mdc-text-button-label-text-color);"> | ||||
|             Нет подходящих элементов | ||||
|           </div> | ||||
|         } | ||||
|       </mat-selection-list> | ||||
|     } | ||||
|   </div> | ||||
| </mat-menu> | ||||
| @@ -0,0 +1,139 @@ | ||||
| import {Component, HostListener, Input, ViewChild} from '@angular/core'; | ||||
| import {MatMenu, MatMenuTrigger} from "@angular/material/menu"; | ||||
| import {MatCheckbox} from "@angular/material/checkbox"; | ||||
| import {BehaviorSubject} from "rxjs"; | ||||
| import {MatButton, MatIconButton} from "@angular/material/button"; | ||||
| import {MatFormField, MatSuffix} from "@angular/material/form-field"; | ||||
| import {MatIcon} from "@angular/material/icon"; | ||||
| import {FormsModule} from "@angular/forms"; | ||||
| import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; | ||||
| import {MatListOption, MatSelectionList} from "@angular/material/list"; | ||||
| import {ScrollingModule} from "@angular/cdk/scrolling"; | ||||
| import {ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling'; | ||||
| import {MatDivider} from "@angular/material/divider"; | ||||
| import {MatInput} from "@angular/material/input"; | ||||
|  | ||||
| export interface SelectData { | ||||
|   id: number, | ||||
|   name: string, | ||||
|   selected: boolean | ||||
| } | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-schedule-tabs-other', | ||||
|   standalone: true, | ||||
|   imports: [ | ||||
|     MatMenuTrigger, | ||||
|     MatButton, | ||||
|     MatMenu, | ||||
|     MatFormField, | ||||
|     MatIcon, | ||||
|     FormsModule, | ||||
|     MatCheckbox, | ||||
|     DataSpinnerComponent, | ||||
|     MatSelectionList, | ||||
|     MatListOption, | ||||
|     MatDivider, | ||||
|     MatIconButton, | ||||
|     MatInput, | ||||
|     MatSuffix, | ||||
|     ScrollingModule, | ||||
|     ExperimentalScrollingModule | ||||
|   ], | ||||
|   templateUrl: './schedule-tabs-other.component.html', | ||||
|   styleUrl: './schedule-tabs-other.component.css' | ||||
| }) | ||||
| export class ScheduleTabsOtherComponent { | ||||
|   private _searchQuery: string = ''; | ||||
|   protected filteredData: BehaviorSubject<SelectData[]> = new BehaviorSubject<SelectData[]>([]); | ||||
|   protected data: SelectData[] = []; | ||||
|  | ||||
|   @Input() idButton!: string; | ||||
|   @Input() buttonText!: string; | ||||
|   @ViewChild('menuTrigger') menuTrigger!: MatMenuTrigger; | ||||
|   @ViewChild('chooseCheckbox') chooseCheckbox!: MatCheckbox; | ||||
|  | ||||
|   get selectedIds(): number[] { | ||||
|     return this.data.filter(x => x.selected).map(x => x.id); | ||||
|   } | ||||
|  | ||||
|   set Data(data: SelectData[]) { | ||||
|     this.data = data; | ||||
|     this.data.forEach(x => x.selected = false); | ||||
|     this.filteredData.next(this.data) | ||||
|   } | ||||
|  | ||||
|   protected get searchQuery(): string { | ||||
|     return this._searchQuery; | ||||
|   } | ||||
|  | ||||
|   private updateCheckBox() { | ||||
|     this.chooseCheckbox.checked = this.data.every(x => x.selected); | ||||
|     this.chooseCheckbox.indeterminate = this.data.some(x => x.selected) && !this.chooseCheckbox.checked; | ||||
|   } | ||||
|  | ||||
|   protected set searchQuery(value: string) { | ||||
|     this._searchQuery = value; | ||||
|     this.updateFilteredData(); | ||||
|   } | ||||
|  | ||||
|   protected updateFilteredData(): void { | ||||
|     this.filteredData.next(this.data.filter(x => | ||||
|       x.name.toLowerCase().includes(this.searchQuery.toLowerCase()) | ||||
|     )); | ||||
|   } | ||||
|  | ||||
|   protected clearSearch(): void { | ||||
|     this.searchQuery = ''; | ||||
|   } | ||||
|  | ||||
|   protected clearAll(): void { | ||||
|     this.data.forEach(x => x.selected = false); | ||||
|  | ||||
|     if (this.searchQuery !== '') { | ||||
|       const updatedData = this.filteredData.value.map(x => { | ||||
|         return {...x, selected: false}; | ||||
|       }); | ||||
|       this.filteredData.next(updatedData); | ||||
|     } else | ||||
|       this.updateFilteredData(); | ||||
|  | ||||
|     this.updateCheckBox(); | ||||
|   } | ||||
|  | ||||
|   protected selectData() { | ||||
|     const check: boolean = this.filteredData.value.some(x => !x.selected) && !this.filteredData.value.every(x => x.selected); | ||||
|  | ||||
|     const updatedData = this.filteredData.value.map(data => { | ||||
|       this.data.find(x => x.id === data.id)!.selected = check; | ||||
|       return {...data, selected: check}; | ||||
|     }); | ||||
|  | ||||
|     this.filteredData.next(updatedData); | ||||
|     console.log(this.filteredData.value); | ||||
|     this.updateCheckBox(); | ||||
|   } | ||||
|  | ||||
|   selectChange(item: number) { | ||||
|     const data = this.data.find(x => x.id === item)!; | ||||
|     data.selected = !data.selected; | ||||
|     const updatedData = this.filteredData.value; | ||||
|     updatedData.find(x => x.id === item)!.selected = data.selected; | ||||
|     this.filteredData.next(updatedData); | ||||
|     this.updateCheckBox(); | ||||
|   } | ||||
|  | ||||
|   @HostListener('document:click', ['$event']) | ||||
|   private onClick(event: MouseEvent): void { | ||||
|     if (this.menuTrigger && this.menuTrigger.menuOpen | ||||
|       && (event.target as HTMLElement).closest('.mat-menu-content') === null | ||||
|       && !this.isInsideMenuButton(event.target as HTMLElement, this.idButton)) { | ||||
|       this.menuTrigger.closeMenu(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private isInsideMenuButton(target: HTMLElement, id: string): boolean { | ||||
|     let parentElement: HTMLElement | null = target.parentElement; | ||||
|     return (parentElement !== null && parentElement.id === id); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user