blob: ce71ad0f8cb0a26932a1afcbbb3a0d9f1c4eb8a6 [file] [edit]
/**
* --------------------------------------------------------------------------
* Bootstrap drawer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import DialogBase from './dialog-base.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import Swipe from './util/swipe.js'
import { enableDismissTrigger } from './util/component-functions.js'
import {
isDisabled,
isRTL,
isVisible
} from './util/index.js'
/**
* Constants
*/
const NAME = 'drawer'
const DATA_KEY = 'bs.drawer'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="drawer"]'
const Default = {
backdrop: true,
keyboard: true,
scroll: false
}
const DefaultType = {
backdrop: '(boolean|string)',
keyboard: 'boolean',
scroll: 'boolean'
}
/**
* Class definition
*/
class Drawer extends DialogBase {
constructor(element, config) {
super(element, config)
this._swipeHelper = null
}
// Getters
static get Default() {
return Default
}
static get DefaultType() {
return DefaultType
}
static get NAME() {
return NAME
}
// Public
dispose() {
if (this._swipeHelper) {
this._swipeHelper.dispose()
}
super.dispose()
}
// Protected — hook overrides
_getShowOptions() {
const useModal = Boolean(this._config.backdrop) || !this._config.scroll
return {
modal: useModal,
preventBodyScroll: !this._config.scroll
}
}
_onBeforeShow() {
this._initSwipe()
}
_getInstantClassName() {
return 'drawer-instant'
}
_getStaticClassName() {
return 'drawer-static'
}
// Private
_initSwipe() {
if (this._swipeHelper || !Swipe.isSupported()) {
return
}
// Determine which swipe direction dismisses based on placement
const swipeConfig = {}
const element = this._element
if (element.classList.contains('drawer-bottom')) {
swipeConfig.downCallback = () => this.hide()
} else if (element.classList.contains('drawer-top')) {
swipeConfig.upCallback = () => this.hide()
} else if (element.classList.contains('drawer-end')) {
// RTL: swipe left to dismiss end drawer
if (isRTL()) {
swipeConfig.leftCallback = () => this.hide()
} else {
swipeConfig.rightCallback = () => this.hide()
}
} else if (isRTL()) {
// drawer-start (default): swipe right to dismiss in RTL
swipeConfig.rightCallback = () => this.hide()
} else {
// drawer-start (default): swipe left to dismiss in LTR
swipeConfig.leftCallback = () => this.hide()
}
this._swipeHelper = new Swipe(element, swipeConfig)
}
}
/**
* Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
}
if (isDisabled(this)) {
return
}
EventHandler.one(target, EVENT_HIDDEN, () => {
if (isVisible(this)) {
this.focus()
}
})
// Avoid conflict when clicking a toggler of a drawer, while another is open
const alreadyOpen = SelectorEngine.findOne('dialog.drawer[open]')
if (alreadyOpen && alreadyOpen !== target) {
Drawer.getInstance(alreadyOpen).hide()
}
const data = Drawer.getOrCreateInstance(target)
data.toggle(this)
})
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
for (const selector of SelectorEngine.find('dialog.drawer[open]')) {
Drawer.getOrCreateInstance(selector).show()
}
})
EventHandler.on(window, EVENT_RESIZE, () => {
for (const element of SelectorEngine.find('dialog[open][class*="\\:drawer"]')) {
if (getComputedStyle(element).position !== 'fixed') {
Drawer.getOrCreateInstance(element).hide()
}
}
})
enableDismissTrigger(Drawer)
export default Drawer