ui/panels/content-panel.directive.js

/* global TweenLite */

const templateUrl = require('./content-panel.html');

const HEADER_CLASS = '.av-header';
const FOOTER_CLASS = '.av-footer';
const SPACER_CLASS = '.av-spacer';
/**
 * @module avContentPanel
 * @memberof app.ui
 * @description
 *
 * The `avContentPanel` directive is a panel inner container holding the panel's content.
 *
 * `title-value` a string to be displayed in the pane's header; if ommited, the header is not shown
 * `title-style` sets the style of the pane's title; options: "headline", "title", "subhead"
 * `sub-title-value` a string to be displayed in the pane's header; if ommited, not shown; if the `title-value` is ommited, not shown;
 * `is-loading` a flag to show/hide the loading indicator
 * `hide-when-loading` if true, hides the content of the pane when the loading indicator is active
 * `header-controls` a list of directive names separated by ';' to be inserted into the header (extra controls like a menu for example)
 * `header-controls-scope` a scope to be passed to the header controls directives; if not supplied, the current scope of the panel is used
 * `footer` directive name to insert into the footer
 * `close-panel` a custom "close" function to call when the pane is closed
 * `static-content` makes main content section non-scrollable
 *
 * Usage example:
 * ```html
 * <av-content-panel
 *         title-value="Panel"
 *         title-style="title"
 *         sub-title-value="sub title"
 *         is-loading="true"
 *         hide-when-loading="true"
 *         header-controls="table-default-menu"
 *         header-controls-scope=""
 *         floating-header="true"
 *         footer=""
 *         close-panel=""
 *         static-content="false">
 *
 * </av-content-panel>
 * ```
 */
angular
    .module('app.ui')
    .directive('avContentPanel', avContentPanel);

function avContentPanel($compile) {
    const directive = {
        restrict: 'E',
        require: '?^avPanel', // require plug controller
        templateUrl,
        scope: {
            titleValue: '@?', // binds to the evaluated dom property
            titleStyle: '@?',
            subTitleValue: '@?',
            isLoading: '=?', // bind to a property
            hideWhenLoading: '=?',
            headerControls: '@?',
            headerControlsScope: '=?',
            floatingHeader: '=?',
            footer: '@?',
            closePanel: '&?', // https://docs.angularjs.org/api/ng/service/$compile
            staticContent: '=?'
        },
        transclude: true,
        link: link,
        controller: () => {},
        controllerAs: 'self',
        bindToController: true
    };

    return directive;

    /**
     * Sets defaults; binds the `closePanel` method from the panel plug controller; compiles footer and extra header controls.
     * @function link
     * @param {Object} scope Angular scope
     * @param {Object} element HTML element
     * @param {Object} attr Attributes
     * @param {Object} ctrl Angular controller
     */
    function link(scope, element, attr, ctrl) {
        const self = scope.self;

        // apply defaults
        self.isLoading = angular.isDefined(self.isLoading) ? self.isLoading : false;
        self.hideWhenLoading = angular.isDefined(self.hideWhenLoading) ? self.hideWhenLoading : true;
        self.staticContent = angular.isDefined(self.staticContent) ? self.staticContent : false;

        // first, try to used passed closePanel function; if not, use one on the parent panel controller, or nothing
        if (!self.closePanel && ctrl) {
            self.closePanel = ctrl.closePanel || undefined;
        }

        initHeaderControls();
        initFooter();

        function initHeaderControls() {
            // add header controls
            addHeaderControl(element, HEADER_CLASS);
        }

        /**
        * Add controls to panel header
        * @private
        * @function addHeaderControl
        * @param {Object} element panel header to add control to
        * @param {String} headerClass class use to find the element where to add the controls
        */
        function addHeaderControl(element, headerClass) {
            // `self.headerControls` is a string of directive names separated by ';' to be inserted in the content pane's header
            if (self.headerControls) {
                const headerSpacer = element.find(`${headerClass} ${SPACER_CLASS}`);

                self.headerControls.split(';')
                    .forEach(controlName => {
                        const controlElement =
                            $compile(`<${controlName}></${controlName}>`)(self.headerControlsScope || scope);
                        headerSpacer.after(controlElement);
                    });
            }
        }

        /**
        * Initialize footer
        * @private
        * @function initFooter
        */
        function initFooter() {
            // `self.footer` is a name string of a directive; if specified, directive is compiled and inserted into the pane template
            if (self.footer) {
                const footer = element.find(FOOTER_CLASS);

                const footerElement = $compile(`<${self.footer}></${self.footer}>`)(scope);
                footer.append(footerElement);
            }
        }
    }
}