mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
Implementing add saved search button on the albums page #45
This commit is contained in:
parent
c31e7b8e3a
commit
fc854f7295
@ -39,7 +39,7 @@ export class ClientSearchConfig {
|
||||
@SubConfigClass()
|
||||
export class ClientAlbumConfig {
|
||||
@ConfigProperty()
|
||||
enabled: boolean = true;
|
||||
enabled: boolean = false;
|
||||
}
|
||||
|
||||
@SubConfigClass()
|
||||
|
@ -103,6 +103,7 @@ import {icon, Marker} from 'leaflet';
|
||||
import {AlbumsComponent} from './ui/albums/albums.component';
|
||||
import {AlbumComponent} from './ui/albums/album/album.component';
|
||||
import {AlbumsService} from './ui/albums/albums.service';
|
||||
import {GallerySearchQueryBuilderComponent} from './ui/gallery/search/query-builder/query-bulder.gallery.component';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@ -196,6 +197,7 @@ Marker.prototype.options.icon = iconDefault;
|
||||
GallerySearchComponent,
|
||||
GallerySearchQueryEntryComponent,
|
||||
GallerySearchFieldComponent,
|
||||
GallerySearchQueryBuilderComponent,
|
||||
GalleryShareComponent,
|
||||
GalleryNavigatorComponent,
|
||||
GalleryPhotoComponent,
|
||||
|
@ -3,11 +3,27 @@ app-album {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.no-item-msg{
|
||||
.no-item-msg {
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-face-msg h2{
|
||||
.no-face-msg h2 {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.add-saved-search {
|
||||
vertical-align: baseline;
|
||||
position: absolute;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.add-saved-search .text {
|
||||
position: relative;
|
||||
top: calc(50% - 40px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-saved-search .text .oi {
|
||||
font-size: 80px;
|
||||
}
|
||||
|
@ -2,15 +2,73 @@
|
||||
|
||||
<div body #container class="container-fluid">
|
||||
<app-album *ngFor="let album of albumsService.albums | async"
|
||||
[album]="album"
|
||||
[size]="size"></app-album>
|
||||
[album]="album"
|
||||
[size]="size"></app-album>
|
||||
|
||||
<div class="add-saved-search btn btn-secondary"
|
||||
[style.width.px]="size"
|
||||
[style.height.px]="size"
|
||||
(click)="openModal(modal)">
|
||||
<div class="text">
|
||||
<span class="oi oi-plus" aria-hidden="true"> </span><br/>
|
||||
<span i18n>Add saved search</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex no-item-msg"
|
||||
*ngIf="(albumsService.albums | async) && (albumsService.albums | async).length == 0">
|
||||
<div class="flex-fill">
|
||||
<h2>:( <ng-container i18n>No albums to show.</ng-container>
|
||||
<h2>:(
|
||||
<ng-container i18n>No albums to show.</ng-container>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</app-frame>
|
||||
|
||||
|
||||
<ng-template #modal>
|
||||
<!-- sharing Modal-->
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" i18n>Add Saved Search</h5>
|
||||
<button type="button" class="close" (click)="hideModal()" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form #savedSearchPanelForm="ngForm" class="form-horizontal">
|
||||
<div class="row">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="saveSearchName">Album name</label>
|
||||
<input
|
||||
id="saveSearchName"
|
||||
name="saveSearchName"
|
||||
placeholder="Search text"
|
||||
class="form-control input-md"
|
||||
[(ngModel)]="savedSearch.name"
|
||||
type="text"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="album-search-query-builder">Search query</label>
|
||||
<app-gallery-search-query-builder
|
||||
id="album-search-query-builder"
|
||||
name="album-search-query-builder"
|
||||
[(ngModel)]="savedSearch.searchQuery">
|
||||
</app-gallery-search-query-builder>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group-btn float-right row" style="display: block">
|
||||
|
||||
<button class="btn btn-primary" type="button"
|
||||
[disabled]="savedSearch.searchQuery.text == ''"
|
||||
(click)="saveSearch()">
|
||||
<span class="oi oi-folder"></span> Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
|
||||
import {Component, ElementRef, OnInit, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {AlbumsService} from './albums.service';
|
||||
import {BsModalService} from 'ngx-bootstrap/modal';
|
||||
import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
import {SearchQueryTypes, TextSearch} from '../../../../common/entities/SearchQueryDTO';
|
||||
|
||||
@Component({
|
||||
selector: 'app-albums',
|
||||
@ -9,9 +12,14 @@ import {AlbumsService} from './albums.service';
|
||||
export class AlbumsComponent implements OnInit {
|
||||
@ViewChild('container', {static: true}) container: ElementRef;
|
||||
public size: number;
|
||||
public savedSearch = {
|
||||
name: '',
|
||||
searchQuery: {type: SearchQueryTypes.any_text, text: ''} as TextSearch
|
||||
};
|
||||
private modalRef: BsModalRef;
|
||||
|
||||
|
||||
constructor(public albumsService: AlbumsService) {
|
||||
constructor(public albumsService: AlbumsService,
|
||||
private modalService: BsModalService) {
|
||||
this.albumsService.getAlbums().catch(console.error);
|
||||
}
|
||||
|
||||
@ -20,6 +28,22 @@ export class AlbumsComponent implements OnInit {
|
||||
this.updateSize();
|
||||
}
|
||||
|
||||
|
||||
public async openModal(template: TemplateRef<any>): Promise<void> {
|
||||
this.modalRef = this.modalService.show(template, {class: 'modal-lg'});
|
||||
document.body.style.paddingRight = '0px';
|
||||
}
|
||||
|
||||
public hideModal(): void {
|
||||
this.modalRef.hide();
|
||||
this.modalRef = null;
|
||||
}
|
||||
|
||||
async saveSearch(): Promise<void> {
|
||||
await this.albumsService.addSavedSearch(this.savedSearch.name, this.savedSearch.searchQuery);
|
||||
this.hideModal();
|
||||
}
|
||||
|
||||
private updateSize(): void {
|
||||
const size = 220 + 5;
|
||||
// body - container margin
|
||||
|
@ -0,0 +1,69 @@
|
||||
.autocomplete-list {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 34px;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 5px 0;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.insert-button {
|
||||
margin-right: -15px;
|
||||
display: none;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.autocomplete-item-selected .insert-button {
|
||||
display: block;
|
||||
}
|
||||
@media (hover: none) {
|
||||
.insert-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.autocomplete-item-selected .insert-button:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
cursor: pointer;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
color: #333;
|
||||
padding: 0 20px;
|
||||
line-height: 1.42857143;
|
||||
font-weight: 400;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.autocomplete-item-selected {
|
||||
background-color: #007bff;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
|
||||
.search-text {
|
||||
z-index: 6;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.search-hint {
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<app-gallery-search-field [(ngModel)]="rawSearchText"
|
||||
(ngModelChange)="validateRawSearchText()"
|
||||
(search)="search.emit()"
|
||||
name="form-search-field">
|
||||
|
||||
</app-gallery-search-field>
|
||||
<hr>
|
||||
<app-gallery-search-query-entry
|
||||
[(ngModel)]="searchQueryDTO"
|
||||
(change)="onQueryChange()"
|
||||
(ngModelChange)="onChange()"
|
||||
name="search-root"
|
||||
(delete)="resetQuery()">
|
||||
|
||||
</app-gallery-search-query-entry>
|
@ -0,0 +1,82 @@
|
||||
import {Component, EventEmitter, forwardRef, Output} from '@angular/core';
|
||||
import {SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../../../../../common/entities/SearchQueryDTO';
|
||||
import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator} from '@angular/forms';
|
||||
import {SearchQueryParserService} from '../search-query-parser.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-search-query-builder',
|
||||
templateUrl: './query-builder.gallery.component.html',
|
||||
styleUrls: ['./query-builder.gallery.component.css'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => GallerySearchQueryBuilderComponent),
|
||||
multi: true
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => GallerySearchQueryBuilderComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class GallerySearchQueryBuilderComponent implements ControlValueAccessor, Validator {
|
||||
public searchQueryDTO: SearchQueryDTO = {type: SearchQueryTypes.any_text, text: ''} as TextSearch;
|
||||
@Output() search = new EventEmitter<void>();
|
||||
public rawSearchText = '';
|
||||
|
||||
|
||||
constructor(
|
||||
private searchQueryParserService: SearchQueryParserService) {
|
||||
}
|
||||
|
||||
|
||||
validateRawSearchText(): void {
|
||||
try {
|
||||
this.searchQueryDTO = this.searchQueryParserService.parse(this.rawSearchText);
|
||||
this.onChange();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetQuery(): void {
|
||||
this.searchQueryDTO = ({text: '', type: SearchQueryTypes.any_text} as TextSearch);
|
||||
}
|
||||
|
||||
onQueryChange(): void {
|
||||
this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO);
|
||||
this.onChange();
|
||||
}
|
||||
|
||||
validate(control: FormControl): ValidationErrors {
|
||||
return {required: true};
|
||||
}
|
||||
|
||||
public onTouched(): void {
|
||||
}
|
||||
|
||||
public writeValue(obj: any): void {
|
||||
this.searchQueryDTO = obj;
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this.propagateTouch = fn;
|
||||
}
|
||||
|
||||
public onChange(): void {
|
||||
this.propagateChange(this.searchQueryDTO);
|
||||
}
|
||||
|
||||
|
||||
private propagateChange = (_: any): void => {
|
||||
};
|
||||
|
||||
private propagateTouch = (_: any): void => {
|
||||
};
|
||||
}
|
@ -36,24 +36,19 @@
|
||||
<div class="modal-body">
|
||||
<form #searchPanelForm="ngForm" class="form-horizontal">
|
||||
|
||||
<app-gallery-search-field [(ngModel)]="rawSearchText"
|
||||
(ngModelChange)="validateRawSearchText()"
|
||||
(search)="Search()"
|
||||
name="form-search-field">
|
||||
|
||||
</app-gallery-search-field>
|
||||
<hr>
|
||||
<app-gallery-search-query-entry
|
||||
<app-gallery-search-query-builder
|
||||
name="search-query-builder"
|
||||
[(ngModel)]="searchQueryDTO"
|
||||
(change)="onQueryChange()"
|
||||
name="search-root"
|
||||
(delete)="resetQuery()">
|
||||
(ngModelChange)="onQueryChange()"
|
||||
(search)="Search()">
|
||||
</app-gallery-search-query-builder>
|
||||
|
||||
</app-gallery-search-query-entry>
|
||||
|
||||
<div class="input-group-btn float-right row" style="display: block">
|
||||
|
||||
<button class="btn btn-secondary mr-2" type="button"
|
||||
<button *ngIf="AlbumsEnabled"
|
||||
class="btn btn-secondary mr-2" type="button"
|
||||
[disabled]="rawSearchText == ''"
|
||||
(click)="openSaveSearchModal(saveSearchModal)">
|
||||
<span class="oi oi-folder"></span> Save
|
||||
|
@ -10,6 +10,7 @@ import {BsModalService} from 'ngx-bootstrap/modal';
|
||||
import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
import {SearchQueryParserService} from './search-query-parser.service';
|
||||
import {AlbumsService} from '../../albums/albums.service';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-search',
|
||||
@ -24,10 +25,11 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
mouseOverAutoComplete = false;
|
||||
readonly SearchQueryTypes: typeof SearchQueryTypes;
|
||||
public readonly MetadataSearchQueryTypes: { value: string; key: SearchQueryTypes }[];
|
||||
public saveSearchName: string;
|
||||
AlbumsEnabled = Config.Client.Album.enabled;
|
||||
private searchModalRef: BsModalRef;
|
||||
private readonly subscription: Subscription = null;
|
||||
private saveSearchModalRef: BsModalRef;
|
||||
public saveSearchName: string;
|
||||
|
||||
constructor(private autoCompleteService: AutoCompleteService,
|
||||
private searchQueryParserService: SearchQueryParserService,
|
||||
@ -69,6 +71,11 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
document.body.style.paddingRight = '0px';
|
||||
}
|
||||
|
||||
public hideSearchModal(): void {
|
||||
this.searchModalRef.hide();
|
||||
this.searchModalRef = null;
|
||||
}
|
||||
|
||||
public async openSaveSearchModal(template: TemplateRef<any>): Promise<void> {
|
||||
this.saveSearchModalRef = this.modalService.show(template, {class: 'modal-lg'});
|
||||
document.body.style.paddingRight = '0px';
|
||||
@ -79,20 +86,14 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
this.saveSearchModalRef = null;
|
||||
}
|
||||
|
||||
public hideSearchModal(): void {
|
||||
this.searchModalRef.hide();
|
||||
this.searchModalRef = null;
|
||||
}
|
||||
|
||||
resetQuery(): void {
|
||||
this.searchQueryDTO = ({text: '', type: SearchQueryTypes.any_text} as TextSearch);
|
||||
}
|
||||
|
||||
onQueryChange(): void {
|
||||
console.log('cahnge', this.searchQueryDTO);
|
||||
this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO);
|
||||
// this.validateRawSearchText();
|
||||
}
|
||||
|
||||
|
||||
validateRawSearchText(): void {
|
||||
try {
|
||||
this.searchQueryDTO = this.searchQueryParserService.parse(this.rawSearchText);
|
||||
|
Loading…
x
Reference in New Issue
Block a user