JSFiddle - React, Tailwind, and code Playground

by Dogbert

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div id="app" class="ui grid">
  <div class="centered row">
    <div class="eight wide column">
      <div class="ui form" :class="{'error': !isValid}">
        <div class="field">
          <input type="text" v-model="input" @keyup="search" @keydown="search" id="input">
        </div>
        <div class="ui error message">
          Please enter only alphabets, numbers, and hyphens.
        </div>
      </div>
    </div>
  </div>
  <div class="sixteen wide column">
    <table id="results" class="ui celled striped selectable table unstackable">
      <tbody>
        <tr v-for="(index, result) in results">
          <td class="domain">{{result.domain}}</td>
          <td class="availability">
            <span v-for="tld in tlds" :class="{available: result.availability[tld] === true, taken: result.availability[tld] === false, unknown: result.availability[tld] === undefined}">
            <template v-if="result.availability[tld] === undefined">...</template><template v-else>{{tld}}</template>
          </span>
          </td>
          <td class="actions">
            <button class="mini ui yellow button" :class="{basic: !result.isFavorite}" @click="result.toggleFavorite();">Favorite</button>
            <button class="mini ui red button" @click="removeResult(index)">Remove</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

SCSS

body {
  margin: 20px;
  text-align: center;
}

#results .domain {
  width: 100%;
}

#results .availability,
#results .actions {
  white-space: nowrap;
}

#results .availability span {
  margin: 0 10px;
}

#results .availability .available {
  color: green;
}

#results .availability .taken {
  color: red;
}

#results .ui.button {
  padding: 8px 10px;
}

JavaScript

function Result(domain) {
  var self = this;
  this.domain = domain;
  this.availability = {
    com: undefined,
    net: undefined,
    org: undefined,
    info: undefined
  };
  this.isFavorite = false;
  
  this.toggleFavorite = function() {
  	this.isFavorite = !this.isFavorite;
  }

  check('com', 0.4, Math.random() * 4000);
  check('net', 0.5, Math.random() * 5000);
  check('org', 0.6, Math.random() * 6000);
  check('info', 0.7, Math.random() * 7000);

  function check(tld, probability, delay) {
    setTimeout(function() {
      self.availability[tld] = Math.random() < probability;
    }, delay);
  }
}

new Vue({
  el: '#app',
  data: {
    input: "",
    results: [],
    cache: {},
    tlds: ["com", "net", "org", "info"]
  },
  computed: {
    isValid: function() {
      return /^[a-zA-Z0-9-]*$/.test(this.input);
    }
  },
  methods: {
    search: function() {
      if (!this.isValid) {
        return;
      }
      var input = this.input;
      if (!input || this.cache[input]) {
        return;
      }
      this.cache[input] = true;
      var result = new Result(input);
      this.results.unshift(result);
    },
    removeResult: function(index) {
    	// var result = this.results[index];
      // this.cache[result.domain] = false;
      this.results.splice(index, 1);
    }
  },
})