Source: admin/javascript/engine/widgets/panel.js

/* --------------------------------------------------------------
 panel.js 2017-10-10
 Gambio GmbH
 http://www.gambio.de
 Copyright (c) 2017 Gambio GmbH
 Released under the GNU General Public License (Version 2)
 [http://www.gnu.org/licenses/gpl-2.0.html]
 --------------------------------------------------------------
 */

/**
 * ## Panel widget
 *
 * This widget creates a panel container and wraps the inner html inside the panel.
 * The user configuration service is used by this widget, so the state (whether collapsed or expanded) is stored
 * by user and will be used on future usages. To make this functionality possible, the options "user" and "section"
 * are required.
 *
 * ### Options (Required)
 *
 * **Title | `data-panel-title` | String | Required**
 *
 * Panels title value.
 *
 * ### Options (Additional)
 *
 * **Collapsed icon class | `data-panel-collapsed_icon_class` | String | Optional**
 *
 * Font awesome class for collapsed icon. If no value is provided, it defaults to **'fa fa-plus-square-o'**.
 *
 * **Expanded icon class | `data-panel-expanded_icon_class` | String | Optional**
 *
 * Font awesome class for expanded icon. If no value is provided, it defaults to **'fa fa-minus-square-o'**.
 *
 * **Container class | `data-panel-container_class` | String | Optional**
 *
 * Additional class attributes. The values will be append to the .panel element.
 *
 * **Toggle time | `data-panel-toggle_time` | String | Optional**
 *
 * Toggle time for collapsing/expanding the panel. If no value is provided, it defaults to **'200'**.
 *
 * **Collapsed | `data-panel-collapsed` | Boolean | Optional **
 *
 * Determines the panels default state. Collapsed if set to true and expanded otherwise. If no value is provided,
 * the user configuration will be used. If no user configuration was found, the default state is expanded.
 *
 * **User | `data-panel-user` | Integer | Optional**
 *
 * Customer id of user, used by user configuration service to store the collapsed state. This option should be set
 * with the "section" option and will be ignored, if the "collapsed" option is set.
 *
 * **Section | `data-panel-section` | String | Optional**
 *
 * Panel section, used by user configuration service 'configuration_key'. This option should be set with the "user"
 * option and will be ignored, if the "collapsed" option is set.
 *
 * ### Example
 *
 * ```html
 * <!-- usage of user configuration -->
 * <div data-gx-widget="panel"
 *      data-panel-title="Panel Title"
 *      data-panel-user="{$smarty.session.customer_id}"
 *      data-panel-section="panel-sample-section"
 *      data-panel-container_class="additional-sample-class">
 *     <div class="sample-class">
 *         <p>Sample Content</p>!
 *     </div>
 * </div>
 *
 * <!-- usage of collapsed option -->
 * <div data-gx-widget="panel"
 *      data-panel-title="Panel Title"
 *      data-panel-container_class="additional-sample-class"
 *      data-panel-collapsed="false|true">
 *     <div class="sample-class">
 *         <p>Sample Content</p>!
 *     </div>
 * </div>
 * ```
 *
 * @module Admin/Widgets/panel
 */
gx.widgets.module(
	'panel',
	
	// external libraries, used by widget
	['user_configuration_service'],
	
	function(data) {
		'use strict';
		
		/**
		 * Widget reference.
		 *
		 * @type {jQuery}
		 */
		const $this = $(this);
		
		/**
		 * Default options for widget,
		 *
		 * @type {object}
		 */
		const defaults = {
			collapsed_icon_class: 'fa fa-plus-square-o',
			expanded_icon_class: 'fa fa-minus-square-o',
			container_class: '',
			toggle_time: 200
		};
		
		/**
		 * Final widget options.
		 *
		 * @type {object}
		 */
		const options = $.extend(true, {}, defaults, data);
		
		/**
		 * Module object.
		 *
		 * @type {{}}
		 */
		const module = {};
		
		/**
		 * Required widget options, should passed from markup.
		 *
		 * @type {[string,string,string]}
		 */
		const requiredOptions = ['title', 'user', 'section'];
		
		/**
		 * User configuration service to store collapsed/expanded configuration.
		 *
		 * @type {jse.libs.user_configuration_service}
		 */
		const userConfig = jse.libs.user_configuration_service;
		
		/**
		 * Property like values.
		 */
		let $iconContainer;
		let $collapseIcon;
		let $panelBody;
		
		// private methods
		
		/**
		 * Widget initialization.
		 *
		 * @private
		 */
		const _init = () => {
			let collapsed;
			
			if (undefined !== options.collapsed) {
				_renderPanel(options.collapsed)
			} else {
				if (undefined === options.user || undefined === options.section) {
					throw new Error('Required widget options are not set. Set whether the "collapsed" option, or use '
						+ 'the user configuration service with the options "user" and "section".');
				}
				
				userConfig.get({
					data: {
						userId: options.user,
						configurationKey: options.section + '_collapsed'
					},
					onSuccess: result => {
						collapsed = result.length === 0 ? false : result.configurationValue === 'true';
						_renderPanel(collapsed);
					}
				});
			}
		};
		
		/**
		 * Checks if all required options are passed.
		 *
		 * @private
		 */
		const _checkRequiredOptions = () => {
			let i = 0;
			for (; i < requiredOptions.length; i++) {
				if (undefined === options[requiredOptions[i]]) {
					throw new Error('Required widget option "' + requiredOptions[i] + '" is no set!');
				}
			}
		};
		
		/**
		 * Renders the panel.
		 *
		 * @param {boolean} collapsed If true, the panel will be collapsed.
		 * @private
		 */
		const _renderPanel = collapsed => {
			const $panelHeading = $('<div/>', {'class': 'panel-heading'});
			const $panelTitle = $('<span/>', {
				'class': 'title',
				'text': options.title
			});
			
			$iconContainer = $('<span/>', {'class': 'collapser pull-right cursor-pointer'})
			$collapseIcon = $('<i/>', {
				'class': collapsed ? options.collapsed_icon_class : options.expanded_icon_class
			});
			$panelBody = $('<div/>', {'class': 'panel-body'});
			$this.children().detach().appendTo($panelBody);
			
			$this.addClass('panel panel-default ' + options.container_class);
			$panelHeading.append($panelTitle);
			$iconContainer.append($collapseIcon).appendTo($panelHeading);
			$this.append($panelHeading);
			
			if (collapsed) {
				$panelBody.css({'display': 'none'});
			}
			$this.append($panelBody);
			
			// set event handler
			$iconContainer.on('click', _toggle)
		};
		
		/**
		 * Toggle event listener for clicks on panel heading.
		 *
		 * @private
		 */
		const _toggle = () => {
			const isCollapsed = $collapseIcon.hasClass(options.expanded_icon_class);
			
			$collapseIcon.toggleClass(options.collapsed_icon_class);
			$collapseIcon.toggleClass(options.expanded_icon_class);
			$panelBody.toggle(options.toggle_time);
			
			userConfig.set({
				data: {
					userId: options.user,
					configurationKey: options.section + '_collapsed',
					configurationValue: isCollapsed
				}
			});
		};
		
		/**
		 * Widget initialization function.
		 *
		 * @param done
		 */
		module.init = done => {
			_checkRequiredOptions();
			_init();
			done();
		}
		
		return module;
	}
);