Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
/**
2
 * --------------------------------------------------------------------------
3
 * Bootstrap offcanvas.js
4
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
 * --------------------------------------------------------------------------
6
 */
7
 
8
import BaseComponent from './base-component'
9
import EventHandler from './dom/event-handler'
10
import SelectorEngine from './dom/selector-engine'
11
import Backdrop from './util/backdrop'
12
import { enableDismissTrigger } from './util/component-functions'
13
import FocusTrap from './util/focustrap'
14
import {
15
  defineJQueryPlugin,
16
  isDisabled,
17
  isVisible
18
} from './util/index'
19
import ScrollBarHelper from './util/scrollbar'
20
 
21
/**
22
 * Constants
23
 */
24
 
25
const NAME = 'offcanvas'
26
const DATA_KEY = 'bs.offcanvas'
27
const EVENT_KEY = `.${DATA_KEY}`
28
const DATA_API_KEY = '.data-api'
29
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
30
const ESCAPE_KEY = 'Escape'
31
 
32
const CLASS_NAME_SHOW = 'show'
33
const CLASS_NAME_SHOWING = 'showing'
34
const CLASS_NAME_HIDING = 'hiding'
35
const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'
36
const OPEN_SELECTOR = '.offcanvas.show'
37
 
38
const EVENT_SHOW = `show${EVENT_KEY}`
39
const EVENT_SHOWN = `shown${EVENT_KEY}`
40
const EVENT_HIDE = `hide${EVENT_KEY}`
41
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
42
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
43
const EVENT_RESIZE = `resize${EVENT_KEY}`
44
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
45
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
46
 
47
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="offcanvas"]'
48
 
49
const Default = {
50
  backdrop: true,
51
  keyboard: true,
52
  scroll: false
53
}
54
 
55
const DefaultType = {
56
  backdrop: '(boolean|string)',
57
  keyboard: 'boolean',
58
  scroll: 'boolean'
59
}
60
 
61
/**
62
 * Class definition
63
 */
64
 
65
class Offcanvas extends BaseComponent {
66
  constructor(element, config) {
67
    super(element, config)
68
 
69
    this._isShown = false
70
    this._backdrop = this._initializeBackDrop()
71
    this._focustrap = this._initializeFocusTrap()
72
    this._addEventListeners()
73
  }
74
 
75
  // Getters
76
  static get Default() {
77
    return Default
78
  }
79
 
80
  static get DefaultType() {
81
    return DefaultType
82
  }
83
 
84
  static get NAME() {
85
    return NAME
86
  }
87
 
88
  // Public
89
  toggle(relatedTarget) {
90
    return this._isShown ? this.hide() : this.show(relatedTarget)
91
  }
92
 
93
  show(relatedTarget) {
94
    if (this._isShown) {
95
      return
96
    }
97
 
98
    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })
99
 
100
    if (showEvent.defaultPrevented) {
101
      return
102
    }
103
 
104
    this._isShown = true
105
    this._backdrop.show()
106
 
107
    if (!this._config.scroll) {
108
      new ScrollBarHelper().hide()
109
    }
110
 
111
    this._element.setAttribute('aria-modal', true)
112
    this._element.setAttribute('role', 'dialog')
113
    this._element.classList.add(CLASS_NAME_SHOWING)
114
 
115
    const completeCallBack = () => {
116
      if (!this._config.scroll || this._config.backdrop) {
117
        this._focustrap.activate()
118
      }
119
 
120
      this._element.classList.add(CLASS_NAME_SHOW)
121
      this._element.classList.remove(CLASS_NAME_SHOWING)
122
      EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })
123
    }
124
 
125
    this._queueCallback(completeCallBack, this._element, true)
126
  }
127
 
128
  hide() {
129
    if (!this._isShown) {
130
      return
131
    }
132
 
133
    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
134
 
135
    if (hideEvent.defaultPrevented) {
136
      return
137
    }
138
 
139
    this._focustrap.deactivate()
140
    this._element.blur()
141
    this._isShown = false
142
    this._element.classList.add(CLASS_NAME_HIDING)
143
    this._backdrop.hide()
144
 
145
    const completeCallback = () => {
146
      this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)
147
      this._element.removeAttribute('aria-modal')
148
      this._element.removeAttribute('role')
149
 
150
      if (!this._config.scroll) {
151
        new ScrollBarHelper().reset()
152
      }
153
 
154
      EventHandler.trigger(this._element, EVENT_HIDDEN)
155
    }
156
 
157
    this._queueCallback(completeCallback, this._element, true)
158
  }
159
 
160
  dispose() {
161
    this._backdrop.dispose()
162
    this._focustrap.deactivate()
163
    super.dispose()
164
  }
165
 
166
  // Private
167
  _initializeBackDrop() {
168
    const clickCallback = () => {
169
      if (this._config.backdrop === 'static') {
170
        EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
171
        return
172
      }
173
 
174
      this.hide()
175
    }
176
 
177
    // 'static' option will be translated to true, and booleans will keep their value
178
    const isVisible = Boolean(this._config.backdrop)
179
 
180
    return new Backdrop({
181
      className: CLASS_NAME_BACKDROP,
182
      isVisible,
183
      isAnimated: true,
184
      rootElement: this._element.parentNode,
185
      clickCallback: isVisible ? clickCallback : null
186
    })
187
  }
188
 
189
  _initializeFocusTrap() {
190
    return new FocusTrap({
191
      trapElement: this._element
192
    })
193
  }
194
 
195
  _addEventListeners() {
196
    EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
197
      if (event.key !== ESCAPE_KEY) {
198
        return
199
      }
200
 
201
      if (this._config.keyboard) {
202
        this.hide()
203
        return
204
      }
205
 
206
      EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
207
    })
208
  }
209
 
210
  // Static
211
  static jQueryInterface(config) {
212
    return this.each(function () {
213
      const data = Offcanvas.getOrCreateInstance(this, config)
214
 
215
      if (typeof config !== 'string') {
216
        return
217
      }
218
 
219
      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
220
        throw new TypeError(`No method named "${config}"`)
221
      }
222
 
223
      data[config](this)
224
    })
225
  }
226
}
227
 
228
/**
229
 * Data API implementation
230
 */
231
 
232
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
233
  const target = SelectorEngine.getElementFromSelector(this)
234
 
235
  if (['A', 'AREA'].includes(this.tagName)) {
236
    event.preventDefault()
237
  }
238
 
239
  if (isDisabled(this)) {
240
    return
241
  }
242
 
243
  EventHandler.one(target, EVENT_HIDDEN, () => {
244
    // focus on trigger when it is closed
245
    if (isVisible(this)) {
246
      this.focus()
247
    }
248
  })
249
 
250
  // avoid conflict when clicking a toggler of an offcanvas, while another is open
251
  const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)
252
  if (alreadyOpen && alreadyOpen !== target) {
253
    Offcanvas.getInstance(alreadyOpen).hide()
254
  }
255
 
256
  const data = Offcanvas.getOrCreateInstance(target)
257
  data.toggle(this)
258
})
259
 
260
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
261
  for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {
262
    Offcanvas.getOrCreateInstance(selector).show()
263
  }
264
})
265
 
266
EventHandler.on(window, EVENT_RESIZE, () => {
267
  for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {
268
    if (getComputedStyle(element).position !== 'fixed') {
269
      Offcanvas.getOrCreateInstance(element).hide()
270
    }
271
  }
272
})
273
 
274
enableDismissTrigger(Offcanvas)
275
 
276
/**
277
 * jQuery
278
 */
279
 
280
defineJQueryPlugin(Offcanvas)
281
 
282
export default Offcanvas