1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-08-16 18:44:01 +02:00

remove useless iife

This commit is contained in:
Johann-S
2018-09-26 10:39:01 +02:00
parent 02c4444f6c
commit 7c7a1706c3
12 changed files with 3433 additions and 3475 deletions

View File

@@ -8,176 +8,172 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Alert = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'alert' const NAME = 'alert'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.alert' const DATA_KEY = 'bs.alert'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Selector = { const Selector = {
DISMISS : '[data-dismiss="alert"]' DISMISS : '[data-dismiss="alert"]'
}
const Event = {
CLOSE : `close${EVENT_KEY}`,
CLOSED : `closed${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
ALERT : 'alert',
FADE : 'fade',
SHOW : 'show'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Alert {
constructor(element) {
this._element = element
} }
const Event = { // Getters
CLOSE : `close${EVENT_KEY}`,
CLOSED : `closed${EVENT_KEY}`, static get VERSION() {
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` return VERSION
} }
const ClassName = { // Public
ALERT : 'alert',
FADE : 'fade', close(element) {
SHOW : 'show' let rootElement = this._element
if (element) {
rootElement = this._getRootElement(element)
}
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
} }
/** dispose() {
* ------------------------------------------------------------------------ $.removeData(this._element, DATA_KEY)
* Class Definition this._element = null
* ------------------------------------------------------------------------
*/
class Alert {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
close(element) {
let rootElement = this._element
if (element) {
rootElement = this._getRootElement(element)
}
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_getRootElement(element) {
const selector = Util.getSelectorFromElement(element)
let parent = false
if (selector) {
parent = document.querySelector(selector)
}
if (!parent) {
parent = $(element).closest(`.${ClassName.ALERT}`)[0]
}
return parent
}
_triggerCloseEvent(element) {
const closeEvent = $.Event(Event.CLOSE)
$(element).trigger(closeEvent)
return closeEvent
}
_removeElement(element) {
$(element).removeClass(ClassName.SHOW)
if (!$(element).hasClass(ClassName.FADE)) {
this._destroyElement(element)
return
}
const transitionDuration = Util.getTransitionDurationFromElement(element)
$(element)
.one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
.emulateTransitionEnd(transitionDuration)
}
_destroyElement(element) {
$(element)
.detach()
.trigger(Event.CLOSED)
.remove()
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
}
if (config === 'close') {
data[config](this)
}
})
}
static _handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
alertInstance.close(this)
}
}
} }
/** // Private
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on( _getRootElement(element) {
Event.CLICK_DATA_API, const selector = Util.getSelectorFromElement(element)
Selector.DISMISS, let parent = false
Alert._handleDismiss(new Alert())
)
/** if (selector) {
* ------------------------------------------------------------------------ parent = document.querySelector(selector)
* jQuery }
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Alert._jQueryInterface if (!parent) {
$.fn[NAME].Constructor = Alert parent = $(element).closest(`.${ClassName.ALERT}`)[0]
$.fn[NAME].noConflict = () => { }
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface return parent
} }
return Alert _triggerCloseEvent(element) {
})($) const closeEvent = $.Event(Event.CLOSE)
$(element).trigger(closeEvent)
return closeEvent
}
_removeElement(element) {
$(element).removeClass(ClassName.SHOW)
if (!$(element).hasClass(ClassName.FADE)) {
this._destroyElement(element)
return
}
const transitionDuration = Util.getTransitionDurationFromElement(element)
$(element)
.one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
.emulateTransitionEnd(transitionDuration)
}
_destroyElement(element) {
$(element)
.detach()
.trigger(Event.CLOSED)
.remove()
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
}
if (config === 'close') {
data[config](this)
}
})
}
static _handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
alertInstance.close(this)
}
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
Event.CLICK_DATA_API,
Selector.DISMISS,
Alert._handleDismiss(new Alert())
)
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface
}
export default Alert export default Alert

View File

@@ -7,169 +7,165 @@ import $ from 'jquery'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Button = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'button' const NAME = 'button'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.button' const DATA_KEY = 'bs.button'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const ClassName = { const ClassName = {
ACTIVE : 'active', ACTIVE : 'active',
BUTTON : 'btn', BUTTON : 'btn',
FOCUS : 'focus' FOCUS : 'focus'
}
const Selector = {
DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
DATA_TOGGLE : '[data-toggle="buttons"]',
INPUT : 'input',
ACTIVE : '.active',
BUTTON : '.btn'
}
const Event = {
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +
`blur${EVENT_KEY}${DATA_API_KEY}`
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Button {
constructor(element) {
this._element = element
} }
const Selector = { // Getters
DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
DATA_TOGGLE : '[data-toggle="buttons"]', static get VERSION() {
INPUT : 'input', return VERSION
ACTIVE : '.active',
BUTTON : '.btn'
} }
const Event = { // Public
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +
`blur${EVENT_KEY}${DATA_API_KEY}`
}
/** toggle() {
* ------------------------------------------------------------------------ let triggerChangeEvent = true
* Class Definition let addAriaPressed = true
* ------------------------------------------------------------------------ const rootElement = $(this._element).closest(
*/ Selector.DATA_TOGGLE
)[0]
class Button { if (rootElement) {
constructor(element) { const input = this._element.querySelector(Selector.INPUT)
this._element = element
}
// Getters if (input) {
if (input.type === 'radio') {
if (input.checked &&
this._element.classList.contains(ClassName.ACTIVE)) {
triggerChangeEvent = false
} else {
const activeElement = rootElement.querySelector(Selector.ACTIVE)
static get VERSION() { if (activeElement) {
return VERSION $(activeElement).removeClass(ClassName.ACTIVE)
}
// Public
toggle() {
let triggerChangeEvent = true
let addAriaPressed = true
const rootElement = $(this._element).closest(
Selector.DATA_TOGGLE
)[0]
if (rootElement) {
const input = this._element.querySelector(Selector.INPUT)
if (input) {
if (input.type === 'radio') {
if (input.checked &&
this._element.classList.contains(ClassName.ACTIVE)) {
triggerChangeEvent = false
} else {
const activeElement = rootElement.querySelector(Selector.ACTIVE)
if (activeElement) {
$(activeElement).removeClass(ClassName.ACTIVE)
}
} }
} }
}
if (triggerChangeEvent) { if (triggerChangeEvent) {
if (input.hasAttribute('disabled') || if (input.hasAttribute('disabled') ||
rootElement.hasAttribute('disabled') || rootElement.hasAttribute('disabled') ||
input.classList.contains('disabled') || input.classList.contains('disabled') ||
rootElement.classList.contains('disabled')) { rootElement.classList.contains('disabled')) {
return return
}
input.checked = !this._element.classList.contains(ClassName.ACTIVE)
$(input).trigger('change')
} }
input.checked = !this._element.classList.contains(ClassName.ACTIVE)
input.focus() $(input).trigger('change')
addAriaPressed = false
} }
}
if (addAriaPressed) { input.focus()
this._element.setAttribute('aria-pressed', addAriaPressed = false
!this._element.classList.contains(ClassName.ACTIVE))
}
if (triggerChangeEvent) {
$(this._element).toggleClass(ClassName.ACTIVE)
} }
} }
dispose() { if (addAriaPressed) {
$.removeData(this._element, DATA_KEY) this._element.setAttribute('aria-pressed',
this._element = null !this._element.classList.contains(ClassName.ACTIVE))
} }
// Static if (triggerChangeEvent) {
$(this._element).toggleClass(ClassName.ACTIVE)
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
if (!data) {
data = new Button(this)
$(this).data(DATA_KEY, data)
}
if (config === 'toggle') {
data[config]()
}
})
} }
} }
/** dispose() {
* ------------------------------------------------------------------------ $.removeData(this._element, DATA_KEY)
* Data Api implementation this._element = null
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
event.preventDefault()
let button = event.target
if (!$(button).hasClass(ClassName.BUTTON)) {
button = $(button).closest(Selector.BUTTON)
}
Button._jQueryInterface.call($(button), 'toggle')
})
.on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
const button = $(event.target).closest(Selector.BUTTON)[0]
$(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Button._jQueryInterface
$.fn[NAME].Constructor = Button
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Button._jQueryInterface
} }
return Button // Static
})($)
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
if (!data) {
data = new Button(this)
$(this).data(DATA_KEY, data)
}
if (config === 'toggle') {
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
event.preventDefault()
let button = event.target
if (!$(button).hasClass(ClassName.BUTTON)) {
button = $(button).closest(Selector.BUTTON)
}
Button._jQueryInterface.call($(button), 'toggle')
})
.on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
const button = $(event.target).closest(Selector.BUTTON)[0]
$(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Button._jQueryInterface
$.fn[NAME].Constructor = Button
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Button._jQueryInterface
}
export default Button export default Button

View File

@@ -8,521 +8,517 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Carousel = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'carousel' const NAME = 'carousel'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.carousel' const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const Default = { const Default = {
interval : 5000, interval : 5000,
keyboard : true, keyboard : true,
slide : false, slide : false,
pause : 'hover', pause : 'hover',
wrap : true wrap : true
}
const DefaultType = {
interval : '(number|boolean)',
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
wrap : 'boolean'
}
const Direction = {
NEXT : 'next',
PREV : 'prev',
LEFT : 'left',
RIGHT : 'right'
}
const Event = {
SLIDE : `slide${EVENT_KEY}`,
SLID : `slid${EVENT_KEY}`,
KEYDOWN : `keydown${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
CAROUSEL : 'carousel',
ACTIVE : 'active',
SLIDE : 'slide',
RIGHT : 'carousel-item-right',
LEFT : 'carousel-item-left',
NEXT : 'carousel-item-next',
PREV : 'carousel-item-prev',
ITEM : 'carousel-item'
}
const Selector = {
ACTIVE : '.active',
ACTIVE_ITEM : '.active.carousel-item',
ITEM : '.carousel-item',
NEXT_PREV : '.carousel-item-next, .carousel-item-prev',
INDICATORS : '.carousel-indicators',
DATA_SLIDE : '[data-slide], [data-slide-to]',
DATA_RIDE : '[data-ride="carousel"]'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
this._items = null
this._interval = null
this._activeElement = null
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this._config = this._getConfig(config)
this._element = $(element)[0]
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
this._addEventListeners()
} }
const DefaultType = { // Getters
interval : '(number|boolean)',
keyboard : 'boolean', static get VERSION() {
slide : '(boolean|string)', return VERSION
pause : '(string|boolean)',
wrap : 'boolean'
} }
const Direction = { static get Default() {
NEXT : 'next', return Default
PREV : 'prev',
LEFT : 'left',
RIGHT : 'right'
} }
const Event = { // Public
SLIDE : `slide${EVENT_KEY}`,
SLID : `slid${EVENT_KEY}`, next() {
KEYDOWN : `keydown${EVENT_KEY}`, if (!this._isSliding) {
MOUSEENTER : `mouseenter${EVENT_KEY}`, this._slide(Direction.NEXT)
MOUSELEAVE : `mouseleave${EVENT_KEY}`, }
TOUCHEND : `touchend${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { nextWhenVisible() {
CAROUSEL : 'carousel', // Don't call next when the page isn't visible
ACTIVE : 'active', // or the carousel or its parent isn't visible
SLIDE : 'slide', if (!document.hidden &&
RIGHT : 'carousel-item-right', ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
LEFT : 'carousel-item-left', this.next()
NEXT : 'carousel-item-next', }
PREV : 'carousel-item-prev',
ITEM : 'carousel-item'
} }
const Selector = { prev() {
ACTIVE : '.active', if (!this._isSliding) {
ACTIVE_ITEM : '.active.carousel-item', this._slide(Direction.PREV)
ITEM : '.carousel-item', }
NEXT_PREV : '.carousel-item-next, .carousel-item-prev',
INDICATORS : '.carousel-indicators',
DATA_SLIDE : '[data-slide], [data-slide-to]',
DATA_RIDE : '[data-ride="carousel"]'
} }
/** pause(event) {
* ------------------------------------------------------------------------ if (!event) {
* Class Definition this._isPaused = true
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
this._items = null
this._interval = null
this._activeElement = null
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this._config = this._getConfig(config)
this._element = $(element)[0]
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
this._addEventListeners()
} }
// Getters if (this._element.querySelector(Selector.NEXT_PREV)) {
Util.triggerTransitionEnd(this._element)
static get VERSION() { this.cycle(true)
return VERSION
} }
static get Default() { clearInterval(this._interval)
return Default this._interval = null
}
cycle(event) {
if (!event) {
this._isPaused = false
} }
// Public if (this._interval) {
next() {
if (!this._isSliding) {
this._slide(Direction.NEXT)
}
}
nextWhenVisible() {
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden &&
($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
this.next()
}
}
prev() {
if (!this._isSliding) {
this._slide(Direction.PREV)
}
}
pause(event) {
if (!event) {
this._isPaused = true
}
if (this._element.querySelector(Selector.NEXT_PREV)) {
Util.triggerTransitionEnd(this._element)
this.cycle(true)
}
clearInterval(this._interval) clearInterval(this._interval)
this._interval = null this._interval = null
} }
cycle(event) { if (this._config.interval && !this._isPaused) {
if (!event) { this._interval = setInterval(
this._isPaused = false (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
} this._config.interval
)
}
}
if (this._interval) { to(index) {
clearInterval(this._interval) this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
this._interval = null
}
if (this._config.interval && !this._isPaused) { const activeIndex = this._getItemIndex(this._activeElement)
this._interval = setInterval(
(document.visibilityState ? this.nextWhenVisible : this.next).bind(this), if (index > this._items.length - 1 || index < 0) {
this._config.interval return
)
}
} }
to(index) { if (this._isSliding) {
this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM) $(this._element).one(Event.SLID, () => this.to(index))
return
const activeIndex = this._getItemIndex(this._activeElement)
if (index > this._items.length - 1 || index < 0) {
return
}
if (this._isSliding) {
$(this._element).one(Event.SLID, () => this.to(index))
return
}
if (activeIndex === index) {
this.pause()
this.cycle()
return
}
const direction = index > activeIndex
? Direction.NEXT
: Direction.PREV
this._slide(direction, this._items[index])
} }
dispose() { if (activeIndex === index) {
$(this._element).off(EVENT_KEY) this.pause()
$.removeData(this._element, DATA_KEY) this.cycle()
return
this._items = null
this._config = null
this._element = null
this._interval = null
this._isPaused = null
this._isSliding = null
this._activeElement = null
this._indicatorsElement = null
} }
// Private const direction = index > activeIndex
? Direction.NEXT
: Direction.PREV
_getConfig(config) { this._slide(direction, this._items[index])
config = { }
dispose() {
$(this._element).off(EVENT_KEY)
$.removeData(this._element, DATA_KEY)
this._items = null
this._config = null
this._element = null
this._interval = null
this._isPaused = null
this._isSliding = null
this._activeElement = null
this._indicatorsElement = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_addEventListeners() {
if (this._config.keyboard) {
$(this._element)
.on(Event.KEYDOWN, (event) => this._keydown(event))
}
if (this._config.pause === 'hover') {
$(this._element)
.on(Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
if ('ontouchstart' in document.documentElement) {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;
// here, we listen for touchend, explicitly pause the carousel
// (as if it's the second time we tap on it, mouseenter compat event
// is NOT fired) and after a timeout (to allow for mouse compatibility
// events to fire) we explicitly restart cycling
$(this._element).on(Event.TOUCHEND, () => {
this.pause()
if (this.touchTimeout) {
clearTimeout(this.touchTimeout)
}
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
})
}
}
}
_keydown(event) {
if (/input|textarea/i.test(event.target.tagName)) {
return
}
switch (event.which) {
case ARROW_LEFT_KEYCODE:
event.preventDefault()
this.prev()
break
case ARROW_RIGHT_KEYCODE:
event.preventDefault()
this.next()
break
default:
}
}
_getItemIndex(element) {
this._items = element && element.parentNode
? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))
: []
return this._items.indexOf(element)
}
_getItemByDirection(direction, activeElement) {
const isNextDirection = direction === Direction.NEXT
const isPrevDirection = direction === Direction.PREV
const activeIndex = this._getItemIndex(activeElement)
const lastItemIndex = this._items.length - 1
const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
isNextDirection && activeIndex === lastItemIndex
if (isGoingToWrap && !this._config.wrap) {
return activeElement
}
const delta = direction === Direction.PREV ? -1 : 1
const itemIndex = (activeIndex + delta) % this._items.length
return itemIndex === -1
? this._items[this._items.length - 1] : this._items[itemIndex]
}
_triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))
const slideEvent = $.Event(Event.SLIDE, {
relatedTarget,
direction: eventDirectionName,
from: fromIndex,
to: targetIndex
})
$(this._element).trigger(slideEvent)
return slideEvent
}
_setActiveIndicatorElement(element) {
if (this._indicatorsElement) {
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))
$(indicators)
.removeClass(ClassName.ACTIVE)
const nextIndicator = this._indicatorsElement.children[
this._getItemIndex(element)
]
if (nextIndicator) {
$(nextIndicator).addClass(ClassName.ACTIVE)
}
}
}
_slide(direction, element) {
const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval)
let directionalClassName
let orderClassName
let eventDirectionName
if (direction === Direction.NEXT) {
directionalClassName = ClassName.LEFT
orderClassName = ClassName.NEXT
eventDirectionName = Direction.LEFT
} else {
directionalClassName = ClassName.RIGHT
orderClassName = ClassName.PREV
eventDirectionName = Direction.RIGHT
}
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
this._isSliding = false
return
}
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
if (slideEvent.isDefaultPrevented()) {
return
}
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
return
}
this._isSliding = true
if (isCycling) {
this.pause()
}
this._setActiveIndicatorElement(nextElement)
const slidEvent = $.Event(Event.SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
})
if ($(this._element).hasClass(ClassName.SLIDE)) {
$(nextElement).addClass(orderClassName)
Util.reflow(nextElement)
$(activeElement).addClass(directionalClassName)
$(nextElement).addClass(directionalClassName)
const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)
if (nextElementInterval) {
this._config.defaultInterval = this._config.defaultInterval || this._config.interval
this._config.interval = nextElementInterval
} else {
this._config.interval = this._config.defaultInterval || this._config.interval
}
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
$(activeElement)
.one(Util.TRANSITION_END, () => {
$(nextElement)
.removeClass(`${directionalClassName} ${orderClassName}`)
.addClass(ClassName.ACTIVE)
$(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
this._isSliding = false
setTimeout(() => $(this._element).trigger(slidEvent), 0)
})
.emulateTransitionEnd(transitionDuration)
} else {
$(activeElement).removeClass(ClassName.ACTIVE)
$(nextElement).addClass(ClassName.ACTIVE)
this._isSliding = false
$(this._element).trigger(slidEvent)
}
if (isCycling) {
this.cycle()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
let _config = {
...Default, ...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_addEventListeners() {
if (this._config.keyboard) {
$(this._element)
.on(Event.KEYDOWN, (event) => this._keydown(event))
}
if (this._config.pause === 'hover') {
$(this._element)
.on(Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
if ('ontouchstart' in document.documentElement) {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;
// here, we listen for touchend, explicitly pause the carousel
// (as if it's the second time we tap on it, mouseenter compat event
// is NOT fired) and after a timeout (to allow for mouse compatibility
// events to fire) we explicitly restart cycling
$(this._element).on(Event.TOUCHEND, () => {
this.pause()
if (this.touchTimeout) {
clearTimeout(this.touchTimeout)
}
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
})
}
}
}
_keydown(event) {
if (/input|textarea/i.test(event.target.tagName)) {
return
}
switch (event.which) {
case ARROW_LEFT_KEYCODE:
event.preventDefault()
this.prev()
break
case ARROW_RIGHT_KEYCODE:
event.preventDefault()
this.next()
break
default:
}
}
_getItemIndex(element) {
this._items = element && element.parentNode
? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))
: []
return this._items.indexOf(element)
}
_getItemByDirection(direction, activeElement) {
const isNextDirection = direction === Direction.NEXT
const isPrevDirection = direction === Direction.PREV
const activeIndex = this._getItemIndex(activeElement)
const lastItemIndex = this._items.length - 1
const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
isNextDirection && activeIndex === lastItemIndex
if (isGoingToWrap && !this._config.wrap) {
return activeElement
}
const delta = direction === Direction.PREV ? -1 : 1
const itemIndex = (activeIndex + delta) % this._items.length
return itemIndex === -1
? this._items[this._items.length - 1] : this._items[itemIndex]
}
_triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))
const slideEvent = $.Event(Event.SLIDE, {
relatedTarget,
direction: eventDirectionName,
from: fromIndex,
to: targetIndex
})
$(this._element).trigger(slideEvent)
return slideEvent
}
_setActiveIndicatorElement(element) {
if (this._indicatorsElement) {
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))
$(indicators)
.removeClass(ClassName.ACTIVE)
const nextIndicator = this._indicatorsElement.children[
this._getItemIndex(element)
]
if (nextIndicator) {
$(nextIndicator).addClass(ClassName.ACTIVE)
}
}
}
_slide(direction, element) {
const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval)
let directionalClassName
let orderClassName
let eventDirectionName
if (direction === Direction.NEXT) {
directionalClassName = ClassName.LEFT
orderClassName = ClassName.NEXT
eventDirectionName = Direction.LEFT
} else {
directionalClassName = ClassName.RIGHT
orderClassName = ClassName.PREV
eventDirectionName = Direction.RIGHT
}
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
this._isSliding = false
return
}
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
if (slideEvent.isDefaultPrevented()) {
return
}
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
return
}
this._isSliding = true
if (isCycling) {
this.pause()
}
this._setActiveIndicatorElement(nextElement)
const slidEvent = $.Event(Event.SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
})
if ($(this._element).hasClass(ClassName.SLIDE)) {
$(nextElement).addClass(orderClassName)
Util.reflow(nextElement)
$(activeElement).addClass(directionalClassName)
$(nextElement).addClass(directionalClassName)
const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)
if (nextElementInterval) {
this._config.defaultInterval = this._config.defaultInterval || this._config.interval
this._config.interval = nextElementInterval
} else {
this._config.interval = this._config.defaultInterval || this._config.interval
}
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
$(activeElement)
.one(Util.TRANSITION_END, () => {
$(nextElement)
.removeClass(`${directionalClassName} ${orderClassName}`)
.addClass(ClassName.ACTIVE)
$(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
this._isSliding = false
setTimeout(() => $(this._element).trigger(slidEvent), 0)
})
.emulateTransitionEnd(transitionDuration)
} else {
$(activeElement).removeClass(ClassName.ACTIVE)
$(nextElement).addClass(ClassName.ACTIVE)
this._isSliding = false
$(this._element).trigger(slidEvent)
}
if (isCycling) {
this.cycle()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
let _config = {
...Default,
...$(this).data()
}
if (typeof config === 'object') {
_config = {
..._config,
...config
}
}
const action = typeof config === 'string' ? config : _config.slide
if (!data) {
data = new Carousel(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'number') {
data.to(config)
} else if (typeof action === 'string') {
if (typeof data[action] === 'undefined') {
throw new TypeError(`No method named "${action}"`)
}
data[action]()
} else if (_config.interval) {
data.pause()
data.cycle()
}
})
}
static _dataApiClickHandler(event) {
const selector = Util.getSelectorFromElement(this)
if (!selector) {
return
}
const target = $(selector)[0]
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
return
}
const config = {
...$(target).data(),
...$(this).data() ...$(this).data()
} }
const slideIndex = this.getAttribute('data-slide-to')
if (slideIndex) { if (typeof config === 'object') {
config.interval = false _config = {
..._config,
...config
}
} }
Carousel._jQueryInterface.call($(target), config) const action = typeof config === 'string' ? config : _config.slide
if (slideIndex) { if (!data) {
$(target).data(DATA_KEY).to(slideIndex) data = new Carousel(this, _config)
$(this).data(DATA_KEY, data)
} }
event.preventDefault() if (typeof config === 'number') {
} data.to(config)
} else if (typeof action === 'string') {
if (typeof data[action] === 'undefined') {
throw new TypeError(`No method named "${action}"`)
}
data[action]()
} else if (_config.interval) {
data.pause()
data.cycle()
}
})
} }
/** static _dataApiClickHandler(event) {
* ------------------------------------------------------------------------ const selector = Util.getSelectorFromElement(this)
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document) if (!selector) {
.on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler) return
$(window).on(Event.LOAD_DATA_API, () => {
const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) {
const $carousel = $(carousels[i])
Carousel._jQueryInterface.call($carousel, $carousel.data())
} }
})
/** const target = $(selector)[0]
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Carousel._jQueryInterface if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
$.fn[NAME].Constructor = Carousel return
$.fn[NAME].noConflict = () => { }
$.fn[NAME] = JQUERY_NO_CONFLICT
return Carousel._jQueryInterface const config = {
...$(target).data(),
...$(this).data()
}
const slideIndex = this.getAttribute('data-slide-to')
if (slideIndex) {
config.interval = false
}
Carousel._jQueryInterface.call($(target), config)
if (slideIndex) {
$(target).data(DATA_KEY).to(slideIndex)
}
event.preventDefault()
} }
}
return Carousel /**
})($) * ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
$(window).on(Event.LOAD_DATA_API, () => {
const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) {
const $carousel = $(carousels[i])
Carousel._jQueryInterface.call($carousel, $carousel.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Carousel._jQueryInterface
$.fn[NAME].Constructor = Carousel
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Carousel._jQueryInterface
}
export default Carousel export default Carousel

View File

@@ -8,396 +8,392 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Collapse = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'collapse' const NAME = 'collapse'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.collapse' const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = { const Default = {
toggle : true, toggle : true,
parent : '' parent : ''
} }
const DefaultType = { const DefaultType = {
toggle : 'boolean', toggle : 'boolean',
parent : '(string|element)' parent : '(string|element)'
} }
const Event = { const Event = {
SHOW : `show${EVENT_KEY}`, SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`,
HIDE : `hide${EVENT_KEY}`, HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN : `hidden${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
SHOW : 'show', SHOW : 'show',
COLLAPSE : 'collapse', COLLAPSE : 'collapse',
COLLAPSING : 'collapsing', COLLAPSING : 'collapsing',
COLLAPSED : 'collapsed' COLLAPSED : 'collapsed'
} }
const Dimension = { const Dimension = {
WIDTH : 'width', WIDTH : 'width',
HEIGHT : 'height' HEIGHT : 'height'
} }
const Selector = { const Selector = {
ACTIVES : '.show, .collapsing', ACTIVES : '.show, .collapsing',
DATA_TOGGLE : '[data-toggle="collapse"]' DATA_TOGGLE : '[data-toggle="collapse"]'
} }
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Class Definition * Class Definition
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
class Collapse { class Collapse {
constructor(element, config) { constructor(element, config) {
this._isTransitioning = false this._isTransitioning = false
this._element = element this._element = element
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._triggerArray = $.makeArray(document.querySelectorAll( this._triggerArray = $.makeArray(document.querySelectorAll(
`[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]` `[data-toggle="collapse"][data-target="#${element.id}"]`
)) ))
const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE)) const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
for (let i = 0, len = toggleList.length; i < len; i++) { for (let i = 0, len = toggleList.length; i < len; i++) {
const elem = toggleList[i] const elem = toggleList[i]
const selector = Util.getSelectorFromElement(elem) const selector = Util.getSelectorFromElement(elem)
const filterElement = [].slice.call(document.querySelectorAll(selector)) const filterElement = [].slice.call(document.querySelectorAll(selector))
.filter((foundElem) => foundElem === element) .filter((foundElem) => foundElem === element)
if (selector !== null && filterElement.length > 0) { if (selector !== null && filterElement.length > 0) {
this._selector = selector this._selector = selector
this._triggerArray.push(elem) 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 this._parent = this._config.parent ? this._getParent() : null
static get VERSION() { if (!this._config.parent) {
return VERSION this._addAriaAndCollapsedClass(this._element, this._triggerArray)
} }
static get Default() { if (this._config.toggle) {
return Default 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
} }
// Public let actives
let activesData
toggle() { if (this._parent) {
if ($(this._element).hasClass(ClassName.SHOW)) { actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))
this.hide() .filter((elem) => {
} else { if (typeof this._config.parent === 'string') {
this.show() return elem.getAttribute('data-parent') === this._config.parent
}
}
show() {
if (this._isTransitioning ||
$(this._element).hasClass(ClassName.SHOW)) {
return
}
let actives
let activesData
if (this._parent) {
actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))
.filter((elem) => {
if (typeof this._config.parent === 'string') {
return elem.getAttribute('data-parent') === this._config.parent
}
return elem.classList.contains(ClassName.COLLAPSE)
})
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) {
$(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)
}
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
const scrollSize = `scroll${capitalizedDimension}`
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
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)
const triggerArrayLength = this._triggerArray.length
if (triggerArrayLength > 0) {
for (let i = 0; i < triggerArrayLength; i++) {
const trigger = this._triggerArray[i]
const selector = Util.getSelectorFromElement(trigger)
if (selector !== null) {
const $elem = $([].slice.call(document.querySelectorAll(selector)))
if (!$elem.hasClass(ClassName.SHOW)) {
$(trigger).addClass(ClassName.COLLAPSED)
.attr('aria-expanded', false)
}
} }
}
}
this.setTransitioning(true) return elem.classList.contains(ClassName.COLLAPSE)
})
const complete = () => { if (actives.length === 0) {
this.setTransitioning(false) actives = null
$(this._element)
.removeClass(ClassName.COLLAPSING)
.addClass(ClassName.COLLAPSE)
.trigger(Event.HIDDEN)
} }
}
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) {
$(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._element.style[dimension] = ''
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element) this.setTransitioning(false)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration) $(this._element).trigger(Event.SHOWN)
} }
setTransitioning(isTransitioning) { const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
this._isTransitioning = isTransitioning const scrollSize = `scroll${capitalizedDimension}`
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
this._element.style[dimension] = `${this._element[scrollSize]}px`
}
hide() {
if (this._isTransitioning ||
!$(this._element).hasClass(ClassName.SHOW)) {
return
} }
dispose() { const startEvent = $.Event(Event.HIDE)
$.removeData(this._element, DATA_KEY) $(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
this._config = null return
this._parent = null
this._element = null
this._triggerArray = null
this._isTransitioning = null
} }
// Private const dimension = this._getDimension()
_getConfig(config) { this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
config = {
...Default,
...config
}
config.toggle = Boolean(config.toggle) // Coerce string values
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getDimension() { Util.reflow(this._element)
const hasWidth = $(this._element).hasClass(Dimension.WIDTH)
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
}
_getParent() { $(this._element)
let parent .addClass(ClassName.COLLAPSING)
.removeClass(ClassName.COLLAPSE)
.removeClass(ClassName.SHOW)
if (Util.isElement(this._config.parent)) { const triggerArrayLength = this._triggerArray.length
parent = this._config.parent if (triggerArrayLength > 0) {
for (let i = 0; i < triggerArrayLength; i++) {
// It's a jQuery object const trigger = this._triggerArray[i]
if (typeof this._config.parent.jquery !== 'undefined') { const selector = Util.getSelectorFromElement(trigger)
parent = this._config.parent[0] if (selector !== null) {
} const $elem = $([].slice.call(document.querySelectorAll(selector)))
} else { if (!$elem.hasClass(ClassName.SHOW)) {
parent = document.querySelector(this._config.parent) $(trigger).addClass(ClassName.COLLAPSED)
} .attr('aria-expanded', false)
const selector =
`[data-toggle="collapse"][data-parent="${this._config.parent}"]`
const children = [].slice.call(parent.querySelectorAll(selector))
$(children).each((i, element) => {
this._addAriaAndCollapsedClass(
Collapse._getTargetFromElement(element),
[element]
)
})
return parent
}
_addAriaAndCollapsedClass(element, triggerArray) {
const isOpen = $(element).hasClass(ClassName.SHOW)
if (triggerArray.length) {
$(triggerArray)
.toggleClass(ClassName.COLLAPSED, !isOpen)
.attr('aria-expanded', isOpen)
}
}
// Static
static _getTargetFromElement(element) {
const selector = Util.getSelectorFromElement(element)
return selector ? document.querySelector(selector) : 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 ? 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]()
} }
}) }
} }
this.setTransitioning(true)
const complete = () => {
this.setTransitioning(false)
$(this._element)
.removeClass(ClassName.COLLAPSING)
.addClass(ClassName.COLLAPSE)
.trigger(Event.HIDDEN)
}
this._element.style[dimension] = ''
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} }
/** setTransitioning(isTransitioning) {
* ------------------------------------------------------------------------ this._isTransitioning = isTransitioning
* Data Api implementation }
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { dispose() {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element $.removeData(this._element, DATA_KEY)
if (event.currentTarget.tagName === 'A') {
event.preventDefault() 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
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 = document.querySelector(this._config.parent)
} }
const $trigger = $(this) const selector =
const selector = Util.getSelectorFromElement(this) `[data-toggle="collapse"][data-parent="${this._config.parent}"]`
const selectors = [].slice.call(document.querySelectorAll(selector))
$(selectors).each(function () { const children = [].slice.call(parent.querySelectorAll(selector))
const $target = $(this) $(children).each((i, element) => {
const data = $target.data(DATA_KEY) this._addAriaAndCollapsedClass(
const config = data ? 'toggle' : $trigger.data() Collapse._getTargetFromElement(element),
Collapse._jQueryInterface.call($target, config) [element]
)
}) })
})
/** return parent
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Collapse._jQueryInterface
$.fn[NAME].Constructor = Collapse
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Collapse._jQueryInterface
} }
return Collapse _addAriaAndCollapsedClass(element, triggerArray) {
})($) const isOpen = $(element).hasClass(ClassName.SHOW)
if (triggerArray.length) {
$(triggerArray)
.toggleClass(ClassName.COLLAPSED, !isOpen)
.attr('aria-expanded', isOpen)
}
}
// Static
static _getTargetFromElement(element) {
const selector = Util.getSelectorFromElement(element)
return selector ? document.querySelector(selector) : 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 ? 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)
const selectors = [].slice.call(document.querySelectorAll(selector))
$(selectors).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 = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Collapse._jQueryInterface
}
export default Collapse export default Collapse

View File

@@ -9,486 +9,483 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Dropdown = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'dropdown' const NAME = 'dropdown'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.dropdown' const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`, CLICK : `click${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`, CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`, KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,
KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}` KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DISABLED : 'disabled',
SHOW : 'show',
DROPUP : 'dropup',
DROPRIGHT : 'dropright',
DROPLEFT : 'dropleft',
MENURIGHT : 'dropdown-menu-right',
MENULEFT : 'dropdown-menu-left',
POSITION_STATIC : 'position-static'
}
const Selector = {
DATA_TOGGLE : '[data-toggle="dropdown"]',
FORM_CHILD : '.dropdown form',
MENU : '.dropdown-menu',
NAVBAR_NAV : '.navbar-nav',
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
}
const AttachmentMap = {
TOP : 'top-start',
TOPEND : 'top-end',
BOTTOM : 'bottom-start',
BOTTOMEND : 'bottom-end',
RIGHT : 'right-start',
RIGHTEND : 'right-end',
LEFT : 'left-start',
LEFTEND : 'left-end'
}
const Default = {
offset : 0,
flip : true,
boundary : 'scrollParent',
reference : 'toggle',
display : 'dynamic'
}
const DefaultType = {
offset : '(number|string|function)',
flip : 'boolean',
boundary : '(string|element)',
reference : '(string|element)',
display : 'string'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Dropdown {
constructor(element, config) {
this._element = element
this._popper = null
this._config = this._getConfig(config)
this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar()
this._addEventListeners()
} }
const ClassName = { // Getters
DISABLED : 'disabled',
SHOW : 'show', static get VERSION() {
DROPUP : 'dropup', return VERSION
DROPRIGHT : 'dropright',
DROPLEFT : 'dropleft',
MENURIGHT : 'dropdown-menu-right',
MENULEFT : 'dropdown-menu-left',
POSITION_STATIC : 'position-static'
} }
const Selector = { static get Default() {
DATA_TOGGLE : '[data-toggle="dropdown"]', return Default
FORM_CHILD : '.dropdown form',
MENU : '.dropdown-menu',
NAVBAR_NAV : '.navbar-nav',
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
} }
const AttachmentMap = { static get DefaultType() {
TOP : 'top-start', return DefaultType
TOPEND : 'top-end',
BOTTOM : 'bottom-start',
BOTTOMEND : 'bottom-end',
RIGHT : 'right-start',
RIGHTEND : 'right-end',
LEFT : 'left-start',
LEFTEND : 'left-end'
} }
const Default = { // Public
offset : 0,
flip : true,
boundary : 'scrollParent',
reference : 'toggle',
display : 'dynamic'
}
const DefaultType = { toggle() {
offset : '(number|string|function)', if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {
flip : 'boolean', return
boundary : '(string|element)',
reference : '(string|element)',
display : 'string'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Dropdown {
constructor(element, config) {
this._element = element
this._popper = null
this._config = this._getConfig(config)
this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar()
this._addEventListeners()
} }
// Getters const parent = Dropdown._getParentFromElement(this._element)
const isActive = $(this._menu).hasClass(ClassName.SHOW)
static get VERSION() { Dropdown._clearMenus()
return VERSION
if (isActive) {
return
} }
static get Default() { const relatedTarget = {
return Default relatedTarget: this._element
}
const showEvent = $.Event(Event.SHOW, relatedTarget)
$(parent).trigger(showEvent)
if (showEvent.isDefaultPrevented()) {
return
} }
static get DefaultType() { // Disable totally Popper.js for Dropdown in Navbar
return DefaultType if (!this._inNavbar) {
} /**
* Check for Popper dependency
// Public * Popper - https://popper.js.org
*/
toggle() { if (typeof Popper === 'undefined') {
if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) { throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')
return
} }
const parent = Dropdown._getParentFromElement(this._element) let referenceElement = this._element
const isActive = $(this._menu).hasClass(ClassName.SHOW)
Dropdown._clearMenus() if (this._config.reference === 'parent') {
referenceElement = parent
} else if (Util.isElement(this._config.reference)) {
referenceElement = this._config.reference
if (isActive) { // Check if it's jQuery element
return if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0]
}
} }
// If boundary is not `scrollParent`, then set position to `static`
// to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') {
$(parent).addClass(ClassName.POSITION_STATIC)
}
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
}
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement &&
$(parent).closest(Selector.NAVBAR_NAV).length === 0) {
$(document.body).children().on('mouseover', null, $.noop)
}
this._element.focus()
this._element.setAttribute('aria-expanded', true)
$(this._menu).toggleClass(ClassName.SHOW)
$(parent)
.toggleClass(ClassName.SHOW)
.trigger($.Event(Event.SHOWN, relatedTarget))
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._element).off(EVENT_KEY)
this._element = null
this._menu = null
if (this._popper !== null) {
this._popper.destroy()
this._popper = null
}
}
update() {
this._inNavbar = this._detectNavbar()
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Private
_addEventListeners() {
$(this._element).on(Event.CLICK, (event) => {
event.preventDefault()
event.stopPropagation()
this.toggle()
})
}
_getConfig(config) {
config = {
...this.constructor.Default,
...$(this._element).data(),
...config
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_getMenuElement() {
if (!this._menu) {
const parent = Dropdown._getParentFromElement(this._element)
if (parent) {
this._menu = parent.querySelector(Selector.MENU)
}
}
return this._menu
}
_getPlacement() {
const $parentDropdown = $(this._element.parentNode)
let placement = AttachmentMap.BOTTOM
// Handle dropup
if ($parentDropdown.hasClass(ClassName.DROPUP)) {
placement = AttachmentMap.TOP
if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.TOPEND
}
} else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
placement = AttachmentMap.RIGHT
} else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
placement = AttachmentMap.LEFT
} else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND
}
return placement
}
_detectNavbar() {
return $(this._element).closest('.navbar').length > 0
}
_getPopperConfig() {
const offsetConf = {}
if (typeof this._config.offset === 'function') {
offsetConf.fn = (data) => {
data.offsets = {
...data.offsets,
...this._config.offset(data.offsets) || {}
}
return data
}
} else {
offsetConf.offset = this._config.offset
}
const popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: offsetConf,
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
}
// Disable Popper.js if we have a static display
if (this._config.display === 'static') {
popperConfig.modifiers.applyStyle = {
enabled: false
}
}
return popperConfig
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data) {
data = new Dropdown(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]()
}
})
}
static _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return
}
const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown._getParentFromElement(toggles[i])
const context = $(toggles[i]).data(DATA_KEY)
const relatedTarget = { const relatedTarget = {
relatedTarget: this._element relatedTarget: toggles[i]
}
const showEvent = $.Event(Event.SHOW, relatedTarget)
$(parent).trigger(showEvent)
if (showEvent.isDefaultPrevented()) {
return
} }
// Disable totally Popper.js for Dropdown in Navbar if (event && event.type === 'click') {
if (!this._inNavbar) { relatedTarget.clickEvent = event
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')
}
let referenceElement = this._element
if (this._config.reference === 'parent') {
referenceElement = parent
} else if (Util.isElement(this._config.reference)) {
referenceElement = this._config.reference
// Check if it's jQuery element
if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0]
}
}
// If boundary is not `scrollParent`, then set position to `static`
// to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') {
$(parent).addClass(ClassName.POSITION_STATIC)
}
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
} }
// If this is a touch-enabled device we add extra if (!context) {
// empty mouseover listeners to the body's immediate children; continue
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement &&
$(parent).closest(Selector.NAVBAR_NAV).length === 0) {
$(document.body).children().on('mouseover', null, $.noop)
} }
this._element.focus() const dropdownMenu = context._menu
this._element.setAttribute('aria-expanded', true) if (!$(parent).hasClass(ClassName.SHOW)) {
continue
}
$(this._menu).toggleClass(ClassName.SHOW) if (event && (event.type === 'click' &&
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
$.contains(parent, event.target)) {
continue
}
const hideEvent = $.Event(Event.HIDE, relatedTarget)
$(parent).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
continue
}
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop)
}
toggles[i].setAttribute('aria-expanded', 'false')
$(dropdownMenu).removeClass(ClassName.SHOW)
$(parent) $(parent)
.toggleClass(ClassName.SHOW) .removeClass(ClassName.SHOW)
.trigger($.Event(Event.SHOWN, relatedTarget)) .trigger($.Event(Event.HIDDEN, relatedTarget))
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._element).off(EVENT_KEY)
this._element = null
this._menu = null
if (this._popper !== null) {
this._popper.destroy()
this._popper = null
}
}
update() {
this._inNavbar = this._detectNavbar()
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Private
_addEventListeners() {
$(this._element).on(Event.CLICK, (event) => {
event.preventDefault()
event.stopPropagation()
this.toggle()
})
}
_getConfig(config) {
config = {
...this.constructor.Default,
...$(this._element).data(),
...config
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_getMenuElement() {
if (!this._menu) {
const parent = Dropdown._getParentFromElement(this._element)
if (parent) {
this._menu = parent.querySelector(Selector.MENU)
}
}
return this._menu
}
_getPlacement() {
const $parentDropdown = $(this._element.parentNode)
let placement = AttachmentMap.BOTTOM
// Handle dropup
if ($parentDropdown.hasClass(ClassName.DROPUP)) {
placement = AttachmentMap.TOP
if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.TOPEND
}
} else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
placement = AttachmentMap.RIGHT
} else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
placement = AttachmentMap.LEFT
} else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND
}
return placement
}
_detectNavbar() {
return $(this._element).closest('.navbar').length > 0
}
_getPopperConfig() {
const offsetConf = {}
if (typeof this._config.offset === 'function') {
offsetConf.fn = (data) => {
data.offsets = {
...data.offsets,
...this._config.offset(data.offsets) || {}
}
return data
}
} else {
offsetConf.offset = this._config.offset
}
const popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: offsetConf,
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
}
// Disable Popper.js if we have a static display
if (this._config.display === 'static') {
popperConfig.modifiers.applyStyle = {
enabled: false
}
}
return popperConfig
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data) {
data = new Dropdown(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]()
}
})
}
static _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return
}
const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown._getParentFromElement(toggles[i])
const context = $(toggles[i]).data(DATA_KEY)
const relatedTarget = {
relatedTarget: toggles[i]
}
if (event && event.type === 'click') {
relatedTarget.clickEvent = event
}
if (!context) {
continue
}
const dropdownMenu = context._menu
if (!$(parent).hasClass(ClassName.SHOW)) {
continue
}
if (event && (event.type === 'click' &&
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
$.contains(parent, event.target)) {
continue
}
const hideEvent = $.Event(Event.HIDE, relatedTarget)
$(parent).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
continue
}
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop)
}
toggles[i].setAttribute('aria-expanded', 'false')
$(dropdownMenu).removeClass(ClassName.SHOW)
$(parent)
.removeClass(ClassName.SHOW)
.trigger($.Event(Event.HIDDEN, relatedTarget))
}
}
static _getParentFromElement(element) {
let parent
const selector = Util.getSelectorFromElement(element)
if (selector) {
parent = document.querySelector(selector)
}
return parent || element.parentNode
}
// eslint-disable-next-line complexity
static _dataApiKeydownHandler(event) {
// If not input/textarea:
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
// If input/textarea:
// - If space key => not a dropdown command
// - If key is other than escape
// - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName)
? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
$(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
return
}
event.preventDefault()
event.stopPropagation()
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
return
}
const parent = Dropdown._getParentFromElement(this)
const isActive = $(parent).hasClass(ClassName.SHOW)
if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) ||
isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
const toggle = parent.querySelector(Selector.DATA_TOGGLE)
$(toggle).trigger('focus')
}
$(this).trigger('click')
return
}
const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))
if (items.length === 0) {
return
}
let index = items.indexOf(event.target)
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
index--
}
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
index++
}
if (index < 0) {
index = 0
}
items[index].focus()
} }
} }
/** static _getParentFromElement(element) {
* ------------------------------------------------------------------------ let parent
* Data Api implementation const selector = Util.getSelectorFromElement(element)
* ------------------------------------------------------------------------
*/
$(document) if (selector) {
.on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) parent = document.querySelector(selector)
.on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler) }
.on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
event.stopPropagation()
Dropdown._jQueryInterface.call($(this), 'toggle')
})
.on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
e.stopPropagation()
})
/** return parent || element.parentNode
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface
$.fn[NAME].Constructor = Dropdown
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Dropdown._jQueryInterface
} }
return Dropdown // eslint-disable-next-line complexity
})($, Popper) static _dataApiKeydownHandler(event) {
// If not input/textarea:
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
// If input/textarea:
// - If space key => not a dropdown command
// - If key is other than escape
// - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName)
? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
$(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
return
}
event.preventDefault()
event.stopPropagation()
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
return
}
const parent = Dropdown._getParentFromElement(this)
const isActive = $(parent).hasClass(ClassName.SHOW)
if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) ||
isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
const toggle = parent.querySelector(Selector.DATA_TOGGLE)
$(toggle).trigger('focus')
}
$(this).trigger('click')
return
}
const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))
if (items.length === 0) {
return
}
let index = items.indexOf(event.target)
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
index--
}
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
index++
}
if (index < 0) {
index = 0
}
items[index].focus()
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
.on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)
.on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
event.stopPropagation()
Dropdown._jQueryInterface.call($(this), 'toggle')
})
.on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
e.stopPropagation()
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface
$.fn[NAME].Constructor = Dropdown
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Dropdown._jQueryInterface
}
export default Dropdown export default Dropdown

View File

@@ -18,7 +18,7 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
(($) => { (() => {
if (typeof $ === 'undefined') { if (typeof $ === 'undefined') {
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.') throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.')
} }
@@ -33,7 +33,7 @@ import Util from './util'
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) { if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0') throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0')
} }
})($) })()
export { export {
Util, Util,

File diff suppressed because it is too large Load Diff

View File

@@ -8,181 +8,177 @@ import Tooltip from './tooltip'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Popover = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'popover' const NAME = 'popover'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.popover' const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const CLASS_PREFIX = 'bs-popover' const CLASS_PREFIX = 'bs-popover'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const Default = { const Default = {
...Tooltip.Default, ...Tooltip.Default,
placement : 'right', placement : 'right',
trigger : 'click', trigger : 'click',
content : '', content : '',
template : '<div class="popover" role="tooltip">' + template : '<div class="popover" role="tooltip">' +
'<div class="arrow"></div>' + '<div class="arrow"></div>' +
'<h3 class="popover-header"></h3>' + '<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div></div>' '<div class="popover-body"></div></div>'
}
const DefaultType = {
...Tooltip.DefaultType,
content : '(string|element|function)'
}
const ClassName = {
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
TITLE : '.popover-header',
CONTENT : '.popover-body'
}
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Popover extends Tooltip {
// Getters
static get VERSION() {
return VERSION
} }
const DefaultType = { static get Default() {
...Tooltip.DefaultType, return Default
content : '(string|element|function)'
} }
const ClassName = { static get NAME() {
FADE : 'fade', return NAME
SHOW : 'show'
} }
const Selector = { static get DATA_KEY() {
TITLE : '.popover-header', return DATA_KEY
CONTENT : '.popover-body'
} }
const Event = { static get Event() {
HIDE : `hide${EVENT_KEY}`, return Event
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`
} }
/** static get EVENT_KEY() {
* ------------------------------------------------------------------------ return EVENT_KEY
* Class Definition }
* ------------------------------------------------------------------------
*/
class Popover extends Tooltip { static get DefaultType() {
// Getters return DefaultType
}
static get VERSION() { // Overrides
return VERSION
isWithContent() {
return this.getTitle() || this._getContent()
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const $tip = $(this.getTipElement())
// We use append for html objects to maintain js events
this.setElementContent($tip.find(Selector.TITLE), this.getTitle())
let content = this._getContent()
if (typeof content === 'function') {
content = content.call(this.element)
} }
this.setElementContent($tip.find(Selector.CONTENT), content)
static get Default() { $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
return Default }
// Private
_getContent() {
return this.element.getAttribute('data-content') ||
this.config.content
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''))
} }
}
static get NAME() { // Static
return NAME
}
static get DATA_KEY() { static _jQueryInterface(config) {
return DATA_KEY return this.each(function () {
} let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
static get Event() { if (!data && /dispose|hide/.test(config)) {
return Event return
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
// Overrides
isWithContent() {
return this.getTitle() || this._getContent()
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const $tip = $(this.getTipElement())
// We use append for html objects to maintain js events
this.setElementContent($tip.find(Selector.TITLE), this.getTitle())
let content = this._getContent()
if (typeof content === 'function') {
content = content.call(this.element)
} }
this.setElementContent($tip.find(Selector.CONTENT), content)
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) if (!data) {
} data = new Popover(this, _config)
$(this).data(DATA_KEY, data)
// Private
_getContent() {
return this.element.getAttribute('data-content') ||
this.config.content
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''))
} }
}
// Static if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
static _jQueryInterface(config) { throw new TypeError(`No method named "${config}"`)
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data && /dispose|hide/.test(config)) {
return
} }
data[config]()
if (!data) { }
data = new Popover(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]()
}
})
}
} }
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* jQuery * jQuery
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
$.fn[NAME] = Popover._jQueryInterface $.fn[NAME] = Popover._jQueryInterface
$.fn[NAME].Constructor = Popover $.fn[NAME].Constructor = Popover
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Popover._jQueryInterface return Popover._jQueryInterface
} }
return Popover
})($)
export default Popover export default Popover

View File

@@ -8,325 +8,321 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const ScrollSpy = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'scrollspy' const NAME = 'scrollspy'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.scrollspy' const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = { const Default = {
offset : 10, offset : 10,
method : 'auto', method : 'auto',
target : '' target : ''
}
const DefaultType = {
offset : 'number',
method : 'string',
target : '(string|element)'
}
const Event = {
ACTIVATE : `activate${EVENT_KEY}`,
SCROLL : `scroll${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_ITEM : 'dropdown-item',
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active'
}
const Selector = {
DATA_SPY : '[data-spy="scroll"]',
ACTIVE : '.active',
NAV_LIST_GROUP : '.nav, .list-group',
NAV_LINKS : '.nav-link',
NAV_ITEMS : '.nav-item',
LIST_ITEMS : '.list-group-item',
DROPDOWN : '.dropdown',
DROPDOWN_ITEMS : '.dropdown-item',
DROPDOWN_TOGGLE : '.dropdown-toggle'
}
const OffsetMethod = {
OFFSET : 'offset',
POSITION : 'position'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class ScrollSpy {
constructor(element, config) {
this._element = element
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config)
this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +
`${this._config.target} ${Selector.LIST_ITEMS},` +
`${this._config.target} ${Selector.DROPDOWN_ITEMS}`
this._offsets = []
this._targets = []
this._activeTarget = null
this._scrollHeight = 0
$(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))
this.refresh()
this._process()
} }
const DefaultType = { // Getters
offset : 'number',
method : 'string', static get VERSION() {
target : '(string|element)' return VERSION
} }
const Event = { static get Default() {
ACTIVATE : `activate${EVENT_KEY}`, return Default
SCROLL : `scroll${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { // Public
DROPDOWN_ITEM : 'dropdown-item',
DROPDOWN_MENU : 'dropdown-menu', refresh() {
ACTIVE : 'active' const autoMethod = this._scrollElement === this._scrollElement.window
? OffsetMethod.OFFSET : OffsetMethod.POSITION
const offsetMethod = this._config.method === 'auto'
? autoMethod : this._config.method
const offsetBase = offsetMethod === OffsetMethod.POSITION
? this._getScrollTop() : 0
this._offsets = []
this._targets = []
this._scrollHeight = this._getScrollHeight()
const targets = [].slice.call(document.querySelectorAll(this._selector))
targets
.map((element) => {
let target
const targetSelector = Util.getSelectorFromElement(element)
if (targetSelector) {
target = document.querySelector(targetSelector)
}
if (target) {
const targetBCR = target.getBoundingClientRect()
if (targetBCR.width || targetBCR.height) {
// TODO (fat): remove sketch reliance on jQuery position/offset
return [
$(target)[offsetMethod]().top + offsetBase,
targetSelector
]
}
}
return null
})
.filter((item) => item)
.sort((a, b) => a[0] - b[0])
.forEach((item) => {
this._offsets.push(item[0])
this._targets.push(item[1])
})
} }
const Selector = { dispose() {
DATA_SPY : '[data-spy="scroll"]', $.removeData(this._element, DATA_KEY)
ACTIVE : '.active', $(this._scrollElement).off(EVENT_KEY)
NAV_LIST_GROUP : '.nav, .list-group',
NAV_LINKS : '.nav-link', this._element = null
NAV_ITEMS : '.nav-item', this._scrollElement = null
LIST_ITEMS : '.list-group-item', this._config = null
DROPDOWN : '.dropdown', this._selector = null
DROPDOWN_ITEMS : '.dropdown-item', this._offsets = null
DROPDOWN_TOGGLE : '.dropdown-toggle' this._targets = null
this._activeTarget = null
this._scrollHeight = null
} }
const OffsetMethod = { // Private
OFFSET : 'offset',
POSITION : 'position' _getConfig(config) {
config = {
...Default,
...typeof config === 'object' && config ? config : {}
}
if (typeof config.target !== 'string') {
let id = $(config.target).attr('id')
if (!id) {
id = Util.getUID(NAME)
$(config.target).attr('id', id)
}
config.target = `#${id}`
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
} }
/** _getScrollTop() {
* ------------------------------------------------------------------------ return this._scrollElement === window
* Class Definition ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop
* ------------------------------------------------------------------------ }
*/
class ScrollSpy { _getScrollHeight() {
constructor(element, config) { return this._scrollElement.scrollHeight || Math.max(
this._element = element document.body.scrollHeight,
this._scrollElement = element.tagName === 'BODY' ? window : element document.documentElement.scrollHeight
this._config = this._getConfig(config) )
this._selector = `${this._config.target} ${Selector.NAV_LINKS},` + }
`${this._config.target} ${Selector.LIST_ITEMS},` +
`${this._config.target} ${Selector.DROPDOWN_ITEMS}`
this._offsets = []
this._targets = []
this._activeTarget = null
this._scrollHeight = 0
$(this._scrollElement).on(Event.SCROLL, (event) => this._process(event)) _getOffsetHeight() {
return this._scrollElement === window
? window.innerHeight : this._scrollElement.getBoundingClientRect().height
}
_process() {
const scrollTop = this._getScrollTop() + this._config.offset
const scrollHeight = this._getScrollHeight()
const maxScroll = this._config.offset +
scrollHeight -
this._getOffsetHeight()
if (this._scrollHeight !== scrollHeight) {
this.refresh() this.refresh()
this._process()
} }
// Getters if (scrollTop >= maxScroll) {
const target = this._targets[this._targets.length - 1]
static get VERSION() { if (this._activeTarget !== target) {
return VERSION this._activate(target)
}
static get Default() {
return Default
}
// Public
refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window
? OffsetMethod.OFFSET : OffsetMethod.POSITION
const offsetMethod = this._config.method === 'auto'
? autoMethod : this._config.method
const offsetBase = offsetMethod === OffsetMethod.POSITION
? this._getScrollTop() : 0
this._offsets = []
this._targets = []
this._scrollHeight = this._getScrollHeight()
const targets = [].slice.call(document.querySelectorAll(this._selector))
targets
.map((element) => {
let target
const targetSelector = Util.getSelectorFromElement(element)
if (targetSelector) {
target = document.querySelector(targetSelector)
}
if (target) {
const targetBCR = target.getBoundingClientRect()
if (targetBCR.width || targetBCR.height) {
// TODO (fat): remove sketch reliance on jQuery position/offset
return [
$(target)[offsetMethod]().top + offsetBase,
targetSelector
]
}
}
return null
})
.filter((item) => item)
.sort((a, b) => a[0] - b[0])
.forEach((item) => {
this._offsets.push(item[0])
this._targets.push(item[1])
})
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._scrollElement).off(EVENT_KEY)
this._element = null
this._scrollElement = null
this._config = null
this._selector = null
this._offsets = null
this._targets = null
this._activeTarget = null
this._scrollHeight = null
}
// Private
_getConfig(config) {
config = {
...Default,
...typeof config === 'object' && config ? config : {}
} }
return
if (typeof config.target !== 'string') {
let id = $(config.target).attr('id')
if (!id) {
id = Util.getUID(NAME)
$(config.target).attr('id', id)
}
config.target = `#${id}`
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
} }
_getScrollTop() { if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
return this._scrollElement === window this._activeTarget = null
? this._scrollElement.pageYOffset : this._scrollElement.scrollTop
}
_getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
)
}
_getOffsetHeight() {
return this._scrollElement === window
? window.innerHeight : this._scrollElement.getBoundingClientRect().height
}
_process() {
const scrollTop = this._getScrollTop() + this._config.offset
const scrollHeight = this._getScrollHeight()
const maxScroll = this._config.offset +
scrollHeight -
this._getOffsetHeight()
if (this._scrollHeight !== scrollHeight) {
this.refresh()
}
if (scrollTop >= maxScroll) {
const target = this._targets[this._targets.length - 1]
if (this._activeTarget !== target) {
this._activate(target)
}
return
}
if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
this._activeTarget = null
this._clear()
return
}
const offsetLength = this._offsets.length
for (let i = offsetLength; i--;) {
const isActiveTarget = this._activeTarget !== this._targets[i] &&
scrollTop >= this._offsets[i] &&
(typeof this._offsets[i + 1] === 'undefined' ||
scrollTop < this._offsets[i + 1])
if (isActiveTarget) {
this._activate(this._targets[i])
}
}
}
_activate(target) {
this._activeTarget = target
this._clear() this._clear()
return
}
let queries = this._selector.split(',') const offsetLength = this._offsets.length
// eslint-disable-next-line arrow-body-style for (let i = offsetLength; i--;) {
queries = queries.map((selector) => { const isActiveTarget = this._activeTarget !== this._targets[i] &&
return `${selector}[data-target="${target}"],` + scrollTop >= this._offsets[i] &&
`${selector}[href="${target}"]` (typeof this._offsets[i + 1] === 'undefined' ||
}) scrollTop < this._offsets[i + 1])
const $link = $([].slice.call(document.querySelectorAll(queries.join(',')))) if (isActiveTarget) {
this._activate(this._targets[i])
}
}
}
if ($link.hasClass(ClassName.DROPDOWN_ITEM)) { _activate(target) {
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE) this._activeTarget = target
$link.addClass(ClassName.ACTIVE)
} else { this._clear()
// Set triggered link as active
$link.addClass(ClassName.ACTIVE) let queries = this._selector.split(',')
// Set triggered links parents as active // eslint-disable-next-line arrow-body-style
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor queries = queries.map((selector) => {
$link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE) return `${selector}[data-target="${target}"],` +
// Handle special case when .nav-link is inside .nav-item `${selector}[href="${target}"]`
$link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE) })
const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))
if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
$link.addClass(ClassName.ACTIVE)
} else {
// Set triggered link as active
$link.addClass(ClassName.ACTIVE)
// Set triggered links parents as active
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
$link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
// Handle special case when .nav-link is inside .nav-item
$link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE)
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
relatedTarget: target
})
}
_clear() {
const nodes = [].slice.call(document.querySelectorAll(this._selector))
$(nodes).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data) {
data = new ScrollSpy(this, _config)
$(this).data(DATA_KEY, data)
} }
$(this._scrollElement).trigger(Event.ACTIVATE, { if (typeof config === 'string') {
relatedTarget: target if (typeof data[config] === 'undefined') {
}) throw new TypeError(`No method named "${config}"`)
}
_clear() {
const nodes = [].slice.call(document.querySelectorAll(this._selector))
$(nodes).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data) {
data = new ScrollSpy(this, _config)
$(this).data(DATA_KEY, data)
} }
data[config]()
if (typeof config === 'string') { }
if (typeof data[config] === 'undefined') { })
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
} }
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Data Api implementation * Data Api implementation
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
$(window).on(Event.LOAD_DATA_API, () => { $(window).on(Event.LOAD_DATA_API, () => {
const scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY)) const scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY))
const scrollSpysLength = scrollSpys.length const scrollSpysLength = scrollSpys.length
for (let i = scrollSpysLength; i--;) { for (let i = scrollSpysLength; i--;) {
const $spy = $(scrollSpys[i]) const $spy = $(scrollSpys[i])
ScrollSpy._jQueryInterface.call($spy, $spy.data()) ScrollSpy._jQueryInterface.call($spy, $spy.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface
} }
})
return ScrollSpy /**
})($) * ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface
}
export default ScrollSpy export default ScrollSpy

View File

@@ -8,257 +8,253 @@ import Util from './util'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Tab = (($) => { /**
/** * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ * Constants
* Constants * ------------------------------------------------------------------------
* ------------------------------------------------------------------------ */
*/
const NAME = 'tab' const NAME = 'tab'
const VERSION = '4.1.3' const VERSION = '4.1.3'
const DATA_KEY = 'bs.tab' const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active',
DISABLED : 'disabled',
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
DROPDOWN : '.dropdown',
NAV_LIST_GROUP : '.nav, .list-group',
ACTIVE : '.active',
ACTIVE_UL : '> li > .active',
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tab {
constructor(element) {
this._element = element
} }
const ClassName = { // Getters
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active', static get VERSION() {
DISABLED : 'disabled', return VERSION
FADE : 'fade',
SHOW : 'show'
} }
const Selector = { // Public
DROPDOWN : '.dropdown',
NAV_LIST_GROUP : '.nav, .list-group',
ACTIVE : '.active',
ACTIVE_UL : '> li > .active',
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
}
/** show() {
* ------------------------------------------------------------------------ if (this._element.parentNode &&
* Class Definition this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
* ------------------------------------------------------------------------ $(this._element).hasClass(ClassName.ACTIVE) ||
*/ $(this._element).hasClass(ClassName.DISABLED)) {
return
class Tab {
constructor(element) {
this._element = element
} }
// Getters let target
let previous
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
const selector = Util.getSelectorFromElement(this._element)
static get VERSION() { if (listElement) {
return VERSION const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
previous = $.makeArray($(listElement).find(itemSelector))
previous = previous[previous.length - 1]
} }
// Public const hideEvent = $.Event(Event.HIDE, {
relatedTarget: this._element
})
show() { const showEvent = $.Event(Event.SHOW, {
if (this._element.parentNode && relatedTarget: previous
this._element.parentNode.nodeType === Node.ELEMENT_NODE && })
$(this._element).hasClass(ClassName.ACTIVE) ||
$(this._element).hasClass(ClassName.DISABLED)) {
return
}
let target if (previous) {
let previous $(previous).trigger(hideEvent)
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0] }
const selector = Util.getSelectorFromElement(this._element)
if (listElement) { $(this._element).trigger(showEvent)
const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
previous = $.makeArray($(listElement).find(itemSelector))
previous = previous[previous.length - 1]
}
const hideEvent = $.Event(Event.HIDE, { if (showEvent.isDefaultPrevented() ||
hideEvent.isDefaultPrevented()) {
return
}
if (selector) {
target = document.querySelector(selector)
}
this._activate(
this._element,
listElement
)
const complete = () => {
const hiddenEvent = $.Event(Event.HIDDEN, {
relatedTarget: this._element relatedTarget: this._element
}) })
const showEvent = $.Event(Event.SHOW, { const shownEvent = $.Event(Event.SHOWN, {
relatedTarget: previous relatedTarget: previous
}) })
if (previous) { $(previous).trigger(hiddenEvent)
$(previous).trigger(hideEvent) $(this._element).trigger(shownEvent)
}
$(this._element).trigger(showEvent)
if (showEvent.isDefaultPrevented() ||
hideEvent.isDefaultPrevented()) {
return
}
if (selector) {
target = document.querySelector(selector)
}
this._activate(
this._element,
listElement
)
const complete = () => {
const hiddenEvent = $.Event(Event.HIDDEN, {
relatedTarget: this._element
})
const shownEvent = $.Event(Event.SHOWN, {
relatedTarget: previous
})
$(previous).trigger(hiddenEvent)
$(this._element).trigger(shownEvent)
}
if (target) {
this._activate(target, target.parentNode, complete)
} else {
complete()
}
} }
dispose() { if (target) {
$.removeData(this._element, DATA_KEY) this._activate(target, target.parentNode, complete)
this._element = null } else {
} complete()
// Private
_activate(element, container, callback) {
let activeElements
if (container.nodeName === 'UL') {
activeElements = $(container).find(Selector.ACTIVE_UL)
} else {
activeElements = $(container).children(Selector.ACTIVE)
}
const active = activeElements[0]
const isTransitioning = callback &&
(active && $(active).hasClass(ClassName.FADE))
const complete = () => this._transitionComplete(
element,
active,
callback
)
if (active && isTransitioning) {
const transitionDuration = Util.getTransitionDurationFromElement(active)
$(active)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
_transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
const dropdownChild = $(active.parentNode).find(
Selector.DROPDOWN_ACTIVE_CHILD
)[0]
if (dropdownChild) {
$(dropdownChild).removeClass(ClassName.ACTIVE)
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
}
}
$(element).addClass(ClassName.ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
}
Util.reflow(element)
$(element).addClass(ClassName.SHOW)
if (element.parentNode &&
$(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
if (dropdownElement) {
const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector.DROPDOWN_TOGGLE))
$(dropdownToggleList).addClass(ClassName.ACTIVE)
}
element.setAttribute('aria-expanded', true)
}
if (callback) {
callback()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
if (!data) {
data = new Tab(this)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
} }
} }
/** dispose() {
* ------------------------------------------------------------------------ $.removeData(this._element, DATA_KEY)
* Data Api implementation this._element = null
* ------------------------------------------------------------------------ }
*/
$(document) // Private
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault() _activate(element, container, callback) {
Tab._jQueryInterface.call($(this), 'show') let activeElements
if (container.nodeName === 'UL') {
activeElements = $(container).find(Selector.ACTIVE_UL)
} else {
activeElements = $(container).children(Selector.ACTIVE)
}
const active = activeElements[0]
const isTransitioning = callback &&
(active && $(active).hasClass(ClassName.FADE))
const complete = () => this._transitionComplete(
element,
active,
callback
)
if (active && isTransitioning) {
const transitionDuration = Util.getTransitionDurationFromElement(active)
$(active)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
_transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
const dropdownChild = $(active.parentNode).find(
Selector.DROPDOWN_ACTIVE_CHILD
)[0]
if (dropdownChild) {
$(dropdownChild).removeClass(ClassName.ACTIVE)
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
}
}
$(element).addClass(ClassName.ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
}
Util.reflow(element)
$(element).addClass(ClassName.SHOW)
if (element.parentNode &&
$(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
if (dropdownElement) {
const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector.DROPDOWN_TOGGLE))
$(dropdownToggleList).addClass(ClassName.ACTIVE)
}
element.setAttribute('aria-expanded', true)
}
if (callback) {
callback()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
if (!data) {
data = new Tab(this)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
}) })
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface
} }
}
return Tab /**
})($) * ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
Tab._jQueryInterface.call($(this), 'show')
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface
}
export default Tab export default Tab

File diff suppressed because it is too large Load Diff

View File

@@ -7,144 +7,141 @@ import $ from 'jquery'
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Util = (($) => {
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
const TRANSITION_END = 'transitionend' /**
const MAX_UID = 1000000 * ------------------------------------------------------------------------
const MILLISECONDS_MULTIPLIER = 1000 * Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
// Shoutout AngusCroll (https://goo.gl/pxwQGp) const TRANSITION_END = 'transitionend'
function toType(obj) { const MAX_UID = 1000000
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase() const MILLISECONDS_MULTIPLIER = 1000
}
function getSpecialTransitionEndEvent() { // Shoutout AngusCroll (https://goo.gl/pxwQGp)
return { function toType(obj) {
bindType: TRANSITION_END, return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
delegateType: TRANSITION_END, }
handle(event) {
if ($(event.target).is(this)) { function getSpecialTransitionEndEvent() {
return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params return {
} bindType: TRANSITION_END,
return undefined // eslint-disable-line no-undefined delegateType: TRANSITION_END,
handle(event) {
if ($(event.target).is(this)) {
return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params
} }
return undefined // eslint-disable-line no-undefined
} }
} }
}
function transitionEndEmulator(duration) { function transitionEndEmulator(duration) {
let called = false let called = false
$(this).one(Util.TRANSITION_END, () => { $(this).one(Util.TRANSITION_END, () => {
called = true called = true
}) })
setTimeout(() => { setTimeout(() => {
if (!called) { if (!called) {
Util.triggerTransitionEnd(this) Util.triggerTransitionEnd(this)
} }
}, duration) }, duration)
return this return this
} }
function setTransitionEndSupport() { function setTransitionEndSupport() {
$.fn.emulateTransitionEnd = transitionEndEmulator $.fn.emulateTransitionEnd = transitionEndEmulator
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent() $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()
} }
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Public Util Api * Public Util Api
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const Util = { const Util = {
TRANSITION_END: 'bsTransitionEnd', TRANSITION_END: 'bsTransitionEnd',
getUID(prefix) { getUID(prefix) {
do { do {
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix)) } while (document.getElementById(prefix))
return prefix return prefix
}, },
getSelectorFromElement(element) { getSelectorFromElement(element) {
let selector = element.getAttribute('data-target') let selector = element.getAttribute('data-target')
if (!selector || selector === '#') { if (!selector || selector === '#') {
const hrefAttr = element.getAttribute('href') const hrefAttr = element.getAttribute('href')
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '' selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''
} }
return selector && document.querySelector(selector) ? selector : null return selector && document.querySelector(selector) ? selector : null
}, },
getTransitionDurationFromElement(element) { getTransitionDurationFromElement(element) {
if (!element) { if (!element) {
return 0 return 0
} }
// Get transition-duration of the element // Get transition-duration of the element
let transitionDuration = $(element).css('transition-duration') let transitionDuration = $(element).css('transition-duration')
const floatTransitionDuration = parseFloat(transitionDuration) const floatTransitionDuration = parseFloat(transitionDuration)
// Return 0 if element or transition duration is not found // Return 0 if element or transition duration is not found
if (!floatTransitionDuration) { if (!floatTransitionDuration) {
return 0 return 0
} }
// If multiple durations are defined, take the first // If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0] transitionDuration = transitionDuration.split(',')[0]
return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER
}, },
reflow(element) { reflow(element) {
return element.offsetHeight return element.offsetHeight
}, },
triggerTransitionEnd(element) { triggerTransitionEnd(element) {
$(element).trigger(TRANSITION_END) $(element).trigger(TRANSITION_END)
}, },
// TODO: Remove in v5 // TODO: Remove in v5
supportsTransitionEnd() { supportsTransitionEnd() {
return Boolean(TRANSITION_END) return Boolean(TRANSITION_END)
}, },
isElement(obj) { isElement(obj) {
return (obj[0] || obj).nodeType return (obj[0] || obj).nodeType
}, },
typeCheckConfig(componentName, config, configTypes) { typeCheckConfig(componentName, config, configTypes) {
for (const property in configTypes) { for (const property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) { if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
const expectedTypes = configTypes[property] const expectedTypes = configTypes[property]
const value = config[property] const value = config[property]
const valueType = value && Util.isElement(value) const valueType = value && Util.isElement(value)
? 'element' : toType(value) ? 'element' : toType(value)
if (!new RegExp(expectedTypes).test(valueType)) { if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error( throw new Error(
`${componentName.toUpperCase()}: ` + `${componentName.toUpperCase()}: ` +
`Option "${property}" provided type "${valueType}" ` + `Option "${property}" provided type "${valueType}" ` +
`but expected type "${expectedTypes}".`) `but expected type "${expectedTypes}".`)
}
} }
} }
} }
} }
}
setTransitionEndSupport() setTransitionEndSupport()
return Util
})($)
export default Util export default Util