<commit_before>import Ember from 'ember';
import layout from '../templates/components/x-select';

const {
  Component,
  computed,
  get,
  inject,
  isPresent,
  run
} = Ember;

export default Component.extend({
  layout,
  classNames: ['ember-select'],
  classNameBindings: [
    'isOpen:es-open', 'isFocus:es-focus',
    'canSearch::es-select', 'multiple:es-multiple'
  ],

  autofocus: false,
  canSearch: true,
  disabled: false,
  dropdown: 'select-dropdown',
  freeText: false,
  isDirty: false,
  isFocus: false,
  isOpen: false,
  openOnFocus: false,
  placeholder: 'Type...',
  token: '',
  value: '',

  labelKey: 'label',
  valueKey: 'value',

  messageBus: inject.service(),

  canClear: computed.and('enabled', 'canSearch', 'hasOptions'),
  canOpen: computed.or('hasInput', 'openOnFocus'),
  enabled: computed.not('disabled'),
  hasDropdown: computed.and('enabled', 'hasModel'),
  hasInput: computed.notEmpty('token'),
  hasModel: computed.notEmpty('model'),
  hasOptions: computed.or('hasInput', 'hasValues'),
  hasValues: computed.notEmpty('values'),
  multiple: computed.bool('values'),
  shouldFilter: computed.or('isDirty', 'multiple', 'hasChanged'),

  input: computed(function() {
    return document.querySelector(`#${this.elementId} input`);
  }),

  hasChanged: computed('token', 'value', function() {
    let token = this.get('token');
    let option = this.get('value');

    if (isPresent(token) && isPresent(option)) {
      let { label } = this.retrieveOption(option);
      return token !== label;
    }
  }),

  init() {
    this._super(...arguments);

    if (this.disabled) {
      this.set('canSearch', false);
    }

    if (!this.canSearch) {
      this.set('openOnFocus', true);
    }

    /* IE10+ Triggers an input event when focus changes on
     * an input element if the element has a placeholder.
     * https://connect.microsoft.com/IE/feedback/details/810538/
     */
    if (document.documentMode) {
      this.set('placeholder', null);
    }
  },

  didInsertElement() {
    this._super(...arguments);

    let value = this.get('value');
    if (isPresent(value)) {
      run.next(this, () => this.setOption(value));
    }
  },

  didUpdateAttrs() {
    this._super(...arguments);

    // Need to open on lazy models
    if (this.get('isDirty')) {
      this.open();
    }
  },

  actions: {
    blur() {
      if (this.get('isDirty')) {
        // Clear unallowed input in strict single mode
        let option = this.get('freeText') ? this.get('token') : '';
        this.setOption(option, false, !this.get('multiple'));
      }

      this.setProperties({
        isFocus: false,
        isOpen: false
      });
    },

    change(query) {
      this.set('token', query);
      this.set('isDirty', true);
      this.sendAction('onChange', query);

      if (isPresent(query)) {
        this.open();
      }
    },

    clear() {
      this.setOption('', false, !this.get('multiple'));
      this.sendAction('onClear');
      this.send('focus');
    },

    dropdown() {
      let isOpen = this.toggleProperty('isOpen');
      if (isOpen) {
        this.get('input').focus();
      }
    },

    focus() {
      if (this.get('disabled')) {
        return this.send('blur');
      }

      this.get('input').focus();
      if (!this.get('isOpen')) {
        this.open();
      }
    },

    keypress(e) {
      let isOpen = this.get('isOpen');

      switch (e.keyCode) {
        case 8: { // Backspace
          let values = this.get('values');
          if (isPresent(values) && this.get('token') === '') {
            let last = this.getElement(values, values.length - 1);
            this.attrs.onRemove(last);
          }

          break;
        }

        case 9: // TAB
        case 13: // Enter
          if (isOpen && this.get('isDirty')) {
            this.get('messageBus').publish('select-key', e);
          } else if (this.get('freeText')) {
            this.send('select', this.get('token'), false);
          }

          break;
        case 27: // ESC
          this.send('clear');
          break;
        case 38: // Up Arrow
        case 40: // Down Arrow
          if (isOpen) {
            this.get('messageBus').publish('select-key', e);
          } else {
            this.set('isOpen', true);
          }

          e.preventDefault();
          break;
      }
    },

    remove(selection) {
      this.attrs.onRemove(selection);
      this.send('focus');
    },

    select(option, selected) {
      let isNew = !selected && this.get('freeText') && this.get('isDirty');
      let allowNew = isPresent(this.attrs.onCreate);
      let valid = isPresent(option);

      /* Notify when option is either
       *  - selected
       *  - new, empty and cannot be created */
      let notify = selected || isNew && !allowNew;

      if (allowNew && valid && isNew) {
        this.attrs.onCreate(option);
      }

      this.setOption(option, selected, notify);
    }
  },

  // Handle plain arrays and Ember Data relationships
  getElement(model, position) {
    return model[position] || model.objectAt(position);
  },

  open() {
    this.setProperties({
      isOpen: this.get('hasDropdown') && this.get('canOpen'),
      isFocus: true
    });
  },

  /* Retrieve `option`, `value` and `label` given a selection
   * which can be either an option (object) or a value */
  retrieveOption(option) {
    let model = this.get('model');
    let label = option;
    let value = option;

    if (typeof option === 'object') {
      label = get(option, this.get('labelKey'));
      value = get(option, this.get('valueKey'));
    } else if (isPresent(model) && typeof this.getElement(model, 0) === 'object') {
      let id = this.get('valueKey');
      option = model.filter(x => get(x, id) === option).shift();

      if (option) {
        label = get(option, this.get('labelKey'));
      }
    }

    return { option, value, label };
  },

  setOption(selection, selected, notify) {
    let { option, value, label } = this.retrieveOption(selection);

    if (this.get('multiple')) {
      label = '';
    }

    if (this.get('canSearch')) {
      this.set('token', label);
    }

    this.get('input').value = label;
    this.set('isDirty', false);

    if (notify) {
      this.sendAction('onSelect', value, option, selected);
      this.set('isOpen', false);
    }
  }
});
<commit_msg>Update input when parent changes the value. Closes #1.<commit_after>import Ember from 'ember';
import layout from '../templates/components/x-select';

const {
  Component,
  computed,
  get,
  inject,
  isPresent,
  run
} = Ember;

export default Component.extend({
  layout,
  classNames: ['ember-select'],
  classNameBindings: [
    'isOpen:es-open', 'isFocus:es-focus',
    'canSearch::es-select', 'multiple:es-multiple'
  ],

  autofocus: false,
  canSearch: true,
  disabled: false,
  dropdown: 'select-dropdown',
  freeText: false,
  isDirty: false,
  isFocus: false,
  isOpen: false,
  openOnFocus: false,
  placeholder: 'Type...',
  token: '',
  value: '',

  labelKey: 'label',
  valueKey: 'value',

  messageBus: inject.service(),

  canClear: computed.and('enabled', 'canSearch', 'hasOptions'),
  canOpen: computed.or('hasInput', 'openOnFocus'),
  enabled: computed.not('disabled'),
  hasDropdown: computed.and('enabled', 'hasModel'),
  hasInput: computed.notEmpty('token'),
  hasModel: computed.notEmpty('model'),
  hasOptions: computed.or('hasInput', 'hasValues'),
  hasValues: computed.notEmpty('values'),
  multiple: computed.bool('values'),
  shouldFilter: computed.or('isDirty', 'multiple', 'hasChanged'),

  input: computed(function() {
    return document.querySelector(`#${this.elementId} input`);
  }),

  hasChanged: computed('token', 'value', function() {
    let token = this.get('token');
    let option = this.get('value');

    if (isPresent(token) && isPresent(option)) {
      let { label } = this.retrieveOption(option);
      return token !== label;
    }
  }),

  init() {
    this._super(...arguments);

    if (this.disabled) {
      this.set('canSearch', false);
    }

    if (!this.canSearch) {
      this.set('openOnFocus', true);
    }

    /* IE10+ Triggers an input event when focus changes on
     * an input element if the element has a placeholder.
     * https://connect.microsoft.com/IE/feedback/details/810538/
     */
    if (document.documentMode) {
      this.set('placeholder', null);
    }
  },

  didInsertElement() {
    this._super(...arguments);

    let value = this.get('value');
    if (isPresent(value)) {
      run.next(this, () => this.setOption(value));
    }
  },

  didUpdateAttrs(attrs) {
    this._super(...arguments);

    // Need to open on lazy models
    if (this.get('isDirty')) {
      this.open();
    }

    // Update input if value has changed
    let { value } = attrs.newAttrs;
    if (value) {
      let { label } = this.retrieveOption(value.value);
      if (label !== this.get('token')) {
        this.setOption(value.value);
      }
    }
  },

  actions: {
    blur() {
      if (this.get('isDirty')) {
        // Clear unallowed input in strict single mode
        let option = this.get('freeText') ? this.get('token') : '';
        this.setOption(option, false, !this.get('multiple'));
      }

      this.setProperties({
        isFocus: false,
        isOpen: false
      });
    },

    change(query) {
      this.set('token', query);
      this.set('isDirty', true);
      this.sendAction('onChange', query);

      if (isPresent(query)) {
        this.open();
      }
    },

    clear() {
      this.setOption('', false, !this.get('multiple'));
      this.sendAction('onClear');
      this.send('focus');
    },

    dropdown() {
      let isOpen = this.toggleProperty('isOpen');
      if (isOpen) {
        this.get('input').focus();
      }
    },

    focus() {
      if (this.get('disabled')) {
        return this.send('blur');
      }

      this.get('input').focus();
      if (!this.get('isOpen')) {
        this.open();
      }
    },

    keypress(e) {
      let isOpen = this.get('isOpen');

      switch (e.keyCode) {
        case 8: { // Backspace
          let values = this.get('values');
          if (isPresent(values) && this.get('token') === '') {
            let last = this.getElement(values, values.length - 1);
            this.attrs.onRemove(last);
          }

          break;
        }

        case 9: // TAB
        case 13: // Enter
          if (isOpen && this.get('isDirty')) {
            this.get('messageBus').publish('select-key', e);
          } else if (this.get('freeText')) {
            this.send('select', this.get('token'), false);
          }

          break;
        case 27: // ESC
          this.send('clear');
          break;
        case 38: // Up Arrow
        case 40: // Down Arrow
          if (isOpen) {
            this.get('messageBus').publish('select-key', e);
          } else {
            this.set('isOpen', true);
          }

          e.preventDefault();
          break;
      }
    },

    remove(selection) {
      this.attrs.onRemove(selection);
      this.send('focus');
    },

    select(option, selected) {
      let isNew = !selected && this.get('freeText') && this.get('isDirty');
      let allowNew = isPresent(this.attrs.onCreate);
      let valid = isPresent(option);

      /* Notify when option is either
       *  - selected
       *  - new, empty and cannot be created */
      let notify = selected || isNew && !allowNew;

      if (allowNew && valid && isNew) {
        this.attrs.onCreate(option);
      }

      this.setOption(option, selected, notify);
    }
  },

  // Handle plain arrays and Ember Data relationships
  getElement(model, position) {
    return model[position] || model.objectAt(position);
  },

  open() {
    this.setProperties({
      isOpen: this.get('hasDropdown') && this.get('canOpen'),
      isFocus: true
    });
  },

  /* Retrieve `option`, `value` and `label` given a selection
   * which can be either an option (object) or a value */
  retrieveOption(option) {
    let model = this.get('model');
    let label = option;
    let value = option;

    if (typeof option === 'object') {
      label = get(option, this.get('labelKey'));
      value = get(option, this.get('valueKey'));
    } else if (isPresent(model) && typeof this.getElement(model, 0) === 'object') {
      let id = this.get('valueKey');
      option = model.filter(x => get(x, id) === option).shift();

      if (option) {
        label = get(option, this.get('labelKey'));
      }
    }

    return { option, value, label };
  },

  setOption(selection, selected, notify) {
    let { option, value, label } = this.retrieveOption(selection);

    if (this.get('multiple')) {
      label = '';
    }

    if (this.get('canSearch')) {
      this.set('token', label);
    }

    this.get('input').value = label;
    this.set('isDirty', false);

    if (notify) {
      this.sendAction('onSelect', value, option, selected);
      this.set('isOpen', false);
    }
  }
});

{'fraction_non_alphanumeric': 0.16860330896498654, 'fraction_numerical': 0.003155059638322432, 'mean_word_length': 2.3896713615023475, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2856953', 'n_tokens_mistral': 4139, 'n_tokens_neox': 4006, 'n_words': 878}