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()
|
@SubConfigClass()
|
||||||
export class ClientAlbumConfig {
|
export class ClientAlbumConfig {
|
||||||
@ConfigProperty()
|
@ConfigProperty()
|
||||||
enabled: boolean = true;
|
enabled: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass()
|
@SubConfigClass()
|
||||||
|
@ -103,6 +103,7 @@ import {icon, Marker} from 'leaflet';
|
|||||||
import {AlbumsComponent} from './ui/albums/albums.component';
|
import {AlbumsComponent} from './ui/albums/albums.component';
|
||||||
import {AlbumComponent} from './ui/albums/album/album.component';
|
import {AlbumComponent} from './ui/albums/album/album.component';
|
||||||
import {AlbumsService} from './ui/albums/albums.service';
|
import {AlbumsService} from './ui/albums/albums.service';
|
||||||
|
import {GallerySearchQueryBuilderComponent} from './ui/gallery/search/query-builder/query-bulder.gallery.component';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -196,6 +197,7 @@ Marker.prototype.options.icon = iconDefault;
|
|||||||
GallerySearchComponent,
|
GallerySearchComponent,
|
||||||
GallerySearchQueryEntryComponent,
|
GallerySearchQueryEntryComponent,
|
||||||
GallerySearchFieldComponent,
|
GallerySearchFieldComponent,
|
||||||
|
GallerySearchQueryBuilderComponent,
|
||||||
GalleryShareComponent,
|
GalleryShareComponent,
|
||||||
GalleryNavigatorComponent,
|
GalleryNavigatorComponent,
|
||||||
GalleryPhotoComponent,
|
GalleryPhotoComponent,
|
||||||
|
@ -3,11 +3,27 @@ app-album {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-item-msg{
|
.no-item-msg {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-face-msg h2{
|
.no-face-msg h2 {
|
||||||
color: #6c757d;
|
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;
|
||||||
|
}
|
||||||
|
@ -5,12 +5,70 @@
|
|||||||
[album]="album"
|
[album]="album"
|
||||||
[size]="size"></app-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"
|
<div class="d-flex no-item-msg"
|
||||||
*ngIf="(albumsService.albums | async) && (albumsService.albums | async).length == 0">
|
*ngIf="(albumsService.albums | async) && (albumsService.albums | async).length == 0">
|
||||||
<div class="flex-fill">
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-frame>
|
</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 {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({
|
@Component({
|
||||||
selector: 'app-albums',
|
selector: 'app-albums',
|
||||||
@ -9,9 +12,14 @@ import {AlbumsService} from './albums.service';
|
|||||||
export class AlbumsComponent implements OnInit {
|
export class AlbumsComponent implements OnInit {
|
||||||
@ViewChild('container', {static: true}) container: ElementRef;
|
@ViewChild('container', {static: true}) container: ElementRef;
|
||||||
public size: number;
|
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);
|
this.albumsService.getAlbums().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,6 +28,22 @@ export class AlbumsComponent implements OnInit {
|
|||||||
this.updateSize();
|
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 {
|
private updateSize(): void {
|
||||||
const size = 220 + 5;
|
const size = 220 + 5;
|
||||||
// body - container margin
|
// 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">
|
<div class="modal-body">
|
||||||
<form #searchPanelForm="ngForm" class="form-horizontal">
|
<form #searchPanelForm="ngForm" class="form-horizontal">
|
||||||
|
|
||||||
<app-gallery-search-field [(ngModel)]="rawSearchText"
|
<app-gallery-search-query-builder
|
||||||
(ngModelChange)="validateRawSearchText()"
|
name="search-query-builder"
|
||||||
(search)="Search()"
|
|
||||||
name="form-search-field">
|
|
||||||
|
|
||||||
</app-gallery-search-field>
|
|
||||||
<hr>
|
|
||||||
<app-gallery-search-query-entry
|
|
||||||
[(ngModel)]="searchQueryDTO"
|
[(ngModel)]="searchQueryDTO"
|
||||||
(change)="onQueryChange()"
|
(change)="onQueryChange()"
|
||||||
name="search-root"
|
(ngModelChange)="onQueryChange()"
|
||||||
(delete)="resetQuery()">
|
(search)="Search()">
|
||||||
|
</app-gallery-search-query-builder>
|
||||||
|
|
||||||
</app-gallery-search-query-entry>
|
|
||||||
|
|
||||||
<div class="input-group-btn float-right row" style="display: block">
|
<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 == ''"
|
[disabled]="rawSearchText == ''"
|
||||||
(click)="openSaveSearchModal(saveSearchModal)">
|
(click)="openSaveSearchModal(saveSearchModal)">
|
||||||
<span class="oi oi-folder"></span> Save
|
<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 {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||||
import {SearchQueryParserService} from './search-query-parser.service';
|
import {SearchQueryParserService} from './search-query-parser.service';
|
||||||
import {AlbumsService} from '../../albums/albums.service';
|
import {AlbumsService} from '../../albums/albums.service';
|
||||||
|
import {Config} from '../../../../../common/config/public/Config';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-search',
|
selector: 'app-gallery-search',
|
||||||
@ -24,10 +25,11 @@ export class GallerySearchComponent implements OnDestroy {
|
|||||||
mouseOverAutoComplete = false;
|
mouseOverAutoComplete = false;
|
||||||
readonly SearchQueryTypes: typeof SearchQueryTypes;
|
readonly SearchQueryTypes: typeof SearchQueryTypes;
|
||||||
public readonly MetadataSearchQueryTypes: { value: string; key: SearchQueryTypes }[];
|
public readonly MetadataSearchQueryTypes: { value: string; key: SearchQueryTypes }[];
|
||||||
|
public saveSearchName: string;
|
||||||
|
AlbumsEnabled = Config.Client.Album.enabled;
|
||||||
private searchModalRef: BsModalRef;
|
private searchModalRef: BsModalRef;
|
||||||
private readonly subscription: Subscription = null;
|
private readonly subscription: Subscription = null;
|
||||||
private saveSearchModalRef: BsModalRef;
|
private saveSearchModalRef: BsModalRef;
|
||||||
public saveSearchName: string;
|
|
||||||
|
|
||||||
constructor(private autoCompleteService: AutoCompleteService,
|
constructor(private autoCompleteService: AutoCompleteService,
|
||||||
private searchQueryParserService: SearchQueryParserService,
|
private searchQueryParserService: SearchQueryParserService,
|
||||||
@ -69,6 +71,11 @@ export class GallerySearchComponent implements OnDestroy {
|
|||||||
document.body.style.paddingRight = '0px';
|
document.body.style.paddingRight = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hideSearchModal(): void {
|
||||||
|
this.searchModalRef.hide();
|
||||||
|
this.searchModalRef = null;
|
||||||
|
}
|
||||||
|
|
||||||
public async openSaveSearchModal(template: TemplateRef<any>): Promise<void> {
|
public async openSaveSearchModal(template: TemplateRef<any>): Promise<void> {
|
||||||
this.saveSearchModalRef = this.modalService.show(template, {class: 'modal-lg'});
|
this.saveSearchModalRef = this.modalService.show(template, {class: 'modal-lg'});
|
||||||
document.body.style.paddingRight = '0px';
|
document.body.style.paddingRight = '0px';
|
||||||
@ -79,20 +86,14 @@ export class GallerySearchComponent implements OnDestroy {
|
|||||||
this.saveSearchModalRef = null;
|
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 {
|
onQueryChange(): void {
|
||||||
|
console.log('cahnge', this.searchQueryDTO);
|
||||||
this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO);
|
this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO);
|
||||||
// this.validateRawSearchText();
|
// this.validateRawSearchText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
validateRawSearchText(): void {
|
validateRawSearchText(): void {
|
||||||
try {
|
try {
|
||||||
this.searchQueryDTO = this.searchQueryParserService.parse(this.rawSearchText);
|
this.searchQueryDTO = this.searchQueryParserService.parse(this.rawSearchText);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user