API Docs 0.11.3.33c7ec07

import Ember from 'ember';
import SizeClass from 'ember-bootstrap/mixins/size-class';
import ComponentParent from 'ember-bootstrap/mixins/component-parent';

const { computed, observer } = Ember;

/**
 Bootstrap-style button group, that visually groups buttons, and optionally adds radio/checkbox like behaviour.
 See http://getbootstrap.com/components/#btn-groups

 Use as a block level component with any number of [Components.Button](Components.Button.html) components as children:

 ```handlebars
 {{#bs-button-group}}
   {{#bs-button}}1{{/bs-button}}
   {{#bs-button}}2{{/bs-button}}
   {{#bs-button}}3{{/bs-button}}
 {{/bs-button-group}}
 ```

 ### Radio-like behaviour

 Use the `type` property set to "radio" to make the child buttons toggle like radio buttons, i.e. only one button can be active.
 Set the `value` property of the buttons to something meaningful. The `value` property of the button group will then reflect
 the value of the active button:

 ```handlebars
 {{#bs-button-group value=buttonGroupValue type="radio"}}
   {{#bs-button value='foo'}}foo{{/bs-button}}
   {{#bs-button value='bar'}}bar{{/bs-button}}
   {{#bs-button value='baz'}}baz{{/bs-button}}
 {{/bs-button-group}}

 You selected: {{buttonGroupValue}}!
 ```

 ### Checkbox-like behaviour

 Set `type` to "checkbox" to make any number of child buttons selectable. The `value` property will be an array
 of all the values of the active buttons:

 ```handlebars
 {{#bs-button-group value=buttonGroupValue type="checkbox"}}
   {{#bs-button value='foo'}}foo{{/bs-button}}
   {{#bs-button value='bar'}}bar{{/bs-button}}
   {{#bs-button value='baz'}}baz{{/bs-button}}
 {{/bs-button-group}}

 You selected:
 <ul>
 {{#each value in buttonGroupValue}}
   <li>{{value}}</li>
 {{/each}}
 </ul>
 ```

 @class ButtonGroup
 @namespace Components
 @extends Ember.Component
 @uses Mixins.SizeClass
 @public
 */
export default Ember.Component.extend(ComponentParent, SizeClass, {
  /**
   * @type string
   * @property ariaRole
   * @default 'group'
   * @protected
   */
  ariaRole: 'group',

  /**
   * @property classNames
   * @type array
   * @default ['btn-group']
   * @protected
   */
  classNames: ['btn-group'],

  /**
   * @property classNameBindings
   * @type array
   * @protected
   */
  classNameBindings: ['vertical:btn-group-vertical', 'justified:btn-group-justified'],

  /**
   * @property classTypePrefix
   * @type String
   * @default 'btn-group'
   * @protected
   */
  classTypePrefix: 'btn-group',

  /**
   * Set to true for a vertically stacked button group, see http://getbootstrap.com/components/#btn-groups-vertical
   *
   * @property vertical
   * @type boolean
   * @default false
   * @public
   */
  vertical: false,

  /**
   * Set to true for the buttons to stretch at equal sizes to span the entire width of its parent.
   *
   * *Important*: You have to wrap every button component in a `div class="btn-group">`:
   *
   * ```handlebars
   * <div class="btn-group" role="group">
   * {{#bs-button}}My Button{{/bs-button}}
   * </div>
   * ```
   *
   * See http://getbootstrap.com/components/#btn-groups-justified
   *
   * @property justified
   * @type boolean
   * @default false
   * @public
   */
  justified: false,

  /**
   * The type of the button group specifies how child buttons behave and how the `value` property will be computed:
   *
   * ### null
   * If `type` is not set (null), the button group will add no functionality besides Bootstrap styling
   *
   * ### radio
   * if `type` is set to "radio", the buttons will behave like radio buttons:
   * * the buttons will toggle (`toggle` property of the child buttons will be set to true)
   * * only one button may be active
   * * the `value` property of the button group will reflect the `value` property of the active button
   *
   * ### checkbox
   * if `type` is set to "checkbox", the buttons will behave like checkboxes:
   * * the buttons will toggle (`toggle` property of the child buttons will be set to true)
   * * any number of buttons may be active
   * * the `value` property of the button group will be an array containing the `value` properties of all active buttons
   *
   * @property type
   * @type string
   * @default null
   * @public
   */
  type: null,

  /**
   * The value of the button group, computed by its child buttons.
   * See the `type` property for how the value property is constructed.
   *
   * When you set the value, the corresponding buttons will be activated:
   * * use a single value for a radio button group to activate the button with the same value
   * * use an array of values for a checkbox button group to activate all the buttons with values contained in the array
   *
   * @property value
   * @type array
   * @public
   */
  value: undefined,

  _syncValueToActiveButtons: observer('value', 'children.@each.value', '_inDOM', function() {
    if (!this._inDOM) {
      return;
    }
    let value = this.get('value');
    let values = Ember.A(!Ember.isArray(value) ? [value] : value);
    this.get('children')
      .forEach(function(button) {
        button.set('active', values.includes(button.get('value')));
      });
  }),

  /**
   * Child buttons that are active (pressed)
   * @property activeChildren
   * @type array
   * @protected
   */
  activeChildren: computed.filterBy('children', 'active', true),

  lastActiveChildren: null,
  newActiveChildren: computed.setDiff('activeChildren', 'lastActiveChildren'),
  _observeButtons: observer('activeChildren.[]', 'type', function() {
    let type = this.get('type');

    if (!this._inDOM || (type !== 'radio' && type !== 'checkbox')) {
      return;
    }

    Ember.run.scheduleOnce('actions', this, function() {
      // the button that just became active
      let value;

      switch (type) {
        case 'radio':
          let newActive = Ember.A(this.get('newActiveChildren')).objectAt(0);
          if (newActive) {
            value = newActive.get('value');
          } else {
            let lastActive = this.get('lastActiveChildren.firstObject');
            if (lastActive) {
              lastActive.set('active', this.get('value') ? true : false);
            }
          }
          break;
        case 'checkbox':
          value = this.get('activeChildren').mapBy('value');
          break;
      }
      if (typeof value !== 'undefined') {
        this.set('value', value);
      }
      // remember activeChildren, used as a replacement for a before observer as they will be deprecated in the future...
      this.set('lastActiveChildren', Ember.A(this.get('activeChildren').slice()));
    });
  }),

  _observeType: observer('type', 'children.[]', function() {
    if (this.get('type') === 'radio' || this.get('type') === 'checkbox') {
      // set all child buttons to toggle
      this.get('children').forEach(function(button) {
        button.set('toggle', true);
      });
    }
  }),

  init() {
    this._super();
    this.set('lastActiveChildren', Ember.A());
  },

  _inDOM: false,

  didInsertElement() {
    this.set('_inDOM', true);
    this.get('activeChildren');
  }
});