Browse Source

Bugfixes with autocompletion.

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
5a8e33c5d4
  1. 9
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html
  2. 15
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.scss
  3. 12
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  4. 7
      src/Squidex/app/framework/angular/autocomplete.component.html
  5. 24
      src/Squidex/app/framework/angular/autocomplete.component.scss
  6. 53
      src/Squidex/app/framework/angular/autocomplete.component.ts
  7. 8
      src/Squidex/app/framework/angular/dropdown.component.html
  8. 25
      src/Squidex/app/framework/angular/dropdown.component.ts
  9. 18
      src/Squidex/app/framework/angular/template-wrapper.directive.ts

9
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html

@ -57,7 +57,14 @@
<div class="table-items-footer">
<form class="form-inline" [formGroup]="addContributorForm" (ngSubmit)="assignContributor()">
<div class="form-group mr-1">
<sqx-autocomplete [source]="usersDataSource" formControlName="user" [inputName]="'contributor'" placeholder="Find existing user"></sqx-autocomplete>
<sqx-autocomplete [source]="usersDataSource" formControlName="user" [inputName]="'contributor'" placeholder="Find existing user" displayProperty="email">
<ng-template let-user="$implicit">
<img class="user-picture autocomplete-user-picture" [attr.src]="user | userDtoPicture" />
<span class="user-name autocomplete-user-name">{{user.displayName}}</span>
<span class="user-email autocomplete-user-email">{{user.email}}</span>
</ng-template>
</sqx-autocomplete>
</div>
<button type="submit" class="btn btn-success" [disabled]="!canAddContributor">Add Contributor</button>

15
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.scss

@ -1,2 +1,15 @@
@import '_vars';
@import '_mixins';
@import '_mixins';
.autocomplete-user {
&-picture {
float: left;
margin-top: .4rem;
}
&-name,
&-email {
@include truncate;
margin-left: 3rem;
}
}

12
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -15,7 +15,6 @@ import {
AppContributorsService,
AppsStoreService,
AuthService,
AutocompleteItem,
AutocompleteSource,
HistoryChannelUpdated,
ImmutableArray,
@ -32,19 +31,14 @@ export class UsersDataSource implements AutocompleteSource {
) {
}
public find(query: string): Observable<AutocompleteItem[]> {
public find(query: string): Observable<any[]> {
return this.usersService.getUsers(query)
.map(users => {
const results: AutocompleteItem[] = [];
const results: any[] = [];
for (let user of users) {
if (!this.component.appContributors || !this.component.appContributors.find(t => t.contributorId === user.id)) {
results.push(
new AutocompleteItem(
user.displayName,
user.email,
user.pictureUrl!,
user));
results.push(user);
}
}
return results;

7
src/Squidex/app/framework/angular/autocomplete.component.html

@ -6,11 +6,10 @@
autocapitalize="off">
<div *ngIf="items.length > 0" [sqxModalTarget]="input" class="control-dropdown" #container>
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === itemSelection" (mousedown)="chooseItem(item)" (mouseover)="selectIndex(i)" [sqxScrollActive]="i === itemSelection" [container]="container">
<img class="control-dropdown-item-image" [attr.src]="item.image" />
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === selectedIndex" (mousedown)="selectItem(item)" (mouseover)="selectIndex(i)" [sqxScrollActive]="i === itemSelection" [container]="container">
<span *ngIf="!itemTemplate">{{item}}</span>
<span class="control-dropdown-item-title">{{item.title}}</span>
<span class="control-dropdown-item-description">{{item.description}}</span>
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
</div>
</div>
</span>

24
src/Squidex/app/framework/angular/autocomplete.component.scss

@ -1,28 +1,6 @@
@import '_mixins';
@import '_vars';
$color-input-border: rgba(0, 0, 0, .15);
.control-dropdown {
& {
width: 18rem;
}
&-item {
&-image {
@include circle(2.5rem);
float: left;
}
&-title,
&-description {
@include truncate;
margin-left: 3rem;
}
&-description {
font-size: .8rem;
font-style: italic;
}
}
width: 18rem;
}

53
src/Squidex/app/framework/angular/autocomplete.component.ts

@ -5,22 +5,12 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, ContentChild, forwardRef, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
export interface AutocompleteSource {
find(query: string): Observable<AutocompleteItem[]>;
}
export class AutocompleteItem {
constructor(
public readonly title: string,
public readonly description: string,
public readonly image: string,
public readonly model: any
) {
}
find(query: string): Observable<any[]>;
}
const KEY_ENTER = 13;
@ -50,11 +40,18 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
@Input()
public inputName = 'autocompletion';
@Input()
public displayProperty = '';
@Input()
public placeholder = '';
public items: AutocompleteItem[] = [];
public itemSelection = -1;
@ContentChild(TemplateRef)
public itemTemplate: TemplateRef<any>;
public items: any[] = [];
public selectedIndex = -1;
public queryInput = new FormControl();
@ -62,13 +59,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
if (!value) {
this.resetValue();
} else {
let item: AutocompleteItem | null = null;
if (value instanceof AutocompleteItem) {
item = value;
} else {
item = this.items.find(i => i.model === value);
}
let item = this.items.find(i => i === value);
if (item) {
this.queryInput.setValue(value.title || '');
@ -133,7 +124,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
return false;
case KEY_ENTER:
if (this.items.length > 0) {
this.chooseItem();
this.selectItem();
return false;
}
break;
@ -145,9 +136,9 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
this.touchedCallback();
}
public chooseItem(selection: AutocompleteItem | null = null) {
public selectItem(selection: any | null = null) {
if (!selection) {
selection = this.items[this.itemSelection];
selection = this.items[this.selectedIndex];
}
if (!selection && this.items.length === 1) {
@ -156,7 +147,11 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
if (selection) {
try {
this.queryInput.setValue(selection.title);
if (this.displayProperty && this.displayProperty.length > 0) {
this.queryInput.setValue(selection[this.displayProperty], { emitEvent: false });
} else {
this.queryInput.setValue(selection.toString(), { emitEvent: false });
}
this.changeCallback(selection);
} finally {
this.reset();
@ -165,11 +160,11 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
}
private up() {
this.selectIndex(this.itemSelection - 1);
this.selectIndex(this.selectedIndex - 1);
}
private down() {
this.selectIndex(this.itemSelection + 1);
this.selectIndex(this.selectedIndex + 1);
}
private resetValue() {
@ -185,11 +180,11 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
selection = this.items.length - 1;
}
this.itemSelection = selection;
this.selectedIndex = selection;
}
private reset() {
this.items = [];
this.itemSelection = -1;
this.selectedIndex = -1;
}
}

8
src/Squidex/app/framework/angular/dropdown.component.html

@ -6,7 +6,9 @@
autocapitalize="off">
<div class="control-dropdown-item" *ngIf="selectedItem">
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="selectedItem"></ng-template>
<span *ngIf="!selectionTemplate">{{selectedItem}}</span>
<ng-template *ngIf="selectionTemplate" [sqxTemplateWrapper]="selectionTemplate" [item]="selectedItem"></ng-template>
</div>
<i class="icon-caret-down"></i>
@ -15,7 +17,9 @@
<div class="items-container">
<div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input">
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === selectedIndex" [container]="container">
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
<span *ngIf="!itemTemplate">{{item}}</span>
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
</div>
</div>
</div>

25
src/Squidex/app/framework/angular/dropdown.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Component, ContentChild, forwardRef, Input, TemplateRef } from '@angular/core';
import { AfterContentInit, Component, ContentChildren, forwardRef, Input, QueryList, TemplateRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
const KEY_ENTER = 13;
@ -26,25 +26,38 @@ export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = {
templateUrl: './dropdown.component.html',
providers: [SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR]
})
export class DropdownComponent implements ControlValueAccessor {
export class DropdownComponent implements AfterContentInit, ControlValueAccessor {
private changeCallback: (value: any) => void = NOOP;
private touchedCallback: () => void = NOOP;
@Input()
public items: any[] = [];
@ContentChild(TemplateRef)
public itemTemplate: TemplateRef<any>;
@ContentChildren(TemplateRef)
public templates: QueryList<any>;
public dropdown = new ModalView();
public selectedItem: any;
public selectedIndex = -1;
public selectionTemplate: TemplateRef<any>;
public itemTemplate: TemplateRef<any>;
public isDisabled = false;
private get safeItems(): any[] {
return this.items || [];
public ngAfterContentInit() {
if (this.templates.length === 1) {
this.itemTemplate = this.selectionTemplate = this.templates.first;
} else {
this.templates.forEach(template => {
if (template.name === 'selection') {
this.selectionTemplate = template;
} else {
this.itemTemplate = template;
}
});
}
}
public writeValue(value: any) {

18
src/Squidex/app/framework/angular/template-wrapper.directive.ts

@ -27,13 +27,6 @@ export class TemplateWrapper implements OnInit, OnDestroy, OnChanges {
) {
}
public ngOnChanges() {
if (this.view) {
this.view.context.$implicit = this.item;
this.view.context.index = this.index;
}
}
public ngOnInit() {
this.view = this.viewContainer.createEmbeddedView(this.templateRef, {
'\$implicit': this.item,
@ -41,7 +34,16 @@ export class TemplateWrapper implements OnInit, OnDestroy, OnChanges {
});
}
public ngOnChanges() {
if (this.view) {
this.view.context.$implicit = this.item;
this.view.context.index = this.index;
}
}
public ngOnDestroy() {
this.view.destroy();
if (this.view) {
this.view.destroy();
}
}
}
Loading…
Cancel
Save