Skip to content

Commit

Permalink
Feature Go Search
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamhency authored and AlexOverbeck committed Jul 24, 2019
1 parent 82d8b7b commit a3b588b
Show file tree
Hide file tree
Showing 15 changed files with 535 additions and 11 deletions.
57 changes: 57 additions & 0 deletions projects/go-lib/src/lib/animations/search.animation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
animate,
state,
style,
transition,
trigger
} from '@angular/animations';

const timing = '.5s'
const easing = 'cubic-bezier(.25, .8, .25, 1)';

export const searchLoaderAnim = trigger('searchLoaderAnim', [
transition(':enter', [
style({
height: 0,
opacity: 0,
padding: 0
}),
animate(timing + ' ' + easing, style({
height: '*',
opacity: 1,
padding: '2rem'
}))
]),
transition(':leave', [
animate(timing + ' ' + easing, style({
height: 0,
opacity: 0,
padding: 0
}))
])
])

export const searchResultsAnim = trigger('searchResultsAnim', [
transition(':enter', [
style({
height: 0,
margin: 0,
opacity: 0
}),
animate(timing + ' .25s ' + easing, style({
height: '*',
margin: '1rem 0 0.5rem 0',
opacity: 1
}))
]),
transition(':leave', [
style({
overflowY: 'hidden'
}),
animate(timing + ' ' + easing, style({
height: 0,
margin: 0,
opacity: 0
}))
])
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="go-search">
<div class="go-search__container"
[ngClass]="{ 'go-search__container--active': searchActive, 'go-search__container--results': goSearchService.hasResults }">
<form [formGroup]="goSearchForm"
class="go-search__field">
<button class="go-search__submit" type="button">
<go-icon icon="search" class="go-search__icon"></go-icon>
</button>
<input class="go-search__input"
placeholder="Search..."
(focus)="toggleActive()"
(blur)="leaveInput($event)"
formControlName="term"/>
</form>
<div @searchLoaderAnim
*ngIf="goSearchService.showLoader"
class="go-search__loader-container">
<go-loader class="go-search__loader" loaderSize="small"></go-loader>
</div>
<div class="go-search__results"
@searchResultsAnim
(@searchResultsAnim.start)="resultsStarted($event)"
(@searchResultsAnim.done)="resultsEnded($event)"
[ngStyle]="{ 'overflow-y': resultsOverflow }"
*ngIf="goSearchService.hasResults && !goSearchService.showLoader">
<ng-content></ng-content>
</div>
<div class="go-search__results"
@searchResultsAnim
(@searchResultsAnim.start)="resultsStarted($event)"
(@searchResultsAnim.done)="resultsEnded($event)"
[ngStyle]="{ 'overflow-y': resultsOverflow }"
*ngIf="goSearchService.showNoResultsMessage">
{{ goSearchService.noResultsMessage }}
</div>
</div>
</div>
143 changes: 143 additions & 0 deletions projects/go-lib/src/lib/components/go-search/go-search.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
@import '~@tangoe/gosheets/base/variables';
@import '~@tangoe/gosheets/base/mixins';

.go-search {
position: relative;
}

.go-search__container {
background: $theme-light-bg;
border: 1px solid $theme-light-border;
border-radius: 1rem;
box-shadow: none;
color: $theme-light-border;
display: flex;
flex-direction: column;
position: absolute;
top: calc(50% - ((1.875rem + 2px) / 2));
// height of input + border, halfed
width: 600px;
@include transition(all);

&:hover {
background: lighten($theme-light-app-bg, 3%);
}
}

.go-search__container--active {
border: 0;
box-shadow: $global-box-shadow;
padding: 0.5rem;
top: calc(50% - (2.875rem / 2));
// height of input with padding, halfed

&:hover {
background: $theme-light-bg;
}
}

.go-search__field {
align-items: center;
display: flex;
@include transition(all);
}

.go-search__submit {
align-items: center;
background: transparent;
border: 0;
color: $theme-light-border;
display: flex;
font-size: 1rem;
padding: 0 0.5rem;

&:hover {
cursor: pointer;
}

&:active, &:focus {
outline: none;
}
}

.go-search__icon {
height: 1rem;
}

.go-search__input {
background: transparent;
border: 0;
flex: 1;
font-family: $base-font-stack;
font-size: 0.875rem;
font-weight: 300;
letter-spacing: 0.02rem;
min-width: 250px;
padding: .5rem .5rem .5rem 0;

&:-ms-input-placeholder {
color: $theme-light-color;
}

&::placeholder {
color: $theme-light-color;
}

&:active, &:focus {
outline: none;
}
}

.go-search__loader-container {
display: flex;
height: calc(4rem + 50px);
justify-content: center;
overflow: hidden;
padding: 2rem;
position: relative;
}

.go-search__loader {
position: absolute;
}

.go-search__results {
background: $theme-light-bg;
color: $theme-light-color;
font-size: 0.875rem;
max-height: 400px;
margin: 1rem 0 0.5rem 0;
overflow-x: hidden;
overflow-y: auto;
padding: 0 0.5rem;
}

/**
* This section should be included in gosheets as a global change.
* Until that happens, we should keep this here.
**/
::-webkit-scrollbar {
height: 12px;
width: 12px;

@media (max-width: 768px) {
height: 0 !important;
width: 0 !important;
}
}

::-webkit-scrollbar-track {
background-color: $theme-light-app-bg;
border-radius: 6px;
}

::-webkit-scrollbar-thumb {
background: $base-light-secondary;
border: 2px solid $theme-light-app-bg;
border-radius: 6px;
@include transition(all);

&:hover {
background: $ui-color-neutral-gradient;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { GoSearchComponent } from './go-search.component';

import { GoIconModule } from '../go-icon/go-icon.module';
import { GoLoaderModule } from '../go-loader/go-loader.module';

describe('GoSearchComponent', () => {
let component: GoSearchComponent;
let fixture: ComponentFixture<GoSearchComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ GoSearchComponent ],
imports: [
CommonModule,
GoIconModule,
GoLoaderModule,
ReactiveFormsModule
]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(GoSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Component, ElementRef, HostListener, OnInit } from '@angular/core';
import { AnimationEvent } from '@angular/animations';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

import { searchLoaderAnim, searchResultsAnim } from '../../animations/search.animation';
import { GoSearchService } from './go-search.service';

@Component({
selector: 'go-search',
templateUrl: './go-search.component.html',
styleUrls: ['./go-search.component.scss'],
animations: [searchLoaderAnim, searchResultsAnim]
})
export class GoSearchComponent implements OnInit {

goSearchForm: FormGroup;
searchActive: boolean = false;
resultsOverflow: string = 'hidden';

@HostListener('document:click') onDocumentClick(event) {
this.closeSearchEvent(event);
}

constructor(
public goSearchService: GoSearchService,
private elementRef: ElementRef,
private fb: FormBuilder
) {
this.goSearchForm = this.fb.group({
term: ''
});
}

ngOnInit(): void {
this.goSearchForm.valueChanges.pipe(
debounceTime(500),
distinctUntilChanged()
).subscribe(changes => {
if (changes['term'].length >= this.goSearchService.termLength) {
this.goSearchService.showNoResultsMessage = false;
this.goSearchService.showLoader = true;
this.goSearchService.updateSearchTerm(changes['term']);
} else {
this.goSearchService.showNoResultsMessage = false;
this.goSearchService.hasResults = false;
this.goSearchService.showLoader = false;
}
});
}

resultsStarted(event: AnimationEvent): void {
this.resultsOverflow = 'hidden';
}

resultsEnded(event: AnimationEvent): void {
this.resultsOverflow = event.toState === null ? 'auto' : 'hidden';
}

toggleActive(): void {
this.searchActive = true;
}

leaveInput(event: any): void {
if (!this.elementRef.nativeElement.contains(event.relatedTarget)) {
this.closeSearch();
}
}

closeSearchEvent(event: any): void {
if (event && !this.elementRef.nativeElement.contains(event.target)) {
this.closeSearch();
}
}

closeSearch(): void {
this.searchActive = false;
this.goSearchService.hasResults = false;
this.goSearchService.showNoResultsMessage = false;
this.goSearchForm.reset('', {onlySelf: true, emitEvent: false});
}
}
25 changes: 25 additions & 0 deletions projects/go-lib/src/lib/components/go-search/go-search.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { GoIconModule } from '../go-icon/go-icon.module';
import { GoLoaderModule } from '../go-loader/go-loader.module';

import { GoSearchComponent } from './go-search.component';

@NgModule({
declarations: [GoSearchComponent],
imports: [
BrowserAnimationsModule,
BrowserModule,
CommonModule,
GoIconModule,
GoLoaderModule,
ReactiveFormsModule
],
exports: [GoSearchComponent]
})

export class GoSearchModule { }
Loading

0 comments on commit a3b588b

Please sign in to comment.