From 8bab38bb7176d2716b72664dbafcc326824a2ee9 Mon Sep 17 00:00:00 2001 From: fat Date: Sat, 9 May 2015 23:00:59 -0700 Subject: add collapse --- js/src/collapse.js | 345 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 js/src/collapse.js (limited to 'js/src/collapse.js') diff --git a/js/src/collapse.js b/js/src/collapse.js new file mode 100644 index 000000000..00ae29097 --- /dev/null +++ b/js/src/collapse.js @@ -0,0 +1,345 @@ +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 JQUERY_NO_CONFLICT = $.fn[NAME] + const TRANSITION_DURATION = 600 + + const Defaults = { + toggle : true, + parent : null + } + + const Event = { + SHOW : 'show.bs.collapse', + SHOWN : 'shown.bs.collapse', + HIDE : 'hide.bs.collapse', + HIDDEN : 'hidden.bs.collapse', + CLICK : 'click.bs.collapse.data-api' + } + + const ClassName = { + IN : 'in', + COLLAPSE : 'collapse', + COLLAPSING : 'collapsing', + COLLAPSED : 'collapsed' + } + + const Dimension = { + WIDTH : 'width', + HEIGHT : 'height' + } + + const Selector = { + ACTIVES : '.panel > .in, .panel > .collapsing', + DATA_TOGGLE : '[data-toggle="collapse"]' + } + + + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + class Collapse { + + constructor(element, config) { + + this._isTransitioning = false + this._element = element + this._config = $.extend({}, Defaults, config) + this._triggerArray = $.makeArray($( + `[data-toggle="collapse"][href="#${element.id}"],` + + `[data-toggle="collapse"][data-target="#${element.id}"]` + )) + + this._parent = this._config.parent ? this._getParent() : null + + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._element, this._triggerArray) + } + + if (this._config.toggle) { + this.toggle() + } + + } + + // public + + toggle() { + if ($(this._element).hasClass(ClassName.IN)) { + this.hide() + } else { + this.show() + } + } + + show() { + if (this._isTransitioning || + $(this._element).hasClass(ClassName.IN)) { + return + } + + let activesData + let actives + + if (this._parent) { + actives = $.makeArray($(Selector.ACTIVES)) + if (!actives.length) { + actives = null + } + } + + if (actives) { + activesData = $(actives).data(DATA_KEY) + if (activesData && activesData._isTransitioning) { + return + } + } + + let startEvent = $.Event(Event.SHOW) + $(this._element).trigger(startEvent) + if (startEvent.isDefaultPrevented()) { + return + } + + if (actives) { + Collapse._jQueryInterface.call($(actives), 'hide') + if (!activesData) { + $(actives).data(DATA_KEY, null) + } + } + + let dimension = this._getDimension() + + $(this._element) + .removeClass(ClassName.COLLAPSE) + .addClass(ClassName.COLLAPSING) + + this._element.style[dimension] = 0 + this._element.setAttribute('aria-expanded', true) + + if (this._triggerArray.length) { + $(this._triggerArray) + .removeClass(ClassName.COLLAPSED) + .attr('aria-expanded', true) + } + + this.setTransitioning(true) + + let complete = () => { + $(this._element) + .removeClass(ClassName.COLLAPSING) + .addClass(ClassName.COLLAPSE) + .addClass(ClassName.IN) + + this._element.style[dimension] = '' + + this.setTransitioning(false) + + $(this._element).trigger(Event.SHOWN) + } + + if (!Util.supportsTransitionEnd()) { + complete() + return + } + + let scrollSize = 'scroll' + + (dimension[0].toUpperCase() + + dimension.slice(1)) + + $(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.IN)) { + return + } + + let startEvent = $.Event(Event.HIDE) + $(this._element).trigger(startEvent) + if (startEvent.isDefaultPrevented()) { + return + } + + let dimension = this._getDimension() + let offsetDimension = dimension === Dimension.WIDTH ? + 'offsetWidth' : 'offsetHeight' + + this._element.style[dimension] = this._element[offsetDimension] + 'px' + + Util.reflow(this._element) + + $(this._element) + .addClass(ClassName.COLLAPSING) + .removeClass(ClassName.COLLAPSE) + .removeClass(ClassName.IN) + + this._element.setAttribute('aria-expanded', false) + + if (this._triggerArray.length) { + $(this._triggerArray) + .addClass(ClassName.COLLAPSED) + .attr('aria-expanded', false) + } + + this.setTransitioning(true) + + let complete = () => { + this.setTransitioning(false) + $(this._element) + .removeClass(ClassName.COLLAPSING) + .addClass(ClassName.COLLAPSE) + .trigger(Event.HIDDEN) + } + + this._element.style[dimension] = 0 + + if (!Util.supportsTransitionEnd()) { + return complete() + } + + $(this._element) + .one(Util.TRANSITION_END, complete) + .emulateTransitionEnd(TRANSITION_DURATION) + } + + setTransitioning(isTransitioning) { + this._isTransitioning = isTransitioning + } + + + // private + + _getDimension() { + let hasWidth = $(this._element).hasClass(Dimension.WIDTH) + return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT + } + + _getParent() { + let parent = $(this._config.parent)[0] + let 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) { + let isOpen = $(element).hasClass(ClassName.IN) + element.setAttribute('aria-expanded', isOpen) + + if (triggerArray.length) { + $(triggerArray) + .toggleClass(ClassName.COLLAPSED, !isOpen) + .attr('aria-expanded', isOpen) + } + } + } + + + // static + + static _getTargetFromElement(element) { + let selector = Util.getSelectorFromElement(element) + return selector ? $(selector)[0] : null + } + + static _jQueryInterface(config) { + return this.each(function () { + let $this = $(this) + let data = $this.data(DATA_KEY) + let _config = $.extend( + {}, + Defaults, + $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') { + data[config]() + } + }) + } + + } + + + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + $(document).on(Event.CLICK, Selector.DATA_TOGGLE, function (event) { + event.preventDefault() + + let target = Collapse._getTargetFromElement(this) + + let data = $(target).data(DATA_KEY) + let config = data ? 'toggle' : $(this).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 + +})(jQuery) + +export default Collapse -- cgit v1.2.3 From ca9c850ebbfc880dc5021b451ffc9fa12184ff87 Mon Sep 17 00:00:00 2001 From: fat Date: Sun, 10 May 2015 19:45:38 -0700 Subject: add getters for Version and Default where applicable add modal my gawd --- js/src/collapse.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'js/src/collapse.js') diff --git a/js/src/collapse.js b/js/src/collapse.js index 00ae29097..ac506b86b 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -23,7 +23,7 @@ const Collapse = (($) => { const JQUERY_NO_CONFLICT = $.fn[NAME] const TRANSITION_DURATION = 600 - const Defaults = { + const Default = { toggle : true, parent : null } @@ -63,10 +63,9 @@ const Collapse = (($) => { class Collapse { constructor(element, config) { - this._isTransitioning = false this._element = element - this._config = $.extend({}, Defaults, config) + this._config = $.extend({}, Default, config) this._triggerArray = $.makeArray($( `[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][data-target="#${element.id}"]` @@ -81,9 +80,20 @@ const Collapse = (($) => { if (this._config.toggle) { this.toggle() } + } + + + // getters + static get VERSION() { + return VERSION } + static get Default() { + return Default + } + + // public toggle() { @@ -284,7 +294,7 @@ const Collapse = (($) => { let data = $this.data(DATA_KEY) let _config = $.extend( {}, - Defaults, + Default, $this.data(), typeof config === 'object' && config ) -- cgit v1.2.3 From f8b2569ec8956a1f4d09fe6fc9865bd200ecde43 Mon Sep 17 00:00:00 2001 From: fat Date: Wed, 13 May 2015 12:48:34 -0700 Subject: implement global dispose method --- js/src/collapse.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'js/src/collapse.js') diff --git a/js/src/collapse.js b/js/src/collapse.js index ac506b86b..ded623448 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -20,6 +20,8 @@ const Collapse = (($) => { 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 @@ -29,11 +31,11 @@ const Collapse = (($) => { } const Event = { - SHOW : 'show.bs.collapse', - SHOWN : 'shown.bs.collapse', - HIDE : 'hide.bs.collapse', - HIDDEN : 'hidden.bs.collapse', - CLICK : 'click.bs.collapse.data-api' + 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 = { @@ -110,8 +112,8 @@ const Collapse = (($) => { return } - let activesData let actives + let activesData if (this._parent) { actives = $.makeArray($(Selector.ACTIVES)) @@ -244,6 +246,16 @@ const Collapse = (($) => { 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 @@ -323,7 +335,7 @@ const Collapse = (($) => { * ------------------------------------------------------------------------ */ - $(document).on(Event.CLICK, Selector.DATA_TOGGLE, function (event) { + $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { event.preventDefault() let target = Collapse._getTargetFromElement(this) -- cgit v1.2.3 From eaab1def7af7d7e1ab32ff69d043b46e2815ca22 Mon Sep 17 00:00:00 2001 From: fat Date: Wed, 13 May 2015 14:46:50 -0700 Subject: add simple type checker implementation --- js/src/collapse.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'js/src/collapse.js') diff --git a/js/src/collapse.js b/js/src/collapse.js index ded623448..6a5fcd854 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -30,6 +30,11 @@ const Collapse = (($) => { parent : null } + const DefaultType = { + toggle : 'boolean', + parent : '(string|null)' + } + const Event = { SHOW : `show${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`, @@ -67,7 +72,7 @@ const Collapse = (($) => { constructor(element, config) { this._isTransitioning = false this._element = element - this._config = $.extend({}, Default, config) + this._config = this._getConfig(config) this._triggerArray = $.makeArray($( `[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][data-target="#${element.id}"]` @@ -259,6 +264,13 @@ const Collapse = (($) => { // private + _getConfig(config) { + config = $.extend({}, Default, config) + config.toggle = !!config.toggle // coerce string values + Util.typeCheckConfig(NAME, config, DefaultType) + return config + } + _getDimension() { let hasWidth = $(this._element).hasClass(Dimension.WIDTH) return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT -- cgit v1.2.3 From 6b2b0ed32f485103f58fe42057e93a175e14bc2a Mon Sep 17 00:00:00 2001 From: fat Date: Wed, 13 May 2015 14:52:46 -0700 Subject: al tests passing, dist rebuilt, w/typechecker --- js/src/collapse.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/src/collapse.js') diff --git a/js/src/collapse.js b/js/src/collapse.js index 6a5fcd854..e911c98d1 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -27,12 +27,12 @@ const Collapse = (($) => { const Default = { toggle : true, - parent : null + parent : '' } const DefaultType = { toggle : 'boolean', - parent : '(string|null)' + parent : 'string' } const Event = { -- cgit v1.2.3