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