• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import {Component, EventEmitter, Input, Output} from '@angular/core';
17import {MatSelect, MatSelectChange} from '@angular/material/select';
18
19@Component({
20  selector: 'select-with-filter',
21  template: `
22    <mat-form-field
23      [style]="getOuterFormFieldStyle()"
24      [style.text-align]="'unset'"
25      [appearance]="appearance"
26      [class]="formFieldClass"
27      [class.mat-body-2]="!select.value || select.value.length === 0">
28      <mat-label>{{ label }}</mat-label>
29      <mat-select
30        (opened)="filter.focus()"
31        (closed)="onSelectClosed()"
32        (selectionChange)="onSelectChange($event)"
33        [multiple]="true"
34        #select>
35        <mat-form-field class="select-filter" [style]="getInnerFormFieldStyle()">
36          <mat-label>Filter options</mat-label>
37          <input matInput #filter [(ngModel)]="filterString" />
38        </mat-form-field>
39        <div *ngIf="(select.value?.length ?? 0) > 0" class="selected-options">
40          <span class="mat-option mat-active">Selected:</span>
41          <div
42            class="mat-option mat-selected mat-option-multiple mat-active selected-option"
43            *ngFor="let option of selectedOptions(select)"
44            (click)="onSelectedOptionClick(option, select)">
45          <mat-pseudo-checkbox
46            color="primary"
47            state="checked"
48            class="mat-option-pseudo-checkbox"></mat-pseudo-checkbox>
49          <div class="mat-option-text">{{option}}</div>
50          </div>
51        </div>
52        <mat-divider [vertical]="false"></mat-divider>
53        <mat-option
54          *ngFor="let option of options"
55          [value]="option"
56          class="option no-focus"
57          [class.hidden-option]="hideOption(option)">{{ option }}</mat-option>
58      </mat-select>
59    </mat-form-field>
60  `,
61  styles: [
62    `
63      mat-form-field {
64        width: 100%;
65      }
66
67      .hidden-option {
68        display: none;
69      }
70
71      .selected-options {
72        display: flex;
73        flex-direction: column;
74      }
75    `,
76  ],
77})
78export class SelectWithFilterComponent {
79  @Input() label: string = '';
80  @Input() options: string[] = [];
81  @Input() outerFilterWidth = '100px';
82  @Input() innerFilterWidth = '100';
83  @Input() flex = 'none';
84  @Input() appearance = '';
85  @Input() formFieldClass = '';
86
87  @Output() readonly selectChange = new EventEmitter<MatSelectChange>();
88
89  filterString: string = '';
90
91  onSelectChange(event: MatSelectChange) {
92    this.selectChange.emit(event);
93  }
94
95  getOuterFormFieldStyle() {
96    return {
97      flex: this.flex,
98      width: this.outerFilterWidth,
99    };
100  }
101
102  getInnerFormFieldStyle() {
103    return {
104      flex: 'none',
105      paddingTop: '2px',
106      paddingLeft: '10px',
107      paddingRight: '20px',
108      width: this.innerFilterWidth + 'px',
109    };
110  }
111
112  onSelectClosed() {
113    this.filterString = '';
114  }
115
116  hideOption(option: string) {
117    return !option.toLowerCase().includes(this.filterString.toLowerCase());
118  }
119
120  selectedOptions(select: MatSelect) {
121    return this.options.filter((o) => select.value.includes(o));
122  }
123
124  onSelectedOptionClick(option: string, select: MatSelect) {
125    select.value = select.value.filter((val: string) => val !== option);
126    this.selectChange.emit(new MatSelectChange(select, select.value));
127  }
128}
129