Source: components/sl-tab-panel.js

import Ember from 'ember';
import layout from '../templates/components/sl-tab-panel';
import { containsValue, warn } from '../utils/all';

/**
 * Valid `align` property values
 *
 * @memberof module:components/sl-tab-panel
 * @enum {String}
 */
const Alignment = Object.freeze({
    LEFT: 'left',
    RIGHT: 'right'
});
export { Alignment };

/**
 * @module
 * @augments ember/Component
 */
export default Ember.Component.extend({

    // -------------------------------------------------------------------------
    // Dependencies

    // -------------------------------------------------------------------------
    // Attributes

    /** @type {String[]} */
    classNameBindings: [
        'tabAlignmentClass'
    ],

    /** @type {String[]} */
    classNames: [
        'sl-tab-panel'
    ],

    /** @type {Object} */
    layout,

    // -------------------------------------------------------------------------
    // Actions

    /** @type {Object} */
    actions: {

        /**
         * Action to trigger when a tab is clicked
         *
         * @function actions:change
         * @param {String} tabName - The name of the tab to change into
         * @returns {undefined}
         */
        change( tabName ) {
            const activeTabName = this.get( 'activeTabName' );

            if ( activeTabName !== tabName ) {
                this.setActiveTab( tabName );
                this.deactivatePane( () => {
                    this.activatePane( tabName );
                });
            }
        }
    },

    // -------------------------------------------------------------------------
    // Events

    // -------------------------------------------------------------------------
    // Properties

    /**
     * The currently active tab name
     *
     * @type {?String}
     */
    activeTabName: null,

    /**
     * Determines the alignment of tabs at the top of the panel,
     * "left" or "right"
     *
     * @type {Alignment}
     */
    alignTabs: Alignment.LEFT,

    /**
     * The height of the tab-content in pixels
     *
     * @type {Number}
     */
    contentHeight: 0,

    /**
     * The name of the tab to open when the component is first rendered
     *
     * @type {?String}
     */
    initialTabName: null,

    // -------------------------------------------------------------------------
    // Observers

    /**
     * Sets up the initial tab, and parses the content of the tab panel to
     * determine tab labels and names.
     *
     * @function
     * @returns {undefined}
     */
    setupTabs: Ember.on(
        'didInsertElement',
        function() {
            let initialTabName = this.get( 'initialTabName' );
            const tabs = new Ember.A();

            if ( !initialTabName ) {
                initialTabName = this
                    .$( '.tab-pane:first' )
                    .attr( 'data-tab-name' );
            }

            this.setActiveTab( initialTabName );
            this.activatePane( initialTabName );

            this.$( '.tab-pane' ).each( function() {
                const tabName = this.getAttribute( 'data-tab-name' );

                tabs.push({
                    active: tabName === initialTabName,
                    label: this.getAttribute( 'data-tab-label' ),
                    name: tabName
                });
            });

            this.set( 'tabs', tabs );
        }
    ),

    /**
     * Sets the tab-content div height based on current contentHeight value
     *
     * @function
     * @returns {undefined}
     */
    updateContentHeight: Ember.observer(
        'contentHeight',
        function() {
            this.$( '.tab-content' ).height( this.get( 'contentHeight' ) );
        }
    ),

    // -------------------------------------------------------------------------
    // Methods

    /**
     * Activate a tab pane, animating the transition
     *
     * @function
     * @param {String} tabName - The name of the tab to activate
     * @returns {undefined}
     */
    activatePane( tabName ) {
        const pane = this.paneFor( tabName );

        pane.fadeIn( 'fast', function() {
            pane.addClass( 'active' );
        });

        this.set( 'activeTabName', tabName );
        this.set( 'contentHeight', parseInt( pane.css( 'height' ) ) );
    },

    /**
     * Deactivate a tab pane, animating the transition
     *
     * @function
     * @param {Function} callback - Function called when the pane is deactivated
     * @returns {undefined}
     */
    deactivatePane( callback ) {
        const pane = this.paneFor( this.get( 'activeTabName' ) );

        pane.fadeOut( 'fast', function() {
            pane.removeClass( 'active' );

            if ( 'function' === Ember.typeOf( callback ) ) {
                callback();
            }
        });
    },

    /**
     * Get the tab-panel's tab-pane for the specified tabName
     *
     * @function
     * @param {String} tabName - The name of the tab to get the pane for
     * @returns {jQuery.Object}
     */
    paneFor( tabName ) {
        return this.$( `.tab-pane[data-tab-name="${tabName}"]` );
    },

    /**
     * Update the internal active tab name and handle tabs' statuses
     *
     * @function
     * @param {String} tabName - The name of the tab to switch state to
     * @returns {undefined}
     */
    setActiveTab( tabName ) {
        const activeTabName = this.get( 'activeTabName' );

        this.tabFor( activeTabName ).removeClass( 'active' );
        this.tabFor( tabName ).addClass( 'active' );
    },

    /**
     * The class determining how to align tabs
     *
     * @function
     * @throws {ember.assert} Thrown if supplied `alignTabs` is a value not
     *         defined in enum Alignment
     * @returns {String}
     */
    tabAlignmentClass: Ember.computed(
        'alignTabs',
        function() {
            const alignTabs = this.get( 'alignTabs' );

            if ( !containsValue( alignTabs, Alignment ) ) {
                warn( `Invalid alignTabs property value "${alignTabs}"` );
            }

            return `sl-align-tabs-${alignTabs}`;
        }
    ),

    /**
     * Get the tab with the specified tabName
     *
     * @function
     * @param {String} tabName - The name for the tab to get
     * @returns {jQuery.Object} DOM Element
     */
    tabFor( tabName ) {
        return this.$( '.tab[data-tab-name="' + tabName + '"]' );
    }

});