- - -
+ +
- + - +
- +
diff --git a/ui-ngx/src/app/modules/home/home.component.scss b/ui-ngx/src/app/modules/home/home.component.scss index 7d26343517..08852bb9fe 100644 --- a/ui-ngx/src/app/modules/home/home.component.scss +++ b/ui-ngx/src/app/modules/home/home.component.scss @@ -17,6 +17,11 @@ display: flex; width: 100%; height: 100%; + + .tb-invisible { + display: none !important; + } + mat-sidenav-container { flex: 1; } diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index 5ff321f208..daf22ee81a 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -14,10 +14,10 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, ViewChild } from '@angular/core'; -import { Observable } from 'rxjs'; +import { AfterViewInit, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; +import { fromEvent, Observable } from 'rxjs'; import { select, Store } from '@ngrx/store'; -import { map, mergeMap, take } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, map, mergeMap, take, tap } from 'rxjs/operators'; import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; import { User } from '@shared/models/user.model'; @@ -34,19 +34,21 @@ import * as screenfull from 'screenfull'; import { MatSidenav } from '@angular/material'; import { AuthState } from '@core/auth/auth.models'; import { WINDOW } from '@core/services/window.service'; +import { ISearchableComponent, instanceOfSearchableComponent } from '@home/models/searchable-component.models'; @Component({ selector: 'tb-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'] }) -export class HomeComponent extends PageComponent implements OnInit { +export class HomeComponent extends PageComponent implements AfterViewInit, OnInit { authState: AuthState = getCurrentAuthState(this.store); forceFullscreen = this.authState.forceFullscreen; activeComponent: any; + searchableComponent: ISearchableComponent; sidenavMode = 'side'; sidenavOpened = true; @@ -56,6 +58,8 @@ export class HomeComponent extends PageComponent implements OnInit { @ViewChild('sidenav', {static: false}) sidenav: MatSidenav; + @ViewChild('searchInput', {static: false}) searchInputField: ElementRef; + // @ts-ignore fullscreenEnabled = screenfull.enabled; @@ -63,6 +67,10 @@ export class HomeComponent extends PageComponent implements OnInit { userDetails$: Observable; userDetailsString: Observable; + searchEnabled = false; + showSearch = false; + searchText = ''; + constructor(protected store: Store, @Inject(WINDOW) private window: Window, private authService: AuthService, @@ -98,6 +106,18 @@ export class HomeComponent extends PageComponent implements OnInit { ); } + ngAfterViewInit() { + fromEvent(this.searchInputField.nativeElement, 'keyup') + .pipe( + debounceTime(150), + distinctUntilChanged(), + tap(() => { + this.searchTextUpdated(); + }) + ) + .subscribe(); + } + sidenavClicked() { if (this.sidenavMode === 'over') { this.sidenav.toggle(); @@ -120,4 +140,47 @@ export class HomeComponent extends PageComponent implements OnInit { goBack() { this.window.history.back(); } + + activeComponentChanged(activeComponent: any) { + this.showSearch = false; + this.searchText = ''; + this.activeComponent = activeComponent; + if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { + this.searchEnabled = true; + this.searchableComponent = this.activeComponent; + } else { + this.searchEnabled = false; + this.searchableComponent = null; + } + } + + displaySearchMode(): boolean { + return this.searchEnabled && this.showSearch; + } + + openSearch() { + if (this.searchEnabled) { + this.showSearch = true; + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + } + + closeSearch() { + if (this.searchEnabled) { + this.showSearch = false; + if (this.searchText.length) { + this.searchText = ''; + this.searchTextUpdated(); + } + } + } + + private searchTextUpdated() { + if (this.searchableComponent) { + this.searchableComponent.onSearchTextUpdated(this.searchText); + } + } } diff --git a/ui-ngx/src/app/modules/home/models/searchable-component.models.ts b/ui-ngx/src/app/modules/home/models/searchable-component.models.ts new file mode 100644 index 0000000000..caca6283c5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/models/searchable-component.models.ts @@ -0,0 +1,23 @@ +/// +/// Copyright © 2016-2019 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. +/// + +export interface ISearchableComponent { + onSearchTextUpdated(searchText: string); +} + +export function instanceOfSearchableComponent(object: any): object is ISearchableComponent { + return 'onSearchTextUpdated' in object; +} diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html index 8c9cecaaa1..2525745ec8 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html @@ -30,7 +30,7 @@ - -