Skip to content

Commit

Permalink
feat: searchbar, ecFocus + added searchbar to asset-list-pop
Browse files Browse the repository at this point in the history
  • Loading branch information
felixroos committed Jun 1, 2018
1 parent 48635d0 commit 8236caf
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 34 deletions.
7 changes: 5 additions & 2 deletions demo/app/asset-list/asset-list-demo.component.html
@@ -1,5 +1,5 @@
<h2>Asset List</h2>
<ec-asset-list-pop #oldAssetListPop class="ec-pop_toast-top"></ec-asset-list-pop>
<ec-asset-list-pop assetGroupID="legacyAsset" #oldAssetListPop class="ec-pop_toast-top"></ec-asset-list-pop>
<ec-asset-list-pop assetGroupID="test" #newAssetListPop class="ec-pop_toast-top"></ec-asset-list-pop>
<a class="btn" (click)="oldAssetListPop.show()">legacy asset pop</a>
<a class="btn" (click)="newAssetListPop.show()">new asset pop</a>
Expand All @@ -19,6 +19,9 @@ <h2>Asset List</h2>
</ec-tab> -->
<ec-tab label="New public assets">
<p>Using dmAsset.* relation on PublicAPI</p>
<ec-resource-list [api]="sdk.api" *ngIf="sdk.apiResolved" relation="dmAsset.test"></ec-resource-list>
<!-- <ec-searchbar [list]="newAssetList"></ec-searchbar> -->

<ec-resource-list #newAssetList [api]="sdk.api" *ngIf="sdk.apiResolved" relation="dmAsset.test"></ec-resource-list>

</ec-tab>
</ec-tabs>
1 change: 1 addition & 0 deletions demo/app/asset-list/asset-list.demo.component.ts
Expand Up @@ -7,6 +7,7 @@ import DataManagerResource from 'ec.sdk/lib/resources/datamanager/DataManagerRes
})
export class AssetListDemoComponent {
api: DataManagerResource;

constructor(public sdk: SdkService) {
this.sdk.ready.then(() => {
return this.sdk.datamanager.dataManager(this.sdk.api.dataManagerID);
Expand Down
12 changes: 6 additions & 6 deletions demo/app/entry-list/entry-list-demo.component.html
Expand Up @@ -5,22 +5,22 @@ <h2>Muffins</h2>



<h2>Field_test</h2>
<h2>Entry List</h2>

<nav class="dropdown dropdown_center" tabindex="0">
<span class="btn btn_clear btn_minor">
<ec-icon name="binoculars"></ec-icon>
</span>
<ul class="dropdown-options">
<li class="dropdown-option" *ngFor="let field of bakers.list?.fields" [class.is-active]="!field.hidden">
<li class="dropdown-option" *ngFor="let field of entryList.list?.fields" [class.is-active]="!field.hidden">
<a (click)="field.hidden=!field.hidden">
<ec-icon name="check" *ngIf="!field.hidden">{{field.property}}</ec-icon>
<ec-icon name="substract" *ngIf="field.hidden">{{field.property}}</ec-icon>
</a>
</li>
</ul>
</nav>
<ec-heatmap #heatmap (spanChanged)="updateFilter($event,bakers)" style="width:400px;display:block" [timestamps]="timestamps"></ec-heatmap>
<!-- <ec-daterange [timespan]="timespan" (spanChanged)="updateFilter($event,bakers)"></ec-daterange> -->

<ec-entry-list (change)="updateList($event)" #bakers model="muffin" [config]="{sortBy:heatProperty,desc:true,rawFilter:true}"></ec-entry-list>
<!-- <ec-heatmap #heatmap (spanChanged)="updateFilter($event,entryList)" style="width:400px;display:block" [timestamps]="timestamps"></ec-heatmap> -->
<!-- <ec-daterange [timespan]="timespan" (spanChanged)="updateFilter($event,entryList)"></ec-daterange> -->
<ec-searchbar [list]="entryList" property="name"></ec-searchbar>
<ec-entry-list (change)="updateList($event)" #entryList model="muffin" [config]="{sortBy:heatProperty,desc:true,rawFilter:true}"></ec-entry-list>
6 changes: 5 additions & 1 deletion demo/app/list/list-demo.component.ts
Expand Up @@ -12,7 +12,8 @@ import { songs } from '../../assets/songs';
</pre>
<h2>Templated List</h2>
<ec-list [list]="songs" [solo]="true" #songList class="ec-list_dense ec-list_multiline"></ec-list>
<ec-searchbar (selected)="select($event)" placeholder="Suche.." [list]="songlist" property="title"></ec-searchbar>
<ec-list #songlist [list]="songs" [solo]="true" #songList class="ec-list_dense ec-list_multiline"></ec-list>
<pre>
{{songList.list.config | json}}
</pre>
Expand All @@ -31,4 +32,7 @@ export class ListDemoComponent {
log(wort) {
console.log('log', wort);
}
select(item) {
console.log('select', item);
}
}
Expand Up @@ -7,7 +7,7 @@
</header>
<div class="ec-pop-body ec-dropzone" (ecDropzone)="uploader?.uploadFiles($event.dataTransfer.files,$event)">

<ec-assetgroup-select [readOnly]="config.forceGroup||(assetGroupID==='legacyAsset')" [disableLegacy]="assetGroupID!=='legacyAsset'&&!selection.isEmpty()"
<ec-assetgroup-select *ngIf="!config.hideGroupSelect" [readOnly]="config.forceGroup||(assetGroupID==='legacyAsset')" [disableLegacy]="assetGroupID!=='legacyAsset'&&!selection?.isEmpty()"
[assetGroupID]="assetGroupID" (groupChanged)="setGroup($event)"></ec-assetgroup-select>

<div class="ec-dropzone-info">
Expand All @@ -16,14 +16,12 @@
<div class="ec-asset-upload ec-dropzone" *ngIf="assetGroupID">
<ec-upload (groupChanged)="setGroup($event)" #uploader [assetGroupID]="assetGroupID" [loader]="popLoader" (success)="uploader.selectUpload($event, selection, config.solo)"></ec-upload>
<a (click)="uploader.trigger()">Hochladen</a>

</div>
<!-- legacy asset list -->
<ec-resource-list *ngIf="assetGroupID==='legacyAsset'&&sdk.apiResolved" relation="legacyAsset" [api]="sdk.api" [selection]="selection"
(columnClicked)="select($event)" [config]="config" [loader]="popLoader"></ec-resource-list>
<!-- new asset list -->
<ec-resource-list *ngIf="assetGroupID&&assetGroupID!=='legacyAsset'&&sdk.apiResolved" [relation]="'dmAsset.'+assetGroupID"
[api]="sdk.api" [selection]="selection" (columnClicked)="select($event)" [config]="config" [loader]="popLoader"></ec-resource-list>
<div *ngIf="active&&assetGroupID&&sdk.apiResolved">
<ec-searchbar [list]="resourceList"></ec-searchbar>
<ec-resource-list #resourceList [relation]="getGroupRelation()" [api]="sdk.api" [selection]="selection" (columnClicked)="select($event)"
[config]="config" [loader]="popLoader"></ec-resource-list>
</div>
</div>
<ec-loader #popLoader class="ec-loader ec-loader_global"></ec-loader>
</div>
Expand Down
Expand Up @@ -37,15 +37,14 @@ export class AssetListPopComponent extends PopComponent implements OnInit {
public assetGroups: string[];

/** Injects auth service and calls super constructor. */

constructor(protected popService: PopService,
private auth: AuthService,
private fileService: FileService,
public sdk: SdkService) {
super(popService);
}


/** Changes the assetGroupID to the given value, emits groupChange */
setGroup(group) {
if (!group) {
return;
Expand Down Expand Up @@ -73,4 +72,10 @@ export class AssetListPopComponent extends PopComponent implements OnInit {
// this.selection.toggle($event);
}
}

/** Returns the full resource relation name based on the current assetGroupID */
getGroupRelation() {
return !this.assetGroupID || this.assetGroupID === 'legacyAsset' ? 'legacyAsset'
: 'dmAsset.' + this.assetGroupID;
}
}
1 change: 0 additions & 1 deletion packages/data/src/files/upload/upload.component.ts
Expand Up @@ -58,7 +58,6 @@ export class UploadComponent implements WithLoader, WithNotifications {
console.error('cannot trigger upload: file input element not found!');
return;
}
console.log('triggr.');
/* this.clear(); */
this.fileInput.nativeElement.click();
}
Expand Down
2 changes: 2 additions & 0 deletions packages/data/src/resource-config/resource-config.service.ts
Expand Up @@ -368,6 +368,7 @@ export class ResourceConfig {
legacyAsset: { // old public assets
identifier: 'assetID',
identifierPattern: this.uuid(),
label: 'title',
fields: {
thumb: {
form: false,
Expand Down Expand Up @@ -444,6 +445,7 @@ export class ResourceConfig {
dmAsset: { // new assets
identifier: 'assetID',
identifierPattern: this.base64uuid(),
label: 'title',
fields: {
file: {
label: this.symbol.resolve('dmAsset.field.label.file'),
Expand Down
19 changes: 12 additions & 7 deletions packages/ui/src/list/list.module.ts
@@ -1,33 +1,38 @@
import { CommonModule } from '@angular/common';
import { ListComponent } from './list.component';
import { ListItemsComponent } from './list-items/list-items.component';
import { ListHeaderComponent } from './list-header/list-header.component';
import { GroupPipe } from './group.pipe';
import { PaginationComponent } from './pagination/pagination.component';
import { FormModule } from '../form/form.module';
import { NgModule } from '@angular/core';
import { FormModule } from '../form/form.module';
import { IconModule } from '../icon/icon.module';
import { SymbolModule } from '../symbol/symbol.module';
import { UtilityModule } from '../utility/utility.module';
import { GroupPipe } from './group.pipe';
import { ListHeaderComponent } from './list-header/list-header.component';
import { ListItemsComponent } from './list-items/list-items.component';
import { ListComponent } from './list.component';
import { PaginationComponent } from './pagination/pagination.component';
import { SearchbarComponent } from './searchbar/searchbar.component';

@NgModule({
declarations: [
ListComponent,
ListItemsComponent,
ListHeaderComponent,
PaginationComponent,
SearchbarComponent,
GroupPipe,
],
imports: [
CommonModule,
FormModule,
IconModule,
SymbolModule
SymbolModule,
UtilityModule
],
exports: [
ListComponent,
ListItemsComponent,
ListHeaderComponent,
PaginationComponent,
SearchbarComponent,
GroupPipe,
FormModule,
],
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/list/searchbar/searchbar.component.html
@@ -0,0 +1,3 @@
<input #inputField [ecFocus]="focusEvent" type="text" [placeholder]="placeholder || defaultPlaceholder"
class="input input_clear" (keyup)="keyup.next($event.target.value);preventDefault($event)" (keydown)="arrowNavigation($event)"
(paste)="paste.next($event)" [(ngModel)]="query">
121 changes: 121 additions & 0 deletions packages/ui/src/list/searchbar/searchbar.component.ts
@@ -0,0 +1,121 @@
import { AfterViewInit, Component, EventEmitter, Input, Output } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Item } from '@ec.components/core';
import { ListComponent } from '@ec.components/ui';
import { SymbolService } from '@ec.components/ui/src/symbol/symbol.service';
import { Subject } from 'rxjs/Subject';
import { distinctUntilChanged } from 'rxjs/operators';
import { Focus } from '../../utility/focus/focus.interface';

/** Genereic Searchbar component. Filters a given list its label property (or given property).
* Supports autofocus and arrow navigation. */
@Component({
selector: 'ec-searchbar',
templateUrl: 'searchbar.component.html',
})

export class SearchbarComponent implements AfterViewInit, Focus {
/** Searchbar placeholder */
@Input() placeholder: string;
/** Default placeholder when no placeholder is given */
@Input() defaultPlaceholder: string;
/** The input query that should be prefilled */
@Input() public query: string;
/** Property that should be filtered */
@Input() property: string;
/** If true, the input will be autofocused */
@Input() autofocus = true;
/** The event that focuses the input */
public focusEvent: EventEmitter<boolean> = new EventEmitter();
/** Delay until search is fired */
@Input() debounceTime = 300;
/** Subject that is triggered on keyup */
public keyup: Subject<any> = new Subject<any>();
/** Subject that is nexted when something is pasted */
public paste: Subject<any> = new Subject<any>();
/** The list that should be filtered */
@Input() list: ListComponent<any>;
/** Output that emits when enter is pressed on a selected item */
@Output() selected: EventEmitter<any> = new EventEmitter();

constructor(public route: ActivatedRoute, public symbol: SymbolService) {
this.defaultPlaceholder = this.symbol.resolve('searchbar.placeholder');
const paste = this.paste.asObservable();
this.paste.asObservable()
.subscribe((e) => {
const pasted = (e.clipboardData).getData('text');
if (this.filterList(pasted, true)) {
this.preventDefault(e);
}
});

this.keyup.asObservable().debounceTime(this.debounceTime)
.pipe(distinctUntilChanged())
.subscribe(value => this.filterList(value));

this.route.params
.subscribe(() => {
this.focusEvent.emit(true);
this.query = '';
})
}

/** prevents the event default and disables propagation */
preventDefault(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}

/** After the view is ready, the searchbar needs to be focused (if autofocus is true) */
ngAfterViewInit() {
if (this.autofocus) {
this.focusEvent.emit(true);
}
}

/** Filters the list by the given value, either uses property or list.config.label.
* If paste is true and the value matches the list.config.identifierPattern,
* select is emitted immediately with a pseudo item containing the value as item identifier. */
filterList(value, paste = false) {
if (!this.list || !this.list.list) {
console.warn('could not search: no list given!', this.list);
return;
}
if (!this.property && !this.list.config.label) {
console.warn('cannot filter list: no property set and no label property configured');
return;
}
if (paste && this.list.config.identifierPattern) {
if (value.match(this.list.config.identifierPattern)) {
this.selected.emit(new Item({
[this.list.config.identifier]: value,
}, this.list.config));
/* return true; */
}
}
this.list.list.filter(this.property || this.list.config.label, value);
}

/** called on keydown. if arrow keys are pressed, toggle selection of next/prev elements of list */
arrowNavigation(e) {
if (!this.list || !this.list.selection) {
return;
}
switch (e.key) {
case 'ArrowUp':
this.list.selectPrev();
e.preventDefault();
break;
case 'ArrowDown':
this.list.selectNext();
e.preventDefault();
break;
case 'Enter':
if (!this.list.selection.isEmpty()) {
this.selected.emit(this.list.selection.items[0]);
}
break;
}
}
}
4 changes: 4 additions & 0 deletions packages/ui/src/symbol/de.ts
Expand Up @@ -124,6 +124,10 @@ export default [
name: 'pagination.of',
content: 'von'
},
{
name: 'searchbar.placeholder',
content: 'Suchen..'
},
{
name: 'pagination.size',
content: 'Pro Seite'
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/symbol/en.ts
Expand Up @@ -128,6 +128,10 @@ export default [
name: 'pagination.size',
content: 'per page'
},
{
name: 'searchbar.placeholder',
content: 'Search..'
},
{
name: 'upload.assetGroup',
content: 'Group'
Expand Down
23 changes: 23 additions & 0 deletions packages/ui/src/utility/focus/focus.directive.ts
@@ -0,0 +1,23 @@
import { Directive, ElementRef, EventEmitter, Input, OnInit } from '@angular/core';

@Directive({
// tslint:disable-next-line:directive-selector
selector: '[ecFocus]',
})
export class FocusDirective implements OnInit {
@Input() ecFocus: EventEmitter<boolean>;

constructor(private element: ElementRef) {
}

ngOnInit() {
this.ecFocus
.subscribe((event: boolean) => {
if (event) {
this.element.nativeElement.focus();
} else {
this.element.nativeElement.blur();
}
});
}
}
7 changes: 7 additions & 0 deletions packages/ui/src/utility/focus/focus.interface.ts
@@ -0,0 +1,7 @@
import { EventEmitter } from '@angular/core';

export interface Focus {
focusEvent: EventEmitter<boolean>;

ngAfterViewInit();
}

0 comments on commit 8236caf

Please sign in to comment.