diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.html b/src/app/shared/components/address-autocomplete/address-autocomplete.component.html new file mode 100644 index 0000000000000000000000000000000000000000..537de506db8e233b34cf5ccf81e12f9c98f47f9a --- /dev/null +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.html @@ -0,0 +1,17 @@ +<div class="search-bar"> + <div> + <input + id="search-address" + type="text" + placeholder="ex: 20 rue du lac, Lyon" + (input)="onSearchChange($event.target.value)" + class="form-input" + #searchAddress + /> + </div> + <div class="autocomplete-items"> + <p *ngFor="let hit of data" (click)="selectedResult(hit)" class="autocomplete-item"> + {{ parseHitToAddress(hit) }} + </p> + </div> +</div> diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.scss b/src/app/shared/components/address-autocomplete/address-autocomplete.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..433d3afca2e236a0a53d9e243bf1f15b6803e285 --- /dev/null +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.scss @@ -0,0 +1,22 @@ +@import '../../../../assets/scss/color'; + +.search-bar { + display: flex; + flex-direction: column; +} +.autocomplete-items { + border: 0.0625rem solid #d4d4d4; + border-top: none; + border-bottom: none; + z-index: 99; + background-color: #fff; + cursor: pointer; +} +.autocomplete-item { + margin: 0; + padding: 1em 0; +} +.autocomplete-item:hover { + background-color: #dee6ee; + cursor: pointer; +} diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.spec.ts b/src/app/shared/components/address-autocomplete/address-autocomplete.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a74f2a401dfe1c801ba3a1688acef4fa9d1a453f --- /dev/null +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddressAutocompleteComponent } from './address-autocomplete.component'; + +describe('AddressAutocompleteComponent', () => { + let component: AddressAutocompleteComponent; + let fixture: ComponentFixture<AddressAutocompleteComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AddressAutocompleteComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AddressAutocompleteComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts b/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e6d5cc978a3a20441490af073e3120753ee1bf1 --- /dev/null +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts @@ -0,0 +1,36 @@ +import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core'; +import { AddressService } from '../../service/address.service'; + +@Component({ + selector: 'app-address-autocomplete', + templateUrl: './address-autocomplete.component.html', + styleUrls: ['./address-autocomplete.component.scss'], +}) +export class AddressAutocompleteComponent { + public readonly AUTOCOMPLETE_NBR = 5; + public data = []; + @ViewChild('searchAddress', { static: true }) searchAddress: ElementRef; + @Output() selectedAddress: EventEmitter<string> = new EventEmitter<string>(); + + constructor(private addressService: AddressService) {} + + public onSearchChange(searchString: string) { + this.addressService.searchAddress(searchString).subscribe((data) => { + this.data = data.hits.hits.slice(0, this.AUTOCOMPLETE_NBR); + }); + } + + public selectedResult(hit: any): void { + const value = this.parseHitToAddress(hit); + // Set input value + this.searchAddress.nativeElement.value = value; + // Reset autocomplete + this.data = []; + // Emit choosen value + this.selectedAddress.emit(value); + } + + public parseHitToAddress(hit: any): string { + return `${hit._source['data-fr'].properties.numero_str} ${hit._source['data-fr'].properties.voie_str} ${hit._source['data-fr'].properties.commune_str}`; + } +} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 89b310d7237b90231eb9b548fc868546dfd8290f..e4d55982fd57f00574f4456459841eed11994a92 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -6,6 +6,7 @@ import { SignInModalComponent } from './signin-modal/signin-modal.component'; import { SvgIconComponent } from './svg-icon/svg-icon.component'; import { ValidatorFormComponent } from './validator-form/validator-form.component'; import { CreateAccountFormComponent } from './create-account-form/create-account-form.component'; +import { AddressAutocompleteComponent } from './address-autocomplete/address-autocomplete.component'; // tslint:disable-next-line: max-line-length export { @@ -17,6 +18,7 @@ export { SignUpModalComponent, SignInModalComponent, CreateAccountFormComponent, + AddressAutocompleteComponent, }; // tslint:disable-next-line:variable-name @@ -29,4 +31,5 @@ export const SharedComponents = [ SignUpModalComponent, SignInModalComponent, CreateAccountFormComponent, + AddressAutocompleteComponent, ]; diff --git a/src/app/shared/service/address.service.spec.ts b/src/app/shared/service/address.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0cd890c8048ca5558a6e2c778edcfcd93c81b85 --- /dev/null +++ b/src/app/shared/service/address.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AddressService } from './address.service'; + +describe('AddressService', () => { + let service: AddressService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AddressService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/service/address.service.ts b/src/app/shared/service/address.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..38f66f8d10f2d7c8f51c81bf3079f367d58cb379 --- /dev/null +++ b/src/app/shared/service/address.service.ts @@ -0,0 +1,14 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class AddressService { + constructor(private http: HttpClient) {} + + public searchAddress(searchQuery: string): Observable<any> { + return this.http.post<any>(`api/structures/address`, { searchQuery }); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 7a91eef73550ffb2f1efc354f8f4bf35e26ce7a7..2709454bdff75e2077f2c0fc772a1b073fa58c03 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -7,9 +7,10 @@ import { SharedComponents } from './components'; import { SharedPipes } from './pipes'; import { SharedDirectives } from './directives'; import { SvgIconComponent } from './components/svg-icon/svg-icon.component'; +import { AddressAutocompleteComponent } from './components/address-autocomplete/address-autocomplete.component'; @NgModule({ imports: [CommonModule, RouterModule, FlexLayoutModule, ReactiveFormsModule], - declarations: [...SharedPipes, ...SharedComponents, ...SharedDirectives, SvgIconComponent], + declarations: [...SharedPipes, ...SharedComponents, ...SharedDirectives, SvgIconComponent, AddressAutocompleteComponent], exports: [ ...SharedPipes, ...SharedComponents, diff --git a/src/assets/scss/_inputs.scss b/src/assets/scss/_inputs.scss index dac479e10cf68b8bfad2187cdfb5feff2f38e9da..e672692f6e3e66ced23a2d0175dd1a3bfe61c43d 100644 --- a/src/assets/scss/_inputs.scss +++ b/src/assets/scss/_inputs.scss @@ -1,3 +1,6 @@ +@import './color'; +@import './shapes'; + @mixin input-search { width: 100%; border: none; @@ -7,3 +10,16 @@ outline: none; font-style: italic; } + +.form-input { + min-width: 290px; + background: $grey-6; + border: 1px solid $grey-4; + box-sizing: border-box; + border-radius: $input-radius; + height: 40px; +} +.form-input:focus { + border: 1px solid $blue; + outline: none !important; +} diff --git a/src/assets/scss/_shapes.scss b/src/assets/scss/_shapes.scss index fec384566eade89e0d916b65b1cf54f3845054fe..c85355e0f9d902d9636af3a4f3d464f137190d62 100644 --- a/src/assets/scss/_shapes.scss +++ b/src/assets/scss/_shapes.scss @@ -2,7 +2,7 @@ $card-radius: 0.625em; $bouton-radius: 0.625em; -$input-radius: 0.3125em; +$input-radius: 4px; $bouton-width: 12.25em; $round-bouton-radius: 1.25em; $round-radius: 50%; diff --git a/src/styles.scss b/src/styles.scss index 3e993cf999755166bef51cc59f18f3645bdd2ee0..1773ee54cf9ba8e8bb68207ec02196d7a1b57db6 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -4,6 +4,7 @@ @import 'assets/scss/color'; @import 'assets/scss/breakpoint'; @import 'assets/scss/icons'; +@import 'assets/scss/inputs'; @import '../node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css'; html {