Skip to content

Commit

Permalink
feat(chips): chip templates + object list support + async loading sup…
Browse files Browse the repository at this point in the history
…port. (closes #252) (closes #359) (closes #601) (#626)

* feat(chips): ability to use object lists and set templates for both autocomplete and chips

this will be the base to support contact chips. First we need to make sure it works fine with objects and strings, and add good a11y around this.

* chore(chips): update demo with a better example

* feat(chips): make children be centered and vertical aligned (including children in templates)

* chore(chips): remove unused CSS rule for datalist

* fx(chips): remove [Object] value when adding an object from the autocomplete in a11y mode

* fix(chips): delete by index rather than by value

* *BREAKING CHANGE* feat(chips): abstract the autocomplete filtering and add debounce input

it makes more sense to have the filtering done outside of chips and provide examples on how to achieve it since this way chips are agnostic of local vs server side filtering + string vs object filtering

* chore(chips): update README.md and codeblocks in inputs/outputs

* feat(chips): add [td-autocomplete-header] to be able to add a loader or something on long filters

* chore(chips): remove td-autocomplete-header and have it inject anything under chips

* chore(chips): update demos and load README.md in docs

* fix(): chips README.md not loading highlight correctly

* fix(chips): prevent chip duplication when pressing enter super fast

* fix(chips): check for undefined value from input

* perf(chips): support OnPush change detection

* fix(chips): make 4th demo work again

* fix(chips): validate against use case where selection happened without requireMatch

* chore(chips): fix unit tests and add unit tests for new behavior

* fix(chips): underline stops working in beta.6

* perf(chips): remove usage of async in autocomplete and use simple array

beta.6 has some nuances around autocomplete so its needed to have more control

* fix(): unit tests

* chore(chips): make underline animatate the same way as material

* fix(chips): keep focused state as long as you keep clicking inside the chips context

* fix(): ninja fix mat-color function

* feat(chips): make focused state remain while inside the chip context

* chore(chips): polish code and add code blocks

* fix(chips): block click event when clicking on host or td-chips-wrapper

* fix(chips): a11y left + right arrows

* chore(chips): modify the demos for a better chips experience

* fix(chips): check if value is part of the ngModel

* chore(chips): added more unit tests
  • Loading branch information
emoralesb05 authored May 30, 2017
1 parent 98eaf28 commit 22d4342
Show file tree
Hide file tree
Showing 10 changed files with 1,090 additions and 357 deletions.
273 changes: 193 additions & 80 deletions src/app/components/components/chips/chips.component.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<md-card>
<md-card-title>Chips &amp; Autocomplete</md-card-title>
<md-card-title>Chips &amp; Autocomplete with strings</md-card-title>
<md-card-subtitle>Autocomplete with chips and no custom inputs</md-card-subtitle>
<md-divider></md-divider>
<md-tab-group md-stretch-tabs dynamicHeight>
<md-tab>
<ng-template md-tab-label>Demo</ng-template>
<div class="push">
<div class="md-body-1">Type and select a preset option:</div>
<div class="md-body-1">Type and select a preset option or press enter:</div>
<td-chips [chipAddition]="chipAddition"
[items]="items"
[(ngModel)]="itemsRequireMatch"
[items]="filteredStrings"
[(ngModel)]="stringsModel"
placeholder="Enter autocomplete strings"
[readOnly]="readOnly"
(inputChange)="filterStrings($event)"
requireMatch>
</td-chips>
</div>
Expand All @@ -23,10 +24,11 @@
<td-highlight lang="html">
<![CDATA[
<td-chips [chipAddition]="chipAddition"
[items]="items"
[(ngModel)]="itemsRequireMatch"
[items]="filteredStrings"
[(ngModel)]="stringsModel"
placeholder="Enter autocomplete strings"
[readOnly]="readOnly"
(inputChange)="filterStrings($event)"
requireMatch>
</td-chips>
]]>
Expand All @@ -38,7 +40,7 @@
readOnly: boolean = false;
chipAddition: boolean = true;

items: string[] = [
strings: string[] = [
'stepper',
'expansion-panel',
'markdown',
Expand All @@ -52,8 +54,25 @@
'need more?',
];

itemsRequireMatch: string[] = this.items.slice(0, 6);
filteredStrings: string[];

stringsModel: string[] = this.strings.slice(0, 6);

ngOnInit(): void {
this.filterStrings('');
}

filterStrings(value: string): void {
this.filteredStrings = this.strings.filter((item: any) => {
if (value) {
return item.toLowerCase().indexOf(value.toLowerCase()) > -1;
} else {
return false;
}
}).filter((filteredItem: any) => {
return this.stringsModel ? this.stringsModel.indexOf(filteredItem) < 0 : true;
});
}
}
]]>
</td-highlight>
Expand All @@ -75,6 +94,167 @@
</div>
</md-card-actions>
</md-card>
<md-card>
<md-card-title>Chips &amp; Autocomplete with objects</md-card-title>
<md-card-subtitle>Autocomplete with chips and templates</md-card-subtitle>
<md-divider></md-divider>
<md-tab-group md-stretch-tabs dynamicHeight>
<md-tab>
<ng-template md-tab-label>Demo</ng-template>
<div class="push">
<div class="md-body-1">Type and select a preset option or press enter:</div>
<td-chips [items]="filteredObjects"
[(ngModel)]="objectsModel"
placeholder="Enter autocomplete strings"
(inputChange)="filterObjects($event)"
requireMatch>
<ng-template td-basic-chip let-chip="chip">
{{chip.city}}, (Pop: {{chip.population}})
</ng-template>
<ng-template td-autocomplete-option let-option="option">
<md-icon>location_city</md-icon> {{option.city}}
</ng-template>
</td-chips>
</div>
</md-tab>
<md-tab>
<ng-template md-tab-label>Code</ng-template>
<md-card-content>
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<td-chips [items]="filteredObjects"
[(ngModel)]="objectsModel"
placeholder="Enter autocomplete strings"
(inputChange)="filterObjects($event)"
requireMatch>
<ng-template td-basic-chip let-chip="chip">
{ {chip.city} }, (Pop: { {chip.population} })
</ng-template>
<ng-template td-autocomplete-option let-option="option">
<md-icon>location_city</md-icon> { {option.city} }
</ng-template>
</td-chips>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
export class ChipsDemoComponent {
objects: any[] = [
{id: 1, city: 'San Diego', population: '4M'},
{id: 2, city: 'San Franscisco', population: '6M'},
{id: 3, city: 'Los Angeles', population: '5M'},
{id: 4, city: 'Austin', population: '3M'},
{id: 5, city: 'New York City', population: '10M'},
];

filteredObjects: string[];

objectsModel: string[] = this.objects.slice(0, 2);

ngOnInit(): void {
this.filterObjects('');
}

filterObjects(value: string): void {
this.filteredObjects = this.objects.filter((obj: any) => {
if (value) {
return obj.city.toLowerCase().indexOf(value.toLowerCase()) > -1;
} else {
return false;
}
}).filter((filteredObj: any) => {
return this.objectsModel ? this.objectsModel.indexOf(filteredObj) < 0 : true;
});
}
}
]]>
</td-highlight>
</md-card-content>
</md-tab>
</md-tab-group>
</md-card>
<md-card>
<md-card-title>Chips &amp; async Autocomplete</md-card-title>
<md-card-subtitle>Load autocomplete items asynchronous when typing in the input</md-card-subtitle>
<md-divider></md-divider>
<md-tab-group md-stretch-tabs dynamicHeight>
<md-tab>
<ng-template md-tab-label>Demo</ng-template>
<div class="push">
<div class="md-body-1">Type and see how it will load items async:</div>
<td-chips [items]="filteredAsync"
[(ngModel)]="asyncModel"
placeholder="Enter autocomplete strings"
(inputChange)="filterAsync($event)"
(debounce)="1000"
requireMatch>
<md-progress-bar [style.height.px]="2" *ngIf="filteringAsync" mode="indeterminate"></md-progress-bar>
</td-chips>
</div>
</md-tab>
<md-tab>
<ng-template md-tab-label>Code</ng-template>
<md-card-content>
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<td-chips [items]="filteredAsync"
[(ngModel)]="asyncModel"
placeholder="Enter autocomplete strings"
(inputChange)="filterAsync($event)"
(debounce)="1000"
requireMatch>
<md-progress-bar [style.height.px]="2" *ngIf="filteringAsync" mode="indeterminate"></md-progress-bar>
</td-chips>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
export class ChipsDemoComponent {
filteringAsync: boolean = false;

strings: string[] = [
'stepper',
'expansion-panel',
'markdown',
'highlight',
'loading',
'media',
'chips',
'http',
'json-formatter',
'pipes',
'need more?',
];

filteredAsync: string[];

asyncModel: string[] = this.strings.slice(0, 2);

filterAsync(value: string): void {
this.filteredAsync = undefined;
if (value) {
this.filteringAsync = true;
setTimeout(() => {
this.filteredAsync = this.strings.filter((item: any) => {
return item.toLowerCase().indexOf(value.toLowerCase()) > -1;
}).filter((filteredItem: any) => {
return this.asyncModel ? this.asyncModel.indexOf(filteredItem) < 0 : true;
});
this.filteringAsync = false;
}, 2000);
}
}
}
]]>
</td-highlight>
</md-card-content>
</md-tab>
</md-tab-group>
</md-card>
<md-card>
<md-card-title>Autocomplete with custom inputs</md-card-title>
<md-card-subtitle>Autocomplete demo allowing custom inputs</md-card-subtitle>
Expand All @@ -84,7 +264,7 @@
<ng-template md-tab-label>Demo</ng-template>
<div class="push">
<div class="md-body-1">Type and select option or enter custom text and press enter:</div>
<td-chips [items]="items" placeholder="Enter any string"></td-chips>
<td-chips [items]="strings" placeholder="Enter any string"></td-chips>
</div>
</md-tab>
<md-tab>
Expand All @@ -93,15 +273,15 @@
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<td-chips [items]="items" placeholder="Enter any string"></td-chips>
<td-chips [items]="strings" placeholder="Enter any string"></td-chips>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
export class ChipsDemoComponent {

items: string[] = [
strings: string[] = [
'stepper',
'expansion-panel',
'markdown',
Expand Down Expand Up @@ -155,72 +335,5 @@
</md-tab>
</md-tab-group>
</md-card>
<md-card>
<md-card-title>TdChipsComponent</md-card-title>
<md-card-subtitle>How to use this component</md-card-subtitle>
<md-divider></md-divider>
<md-card-content>
<h2><code><![CDATA[<td-chips>]]></code></h2>
<p>Use <code><![CDATA[<td-chips>]]></code> element to generate a list of strings as chips.</p>
<p>Add the [items] attribute to enable the autocomplete with a search list, and [requireMatch] to validate the input against the provided search list.</p>
<p>When used with <a target="_blank" href="https://angular.io/docs/ts/latest/guide/forms.html">forms</a>, you can track change-states [dirty/pristine] and [touched/untouched].</p>
<p>Since <code>[(ngModel)]</code> would be an array, you need to implement a custom validator for [valid/invalid] when its empty.</p>
<h3>Properties:</h3>
<p>The <code><![CDATA[<td-chips>]]></code> component has {{chipsAttrs.length}} properties:</p>
<md-list>
<ng-template let-attr let-last="attr" ngFor [ngForOf]="chipsAttrs">
<a md-list-item layout-align="row">
<h3 md-line> {{attr.name}}: <span>{{attr.type}}</span></h3>
<p md-line> {{attr.description}} </p>
</a>
<md-divider *ngIf="!last"></md-divider>
</ng-template>
</md-list>
<h3>Example:</h3>
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<td-chips placeholder="Enter string" [items]="items" [(ngModel)]="model" [readOnly]="readOnly" [chipAddition]="chipAddition" (add)="add($event)" (remove)="remove($event)" requireMatch>
</td-chips>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
...
})
export class Demo {
readOnly: boolean: false;
items: string[] = [
'string1',
'string2',
'string3'
];
model: string[] = [];

add(value: string): void {
...
}
remove(value: string): void {
...
}
}
]]>
</td-highlight>
<h3>Setup:</h3>
<p>Import the [CovalentChipsModule] in your NgModule:</p>
<td-highlight lang="typescript">
<![CDATA[
import { CovalentChipsModule } from '@covalent/core';
@NgModule({
imports: [
CovalentChipsModule,
...
],
...
})
export class MyModule {}
]]>
</td-highlight>
</md-card-content>
</md-card>

<td-readme-loader resourceUrl="platform/core/chips/README.md"></td-readme-loader>
Loading

0 comments on commit 22d4342

Please sign in to comment.