JSFiddle - React, Tailwind, and code Playground

by bilbobaggins

HTML

<div ng-controller="ContactChipDemoCtrl as ctrl" layout="column" ng-cloak="" class="chipsdemoContactChips" ng-app="MyApp">

  <md-content class="md-padding autocomplete" layout="column">
    <md-contact-chips ng-model="ctrl.contacts" md-contacts="ctrl.querySearch($query)" md-contact-name="name" md-contact-image="image" md-contact-email="email" md-require-match="true" md-highlight-flags="i" filter-selected="ctrl.filterSelected" placeholder="To">
    </md-contact-chips>

    <md-list class="fixedRows">
      <md-subheader class="md-no-sticky">Contacts</md-subheader>
      <md-list-item class="md-2-line contact-item" ng-repeat="(index, contact) in ctrl.allContacts" ng-if="ctrl.contacts.indexOf(contact) < 0">
        <img ng-src="{{contact.image}}" class="md-avatar" alt="{{contact.name}}">
        <div class="md-list-item-text compact">
          <h3>{{contact.name}}</h3>
          <p>{{contact.email}}</p>
        </div>
      </md-list-item>
      <md-list-item class="md-2-line contact-item selected" ng-repeat="(index, contact) in ctrl.contacts">
        <img ng-src="{{contact.image}}" class="md-avatar" alt="{{contact.name}}">
        <div class="md-list-item-text compact">
          <h3>{{contact.name}}</h3>
          <p>{{contact.email}}</p>
        </div>
      </md-list-item>
    </md-list>

    <br>
    <h2 class="md-title">Searching asynchronously.</h2>
    <md-contact-chips ng-model="ctrl.asyncContacts" md-contacts="ctrl.delayedQuerySearch($query)" md-contact-name="name" md-contact-image="image" md-contact-email="email" md-require-match="true" md-highlight-flags="i" filter-selected="ctrl.filterSelected" placeholder="To">
    </md-contact-chips>
  </md-content>
</div>

<!--
Copyright 2016 Google Inc. All Rights Reserved. 
Use of this source code is governed by an MIT-style license that can be foundin the LICENSE file at http://material.angularjs.org/HEAD/license.
-->

CSS

.chipsdemoContactChips md-content.autocomplete {
  min-height: 250px; }
  .chipsdemoContactChips md-content.autocomplete input {
    min-width: 400px; }

.chipsdemoContactChips .md-item-text.compact {
  padding-top: 8px;
  padding-bottom: 8px; }

.chipsdemoContactChips .contact-item {
  box-sizing: border-box; }
  .chipsdemoContactChips .contact-item.selected {
    opacity: 0.5; }
    .chipsdemoContactChips .contact-item.selected h3 {
      opacity: 0.5; }
  .chipsdemoContactChips .contact-item .md-list-item-text {
    padding: 14px 0;
    max-width: 190px; }
    .chipsdemoContactChips .contact-item .md-list-item-text h3 {
      margin: 0 !important;
      padding: 0;
      line-height: 1.2em !important; }
    .chipsdemoContactChips .contact-item .md-list-item-text h3, .chipsdemoContactChips .contact-item .md-list-item-text p {
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden; }
  @media (min-width: 960px) {
    .chipsdemoContactChips .contact-item {
      float: left;
      width: 33%; } }

.chipsdemoContactChips md-contact-chips {
  margin-bottom: 10px; }

.chipsdemoContactChips .md-chips {
  padding: 5px 0 8px; }

.chipsdemoContactChips .fixedRows {
  height: 250px;
  overflow: hidden; }


/*
Copyright 2016 Google Inc. All Rights Reserved. 
Use of this source code is governed by an MIT-style license that can be foundin the LICENSE file at http://material.angularjs.org/HEAD/license.
*/

JavaScript

(function () {
  'use strict';

  // If we do not have CryptoJS defined; import it


  angular
      .module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
      .controller('ContactChipDemoCtrl', DemoCtrl);

  function DemoCtrl ($q, $timeout) {
    var self = this;
    var pendingSearch, cancelSearch = angular.noop;
    var lastSearch;

    self.allContacts = loadContacts();
    self.contacts = [self.allContacts[0]];
    self.asyncContacts = [];
    self.filterSelected = true;

    self.querySearch = querySearch;
    self.delayedQuerySearch = delayedQuerySearch;

    /**
     * Search for contacts; use a random delay to simulate a remote call
     */
    function querySearch (criteria) {
      return criteria ? self.allContacts.filter(createFilterFor(criteria)) : [];
    }

    /**
     * Async search for contacts
     * Also debounce the queries; since the md-contact-chips does not support this
     */
    function delayedQuerySearch(criteria) {
      if ( !pendingSearch || !debounceSearch() )  {
        cancelSearch();

        return pendingSearch = $q(function(resolve, reject) {
          // Simulate async search... (after debouncing)
          cancelSearch = reject;
          $timeout(function() {

            resolve( self.querySearch(criteria) );

            refreshDebounce();
          }, Math.random() * 500, true)
        });
      }

      return pendingSearch;
    }

    function refreshDebounce() {
      lastSearch = 0;
      pendingSearch = null;
      cancelSearch = angular.noop;
    }

    /**
     * Debounce if querying faster than 300ms
     */
    function debounceSearch() {
      var now = new Date().getMilliseconds();
      lastSearch = lastSearch || now;

      return ((now - lastSearch) < 300);
    }

    /**
     * Create filter function for a query string
     */
    function createFilterFor(query) {
      var lowercaseQuery = angular.lowercase(query);

      return function filterFn(contact) {
        return...