-
-
- {{chip}}
- cancel
-
-
-
+
+
+
+ {{chip}}
+
+ cancel
+
+
+
+
+
+
+
+
+ {{item}}
+
+
+
{
// empty method
@@ -18,7 +21,7 @@ export const TD_CHIPS_CONTROL_VALUE_ACCESSOR: any = {
styleUrls: ['./chips.component.scss' ],
templateUrl: './chips.component.html',
})
-export class TdChipsComponent implements ControlValueAccessor, DoCheck {
+export class TdChipsComponent implements ControlValueAccessor, DoCheck, OnInit {
/**
* Implemented as part of ControlValueAccessor.
@@ -27,6 +30,10 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
private _length: number = 0;
private _requireMatch: boolean = false;
+ private _readOnly: boolean = false;
+
+ @ViewChild(MdInputDirective) _inputChild: MdInputDirective;
+ @ViewChildren(MdChip) _chipsChildren: QueryList;
/**
* Boolean value that specifies if the input is valid against the provieded list.
@@ -37,6 +44,21 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
*/
focused: boolean = false;
+ /**
+ * FormControl for the mdInput element.
+ */
+ inputControl: FormControl = new FormControl();
+
+ /**
+ * Subject to control what items to render in the autocomplete
+ */
+ subject: Subject = new Subject();
+
+ /**
+ * Observable of items to render in the autocomplete
+ */
+ filteredItems: Observable = this.subject.asObservable();
+
/**
* items?: string[]
* Enables Autocompletion with the provided list of strings.
@@ -58,9 +80,20 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
/**
* readOnly?: boolean
- * Disables the chip input and removal.
+ * Disables the chips input and chip removal icon.
*/
- @Input('readOnly') readOnly: boolean = false;
+ @Input('readOnly')
+ set readOnly(readOnly: boolean) {
+ this._readOnly = readOnly;
+ if (readOnly) {
+ this.inputControl.disable();
+ } else {
+ this.inputControl.enable();
+ }
+ }
+ get readOnly(): boolean {
+ return this._readOnly;
+ }
/**
* placeholder?: string
@@ -89,10 +122,26 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
if (v !== this._value) {
this._value = v;
this._length = this._value ? this._value.length : 0;
+ if (this._value) {
+ this._filter(this.inputControl.value);
+ }
}
}
get value(): any { return this._value; };
+ ngOnInit(): void {
+ this.inputControl.valueChanges
+ .debounceTime(100)
+ .subscribe((value: string) => {
+ this.matches = true;
+ this._filter(value);
+ });
+ // filter the autocomplete options after everything is rendered
+ Observable.timer().subscribe(() => {
+ this._filter(this.inputControl.value);
+ });
+ }
+
ngDoCheck(): void {
// Throw onChange event only if array changes size.
if (this._value && this._value.length !== this._length) {
@@ -103,14 +152,10 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
/**
* Returns a list of filtered items.
- * Removes the ones that have been added as value.
*/
- get filteredItems(): string[] {
- if (!this._value) {
- return [];
- }
+ filter(val: string): string[] {
return this.items.filter((item: string) => {
- return this._value.indexOf(item) < 0;
+ return val ? item.indexOf(val) > -1 : true;
});
}
@@ -118,18 +163,22 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
* Method that is executed when trying to create a new chip from the autocomplete.
* returns 'true' if successful, 'false' if it fails.
*/
- addItem(value: string): boolean {
+ addChip(value: string): boolean {
if (value.trim() === '' || this._value.indexOf(value) > -1) {
+ this.matches = false;
return false;
}
if (this.items && this.requireMatch) {
if (this.items.indexOf(value) < 0) {
+ this.matches = false;
return false;
}
}
this._value.push(value);
this.add.emit(value);
this.onChange(this._value);
+ this.inputControl.setValue('');
+ this.matches = true;
return true;
}
@@ -137,7 +186,7 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
* Method that is executed when trying to remove a chip.
* returns 'true' if successful, 'false' if it fails.
*/
- removeItem(value: string): boolean {
+ removeChip(value: string): boolean {
let index: number = this._value.indexOf(value);
if (index < 0) {
return false;
@@ -145,6 +194,7 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
this._value.splice(index, 1);
this.remove.emit(value);
this.onChange(this._value);
+ this.inputControl.setValue('');
return true;
}
@@ -155,10 +205,87 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
handleBlur(): boolean {
this.focused = false;
+ this.matches = true;
this.onTouched();
return true;
}
+ /**
+ * Programmatically focus the input. Since its the component entry point
+ */
+ focus(): void {
+ this._inputChild.focus();
+ }
+
+ /**
+ * Passes relevant input key presses.
+ */
+ _inputKeydown(event: KeyboardEvent): void {
+ switch (event.keyCode) {
+ case LEFT_ARROW:
+ case DELETE:
+ case BACKSPACE:
+ /** Check to see if input is empty when pressing left arrow to move to the last chip */
+ if (!this._inputChild.value) {
+ this._focusLastChip();
+ event.preventDefault();
+ }
+ break;
+ case RIGHT_ARROW:
+ /** Check to see if input is empty when pressing right arrow to move to the first chip */
+ if (!this._inputChild.value) {
+ this._focusFirstChip();
+ event.preventDefault();
+ }
+ break;
+ default:
+ // default
+ }
+ }
+
+ /**
+ * Passes relevant chip key presses.
+ */
+ _chipKeydown(event: KeyboardEvent, index: number): void {
+ switch (event.keyCode) {
+ case DELETE:
+ case BACKSPACE:
+ /** Check to see if not in [readOnly] state to delete a chip */
+ if (!this.readOnly) {
+ /**
+ * Checks if deleting last single chip, to focus input afterwards
+ * Else check if its not the last chip of the list to focus the next one.
+ */
+ if (index === (this._totalChips - 1) && index === 0) {
+ this.focus();
+ } else if (index < (this._totalChips - 1)) {
+ this._focusChip(index + 1);
+ }
+ this.removeChip(this.value[index]);
+ }
+ break;
+ case LEFT_ARROW:
+ /** Check to see if left arrow was pressed while focusing the first chip to focus input next */
+ if (index === 0) {
+ this.focus();
+ event.stopPropagation();
+ }
+ break;
+ case RIGHT_ARROW:
+ /** Check to see if right arrow was pressed while focusing the last chip to focus input next */
+ if (index === (this._totalChips - 1)) {
+ this.focus();
+ event.stopPropagation();
+ }
+ break;
+ case ESCAPE:
+ this.focus();
+ break;
+ default:
+ // default
+ }
+ }
+
/**
* Implemented as part of ControlValueAccessor.
*/
@@ -177,4 +304,44 @@ export class TdChipsComponent implements ControlValueAccessor, DoCheck {
onChange = (_: any) => noop;
onTouched = () => noop;
+ /**
+ *
+ * Method to filter the options for the autocomplete
+ */
+ private _filter(value: string): void {
+ let items: string[] = this.filter(value);
+ items = items.filter((filteredItem: string) => {
+ return this._value && filteredItem ? this._value.indexOf(filteredItem) < 0 : true;
+ });
+ this.subject.next(items);
+ }
+
+ /**
+ * Get total of chips
+ */
+ private get _totalChips(): number {
+ let chips: MdChip[] = this._chipsChildren.toArray();
+ return chips.length;
+ }
+
+ /**
+ * Method to focus a desired chip by index
+ */
+ private _focusChip(index: number): void {
+ /** check to see if index exists in the array before focusing */
+ if (index > -1 && this._totalChips > index) {
+ this._chipsChildren.toArray()[index].focus();
+ }
+ }
+
+ /** Method to focus first chip */
+ private _focusFirstChip(): void {
+ this._focusChip(0);
+ }
+
+ /** MEthod to focus last chip */
+ private _focusLastChip(): void {
+ this._focusChip(this._totalChips - 1);
+ }
+
}
diff --git a/src/platform/core/chips/chips.module.ts b/src/platform/core/chips/chips.module.ts
index 42f997d47a..f9818e5e1c 100644
--- a/src/platform/core/chips/chips.module.ts
+++ b/src/platform/core/chips/chips.module.ts
@@ -1,32 +1,27 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
-import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
-import { MdInputModule, MdIconModule } from '@angular/material';
+import { MdInputModule, MdIconModule, MdAutocompleteModule, MdChipsModule } from '@angular/material';
import { TdChipsComponent } from './chips.component';
-import { TdChipComponent } from './chip.component';
-import { TdAutoCompleteComponent } from './autocomplete/autocomplete.component';
-
export { TdChipsComponent } from './chips.component';
@NgModule({
imports: [
- FormsModule,
+ ReactiveFormsModule,
CommonModule,
MdInputModule,
MdIconModule,
+ MdChipsModule,
+ MdAutocompleteModule,
],
declarations: [
TdChipsComponent,
- TdChipComponent,
- TdAutoCompleteComponent,
],
exports: [
TdChipsComponent,
- TdChipComponent,
- TdAutoCompleteComponent,
],
})
export class CovalentChipsModule {
diff --git a/src/platform/core/common/styles/_input.scss b/src/platform/core/common/styles/_input.scss
index 04017e7dfe..39857b8267 100644
--- a/src/platform/core/common/styles/_input.scss
+++ b/src/platform/core/common/styles/_input.scss
@@ -13,6 +13,14 @@ md-input-container {
}
.mat-input-underline {
border-top-color: md-color($warn);
+ .mat-input-ripple {
+ background-color: md-color($warn);
+ }
}
}
+}
+
+// Proper placeholder font size
+.mat-input-placeholder-wrapper {
+ font-size: 16px;
}
\ No newline at end of file