Skip to content

Commit ff0b647

Browse files
committed
Refined search UI.
1 parent d73d97a commit ff0b647

6 files changed

Lines changed: 193 additions & 201 deletions

File tree

Lines changed: 35 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,40 @@
1-
<form>
2-
<div fxLayout="row"
3-
fxLayoutAlign="center center"
4-
class="search-form"
5-
slot="fixed"
6-
fxLayoutGap="10px"
7-
style="background-color: #424242; position: relative;">
8-
<mat-form-field class="search-input-form-field" fxFlex="25">
9-
<mat-label>关键字</mat-label>
10-
<input type="text" matInput [formControl]="keywordsFormControl"
11-
[(ngModel)]="keywords"
12-
[errorStateMatcher]="matcher" />
13-
<button mat-button *ngIf="keywords" matSuffix mat-icon-button aria-label="Clear" (click)="clearKeywords()">
14-
<mat-icon>close</mat-icon>
15-
</button>
16-
<mat-hint>搜索用到的关键字,以空格分开</mat-hint>
17-
<mat-error *ngIf="keywordsFormControl.hasError('required')">
18-
关键字 <strong>必须提供</strong>
19-
</mat-error>
20-
</mat-form-field>
1+
<div fxLayout="row" fxLayoutAlign="none start" style="background-color: white;">
2+
<!--搜索结果-->
3+
<div fxFlex="65" fxLayout="column"
4+
fxLayoutAlign="start stretch"
5+
fxLayoutGap="10px"
6+
fxFlexOffset="10px">
7+
<mat-card class="search-result" *ngFor="let book of bookList">
8+
<mat-card-header>
9+
<img mat-card-avatar
10+
uiImageLoader
11+
onErrorSrc="assets/images/avatar.png"
12+
[src]="book.writerAvatarUrl" />
13+
<mat-card-title>
14+
{{book.fullName}}
15+
</mat-card-title>
16+
<mat-card-subtitle>
17+
{{book.writerName}}
18+
</mat-card-subtitle>
19+
</mat-card-header>
2120

22-
<mat-radio-group aria-label="Select a platform"
23-
fxLayout="row"
24-
fxLayoutAlign="space-evenly center"
25-
[(ngModel)]="platformSelected" fxLayoutGap="10px">
26-
<mat-radio-button *ngFor="let platform of platforms" [value]="platform.name">
27-
<mat-chip>
28-
<ion-icon [src]="platform.icon"></ion-icon>{{platform.name}}
29-
</mat-chip>
30-
</mat-radio-button>
31-
</mat-radio-group>
21+
<mat-card-content *ngIf="book.desc.length > 0">
22+
<mat-label><b></b>{{book.desc.length > 256 ? book.desc.slice(0,256)+'...' : book.desc}}<b></b></mat-label>
23+
</mat-card-content>
3224

33-
<button mat-stroked-button color="primary"
34-
(click)="search(1)"
35-
[disabled]="!keywordsFormControl.valid">
36-
<ion-text color="light"><ion-icon name="search-outline"></ion-icon>搜索</ion-text>
37-
</button>
38-
</div>
39-
</form>
40-
41-
<div fxLayout="column" fxLayoutAlign="start stretch">
42-
<div fxLayout="row" fxLayoutAlign="none stretch" style="background-color: white;">
43-
<!--搜索结果-->
44-
<div fxFlex="60" fxLayout="column"
45-
fxLayoutAlign="start stretch"
46-
fxLayoutGap="10px"
47-
fxFlexOffset="10px"
48-
style="top: 10px;">
49-
<mat-card class="search-result" *ngFor="let book of bookList">
50-
<mat-card-header>
51-
<img mat-card-avatar
52-
uiImageLoader
53-
onErrorSrc="assets/images/avatar.png"
54-
[src]="book.writerAvatarUrl" />
55-
<mat-card-title>
56-
{{book.fullName}}
57-
</mat-card-title>
58-
<mat-card-subtitle>
59-
{{book.writerName}}
60-
</mat-card-subtitle>
61-
</mat-card-header>
62-
63-
<mat-card-content *ngIf="book.desc.length > 0">
64-
<mat-label><b></b>{{book.desc.length > 256 ? book.desc.slice(0,256)+'...' : book.desc}}<b></b></mat-label>
65-
</mat-card-content>
66-
67-
<mat-card-footer fxLayout="row" fxLayoutAlign="none stretch" fxLayoutGap="50px">
68-
<ion-label>
69-
获星<ion-icon name="star"></ion-icon><i>{{book.stars}}</i>
70-
</ion-label>
71-
<ion-label>
72-
更新时间:<i>{{book.dateUpdated | date : 'yyyy-M-d H:mm:ss' : 'GMT+8' }}</i>
73-
</ion-label>
74-
</mat-card-footer>
75-
</mat-card>
25+
<mat-card-footer fxLayout="row" fxLayoutAlign="none stretch" fxLayoutGap="50px">
26+
<ion-label>
27+
获星<ion-icon name="star"></ion-icon><i>{{book.stars}}</i>
28+
</ion-label>
29+
<ion-label>
30+
更新时间:<i>{{book.dateUpdated | date : 'yyyy-M-d H:mm:ss' : 'GMT+8' }}</i>
31+
</ion-label>
32+
</mat-card-footer>
33+
</mat-card>
34+
<div *ngIf="searching" fxFlexAlign="center">
35+
<ion-icon src="assets/images/loader.svg" size="large"></ion-icon>
7636
</div>
77-
<!--最近搜索条目-->
78-
<div></div>
7937
</div>
38+
<!--最近搜索条目-->
39+
<div></div>
8040
</div>
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
.mat-input-element {
2-
color: white;
3-
}
1+
Lines changed: 6 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,16 @@
1-
import { Component, OnInit } from '@angular/core';
2-
3-
import {FormControl, FormGroupDirective, NgForm, Validators} from '@angular/forms';
4-
import {ErrorStateMatcher} from '@angular/material/core';
5-
6-
import { FetchService } from '../services/fetch.service';
7-
import { MessageService } from '../../services/message.service';
8-
9-
import {
10-
ICloudBook,
11-
IMessage
12-
} from '../../vendor';
13-
14-
/** Error when invalid control is dirty, touched, or submitted. */
15-
export class MyErrorStateMatcher implements ErrorStateMatcher {
16-
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
17-
const isSubmitted = form && form.submitted;
18-
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
19-
}
20-
}
1+
import { Component, OnInit, Input } from '@angular/core';
212

3+
import { ICloudBook } from '../../vendor';
224
@Component({
235
selector: 'app-books-cloud-search',
246
templateUrl: './search.component.html',
257
styleUrls: ['./search.component.scss'],
268
})
279
export class SearchComponent implements OnInit {
28-
keywordsFormControl = new FormControl('', [
29-
Validators.required
30-
]);
31-
32-
bookList: Array<ICloudBook> = [];
33-
34-
platforms = [{
35-
name: 'github.com',
36-
icon: 'assets/images/github-favicon.svg',
37-
},{
38-
name: 'gitee.com',
39-
icon: 'assets/images/logo_gitee_g_red.svg',
40-
},{
41-
name: 'gitlab.com',
42-
icon: 'assets/images/gitlab-seeklogo.com.svg',
43-
}];
44-
45-
matcher = new MyErrorStateMatcher();
46-
keywords: string = '';
47-
platformSelected: string = 'github.com';
48-
bookListCount: number;
49-
50-
constructor(
51-
private fetchService: FetchService,
52-
private message: MessageService
53-
) { }
54-
55-
ngOnInit() {
56-
this.message.getMessage().subscribe(async (msg: IMessage) => {
57-
if(msg.event === 'scrolled-to-end'){
58-
if(this.bookList.length%20 === 0 && !(/gitlab/.test(this.platformSelected))){
59-
this.search(this.bookList.length/20 + 1);
60-
}
61-
}
62-
});
63-
}
64-
65-
clearKeywords = () => {
66-
this.keywords = '';
67-
}
68-
/*
69-
*
70-
* @page, 传入 1 表示首次搜索,需要清空原有 bookList
71-
*/
72-
search = async (page: number) => {
73-
const res = await this.fetchService.searchBooks(this.platformSelected, this.keywords, page) as object[] | object;
74-
75-
let _bookList: object[]
76-
if(/github/.test(this.platformSelected)){
77-
_bookList = res['items'].slice();
78-
this.bookListCount = res['total_count'];
79-
} else {
80-
_bookList = (res as object[]).slice();
81-
}
82-
83-
if(page === 1) this.bookList = [].slice();
84-
_bookList.map((bookRaw: object) => {
85-
const book: ICloudBook = {
86-
fullName: '',
87-
url: '',
88-
desc: '',
89-
writerName: '',
90-
writerAvatarUrl: '',
91-
dateUpdated: new Date(),
92-
stars: 0
93-
};
94-
95-
book.desc = bookRaw['description'] ? bookRaw['description'] : '';
10+
@Input() bookList: Array<ICloudBook>;
11+
@Input() searching: boolean;
9612

97-
if(/gitlab/.test(this.platformSelected)){
98-
book.dateUpdated = bookRaw['last_activity_at'];
99-
book.writerName = bookRaw['namespace']['name'];
100-
book.writerAvatarUrl = bookRaw['namespace']['avatar_url'];
101-
book.url = bookRaw['http_url_to_repo'];
102-
book.stars = bookRaw['star_count'];
103-
book.fullName = bookRaw['path_with_namespace'];
104-
}
105-
if(!(/gitlab/.test(this.platformSelected))){
106-
book.dateUpdated = bookRaw['updated_at'];
107-
book.writerName = /github/.test(this.platformSelected) ? bookRaw['owner']['login'] : bookRaw['owner']['name'];
108-
book.writerAvatarUrl = bookRaw['owner']['avatar_url'];
109-
book.url = /github/.test(this.platformSelected) ? bookRaw['clone_url'] : bookRaw['html_url'];
110-
book.stars = bookRaw['stargazers_count'];
111-
book.fullName = bookRaw['full_name'];
112-
}
13+
constructor() {}
11314

114-
this.bookList.push(book);
115-
});
116-
}
15+
ngOnInit() {}
11716
}

src/app/home/home.page.html

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ion-header>
2-
<ion-toolbar slots="end">
2+
<ion-toolbar slots="end" class="app-toolbar">
33
<ion-title style="position: absolute; left: 150px;">YAGR Gitbook阅读器</ion-title>
44
<ion-fab vertical="center" horizontal="end" edge>
55
<ion-grid>
@@ -29,8 +29,7 @@
2929
<ion-badge color="success" style="position: absolute; right: 0px; top: 0px; overflow: visible!important;">
3030
{{currentlyReading}}
3131
</ion-badge>
32-
<ion-fab-button
33-
color="light" (click)="displayBookListCurrentlyReading()" matTooltip="正在看的书">
32+
<ion-fab-button color="light" (click)="displayBookListCurrentlyReading()" matTooltip="正在看的书">
3433
<ion-avatar id="avatar-currently-reading">
3534
<img src="assets/images/currently-reading-books.png" />
3635
</ion-avatar>
@@ -65,18 +64,58 @@
6564
</ion-grid>
6665
</ion-fab>
6766
</ion-toolbar>
67+
<ion-toolbar class="search-toolbar" *ngIf="search">
68+
<form>
69+
<div fxLayout="row"
70+
fxLayoutAlign="center center"
71+
slot="fixed"
72+
fxLayoutGap="10px">
73+
<mat-form-field class="search-input-form-field" fxFlex="25">
74+
<mat-label>关键字</mat-label>
75+
<input type="text" matInput [formControl]="keywordsFormControl"
76+
[(ngModel)]="keywords"
77+
[errorStateMatcher]="matcher" />
78+
<button mat-button
79+
matTooltip="清除关键字"
80+
*ngIf="keywords" matSuffix mat-icon-button aria-label="Clear" (click)="clearKeywords()">
81+
<ion-icon class="remove-search-keywords" name="close-outline"></ion-icon>
82+
</button>
83+
<mat-hint>搜索用到的关键字,以空格分开</mat-hint>
84+
<mat-error *ngIf="keywordsFormControl.hasError('required')">
85+
关键字 <strong>必须提供</strong>
86+
</mat-error>
87+
</mat-form-field>
88+
89+
<mat-radio-group aria-label="Select a platform"
90+
fxLayout="row"
91+
fxLayoutAlign="space-evenly center"
92+
[(ngModel)]="platformSelected" fxLayoutGap="10px">
93+
<mat-radio-button *ngFor="let platform of platforms" [value]="platform.name">
94+
<mat-chip>
95+
<ion-icon [src]="platform.icon"></ion-icon>{{platform.name}}
96+
</mat-chip>
97+
</mat-radio-button>
98+
</mat-radio-group>
99+
100+
<button mat-stroked-button color="primary"
101+
(click)="cloudSearch(1)"
102+
[disabled]="!keywordsFormControl.valid">
103+
<ion-text color="light"><ion-icon name="search-outline"></ion-icon>搜索</ion-text>
104+
</button>
105+
</div>
106+
</form>
107+
</ion-toolbar>
68108
</ion-header>
69-
<ion-content
70-
[scrollEvents]="true"
71-
(ionScroll)="onScroll($event)"
72-
padding>
109+
110+
<ion-content [scrollEvents]="true" (ionScroll)="onScroll($event)" padding>
73111
<section class="book-list">
74112
<app-book-list [_bookList]="bookList"
75113
*ngIf="!search"
76114
[sortBy]="sortBy"
77115
[beenOpened]="beenOpened"
78116
[displayRecycled]="displayRecycled">
79117
</app-book-list>
80-
<app-books-cloud-search *ngIf="search"></app-books-cloud-search>
118+
<app-books-cloud-search *ngIf="search" [bookList]="bookListCloud" [searching]="searching">
119+
</app-books-cloud-search>
81120
</section>
82121
</ion-content>

src/app/home/home.page.scss

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
ion-header {
2-
ion-toolbar{
2+
ion-toolbar.app-toolbar{
33
--background: url("/assets/images/BANNER-Books-V2.png") no-repeat;
44
--background-size: 100px;
5+
--min-height: 80px;
6+
}
7+
8+
ion-toolbar.search-toolbar{
9+
--background: #424242;
510
}
611
}
712

813
ion-content {
914
--background: lightblue;
1015
}
1116

12-
ion-toolbar {
13-
--min-height: 80px;
14-
}
15-
1617
ion-badge {
1718
z-index: 10;
1819
}
20+
21+
.mat-input-element {
22+
color: white;
23+
}
24+
25+
ion-icon.remove-search-keywords {
26+
color: white;
27+
}

0 commit comments

Comments
 (0)