github.com/resonatecoop/id@v1.1.0-43/frontend/src/components/select-country-list/index.js (about)

     1  const html = require('choo/html')
     2  const Component = require('choo/component')
     3  const isEmpty = require('validator/lib/isEmpty')
     4  const validateFormdata = require('validate-formdata')
     5  const icon = require('@resonate/icon-element')
     6  
     7  // Select country list component class
     8  class SelectCountryList extends Component {
     9    /***
    10     * Create a select country list component
    11     * @param {String} id - The select country list component id (unique)
    12     * @param {Number} state - The choo app state
    13     * @param {Function} emit - Emit event on choo app
    14     */
    15    constructor (id, state, emit) {
    16      super(id)
    17  
    18      this.emit = emit
    19      this.state = state
    20  
    21      this.local = state.components[id] = {}
    22  
    23      this._onchange = this._onchange.bind(this)
    24      this.validator = validateFormdata()
    25      this.form = this.validator.state
    26  
    27      this.nameMap = {}
    28      this.codeMap = {}
    29    }
    30  
    31    /***
    32     * Create select country list component element
    33     * @param {Object} props - The select country list component props
    34     * @param {String} props.country - Initial country name or country Alpha-2 code
    35     * @param {String} props.name - Select element name attribute
    36     * @param {Object} props.form - Form
    37     * @param {Object} props.validator - Validator
    38     * @param {Boolean} props.required - Select element required attribute
    39     * @param {Function} props.onchange - Optional onchange callback
    40     * @returns {HTMLElement}
    41     */
    42    createElement (props = {}) {
    43      this.state.countries.forEach(this.mapCodeAndName.bind(this))
    44  
    45      this.local.options = this.state.countries.map(({ code, name }) => {
    46        return {
    47          value: code,
    48          label: name
    49        }
    50      }).sort((a, b) => a.label.localeCompare(b.label, 'en', {}))
    51  
    52      this.validator = props.validator || this.validator
    53      this.form = props.form || this.validator.state
    54  
    55      this.local.required = props.required || false
    56      this.local.name = props.name || 'country'
    57  
    58      this.onchange = props.onchange // optional callback
    59  
    60      const pristine = this.form.pristine
    61      const errors = this.form.errors
    62      const values = this.form.values
    63  
    64      if (!this.local.country) {
    65        this.local.country = props.country || this.getName(values[this.local.name] || '') || ''
    66      }
    67  
    68      // select attributes
    69      const attrs = {
    70        id: 'select-country',
    71        required: this.local.required,
    72        class: 'w-100 bn br0 bg-black white bg-white--dark black--dark bg-black--light white--light pa3',
    73        onchange: this._onchange,
    74        name: this.local.name
    75      }
    76  
    77      return html`
    78        <div class="mb3">
    79          <div class="flex flex-auto flex-column">
    80  
    81            ${errors[attrs.name] && !pristine[attrs.name]
    82              ? html`
    83                <div class="absolute left-0 ph1 flex items-center" style="top:50%;transform: translate(-100%, -50%);">
    84                  ${icon('info', { class: 'fill-red', size: 'sm' })}
    85                </div>
    86              `
    87              : ''
    88            }
    89            <label for="select-country" class="f5 db mb1 dark-gray">
    90              Country
    91            </label>
    92            <select ${attrs}>
    93              <option value="" selected=${!values[attrs.name]} disabled>Select a country</option>
    94              ${this.local.options.map(({ value, label, disabled = false }) => {
    95                const selected = this.local.country === value || this.getCode(this.local.country) === value
    96                return html`
    97                  <option value=${value} disabled=${disabled} selected=${selected}>
    98                    ${label}
    99                  </option>
   100                `
   101              })}
   102            </select>
   103            ${errors[attrs.name] && !pristine[attrs.name]
   104              ? html`<span class="message f5 pb2">${errors[attrs.name].message}</span>`
   105              : ''
   106            }
   107          </div>
   108        </div>
   109      `
   110    }
   111  
   112    mapCodeAndName (country) {
   113      this.nameMap[country.name.toLowerCase()] = country.code
   114      this.codeMap[country.code.toLowerCase()] = country.name
   115    }
   116  
   117    getCode (name) {
   118      return this.nameMap[name.toLowerCase()]
   119    }
   120  
   121    getName (code) {
   122      return this.codeMap[code.toLowerCase()]
   123    }
   124  
   125    /**
   126     * Select element onchange event handler
   127     * @param {Object} e Event
   128     */
   129    async _onchange (e) {
   130      const value = e.target.value
   131  
   132      this.local.code = value
   133      this.local.country = this.getName(value)
   134  
   135      this.validator.validate('country', value)
   136      this.rerender()
   137  
   138      // optional callback
   139      try {
   140        typeof this.onchange === 'function' && await this.onchange({
   141          country: this.local.country,
   142          code: this.local.code
   143        })
   144  
   145        this.emit('notify', {
   146          message: `Location changed to ${this.local.country}`,
   147          type: 'success'
   148        })
   149      } catch (err) {
   150        this.emit('notify', {
   151          message: 'Location not changed',
   152          type: 'error'
   153        })
   154      }
   155    }
   156  
   157    /***
   158     * Select country list component on load event handler
   159     * @param {HTMLElement} el - The select country list component element
   160     */
   161    load (el) {
   162      this.validator.field(this.local.name, { required: this.local.required }, (data) => {
   163        if (this.local.required && isEmpty(data)) {
   164          return new Error('Please select a country')
   165        }
   166      })
   167    }
   168  
   169    /***
   170     * Select country list component on update event handler
   171     * @param {Object} props - Select country list component props
   172     * @param {String} props.country - The current selected country
   173     * @param {Object} props.validator - Validator
   174     * @returns {Boolean} Should update
   175     */
   176    update (props) {
   177      return props.country !== this.local.country ||
   178        (props.validator && props.validator.changed)
   179    }
   180  }
   181  
   182  module.exports = SelectCountryList