diff --git a/src/app/app.component.html b/src/app/app.component.html
index b3cb23596..83b1b9a63 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,3 +1,4 @@
+
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
index e2dbefdef..d857cf28f 100644
--- a/src/app/app.component.spec.ts
+++ b/src/app/app.component.spec.ts
@@ -4,8 +4,10 @@ import { LanguageService } from './core/language/language.service';
import { TestModule } from './test.module';
import { AppComponent } from './app.component';
+import { SpinnerComponent } from './core/spinner/spinner.component';
import { NavigatorModule, NavigatorRoutingModule } from './pages';
import { MediaService } from './core/media.service';
+import { RequestService } from './core/request.service';
import { provideAppStore } from './core/core.module';
import {} from 'jasmine';
@@ -20,12 +22,14 @@ describe('AppComponent', () => {
NavigatorRoutingModule
],
declarations: [
- AppComponent
+ AppComponent,
+ SpinnerComponent
],
providers: [
LanguageService,
provideAppStore(),
- MediaService
+ MediaService,
+ RequestService
]
});
TestBed.compileComponents();
diff --git a/src/app/app.component.styl b/src/app/app.component.styl
index af45b752c..ffef49d62 100644
--- a/src/app/app.component.styl
+++ b/src/app/app.component.styl
@@ -1,3 +1,5 @@
+@require '../css/theme.styl';
+
:host, main {
width: 100%;
height: 100%;
@@ -8,3 +10,11 @@ main {
padding: 0;
transition: 0.5s;
}
+
+/*--- Spinner ---*/
+igo-spinner {
+ position: absolute;
+ top: $igo-margin;
+ right: $igo-margin;
+ z-index: 5;
+}
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index e39ee6888..b46277b9e 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -1,6 +1,7 @@
import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core';
import { Http, Jsonp } from '@angular/http';
import { CommonModule } from '@angular/common';
+import { MaterialModule } from '@angular/material';
import { MissingTranslationHandler } from 'ng2-translate';
@@ -15,8 +16,8 @@ import { browserMedia, mapView, mapLayers, selectedTool,
focusedResult } from '../reducers';
import { MediaService } from './media.service';
+import { RequestService } from './request.service';
import { MapService } from './map.service';
-
import { ToolService } from './tool.service';
import { SearchService } from './search.service';
import { SearchSourceService } from './search-source.service';
@@ -24,6 +25,8 @@ import { SearchSource } from '../search/sources/search-source';
import { SearchSourceNominatim } from '../search/sources/search-source-nominatim';
import { SearchSourceMSP } from '../search/sources/search-source-msp';
+import { SpinnerComponent } from './spinner/spinner.component';
+
export function searchSourceServiceFactory(sources: SearchSource[]) {
return new SearchSourceService(sources);
}
@@ -77,15 +80,18 @@ export function provideAppStore() {
});
}
-
@NgModule({
imports: [
- CommonModule
+ CommonModule,
+ MaterialModule
],
- exports: [],
- declarations: []
+ exports: [
+ SpinnerComponent
+ ],
+ declarations: [
+ SpinnerComponent
+ ]
})
-
export class CoreModule {
static forRoot(): ModuleWithProviders {
return {
@@ -94,6 +100,7 @@ export class CoreModule {
LanguageService,
{ provide: MissingTranslationHandler, useClass: IgoMissingTranslationHandler },
MediaService,
+ RequestService,
provideAppStore(),
provideSearchSourceService(),
MapService,
diff --git a/src/app/core/request.service.spec.ts b/src/app/core/request.service.spec.ts
new file mode 100644
index 000000000..4e3de278d
--- /dev/null
+++ b/src/app/core/request.service.spec.ts
@@ -0,0 +1,16 @@
+/* tslint:disable:no-unused-variable */
+
+import { TestBed, async, inject } from '@angular/core/testing';
+import { RequestService } from './request.service';
+
+describe('RequestService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [RequestService]
+ });
+ });
+
+ it('should ...', inject([RequestService], (service: RequestService) => {
+ expect(service).toBeTruthy();
+ }));
+});
diff --git a/src/app/core/request.service.ts b/src/app/core/request.service.ts
new file mode 100644
index 000000000..a5fad1bc0
--- /dev/null
+++ b/src/app/core/request.service.ts
@@ -0,0 +1,25 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+
+@Injectable()
+export class RequestService {
+
+ private count = 0;
+ requests = new Subject();
+
+ constructor() { }
+
+ register(request: Observable) {
+ this.count += 1;
+ this.requests.next(this.count);
+
+ return request.finally(this.unregister.call(this));
+ }
+
+ private unregister() {
+ this.count -= 1;
+ this.requests.next(this.count);
+ }
+
+}
diff --git a/src/app/core/search.service.spec.ts b/src/app/core/search.service.spec.ts
index 38b1b6a83..abc57e94c 100644
--- a/src/app/core/search.service.spec.ts
+++ b/src/app/core/search.service.spec.ts
@@ -1,6 +1,7 @@
import { TestBed, inject } from '@angular/core/testing';
import { SearchService } from './search.service';
+import { RequestService } from './request.service';
import { HttpModule, JsonpModule } from '@angular/http';
import {
@@ -20,7 +21,8 @@ describe('SearchService', () => {
provideAppStore(),
provideSearchSourceService(),
provideSearchSource(),
- SearchService
+ SearchService,
+ RequestService
]
});
});
diff --git a/src/app/core/search.service.ts b/src/app/core/search.service.ts
index d7455f79e..efc847130 100644
--- a/src/app/core/search.service.ts
+++ b/src/app/core/search.service.ts
@@ -5,6 +5,7 @@ import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
+import { RequestService } from './request.service';
import { SearchSourceService } from './search-source.service';
import { SearchResult } from '../search/shared/search-result.interface';
import { SearchSource } from '../search/sources/search-source';
@@ -17,7 +18,8 @@ export class SearchService {
subscriptions: Subscription[] = [];
constructor(private store: Store,
- private searchSourceService: SearchSourceService) {
+ private searchSourceService: SearchSourceService,
+ private requestService: RequestService) {
}
search(term?: string) {
@@ -29,7 +31,9 @@ export class SearchService {
}
searchSource(source: SearchSource, term?: string) {
- return source.search(term)
+ const request = source.search(term);
+
+ return this.requestService.register(request)
.catch(this.handleError)
.subscribe((results: SearchResult[]) =>
this.handleSearchResults(results, source));
diff --git a/src/app/core/spinner/spinner.component.html b/src/app/core/spinner/spinner.component.html
new file mode 100644
index 000000000..cfa579f46
--- /dev/null
+++ b/src/app/core/spinner/spinner.component.html
@@ -0,0 +1,4 @@
+
diff --git a/src/app/core/spinner/spinner.component.spec.ts b/src/app/core/spinner/spinner.component.spec.ts
new file mode 100644
index 000000000..fcca86af6
--- /dev/null
+++ b/src/app/core/spinner/spinner.component.spec.ts
@@ -0,0 +1,32 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TestModule } from '../../test.module';
+
+import { RequestService } from '../request.service';
+import { SpinnerComponent } from './spinner.component';
+
+describe('SpinnerComponent', () => {
+ let component: SpinnerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ TestModule
+ ],
+ declarations: [ SpinnerComponent ],
+ providers: [ RequestService ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SpinnerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/core/spinner/spinner.component.styl b/src/app/core/spinner/spinner.component.styl
new file mode 100644
index 000000000..53f3e2337
--- /dev/null
+++ b/src/app/core/spinner/spinner.component.styl
@@ -0,0 +1,33 @@
+@require '../../../css/theme.styl';
+
+$igo-spinner-size = 40px;
+$igo-spinner-border-width = 4px;
+$igo-spinner-top-left = 2px;
+
+.igo-spinner {
+ display: none;
+}
+
+.igo-spinner-shown {
+ display: block;
+}
+
+md-progress-circle {
+ height: $igo-spinner-size;
+ width: $igo-spinner-size;
+ border-radius: 50%;
+}
+
+:host >>> md-progress-circle path {
+ stroke: $igo-primary-color;
+}
+
+.igo-spinner-background {
+ height: $igo-spinner-size - $igo-spinner-border-width;
+ width: $igo-spinner-size - $igo-spinner-border-width;
+ border-radius: 50%;
+ border: $igo-spinner-border-width solid $igo-tertiary-color;
+ position: absolute;
+ top: $igo-spinner-top-left;
+ left: $igo-spinner-top-left;
+}
diff --git a/src/app/core/spinner/spinner.component.ts b/src/app/core/spinner/spinner.component.ts
new file mode 100644
index 000000000..2ef6bad9e
--- /dev/null
+++ b/src/app/core/spinner/spinner.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from '@angular/core';
+
+import { RequestService } from '../request.service';
+
+@Component({
+ selector: 'igo-spinner',
+ templateUrl: './spinner.component.html',
+ styleUrls: ['./spinner.component.styl'],
+})
+export class SpinnerComponent implements OnInit {
+
+ shown: boolean = false;
+
+ constructor(private requestService: RequestService) { }
+
+ ngOnInit() {
+ this.requestService.requests.subscribe((count: number) => {
+ this.shown = count > 0;
+ });
+ }
+
+}
diff --git a/src/app/core/tool.service.ts b/src/app/core/tool.service.ts
index 43f84a371..ca732355d 100644
--- a/src/app/core/tool.service.ts
+++ b/src/app/core/tool.service.ts
@@ -22,6 +22,10 @@ export class ToolService {
ToolService.toolClasses.push(cls);
}
+ constructor() {
+ ToolService.register(SearchToolComponent);
+ }
+
getTool(name: string) {
return ToolService.tools.find(t => t.name === name);
}
@@ -29,9 +33,4 @@ export class ToolService {
getToolClass(name: string) {
return ToolService.toolClasses.find(t => t.name_ === name);
}
-
- constructor() {
- ToolService.register(SearchToolComponent);
- }
-
}
diff --git a/src/app/map/map/map.component.styl b/src/app/map/map/map.component.styl
index f0ca319a4..c7524604d 100644
--- a/src/app/map/map/map.component.styl
+++ b/src/app/map/map/map.component.styl
@@ -5,3 +5,25 @@
width: 100%;
height: 100%;
}
+
+:host >>> .igo-zoom-container {
+ position: absolute;
+ bottom: $igo-margin;
+ right: $igo-margin;
+ width: $igo-icon-button-width;
+
+ +media(mobile) {
+ display: none;
+ }
+}
+
+:host >>> .ol-attribution {
+ left: $igo-margin;
+ right: inherit;
+ text-align: left;
+ padding: 0;
+}
+
+:host >>> .ol-attribution ul {
+ padding: 0;
+}
diff --git a/src/app/map/zoom/zoom.component.styl b/src/app/map/zoom/zoom.component.styl
index 8d70c3b93..917e3cc9f 100644
--- a/src/app/map/zoom/zoom.component.styl
+++ b/src/app/map/zoom/zoom.component.styl
@@ -1,17 +1,6 @@
@require '../../../css/theme.styl';
@require '../../../css/media.styl';
-.igo-zoom-container {
- position: absolute;
- top: $igo-margin;
- right: $igo-margin;
- width: $igo-icon-button-width;
-
- +media(mobile) {
- display: none;
- }
-}
-
.igo-zoom-container button:first-child {
margin-bottom: 2px;
}
diff --git a/src/app/pages/navigator/navigator.component.spec.ts b/src/app/pages/navigator/navigator.component.spec.ts
index 4268e2835..d034c955b 100644
--- a/src/app/pages/navigator/navigator.component.spec.ts
+++ b/src/app/pages/navigator/navigator.component.spec.ts
@@ -8,6 +8,7 @@ import {
import { MapService } from '../../core/map.service';
import { LayerService } from '../../map/shared/layer.service';
import { SearchService } from '../../core/search.service';
+import { RequestService } from '../../core/request.service';
import { TestModule } from '../../test.module';
import { SharedModule } from '../../shared/shared.module';
@@ -40,7 +41,8 @@ describe('NavigatorComponent', () => {
MapService,
LayerService,
SearchService,
- ToolService
+ ToolService,
+ RequestService
]
})
.compileComponents();
diff --git a/src/app/pages/navigator/navigator.component.styl b/src/app/pages/navigator/navigator.component.styl
index 86624db55..9fd219e63 100644
--- a/src/app/pages/navigator/navigator.component.styl
+++ b/src/app/pages/navigator/navigator.component.styl
@@ -5,12 +5,22 @@ $igo-sidenav-width = 400px;
$igo-sidenav-margin-top = 50px;
$igo-mobile-min-space-left = $igo-icon-button-width;
+
/*--- Sidenav ---*/
md-sidenav-container {
width: 100%;
height: 100%;
}
+// This is needed because whe using the
+// sidenav "side" mode, the z-index is 1
+// and the sidenav appears below our backdrop.
+// The "side" mode is required to prevent
+// the sidenav from focusing a random button on open.
+:host >>> md-sidenav.md-sidenav-side {
+ z-index: 3 !important;
+}
+
:host >>> igo-sidenav md-sidenav {
width: $igo-sidenav-width;
@@ -60,6 +70,6 @@ md-sidenav-container {
}
#igo-lower-panel {
- border-top();
+ bordered-top();
border-box();
}
\ No newline at end of file
diff --git a/src/app/search/search-bar/search-bar.component.spec.ts b/src/app/search/search-bar/search-bar.component.spec.ts
index d2c82678a..c0f5a4a29 100644
--- a/src/app/search/search-bar/search-bar.component.spec.ts
+++ b/src/app/search/search-bar/search-bar.component.spec.ts
@@ -7,6 +7,7 @@ import {
} from '../../core/core.module';
import { SearchService } from '../../core/search.service';
+import { RequestService } from '../../core/request.service';
import { TestModule } from '../../test.module';
import { SharedModule } from '../../shared/shared.module';
@@ -27,7 +28,8 @@ describe('SearchBarComponent', () => {
provideAppStore(),
provideSearchSourceService(),
provideSearchSource(),
- SearchService
+ SearchService,
+ RequestService
]
})
.compileComponents();
diff --git a/src/css/styles.styl b/src/css/styles.styl
index 0fb999fd7..3bc4718d8 100644
--- a/src/css/styles.styl
+++ b/src/css/styles.styl
@@ -17,20 +17,11 @@ html, body {
box-sizing: border-box;
}
-/*
-@import '~@angular/material/core/theming/prebuilt/deeppurple-amber.css';
-*/
-@require './theme.styl';
+//@import '~@angular/material/core/theming/prebuilt/deeppurple-amber.css';
-// This is needed because whe using the
-// sidenav "side" mode, the z-index is 1
-// and the sidenav appears below our backdrop.
-// The "side" mode is required to prevent
-// the sidenav from focusing a random button on open.
-md-sidenav.md-sidenav-side {
- z-index: 3 !important;
-}
+
+@require './theme.styl';
input {
height: $igo-icon-button-height;
diff --git a/src/css/theme.styl b/src/css/theme.styl
index e545125c8..e30981eb7 100644
--- a/src/css/theme.styl
+++ b/src/css/theme.styl
@@ -48,31 +48,27 @@ border-box()
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-border()
+bordered()
border-width $igo-border-width
border-style $igo-border-style
border-color $igo-border-color
- -webkit-box-shadow: inset 0px 0px 0px 10px #f00;
- -moz-box-shadow: inset 0px 0px 0px 10px #f00;
- box-shadow: inset 0px 0px 0px 10px #f00;
-
-border-top()
+bordered-top()
border-top-width $igo-border-width
border-top-style $igo-border-style
border-top-color $igo-border-color
-border-bottom()
+bordered-bottom()
border-bottom-width $igo-border-width
border-bottom-style $igo-border-style
border-bottom-color $igo-border-color
-border-left()
+bordered-left()
border-left-width $igo-border-width
border-left-style $igo-border-style
border-left-color $igo-border-color
-border-right()
+bordered-right()
border-right-width $igo-border-width
border-right-style $igo-border-style
border-right-color $igo-border-color