Source: ui/List.js

/*
 * src/ui/List.js
 * Author: H.Alper Tuna <halpertuna@gmail.com>
 * Date: 08.08.2016
 */

'use strict';

define([
    '../core/Utils',
    './ComponentContainer', './Element',
    './utils/setTheme',
    './interfaces/iComponent', './interfaces/iInput'
], function(
    Utils,
    ComponentContainer, Element,
    setTheme,
    iComponent, iInput
){
    function focus(e){
        this.getComponent().addClass('jb-focus');
    }
    function blur(e){
        this.getComponent().removeClass('jb-focus');
    }
    function select(item, callTrigger){
        var selectedItem = this.get('selectedItem');
        if(selectedItem){
            if(selectedItem.value == item.value) return;
            selectedItem.button.removeClass('jb-selected');
        }

        item.button.addClass('jb-selected');
        this.set('selectedItem', item);

        if(callTrigger)
            this.emit('change', {
                'value': item.value,
                'title': item.title
            });

        this.emit('selectedInternally');
    }
    return ComponentContainer.extend(/** @lends ui/List# */{
        /**
         * List component class.
         * @constructs
         * @param {Object[]} items - Item list
         * @param {string} items[].title - Title of an item
         * @param {string|number} items[].value - Value of an item
         * @param {string} items[].type - Type of an item
         * @augments ui/ComponentContainer
         * @implements iInput
         * @implements iComponent
         */
        'init': function(items){
            /**
             * Change event.
             * @event ui/List.ui/List:change
             * @param {string|number} value - Selected items value.
             */
            /**
             * Event of internally selection. It fired not only when user select an item, but also when set a value.
             * @event ui/List.ui/List:selectedInternally
             */
            var component = Element.new('ul')
                .addClass('jb-list');
            this.super(component);

            this.handle('change');
            this.handle('selectedInternally');

            this.addClass('jb-list-container');
            this.setItems(items);
        },

        //Inherited from iComponent interface
        'setDisabled': function(value){
            this.getComponent().setClass('jb-disabled', value);
            Utils.each(this.get('options'), function(item){
                item.button.setAttr('disabled', value);
            });
            return this.ref;
        },
        //Inherited from iComponent interface
        'setTheme': setTheme,
        //Inherited from iComponent interface
        'focus': function(){
            this.get('firstItem').button.emitDom('focus');
            return this.ref;
        },

        //Inherited from iInput interface
        'getValue': function(){
            return this.get('selectedItem').value;
        },
        /**
         * Returns title of selected item.
         * @return {string} Title of selected item.
         */
        'getTitle': function(){
            return this.get('selectedItem').title;
        },
        //Inherited from iInput interface
        'setValue': function(value){
            var options = this.get('options');
            if(Utils.isUnset(options[value])){
                //TODO error
                throw 'There is no option has "' + value + '" value';
                return this.ref;
            }
            select.call(this, options[value]);
            return this.ref;
        },
        //Inherited from iInput interface
        'setDefaultValue': function(value){
            this.set('defaultValue', value);
            this.setValue(value);
            return this.ref;
        },
        //Inherited from iInput interface
        'resetValue': function(){
            this.setValue(this.get('defaultValue'));
            return this.ref;
        },

        /**
         * Sets items of list.
         * @param {Object[]} items - Item list
         * @param {string} items[].title - Title of an item
         * @param {string|number} items[].value - Value of an item
         * @param {string} items[].type - Type of an item
         * @return {Object} Instance reference.
         */
        'setItems': function(items){
            var options = {};
            this.set('options', options);

            var body = this.getComponent();
            var firstItem;
            Utils.each(items, function(item){
                if(item.type == 'SEPARATOR'){
                    var separator = Element.new('li')
                        .addClass('jb-list-separator');
                    if(Utils.isSet(item.title))
                        separator.add(
                            Element.new()
                                .addClass('jb-list-separator-title')
                                .add(item.title)
                        );
                    body.add(separator);
                    return;
                }

                body.add(
                    item.li = Element.new('li').add(
                        item.button = Element.new('button')
                            .addClass('jb-list-button')
                            .add(item.title)
                            .onDom('focus', focus.bind(this))
                            .onDom('blur', blur.bind(this))
                            .onDom('click', select.bind(this, item, true))
                    )
                );
                if(Utils.isSet(item.value))
                    if(Utils.isSet(options[item.value]))
                        //TODO error
                        throw 'Duplicate item value "' + item.value + '" with title "' + item.title + '" at List component.';
                    else
                        options[item.value] = item;
                    if(Utils.isUnset(firstItem)) firstItem = item;
            }, this);

            this.set('firstItem', firstItem);
            this.setDefaultValue(firstItem.value);

            return this.ref;
        },

        /**
         * Sets height to fix.
         * @param {string|number} Css value for height.
         * @return {Object} Instance reference.
         */
        'setHeight': function(height){
            this.getComponent().setStyle('height', height);
            return this.ref;
        }
    }).implement(iComponent, iInput)
})