Search widget

Uses Vue.js for logic and Google Spreadsheets for the data.

by cambraca

HTML

<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js "></script>
<section id="search-widget" v-cloak>
  <input type="search" v-model="search" placeholder="Search for countries or regions" debounce="300">
  <ul>
    <li class="region" v-for="(region, countries) in results">
      <strong>{{ region }}</strong>
      <ul>
        <li class="country" v-for="(country, population) in countries">
          {{ country }} <span class="population">{{ population | numberFormat }}</span>
        </li>
      </ul>
    </li>
  </ul>
</section>

SCSS

[v-cloak] {
  display: none;
}

#search-widget {
  max-width: 400px;

  input[type=search] {
    border-radius: 15px;
    line-height: 20px;
    padding: 5px;
    border: 1px solid gray;
    width: 100%;
    
    &:focus {
      outline: none;
    }
  }
  
  .region {
    list-style: none;
    margin-top: 15px;
    
    ul {
      margin-top: 5px;
    }
  }
  
  .population {
    float: right;
    font-style: italic;
  }
}

JavaScript

var url = 'https://spreadsheets.google.com/feeds/list/16_BOjZ3i51ZaTco_ZICBskfzlmHAWKm4MCUOJnnUIzM/1/public/values?alt=json';

function restructure(res, row)  {
  if (typeof res[row.gsx$region.$t] === 'undefined')
    res[row.gsx$region.$t] = {};

  res[row.gsx$region.$t][row.gsx$country.$t] = parseInt(row.gsx$population.$t);

  return res;
}

$.getJSON(url, json => dataLoaded(json.feed.entry.reduce(restructure, {})));

function dataLoaded(data) {
  Vue.filter('numberFormat', function (value) {
    return Number(value).toLocaleString();
  });

  new Vue({
    el: '#search-widget',
    data: {
      search: '',
      populations: data
    },
    computed: {
      results: function() {
        var query = this.search.trim().toLowerCase();
        var result = {};

        if (query.length < 3)
        	return result;

        for (var region in this.populations) {
        	if (this.matches(query, region)) {
          	result[region] = this.populations[region];
          } else {
          	var regionIncluded = false;
          	for (var country in this.populations[region]) {
            	if (this.matches(query, country)) {
              	if (!regionIncluded) {
                	result[region] = {};
                  regionIncluded = true;
                }
                result[region][country] = this.populations[region][country];
              }
            }
          }
        }
        
        return result;
      }
    },
    methods: {
      matches: function(query, string) {
        return string.toLowerCase().indexOf(query) !== -1;
      }
    }
  });
}