2023-07-13 12:41:36 +03:00
|
|
|
///
|
|
|
|
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
|
|
|
|
///
|
|
|
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
/// you may not use this file except in compliance with the License.
|
|
|
|
|
/// You may obtain a copy of the License at
|
|
|
|
|
///
|
|
|
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
///
|
|
|
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
/// See the License for the specific language governing permissions and
|
|
|
|
|
/// limitations under the License.
|
|
|
|
|
///
|
|
|
|
|
|
2023-07-11 15:10:06 +03:00
|
|
|
import { PageComponent } from '@shared/components/page.component';
|
2023-07-13 12:41:36 +03:00
|
|
|
import {
|
|
|
|
|
ChangeDetectorRef,
|
|
|
|
|
Component,
|
|
|
|
|
EventEmitter,
|
|
|
|
|
Input,
|
|
|
|
|
OnInit,
|
|
|
|
|
Output,
|
|
|
|
|
ViewChild,
|
|
|
|
|
ViewEncapsulation
|
|
|
|
|
} from '@angular/core';
|
2023-07-11 15:10:06 +03:00
|
|
|
import { Store } from '@ngrx/store';
|
|
|
|
|
import { AppState } from '@core/core.state';
|
|
|
|
|
import { UntypedFormControl } from '@angular/forms';
|
2023-07-13 12:41:36 +03:00
|
|
|
import { BehaviorSubject, combineLatest, debounce, Observable, of, timer } from 'rxjs';
|
|
|
|
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
|
|
|
import { getMaterialIcons, MaterialIcon } from '@shared/models/icon.models';
|
|
|
|
|
import { distinctUntilChanged, map, mergeMap, share, startWith, tap } from 'rxjs/operators';
|
|
|
|
|
import { ResourcesService } from '@core/services/resources.service';
|
|
|
|
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
|
|
|
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
|
|
|
import { MediaBreakpoints } from '@shared/models/constants';
|
2023-08-08 18:54:07 +03:00
|
|
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
2023-07-11 15:10:06 +03:00
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-material-icons',
|
|
|
|
|
templateUrl: './material-icons.component.html',
|
|
|
|
|
providers: [],
|
|
|
|
|
styleUrls: ['./material-icons.component.scss'],
|
|
|
|
|
encapsulation: ViewEncapsulation.None
|
|
|
|
|
})
|
2023-07-11 15:10:06 +03:00
|
|
|
export class MaterialIconsComponent extends PageComponent implements OnInit {
|
|
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
@ViewChild('iconsPanel')
|
|
|
|
|
iconsPanel: CdkVirtualScrollViewport;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
selectedIcon: string;
|
|
|
|
|
|
2023-08-08 18:54:07 +03:00
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
iconClearButton = false;
|
|
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
@Input()
|
|
|
|
|
popover: TbPopoverComponent<MaterialIconsComponent>;
|
|
|
|
|
|
|
|
|
|
@Output()
|
|
|
|
|
iconSelected = new EventEmitter<string>();
|
|
|
|
|
|
|
|
|
|
iconRows$: Observable<MaterialIcon[][]>;
|
2023-07-11 15:10:06 +03:00
|
|
|
showAllSubject = new BehaviorSubject<boolean>(false);
|
2023-07-13 12:41:36 +03:00
|
|
|
searchIconControl: UntypedFormControl;
|
|
|
|
|
|
|
|
|
|
iconsRowHeight = 48;
|
|
|
|
|
|
|
|
|
|
iconsPanelHeight: string;
|
|
|
|
|
iconsPanelWidth: string;
|
2023-07-11 15:10:06 +03:00
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
notFound = false;
|
2023-07-11 15:10:06 +03:00
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
constructor(protected store: Store<AppState>,
|
|
|
|
|
private resourcesService: ResourcesService,
|
|
|
|
|
private breakpointObserver: BreakpointObserver,
|
|
|
|
|
private cd: ChangeDetectorRef) {
|
2023-07-11 15:10:06 +03:00
|
|
|
super(store);
|
2023-07-13 12:41:36 +03:00
|
|
|
this.searchIconControl = new UntypedFormControl('');
|
2023-07-11 15:10:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
2023-07-13 12:41:36 +03:00
|
|
|
const iconsRowSize = this.breakpointObserver.isMatched(MediaBreakpoints['lt-md']) ? 8 : 11;
|
|
|
|
|
this.calculatePanelSize(iconsRowSize);
|
|
|
|
|
const iconsRowSizeObservable = this.breakpointObserver
|
|
|
|
|
.observe(MediaBreakpoints['lt-md']).pipe(
|
|
|
|
|
map((state) => state.matches ? 8 : 11),
|
|
|
|
|
startWith(iconsRowSize),
|
|
|
|
|
);
|
|
|
|
|
this.iconRows$ = combineLatest({showAll: this.showAllSubject.asObservable(),
|
|
|
|
|
rowSize: iconsRowSizeObservable,
|
|
|
|
|
searchText: this.searchIconControl.valueChanges.pipe(
|
|
|
|
|
startWith(''),
|
|
|
|
|
debounce((searchText) => searchText ? timer(150) : of({})),
|
|
|
|
|
)}).pipe(
|
|
|
|
|
map((data) => {
|
|
|
|
|
if (data.searchText && !data.showAll) {
|
|
|
|
|
data.showAll = true;
|
|
|
|
|
this.showAllSubject.next(true);
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}),
|
|
|
|
|
distinctUntilChanged((p, c) => c.showAll === p.showAll && c.searchText === p.searchText && c.rowSize === p.rowSize),
|
|
|
|
|
mergeMap((data) => getMaterialIcons(this.resourcesService, data.rowSize, data.showAll, data.searchText).pipe(
|
|
|
|
|
map(iconRows => ({iconRows, iconsRowSize: data.rowSize}))
|
|
|
|
|
)),
|
|
|
|
|
tap((data) => {
|
|
|
|
|
this.notFound = !data.iconRows.length;
|
|
|
|
|
this.calculatePanelSize(data.iconsRowSize, data.iconRows.length);
|
|
|
|
|
this.cd.markForCheck();
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.checkSize();
|
|
|
|
|
}, 0);
|
|
|
|
|
}),
|
|
|
|
|
map((data) => data.iconRows),
|
|
|
|
|
share()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clearSearch() {
|
|
|
|
|
this.searchIconControl.patchValue('', {emitEvent: true});
|
|
|
|
|
}
|
2023-07-11 15:10:06 +03:00
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
selectIcon(icon: MaterialIcon) {
|
|
|
|
|
this.iconSelected.emit(icon.name);
|
2023-07-11 15:10:06 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-08 18:54:07 +03:00
|
|
|
clearIcon() {
|
|
|
|
|
this.iconSelected.emit(null);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-13 12:41:36 +03:00
|
|
|
private calculatePanelSize(iconsRowSize: number, iconRows = 4) {
|
|
|
|
|
this.iconsPanelHeight = Math.min(iconRows * this.iconsRowHeight, 10 * this.iconsRowHeight) + 'px';
|
|
|
|
|
this.iconsPanelWidth = (iconsRowSize * 36 + (iconsRowSize - 1) * 12 + 6) + 'px';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private checkSize() {
|
|
|
|
|
this.iconsPanel?.checkViewportSize();
|
|
|
|
|
this.popover?.updatePosition();
|
|
|
|
|
}
|
2023-07-11 15:10:06 +03:00
|
|
|
}
|