UNPKG

avvo-styleguide

Version:
281 lines (221 loc) 7.32 kB
/* ======================================================================== * Avvo UI - collapse.js * ======================================================================== * Forked from Bootstrap collapse.js v3.3.1 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ const $ = global.jQuery // COLLAPSE PUBLIC CLASS DEFINITION // ================================ const Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$toggler = $(this.options.toggler).filter(`[href="#${element.id}"], [data-target="#${element.id}"]`) this.transitioning = null this.togglerContentCollapsed = null this.togglerContentExpanded = null this.setTogglerStates() if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$toggler) } if (this.options.toggle) { this.toggle() } } Collapse.VERSION = '4.0.0' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { parent: null, toggle: true, toggler: '[data-toggle="collapse"]', toggledText: null, } function getTargetFromToggler($toggler) { const target = $toggler.attr('data-target') || $toggler.attr('href') return $(target) } function isExpanded($element) { return $element.hasClass('in') } function forceRedraw(el) { // Reading "offsetHeight" forces the browser to redraw the element. return el.offsetHeight } Collapse.prototype.dimension = function () { const hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || isExpanded(this.$element)) { return } let activesData const $actives = this.$parent && this.$parent.find('.in, .collapsing') if ($actives && $actives.length) { activesData = $actives.data('ui.collapse') if (activesData && activesData.transitioning) { return } } const startEvent = $.Event('show.ui.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) { return } if ($actives && $actives.length) { Plugin.call($actives, 'hide') if (!activesData) { $actives.data('ui.collapse', null) } } const dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing') [dimension](0) .attr('aria-expanded', true) this.$toggler .html(this.togglerContentExpanded) .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 const complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in') [dimension]('') this.transitioning = 0 this.$element.trigger('shown.ui.collapse') } if (!$.support.transition) { return complete.call(this) } const scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('uiTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !isExpanded(this.$element)) { return } const startEvent = $.Event('hide.ui.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) { return } const dimension = this.dimension() // Hardcoding the element's dimension ensures there's a value to animate this.$element[dimension](this.$element[dimension]()) forceRedraw(this.$element[0]) this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$toggler .html(this.togglerContentCollapsed) .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 const complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.ui.collapse') } if (!$.support.transition) { return complete.call(this) } this.$element[dimension](0) .one('uiTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { const action = isExpanded(this.$element) ? 'hide' : 'show' this[action]() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find(`[data-toggle="collapse"][data-parent="${this.options.parent}"]`) .each($.proxy(function (i, element) { const $element = $(element) this.addAriaAndCollapsedClass(getTargetFromToggler($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $toggler) { const isOpen = isExpanded($element) $element.attr('aria-expanded', isOpen) $toggler .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } Collapse.prototype.setTogglerStates = function () { const isCollapsed = !isExpanded(this.$element) const current = isCollapsed ? 'togglerContentCollapsed' : 'togglerContentExpanded' const toggled = isCollapsed ? 'togglerContentExpanded' : 'togglerContentCollapsed' this[current] = this.$toggler.html() this[toggled] = this.getToggledTogglerContent() } Collapse.prototype.getToggledTogglerContent = function () { if (!this.$toggler.html()) return '' if (this.options.toggledText) return this.options.toggledText if (!isExpanded(this.$element)) { return this.$toggler.html() .replace('More', 'Less') .replace('more', 'less') .replace('chevron-down', 'chevron-up') } return this.$toggler.html() .replace('Less', 'More') .replace('less', 'more') .replace('chevron-up', 'chevron-down') } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { const $this = $(this) let data = $this.data('ui.collapse') const options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option === 'object' && option) if (!data && options.toggle && option === 'show') { options.toggle = false } if (!data) { data = new Collapse(this, options) $this.data('ui.collapse', data) } if (typeof option === 'string') { data[option]() } }) } export function init() { const old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.ui.collapse.data-api', '[data-toggle="collapse"]', function (event) { const $this = $(this) if (!$this.attr('data-target')) { event.preventDefault() } const $target = getTargetFromToggler($this) const data = $target.data('ui.collapse') const option = data ? 'toggle' : $.extend({}, $this.data(), { toggler: this }) Plugin.call($target, option) }) }