Skip to content

Commit

Permalink
feature(chips): covalent chips & autocomplete (#44)
Browse files Browse the repository at this point in the history
* really simple implementation of autocomplete for chips usage

* chip component styling

* fixed md-accent and md-warn color for underline

* first draft of chips (only string support)

* fixed bug where empty string where accepted as input for chip

* removed unused isEmpty method and fixed bug when using ngModel

* renamed searchItems to items

* better styling for chip/chips

* fixed bug where layout was messed up when lots of chips

* added blank chips docs

* basic forms support

* fixed bug with focus/blur and fixed bug with [hidden] state for autocomplete

* removed export of TdAutoCompleteComponent and added chips demo

* added code blocks to chips

* added docs

* added package.json

* fixed docs

* Added README.md

* added events to example docs

* added a11y for removal buttons (tab/enter)

* update(chip): changed to span & styles on :host

* update(chips): moved chip scss into main styles

- similar to material2

* update(chips): add title for delete icon

* moved chips to me grouped with stepper, expansion and fileupload

* added placeholder to chips and updated README.md

* updated docs and demo with placeholder and added toggle readOnly (edit mode) button for demo

* remove arrow picker from autocomplete datalist in chrome

* fixed validated to validate in docs

* remove placeholder when in readOnly

* removed forms demo and added note in docs on its usage
  • Loading branch information
emoralesb05 authored and kyleledbetter committed Aug 18, 2016
1 parent 310fd38 commit 095748e
Show file tree
Hide file tree
Showing 22 changed files with 766 additions and 1 deletion.
10 changes: 9 additions & 1 deletion src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,17 @@ $md-warn: #C62828;
.md-input-placeholder.md-focused {
color: $md-primary !important;
}
.md-progress-bar-fill::after,
.md-progress-bar-fill::after {
background-color: $md-primary !important;
}
.md-input-underline .md-input-ripple {
background-color: $md-primary !important;
&.md-accent {
background-color: $md-accent !important;
}
&.md-warn {
background-color: $md-warn !important;
}
}
.md-progress-bar-buffer {
background-color: lighten($md-primary, 40%) !important;
Expand Down
73 changes: 73 additions & 0 deletions src/app/components/components/chips/chips.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<md-card>
<md-card-title>Chips</md-card-title>
<md-card-subtitle>Build a list of strings</md-card-subtitle>
<md-divider></md-divider>
<md-card-content>
<p>Basic Demo</p>
<td-chips placeholder="Enter any string" [readOnly]="readOnly"></td-chips>
<p>Autocomplete Demo</p>
<td-chips [items]="items" placeholder="Enter any string" [readOnly]="readOnly"></td-chips>
<p>Autocomplete and requireMatch Demo</p>
<td-chips [items]="items" [(ngModel)]="itemsRequireMatch" placeholder="Enter autocomplete strings" [readOnly]="readOnly" requireMatch></td-chips>
</md-card-content>
<md-divider></md-divider>
<md-card-actions>
<button md-button color="primary" (click)="toggleReadOnly()">Toggle ReadOnly</button>
</md-card-actions>
</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>
<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>
</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" (add)="add($event)" (remove)="remove($event)" requireMatch>
</td-chips>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
import { TdChipsComponent } from '@covalent/chips';
...
directives: [ TdChipsComponent ]
})
export class Demo {
readOnly: boolean: false;
items: string[] = [
'string1',
'string2',
'string3'
];
model: string[] = [];

add(value: string): void {
...
}
remove(value: string): void {
...
}
}
]]>
</td-highlight>
</md-card-content>
</md-card>
Empty file.
49 changes: 49 additions & 0 deletions src/app/components/components/chips/chips.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
beforeEach,
addProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ChipsDemoComponent } from './chips.component';

describe('Component: ChipsDemo', () => {
let builder: TestComponentBuilder;

beforeEach(() => {
addProviders([
ChipsDemoComponent,
]);
});

beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder): void {
builder = tcb;
}));

it('should inject the component', inject([ChipsDemoComponent], (component: ChipsDemoComponent) => {
expect(component).toBeTruthy();
}));

it('should create the component', inject([], () => {
return builder.createAsync(ChipsDemoTestControllerComponent)
.then((fixture: ComponentFixture<any>) => {
let query: DebugElement = fixture.debugElement.query(By.directive(ChipsDemoComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});

@Component({
directives: [ChipsDemoComponent],
selector: 'td-test',
template: `
<td-chips-demo></td-chips-demo>
`,
})
class ChipsDemoTestControllerComponent {
}
82 changes: 82 additions & 0 deletions src/app/components/components/chips/chips.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Component } from '@angular/core';

import { MD_CARD_DIRECTIVES } from '@angular2-material/card';
import { MD_LIST_DIRECTIVES } from '@angular2-material/list';
import { MdIcon } from '@angular2-material/icon';
import { MdButton } from '@angular2-material/button';

import { TdChipsComponent } from '../../../../platform/chips';
import { TdHighlightComponent } from '../../../../platform/highlight';

@Component({
directives: [
MD_CARD_DIRECTIVES,
MD_LIST_DIRECTIVES,
MdIcon,
MdButton,
TdChipsComponent,
TdHighlightComponent,
],
moduleId: module.id,
selector: 'td-chips-demo',
styleUrls: ['chips.component.css'],
templateUrl: 'chips.component.html',
})
export class ChipsDemoComponent {

chipsAttrs: Object[] = [{
description: `Enables Autocompletion with the provided list of strings.`,
name: 'items?',
type: 'string[]',
}, {
description: `Disables the chip input and removal.`,
name: 'readOnly?',
type: 'boolean',
}, {
description: `Validates input against the provided search list before adding it to the model.
If it doesnt exist, it cancels the event.`,
name: 'requireMatch?',
type: 'boolean',
}, {
description: `Placeholder for the autocomplete input.`,
name: 'placeholder?',
type: 'string',
}, {
description: `Method to be executed when string is added as chip through the autocomplete.
Sends chip value as event.`,
name: 'add?',
type: 'function',
}, {
description: `Method to be executed when string is removed as chip with the "remove" button.
Sends chip value as event.`,
name: 'remove?',
type: 'function',
}];

readOnly: boolean = false;

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

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

itemsReadOnly: string[] = this.items.slice(2, 8);

itemsForms: string[] = this.items.slice(6, 11);

toggleReadOnly(): void {
this.readOnly = !this.readOnly;
}

}
1 change: 1 addition & 0 deletions src/app/components/components/chips/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ChipsDemoComponent } from './chips.component';
5 changes: 5 additions & 0 deletions src/app/components/components/components.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export class ComponentsComponent {
icon: 'file_upload',
route: 'file-upload',
title: 'File Upload',
}, {
description: 'Build a list of strings',
icon: 'playlist_add',
route: 'chips',
title: 'Chips',
}, {
description: 'Circular or linear progress loader',
icon: 'hourglass_empty',
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/components/components.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { LoadingDemoComponent } from './loading';
import { MarkdownDemoComponent } from './markdown';
import { MediaDemoComponent } from './media';
import { HttpDemoComponent } from './http';
import { ChipsDemoComponent } from './chips';
import { PipesComponent } from './pipes';

export const componentsRoutes: RouterConfig = [{
Expand Down Expand Up @@ -40,6 +41,9 @@ export const componentsRoutes: RouterConfig = [{
}, {
component: HttpDemoComponent,
path: 'http',
}, {
component: ChipsDemoComponent,
path: 'chips',
}, {
component: PipesComponent,
path: 'pipes',
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/components/overview/overview.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export class OverviewComponent {
icon: 'file_upload',
route: 'file-upload',
title: 'File Upload',
}, {
color: 'grey-700',
icon: 'playlist_add',
route: 'chips',
title: 'Chips',
}, {
color: 'light-green-700',
icon: 'hourglass_empty',
Expand Down
27 changes: 27 additions & 0 deletions src/platform/chips/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# td-chips

`td-chips` element generates a list of strings as chips.

Add the [items] attribute to enable the autocomplete with a search list, and [requireMatch] to validated the input against the provided search list.

## API Summary

Properties:

| Name | Type | Description |
| --- | --- | --- |
| `items?` | `string[]` | Enables Autocompletion with the provided list of search strings.
| `readOnly` | `boolean` | Disables the chip input and removal.
| `requireMatch?` | `boolean` | Validates input against the provided search list before adding it to the model. If it doesnt exist, it cancels the event.
| `placeholder?` | `string` | Placeholder for the autocomplete input.
| `add?` | `function` | Method to be executed when string is added as chip through the autocomplete. Sends chip value as event.
| `remove?` | `function` | Method to be executed when string is removed as chip with the "remove" button. Sends chip value as event.

## Usage

Example for HTML usage:

```html
<td-chips placeholder="placeholder" [items]="items" [(ngModel)]="model" readOnly="readOnly" (add)="addEvent($event)" (remove)="removeEvent($event)" requireMatch>
</td-chips>
```
24 changes: 24 additions & 0 deletions src/platform/chips/autocomplete/autocomplete.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<div flex>
<md-input flex="100"
[(ngModel)]="value"
[placeholder]="placeholder"
[autofocus]="autoFocus"
[list]="listName"
[max]="max"
[maxLength]="maxLength"
[min]="min"
[minLength]="minLength"
[readonly]="readOnly"
[disabled]="disabled"
[required]="required"
[name]="name"
(keyup.enter)="handleItemSelect()"
(focus)="handleFocus()"
(blur)="handleBlur()">
</md-input>
<datalist [id]="listName">
<template let-item ngFor [ngForOf]="searchItems">
<option [value]="item"></option>
</template>
</datalist>
</div>
3 changes: 3 additions & 0 deletions src/platform/chips/autocomplete/autocomplete.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
display: block;
}
Loading

0 comments on commit 095748e

Please sign in to comment.