Created
February 9, 2018 11:21
-
-
Save YuCJ/d0dfe979ab4c1de7fdfe64b609280520 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import $ from 'jquery' | |
import Util from './util' | |
/** | |
* -------------------------------------------------------------------------- | |
* Bootstrap (v4.0.0): collapse.js | |
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
* -------------------------------------------------------------------------- | |
*/ | |
const Collapse = (($) => { | |
/** | |
* ------------------------------------------------------------------------ | |
* Constants | |
* ------------------------------------------------------------------------ | |
*/ | |
const NAME = 'collapse' | |
const VERSION = '4.0.0' | |
const DATA_KEY = 'bs.collapse' | |
const EVENT_KEY = `.${DATA_KEY}` | |
const DATA_API_KEY = '.data-api' | |
const JQUERY_NO_CONFLICT = $.fn[NAME] | |
const TRANSITION_DURATION = 600 | |
const Default = { | |
toggle : true, | |
parent : '' | |
} | |
const DefaultType = { | |
toggle : 'boolean', | |
parent : '(string|element)' | |
} | |
const Event = { | |
SHOW : `show${EVENT_KEY}`, | |
SHOWN : `shown${EVENT_KEY}`, | |
HIDE : `hide${EVENT_KEY}`, | |
HIDDEN : `hidden${EVENT_KEY}`, | |
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` | |
} | |
const ClassName = { | |
SHOW : 'show', | |
COLLAPSE : 'collapse', | |
COLLAPSING : 'collapsing', | |
COLLAPSED : 'collapsed' | |
} | |
const Dimension = { | |
WIDTH : 'width', | |
HEIGHT : 'height' | |
} | |
const Selector = { | |
ACTIVES : '.show, .collapsing', | |
DATA_TOGGLE : '[data-toggle="collapse"]' | |
} | |
/** | |
* ------------------------------------------------------------------------ | |
* Class Definition | |
* ------------------------------------------------------------------------ | |
*/ | |
class Collapse { | |
constructor(element, config) { | |
this._isTransitioning = false | |
this._element = element | |
this._config = this._getConfig(config) | |
this._triggerArray = $.makeArray($( | |
`[data-toggle="collapse"][href="#${element.id}"],` + | |
`[data-toggle="collapse"][data-target="#${element.id}"]` | |
)) | |
const tabToggles = $(Selector.DATA_TOGGLE) | |
for (let i = 0; i < tabToggles.length; i++) { | |
const elem = tabToggles[i] | |
const selector = Util.getSelectorFromElement(elem) | |
if (selector !== null && $(selector).filter(element).length > 0) { | |
this._selector = selector | |
this._triggerArray.push(elem) | |
} | |
} | |
this._parent = this._config.parent ? this._getParent() : null | |
if (!this._config.parent) { | |
this._addAriaAndCollapsedClass(this._element, this._triggerArray) | |
} | |
if (this._config.toggle) { | |
this.toggle() | |
} | |
} | |
// Getters | |
static get VERSION() { | |
return VERSION | |
} | |
static get Default() { | |
return Default | |
} | |
// Public | |
toggle() { | |
if ($(this._element).hasClass(ClassName.SHOW)) { | |
this.hide() | |
} else { | |
this.show() | |
} | |
} | |
show() { | |
if (this._isTransitioning || | |
$(this._element).hasClass(ClassName.SHOW)) { | |
return | |
} | |
let actives | |
let activesData | |
if (this._parent) { | |
actives = $.makeArray( | |
$(this._parent) | |
.find(Selector.ACTIVES) | |
.filter(`[data-parent="${this._config.parent}"]`) | |
) | |
if (actives.length === 0) { | |
actives = null | |
} | |
} | |
if (actives) { | |
activesData = $(actives).not(this._selector).data(DATA_KEY) | |
if (activesData && activesData._isTransitioning) { | |
return | |
} | |
} | |
const startEvent = $.Event(Event.SHOW) | |
$(this._element).trigger(startEvent) | |
if (startEvent.isDefaultPrevented()) { | |
return | |
} | |
if (actives) { | |
Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide') | |
if (!activesData) { | |
$(actives).data(DATA_KEY, null) | |
} | |
} | |
const dimension = this._getDimension() | |
$(this._element) | |
.removeClass(ClassName.COLLAPSE) | |
.addClass(ClassName.COLLAPSING) | |
this._element.style[dimension] = 0 | |
if (this._triggerArray.length > 0) { | |
$(this._triggerArray) | |
.removeClass(ClassName.COLLAPSED) | |
.attr('aria-expanded', true) | |
} | |
this.setTransitioning(true) | |
const complete = () => { | |
$(this._element) | |
.removeClass(ClassName.COLLAPSING) | |
.addClass(ClassName.COLLAPSE) | |
.addClass(ClassName.SHOW) | |
this._element.style[dimension] = '' | |
this.setTransitioning(false) | |
$(this._element).trigger(Event.SHOWN) | |
} | |
if (!Util.supportsTransitionEnd()) { | |
complete() | |
return | |
} | |
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) | |
const scrollSize = `scroll${capitalizedDimension}` | |
$(this._element) | |
.one(Util.TRANSITION_END, complete) | |
.emulateTransitionEnd(TRANSITION_DURATION) | |
this._element.style[dimension] = `${this._element[scrollSize]}px` | |
} | |
hide() { | |
if (this._isTransitioning || | |
!$(this._element).hasClass(ClassName.SHOW)) { | |
return | |
} | |
const startEvent = $.Event(Event.HIDE) | |
$(this._element).trigger(startEvent) | |
if (startEvent.isDefaultPrevented()) { | |
return | |
} | |
const dimension = this._getDimension() | |
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px` | |
Util.reflow(this._element) | |
$(this._element) | |
.addClass(ClassName.COLLAPSING) | |
.removeClass(ClassName.COLLAPSE) | |
.removeClass(ClassName.SHOW) | |
if (this._triggerArray.length > 0) { | |
for (let i = 0; i < this._triggerArray.length; i++) { | |
const trigger = this._triggerArray[i] | |
const selector = Util.getSelectorFromElement(trigger) | |
if (selector !== null) { | |
const $elem = $(selector) | |
if (!$elem.hasClass(ClassName.SHOW)) { | |
$(trigger).addClass(ClassName.COLLAPSED) | |
.attr('aria-expanded', false) | |
} | |
} | |
} | |
} | |
this.setTransitioning(true) | |
const complete = () => { | |
this.setTransitioning(false) | |
$(this._element) | |
.removeClass(ClassName.COLLAPSING) | |
.addClass(ClassName.COLLAPSE) | |
.trigger(Event.HIDDEN) | |
} | |
this._element.style[dimension] = '' | |
if (!Util.supportsTransitionEnd()) { | |
complete() | |
return | |
} | |
$(this._element) | |
.one(Util.TRANSITION_END, complete) | |
.emulateTransitionEnd(TRANSITION_DURATION) | |
} | |
setTransitioning(isTransitioning) { | |
this._isTransitioning = isTransitioning | |
} | |
dispose() { | |
$.removeData(this._element, DATA_KEY) | |
this._config = null | |
this._parent = null | |
this._element = null | |
this._triggerArray = null | |
this._isTransitioning = null | |
} | |
// Private | |
_getConfig(config) { | |
config = { | |
...Default, | |
...config | |
} | |
config.toggle = Boolean(config.toggle) // Coerce string values | |
Util.typeCheckConfig(NAME, config, DefaultType) | |
return config | |
} | |
_getDimension() { | |
const hasWidth = $(this._element).hasClass(Dimension.WIDTH) | |
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT | |
} | |
_getParent() { | |
let parent = null | |
if (Util.isElement(this._config.parent)) { | |
parent = this._config.parent | |
// It's a jQuery object | |
if (typeof this._config.parent.jquery !== 'undefined') { | |
parent = this._config.parent[0] | |
} | |
} else { | |
parent = $(this._config.parent)[0] | |
} | |
const selector = | |
`[data-toggle="collapse"][data-parent="${this._config.parent}"]` | |
$(parent).find(selector).each((i, element) => { | |
this._addAriaAndCollapsedClass( | |
Collapse._getTargetFromElement(element), | |
[element] | |
) | |
}) | |
return parent | |
} | |
_addAriaAndCollapsedClass(element, triggerArray) { | |
if (element) { | |
const isOpen = $(element).hasClass(ClassName.SHOW) | |
if (triggerArray.length > 0) { | |
$(triggerArray) | |
.toggleClass(ClassName.COLLAPSED, !isOpen) | |
.attr('aria-expanded', isOpen) | |
} | |
} | |
} | |
// Static | |
static _getTargetFromElement(element) { | |
const selector = Util.getSelectorFromElement(element) | |
return selector ? $(selector)[0] : null | |
} | |
static _jQueryInterface(config) { | |
return this.each(function () { | |
const $this = $(this) | |
let data = $this.data(DATA_KEY) | |
const _config = { | |
...Default, | |
...$this.data(), | |
...typeof config === 'object' && config | |
} | |
if (!data && _config.toggle && /show|hide/.test(config)) { | |
_config.toggle = false | |
} | |
if (!data) { | |
data = new Collapse(this, _config) | |
$this.data(DATA_KEY, data) | |
} | |
if (typeof config === 'string') { | |
if (typeof data[config] === 'undefined') { | |
throw new TypeError(`No method named "${config}"`) | |
} | |
data[config]() | |
} | |
}) | |
} | |
} | |
/** | |
* ------------------------------------------------------------------------ | |
* Data Api implementation | |
* ------------------------------------------------------------------------ | |
*/ | |
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { | |
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element | |
if (event.currentTarget.tagName === 'A') { | |
event.preventDefault() | |
} | |
const $trigger = $(this) | |
const selector = Util.getSelectorFromElement(this) | |
$(selector).each(function () { | |
const $target = $(this) | |
const data = $target.data(DATA_KEY) | |
const config = data ? 'toggle' : $trigger.data() | |
Collapse._jQueryInterface.call($target, config) | |
}) | |
}) | |
/** | |
* ------------------------------------------------------------------------ | |
* jQuery | |
* ------------------------------------------------------------------------ | |
*/ | |
$.fn[NAME] = Collapse._jQueryInterface | |
$.fn[NAME].Constructor = Collapse | |
$.fn[NAME].noConflict = function () { | |
$.fn[NAME] = JQUERY_NO_CONFLICT | |
return Collapse._jQueryInterface | |
} | |
return Collapse | |
})($) | |
export default Collapse |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment