COMPONENT

C-TOGGLER

Demo Section

Each variation will be presented in the following sections.

Default

Lorem ipsum dolor sit amet, consectetur adipisicing elit. A culpa deleniti distinctio eligendi enim explicabo, incidunt laborum laudantium magnam minus mollitia, perspiciatis porro quibusdam quis ratione reiciendis sapiente temporibus vel. Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusamus commodi corporis dicta excepturi, harum id, magnam maxime minima molestias nam nostrum perspiciatis possimus quaerat quis rerum suscipit ut, voluptate. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias aliquam aperiam consectetur culpa dolor dolorem esse in, libero magnam maiores, maxime perspiciatis quasi quod quos ratione sed tempore tenetur totam.

Technical Details

toggler

Represents a simple toggler with global event binding.


Requirements

  • Veams >= v5.0.0 - Veams Framework.

Installation

Installation with Veams

veams install vc toggler

Installation with Bower

bower install veams-component-toggler --save


SASS

Variables

  • $toggler-animation-duration-std {String} [‘200ms’];
  • $toggler-animation-easing-std {Function} [ease-in-out];

Fields

toggler

Settings

  • settings.togglerContextClasses {String} [default] - Context class.
  • settings.togglerClasses {String} - Modifier classes.
  • settings.togglerJsOptions {Object} - Options object which gets stringified.
  • settings.togglerJsModule {Boolean} - _Specify if component is a Javascript module or not.
  • settings.togglerJsModuleWithContext {String} - Reference to specific Javascript module toggler context.
  • settings.togglerId {String} - Id to reference specific toggler component instance.
  • settings.attributes {Array} - List of attributes that consist of name value pairs.

Content

  • content.togglerField {String} - Add description.

JavaScript Options

The module gives you the possibility to override default options:

  • a11yFocusKeyClass {String} [‘a11y-focus-key’] - Class for the accessibility focus key.
  • calculatingClass {String} [‘is-calculating’] - Class used to display calculating state.
  • closeClass {String} [‘is-closed’] - Class when toggler is closed.
  • context {Boolean} [false] - Context property that gets passed to toggler open event.
  • dataMaxAttr {String} [‘data-js-height’] - Dynamic max height attribute.
  • globalEvent {String} [’’] - Reference to global event that when triggered calls toggle method.
  • globalEventId {String} [’’] - Compare toggler’s globaleventid with the globaleventid of the object that triggered the toggle method to determine if toggle should be run or aborted.
  • openClass {String} [‘is-open’] - Class when toggler is open.
  • setOverflow {Boolean} [false] - Specify if overflow should be set or not.
  • toggleTabindexElems {String} [’’] - Selector that targets elements to toggle tab-index.

{
	"variations": {
		"default": {
			"docs": {
				"variationName": "Default",
				"sectionCenter": true
			},
			"settings": {
				"togglerContextClass": "default",
				"togglerClasses": false,
				"togglerJsModule": true,
				"togglerJsOptions": {
					"globalEvent": "toggler:toggle"
				}
			},
			"content": {}
		}
	}
}

c-toggler-usage

{{#parseJSON '{"settings": {
				"ctaButton": true,
				"ctaTarget": "#",
				"ctaContextClass": "default",
				"ctaClass": "class",
				"ctaJsAtom": false,
				"ctaJsModule": true,
				"jsOptions": {
					"globalEvent": "toggler:toggle",
					"data": "My custom text passed by CTA"
				}
			}}'}}

	{{#with @root.cta-bp.variations.withJs}}
		{{#wrapWith "c-cta" settings=../settings content=this.content}}
			{{> c-cta__content this.content}}
		{{/wrapWith}}
	{{/with}}
{{/parseJSON}}

{{! wrapWith START: Slider }}
{{#wrapWith "c-toggler" settings=this.settings content=this.content}}
	Lorem ipsum dolor sit amet, consectetur adipisicing elit. A culpa deleniti distinctio eligendi enim explicabo, incidunt laborum laudantium magnam minus mollitia, perspiciatis porro quibusdam quis ratione reiciendis sapiente temporibus vel. Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusamus commodi corporis dicta excepturi, harum id, magnam maxime minima molestias nam nostrum perspiciatis possimus quaerat quis rerum suscipit ut, voluptate. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias aliquam aperiam consectetur culpa dolor dolorem esse in, libero magnam maiores, maxime perspiciatis quasi quod quos ratione sed tempore tenetur totam.
{{/wrapWith}}
{{! wrapWith END: Slider}}
/* ===================================================
Component: toggler
=================================================== */

/* ---------------------------------------------------
Global Variables
--------------------------------------------------- */
$toggler-animation-duration-std: 500ms;
$toggler-animation-easing-std: ease-in-out;

/* ---------------------------------------------------
Global Styles
--------------------------------------------------- */
[data-css="c-toggler"] {
	height: 0;
	opacity: 0;
	visibility: hidden;
	overflow: hidden;
	transition: opacity $toggler-animation-duration-std $toggler-animation-easing-std, height $toggler-animation-duration-std $toggler-animation-easing-std, visibility 0ms linear $toggler-animation-duration-std;

	&.is-open {
		opacity: 1;
		visibility: visible;
		z-index: 1;
		transition: opacity $toggler-animation-duration-std $toggler-animation-easing-std, height $toggler-animation-duration-std $toggler-animation-easing-std, visibility 0ms linear;
	}

	// !IMPORTANT for calculation
	&.is-calculating {
		position: absolute !important;
		visibility: hidden !important;
		display: block !important;
		height: auto !important;
	}
}

/* ---------------------------------------------------
Context: Default
--------------------------------------------------- */
.c-toggler--default {
}

toggler.js

/**
 * Represents a simple toggler with global event binding.
 *
 * @module Toggler
 * @version v3.0.3
 *
 * @author Andy Gutsche
 */

// Global dependencies
import {Veams} from 'app';

import VeamsComponent from 'veams/src/js/common/component';

const $ = Veams.$;
const Helpers = Veams.helpers;

class Toggler extends VeamsComponent {


	/**
	 * Constructor for our class
	 *
	 * @see module.js
	 *
	 * @param {Object} obj - Object which is passed to our class
	 * @param {Object} obj.el - element which will be saved in this.el
	 * @param {Object} obj.options - options which will be passed in as JSON object
	 */
	constructor(obj) {

		let options = {
			a11yFocusKeyClass: 'a11y-focus-key',
			calculatingClass: 'is-calculating',
			closeClass: 'is-closed',
			context: false,
			dataMaxAttr: 'data-js-height',
			globalEvent: '',
			globalEventId: '',
			openClass: 'is-open',
			setOverflow: false,
			toggleTabindexElems: ''
		};

		super(obj, options);
	}


	/**
	 * Get module information
	 */
	static get info() {
		return {
			version: '3.0.3',
			vc: true,
			mod: false // set to true if source was modified in project
		};
	}


	get height() {
		return this._height;
	}


	set height(height) {
		this._height = height;
	}


	get isOpen() {
		return this._isOpen;
	}


	set isOpen(bool) {
		this._isOpen = bool;
	}


	/**
	 * Get global events
	 *
	 */
	get subscribe() {

		return {
			'{{Veams.EVENTS.resize}}': 'onResize'
		};
	}


	/**
	 * Initialize the view and merge options
	 *
	 */
	initialize() {
		let selfInit = this.$el.attr('data-js-module') && this.$el.attr('data-js-module').indexOf('toggler') > -1;

		if (selfInit && !this.options.globalEvent) {
			console.info('Toggler: this.options.globalEvent not set.');
		}

		this.isOpen = this.$el.hasClass(this.options.openClass);

		this.calculateHeight().then(() => {

			if (!this.isOpen) {
				this.setHeight(0);
			}
		});
	}


	/**
	 * Bind events
	 *
	 * Listen to open and close events
	 */
	bindEvents() {

		// Global events
		if (this.options.globalEvent) {
			this.registerEvent('{{this.options.globalEvent}}', 'toggle', true);
		}
	}

	/**
	 * handle on resize event
	 *
	 * close the toggler
	 *
	 */
	onResize() {

		// give browser some tie to recalculate
		setTimeout(() => {

			this.calculateHeight().then(() => {
				this.setHeight();
			});
		}, 200);
	}


	/**
	 * Enable calc mode.
	 *
	 * @private
	 */
	enableCalcMode() {

		if (!this.isOpen) {
			this.$el.addClass(this.options.openClass);
			this.$el.removeClass(this.options.closeClass);
		}

		this.$el.addClass(this.options.calculatingClass);
	}


	/**
	 * Disable calc mode.
	 *
	 * @private
	 */
	disableCalcMode() {

		this.$el.removeClass(this.options.calculatingClass);

		if (!this.isOpen) {
			this.$el.addClass(this.options.closeClass);
			this.$el.removeClass(this.options.openClass);
		}
		else {
			this.setHeight();
		}
	}


	/**
	 * Set height of current view element to given value or latest calculated value.
	 *
	 * @private
	 * @param {Number} [height] - height
	 */
	setHeight(height) {
		this.$el.css('height',
			typeof height === 'number' ? height + 'px' : this.$el.attr(this.options.dataMaxAttr) + 'px');
	}


	/**
	 * Calc the height of current view element.
	 *
	 * @private
	 */
	calcHeight() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				let wantedHeight = this.$el.outerHeight();

				this.$el.attr(this.options.dataMaxAttr, wantedHeight);
				this.height = wantedHeight !== this.height ? wantedHeight : this.height;

				resolve();
			}, 10);
		});
	}


	/**
	 * Save all styles from current view element
	 *
	 * @private
	 */
	saveStyles() {
		this.savedStyles = this.$el.attr('style');
	}


	/**
	 * Restore all styles from current view element
	 *
	 * @private
	 */
	restoreStyles() {
		this.$el.attr('style', this.savedStyles);
		delete this.savedStyles;
	}


	/**
	 * Toggles content
	 *
	 * @public
	 *
	 * @param {Object} obj - the event data
	 * @param {Boolean} obj.isActive - indicates if panel should open or close itself
	 * @param {String} obj.options.setFocus - element to set focus on open
	 */
	toggle(obj) {

		// if globalEventId is set on both (cta and toggler)
		if (this.options.globalEventId && obj.options && obj.options.globalEventId) {

			// stop here if global event id don't match
			if (this.options.globalEventId !== obj.options.globalEventId) {
				return;
			}
		}


		if (obj.isActive) {
			this.open(obj);
		}
		else {
			this.close();
		}
	}


	/**
	 * Open current view element
	 *
	 * @public
	 *
	 * @param {Object} [obj] - the event object
	 * @param {Boolean} [obj.isActive] - indicates if panel should open or close itself
	 * @param {String} [obj.options.setFocus] - element to set focus on open
	 */
	open(obj) {
		this.$el.css('height', this.$el.attr(this.options.dataMaxAttr) + 'px')
			.attr('aria-hidden', false)
			.removeClass(this.options.closeClass)
			.addClass(this.options.openClass);

		if (obj && obj.focusEl) {

			this.$el.on(Helpers.transitionEndEvent(), () => {
				obj.focusEl.focus();
				this.$el.off(Helpers.transitionEndEvent());
			});
		}

		Veams.Vent.trigger(Veams.EVENTS.toggler.open, {
			context: this.options.context
		});

		if (this.options.setOverflow) {

			this.$el.on(Helpers.transitionEndEvent(), () => {
				this.$el.css('overflow', 'visible');
				this.$el.off(Helpers.transitionEndEvent());
			});
		}

		if (this.options.toggleTabindexElems) {
			$(this.options.toggleTabindexElems, this.el).attr('tabindex', 0);
		}

		this.isOpen = true;
	}


	/**
	 * Close current view element
	 *
	 * @public
	 */
	close() {
		this.$el.css('height', 0)
			.removeAttr('style')
			.attr('aria-hidden', 'true')
			.removeClass(this.options.openClass)
			.addClass(this.options.closeClass);

		if (this.options.setOverflow) {
			this.$el.css('overflow', 'hidden');
		}

		if (this.options.toggleTabindexElems) {
			$(this.options.toggleTabindexElems, this.el).attr('tabindex', -1);
		}

		this.isOpen = false;
	}


	/**
	 * calculateHeight class
	 */
	calculateHeight() {
		return new Promise((resolve, reject) => {
			if (this.el && this.el.hasAttribute('style')) {
				this.saveStyles();
			}

			this.enableCalcMode(true);

			this.calcHeight().then(() => {
				if (this.savedStyles) {
					this.restoreStyles();
				}

				this.disableCalcMode();

				resolve();
			});
		});
	}
}

export default Toggler;

Default

<button class="c-cta--default class" data-css="c-cta" href="#" data-js-module="cta" data-js-options='{&quot;globalEvent&quot;:&quot;toggler:toggle&quot;,&quot;data&quot;:&quot;My custom text passed by CTA&quot;}'>
					<span class="cta__icon"></span>
			<span class="cta__content">Button</span>
</button>
<div class="c-toggler--default" data-css="c-toggler" data-js-module="toggler" data-js-options='{&quot;globalEvent&quot;:&quot;toggler:toggle&quot;}'>
	<div class="toggler__wrapper">
		Lorem ipsum dolor sit amet, consectetur adipisicing elit. A culpa deleniti distinctio eligendi enim explicabo, incidunt laborum laudantium magnam minus mollitia, perspiciatis porro quibusdam quis ratione reiciendis sapiente temporibus vel. Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusamus commodi corporis dicta excepturi, harum id, magnam maxime minima molestias nam nostrum perspiciatis possimus quaerat quis rerum suscipit ut, voluptate. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias aliquam aperiam consectetur culpa dolor dolorem esse in, libero magnam maiores, maxime perspiciatis quasi quod quos ratione sed tempore tenetur totam.
	</div>
</div>