Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit e437b32

Browse files
committed
api-list from ng2.io
1 parent f9ae34f commit e437b32

File tree

2 files changed

+294
-9
lines changed

2 files changed

+294
-9
lines changed
Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,61 @@
1-
<p>
2-
api-list works!
3-
</p>
1+
<div class="banner is-plain api-filter clearfix">
2+
3+
<!-- API Type -->
4+
<div class="form-select-menu">
5+
<div class="overlay" [ngClass]="{ visible: showApiMenu }" (click)="toggleApiMenu()"></div>
6+
<span *ngFor="let apiType of apiTypes; let i = index">
7+
<span *ngIf="selectedApiType === apiType">
8+
<button class="form-select-button has-symbol" (click)="toggleApiMenu()">
9+
<strong *ngIf="selectedApiType.matches[0]">Type:</strong>
10+
<span class="symbol {{apiType.cssClass}}" *ngIf="apiType.cssClass"></span>
11+
<span *ngIf="selectedApiType.matches[0]">{{apiType.title}}</span>
12+
<strong *ngIf="!selectedApiType.matches[0]">Type: All</strong>
13+
</button>
14+
</span>
15+
<button class="form-select-button is-default" *ngIf="i === 0 && selectedApiType === emptyApiType" (click)="toggleApiMenu()">
16+
<strong>Type: All</strong>
17+
</button>
18+
</span>
19+
<ul class="form-select-dropdown" [ngClass]="{ visible: showApiMenu }">
20+
<li class="fw-300" *ngFor="let apiType of apiTypes" [ngClass]="{ active: selectedApiType === apiType }"
21+
(click)="setApiType(apiType)"><span class="symbol {{apiType.cssClass}}"></span>{{apiType.title}}
22+
</li>
23+
</ul>
24+
</div>
25+
26+
<!-- API Status -->
27+
<div class="form-select-menu" *ngIf="ngLang !== 'dart'">
28+
<div class="overlay" [ngClass]="{ visible: showStatusMenu }" (click)="toggleStatusMenu()"></div>
29+
<span *ngFor="let statusType of apiStatuses; let i = index">
30+
<span *ngIf="selectedApiStatus === statusType">
31+
<button class="form-select-button has-symbol" (click)="toggleStatusMenu()">
32+
<strong *ngIf="selectedApiStatus.matches[0]">Status:</strong>
33+
<span *ngIf="selectedApiStatus.matches[0]">{{statusType.title}}</span>
34+
<strong *ngIf="!selectedApiStatus.matches[0]">Status: All</strong>
35+
</button>
36+
</span>
37+
<button class="form-select-button is-default" *ngIf="i === 0 && selectedApiStatus === emptyStatusType" (click)="toggleStatusMenu()">
38+
<strong>Status: All</strong>
39+
</button>
40+
41+
</span>
42+
<ul class="form-select-dropdown" [ngClass]="{ visible: showStatusMenu }">
43+
<li class="fw-300" *ngFor="let statusType of apiStatuses" (click)="setStatus(statusType)">{{statusType.title}}</li>
44+
</ul>
45+
</div>
46+
<div class="form-search">
47+
<i class="material-icons">search</i>
48+
<input placeholder="Filter" class="api-filter fw-300" #input type="text" (keyup)="triggerSearch(input.value)">
49+
</div>
50+
</div>
51+
52+
<article class="l-content-small grid-fluid docs-content">
53+
<div *ngFor="let section of groupedSections">
54+
<h2 *ngIf="hasVisibleEntries(section)">{{ section.title }}</h2>
55+
<ul class="api-list" *ngIf="hasVisibleEntries(section)">
56+
<li *ngFor="let item of section.items" [hidden]="!item.show" class="api-item">
57+
<a href="/docs/{{ngLang}}/latest/api/{{item.path}}"><span class="symbol {{item.docType}}"></span>{{ item.title }}</a>
58+
</li>
59+
</ul>
60+
</div>
61+
</article>
Lines changed: 233 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,242 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import {Component, Input, OnInit} from '@angular/core';
2+
import {Http} from '@angular/http';
3+
import {Router, NavigationExtras} from '@angular/router';
4+
import {DocInfoService} from '../../../../shared';
5+
import 'rxjs/add/operator/toPromise';
6+
7+
type Lang = 'dart' | 'ts' | 'js';
8+
export type ApiStyleClass = '' | 'stable' | 'directive' | 'decorator' | 'class' | 'interface' | 'function' | 'enum' | 'const' | 'pipe';
9+
export type ApiMatch = '' | 'stable' | 'directive' | 'decorator' | 'class' | 'interface' | 'function' | 'enum' | 'var' | 'let' | 'const' | 'pipe';
10+
11+
export interface ApiType {
12+
cssClass: ApiStyleClass,
13+
title: string,
14+
matches: Array<ApiMatch>
15+
}
216

317
@Component({
4-
selector: 'ngio-api-list',
5-
templateUrl: './api-list.component.html',
6-
styleUrls: ['./api-list.component.css']
18+
selector: 'api-list',
19+
templateUrl: 'api-list.component.html'
720
})
821
export class ApiListComponent implements OnInit {
22+
@Input() src: string = 'api-list.json';
23+
24+
isApiLoaded: boolean = false;
25+
showApiMenu: boolean = false;
26+
showStatusMenu: boolean = false;
27+
searchText: string = '';
28+
emptyApiType: ApiType = { cssClass: '', title: 'Type: All', matches: [''] };
29+
emptyStatusType = { cssClass: '', title: 'Status: All', matches: [''] };
30+
selectedApiType: ApiType = this.emptyApiType; // default to all items
31+
selectedApiStatus: any = this.emptyStatusType;
32+
groupedSections = [];
33+
sections: any[];
34+
35+
_apiTypes: Array<ApiType> = [
36+
{ cssClass: '', title: 'All', matches: [''] },
37+
{ cssClass: 'directive', title: 'Directive', matches: ['directive'] },
38+
{ cssClass: 'pipe', title: 'Pipe', matches: ['pipe'] },
39+
{ cssClass: 'decorator', title: 'Decorator', matches: ['decorator'] },
40+
{ cssClass: 'class', title: 'Class', matches: ['class'] },
41+
{ cssClass: 'interface', title: 'Interface', matches: ['interface'] },
42+
{ cssClass: 'function', title: 'Function', matches: ['function'] },
43+
{ cssClass: 'enum', title: 'Enum', matches: ['enum'] },
44+
{ cssClass: 'const', title: 'Const', matches: ['var', 'let', 'const'] }
45+
];
46+
47+
apiStatuses = [
48+
{ cssClass: '', title: 'All', matches: [''] },
49+
{ cssClass: 'stable', title: 'Stable', matches: ['stable']},
50+
{ cssClass: 'deprecated', title: 'Deprecated', matches: ['deprecated']},
51+
{ cssClass: 'experimental', title: 'Experimental', matches: ['experimental']},
52+
{ cssClass: 'security', title: 'Security Risk', matches: ['security']}
53+
];
54+
55+
// Dart API entries are not tagged Stable vs Experimental.
56+
_apiTypesForDart: Array<ApiType> = this._apiTypes.filter(t =>
57+
t.title.match(/All|Class|Const|Function/));
58+
59+
constructor(
60+
private http: Http,
61+
private router: Router,
62+
private docInfoSvc: DocInfoService
63+
) {
64+
router.events.subscribe((event) => {
65+
this.applyFilterOnSections();
66+
});
67+
}
68+
69+
get ngLang() { return this.docInfoSvc.ngLang; }
70+
71+
ngOnInit(): void {
72+
const urlPrefix = this.router.url.split('?')[0].replace(/^\/docs\//, '/assets/');
73+
const url = `${urlPrefix}/${this.src}`;
74+
this.fetchUrl(url);
75+
76+
// extract type and status from url
77+
var split = this.router.url.split('?');
78+
if (split.length > 1) {
79+
const paramsRaw = split[1].split('&');
80+
const type = paramsRaw[0] ? paramsRaw[0].split('=')[1] : '';
81+
const status = paramsRaw[1] ? paramsRaw[1].split('=')[1] : '';
82+
83+
// update our internal model if a match for type is found
84+
const apiTypes = this._apiTypes.filter(t => t.matches[0] === type);
85+
if (apiTypes.length > 0) this.selectedApiType = apiTypes[0];
86+
87+
// update our internal model if a match for status is found
88+
const apiStatuses = this.apiStatuses.filter(t => t.matches[0] === status);
89+
if (apiStatuses.length > 0) this.selectedApiStatus = apiStatuses[0];
90+
}
91+
}
92+
93+
fetchUrl(url): Promise<any> {
94+
return this.http.get(url)
95+
.toPromise()
96+
.then(resp => {
97+
this.sections = resp.json();
98+
this.groupedSections = Object.keys(this.sections).map((title) => {
99+
return { title: title, items: this.sections[title] };
100+
});
101+
this.isApiLoaded = true;
102+
this.applyFilterOnSections();
103+
})
104+
.catch(this.handleError);
105+
}
9106

10-
constructor() { }
107+
private handleError(error: any): Promise<any> {
108+
console.error('An error occurred', error); // TODO: report via page.
109+
return Promise.reject(error.message || error);
110+
}
111+
112+
get apiTypes(): Array<ApiType> {
113+
return this.ngLang === 'dart' ? this._apiTypesForDart : this._apiTypes;
114+
}
115+
116+
117+
filterSectionEntries(section) {
118+
for (let item of section.items) {
119+
const isPassingApiFilters = (this.isSearchingAllBucketsFiltered() || this.isMatchingBucket(item));
120+
const isPassingStatusFilters = this.isSearchingAllStatusesFiltered() || this.isMatchingStatus(item) || this.isSecurityRisk(item);
121+
122+
item.show = (isPassingApiFilters && isPassingStatusFilters) && this.isTextMatching(item)
123+
}
124+
};
125+
126+
applyFilterOnSections() {
127+
// Cannot guarantee that <param> in queryParam=<param> is an ApiMatch so we union type check it with a string.
128+
const selectedApiMatchOnUrl: string | ApiMatch = this.router.url.split('=')[1];
129+
this.changeSelectedApiTypeForApiMatch(selectedApiMatchOnUrl);
130+
if (this.isApiLoaded) {
131+
this.groupedSections.forEach((s) => {
132+
this.filterSectionEntries(s)
133+
});
134+
}
135+
}
136+
137+
changeSelectedApiTypeForApiMatch(match: string | ApiMatch) {
138+
const apiTypesThatMatch = this.apiTypes.filter((apiType) => {
139+
const matchName = apiType.matches[0];
140+
return matchName === match; // Should this be: apiType.matches.indexOf(match as ApiMatch) >= 0; ?
141+
});
142+
143+
const shouldUpdateSelectedApiType = apiTypesThatMatch.length > 0;
144+
145+
if (shouldUpdateSelectedApiType) {
146+
this.selectedApiType = apiTypesThatMatch[0];
147+
}
148+
}
149+
150+
isSearchingAllBucketsFiltered(): boolean {
151+
return this.selectedApiType.matches[0] === '';
152+
}
153+
154+
isSearchingAllStatusesFiltered(): boolean {
155+
return this.selectedApiStatus.matches[0] === '';
156+
}
11157

12-
ngOnInit() {
158+
isTextMatching(item): boolean {
159+
return (!this.searchText || item.title.toLowerCase().indexOf(this.searchText.toLowerCase()) !== -1);
13160
}
14161

162+
isMatchingBucket(item): boolean {
163+
// Hmm, should be able to use [].includes().
164+
return this.selectedApiType.matches.indexOf(item.docType) >= 0;
165+
}
166+
167+
isMatchingStatus(item): boolean {
168+
return this.selectedApiStatus.matches.indexOf(item.stability) >= 0;
169+
}
170+
171+
isSecurityRisk(item): boolean {
172+
// if true "is a security risk", if false "is not a security risk"
173+
// I know, it's confusing.
174+
const _isSecurityRisk = !item.secure;
175+
return this.selectedApiStatus.matches[0] === 'security' && _isSecurityRisk;
176+
}
177+
178+
isEntryStable(item): boolean {
179+
return item.stability === this.selectedApiType.matches[0];
180+
}
181+
182+
isApiTypeSelected(apiType) {
183+
return apiType === this.selectedApiType;
184+
}
185+
186+
hasVisibleEntries(section): boolean {
187+
return section.items.filter((i) => { return i.show }).length > 0;
188+
}
189+
190+
triggerSearch(value) {
191+
this.searchText = value;
192+
this.applyFilterOnSections();
193+
}
194+
195+
setApiType(t) {
196+
this.selectedApiType = t;
197+
198+
const type = t.matches[0];
199+
const status = this.selectedApiStatus.matches[0];
200+
201+
let navigationExtras: NavigationExtras = {
202+
queryParams: {
203+
type: type,
204+
status: status
205+
}
206+
};
207+
208+
this.router.navigate([`/docs/${this.ngLang}/latest/api`], navigationExtras);
209+
210+
this.toggleApiMenu();
211+
}
212+
213+
setStatus(s) {
214+
this.selectedApiStatus = s;
215+
216+
const type = this.selectedApiType.matches[0];
217+
const status = s.matches[0];
218+
219+
let navigationExtras: NavigationExtras = {
220+
queryParams: {
221+
type: type,
222+
status: status
223+
}
224+
};
225+
226+
this.router.navigate([`/docs/${this.ngLang}/latest/api`], navigationExtras);
227+
228+
this.toggleStatusMenu();
229+
}
230+
231+
toggleApiMenu() {
232+
this.showApiMenu = !this.showApiMenu;
233+
}
234+
235+
toggleStatusMenu() {
236+
this.showStatusMenu = !this.showStatusMenu;
237+
}
238+
239+
clearType() {
240+
241+
}
15242
}

0 commit comments

Comments
 (0)