1 | import BaseComponent from 'bootstrap/js/src/base-component.js'
|
2 |
|
3 | import EventHandler from 'bootstrap/js/src/dom/event-handler'
|
4 | import SelectorEngine from 'bootstrap/js/src/dom/selector-engine'
|
5 |
|
6 | import { focusSimbling } from './util/dom'
|
7 |
|
8 | const NAME = 'accordion'
|
9 | const DATA_KEY = 'bs.accordion'
|
10 | const EVENT_KEY = `.${DATA_KEY}`
|
11 | const DATA_API_KEY = '.data-api'
|
12 |
|
13 | const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
|
14 | const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
|
15 |
|
16 | const SELECTOR_HEADBTN_WRAPPER = '.accordion'
|
17 | const SELECTOR_HEADBTN = '.accordion-item > .accordion-header [data-bs-toggle="collapse"]'
|
18 |
|
19 | class Accordion extends BaseComponent {
|
20 | constructor(element) {
|
21 | super(element)
|
22 |
|
23 | this._bindEvents()
|
24 | }
|
25 |
|
26 |
|
27 |
|
28 | static get NAME() {
|
29 | return NAME
|
30 | }
|
31 |
|
32 |
|
33 | handleKeyDown(keyName, target, evt) {
|
34 | const eventKeyCallback = {
|
35 | ArrowDown: (target) => this._focusNext(target),
|
36 | ArrowUp: (target) => this._focusPrev(target),
|
37 | Home: (target) => this._focusFirst(target),
|
38 | End: (target) => this._focusLast(target),
|
39 | }
|
40 | if (typeof eventKeyCallback[keyName] === 'function') {
|
41 | if (evt) {
|
42 | evt.preventDefault()
|
43 | }
|
44 | eventKeyCallback[keyName](target)
|
45 | }
|
46 | }
|
47 |
|
48 |
|
49 | _bindEvents() {
|
50 |
|
51 |
|
52 | SelectorEngine.find(SELECTOR_HEADBTN, this._element).forEach((accHead) => {
|
53 | EventHandler.on(accHead, EVENT_KEYDOWN, (evt) => {
|
54 | this.handleKeyDown(evt.key, evt.currentTarget, evt)
|
55 | })
|
56 | })
|
57 | }
|
58 |
|
59 | _getHeadButtons() {
|
60 | return SelectorEngine.find(':scope > ' + SELECTOR_HEADBTN, this._element)
|
61 | }
|
62 |
|
63 | _focusNext(target) {
|
64 | focusSimbling(target, this._getHeadButtons(), { loop: true })
|
65 | }
|
66 |
|
67 | _focusPrev(target) {
|
68 | focusSimbling(target, this._getHeadButtons(), { isDirectionTop: true, loop: true })
|
69 | }
|
70 |
|
71 | _focusFirst(target) {
|
72 | focusSimbling(target, this._getHeadButtons(), { isDirectionTop: true, isLimit: true })
|
73 | }
|
74 |
|
75 | _focusLast(target) {
|
76 | focusSimbling(target, this._getHeadButtons(), { isLimit: true })
|
77 | }
|
78 | }
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | const accordionToggles = SelectorEngine.find(SELECTOR_HEADBTN)
|
91 | accordionToggles.forEach((toggle) => {
|
92 | EventHandler.one(toggle, EVENT_KEYDOWN_DATA_API, (evt) => {
|
93 | const parent = toggle.closest(SELECTOR_HEADBTN_WRAPPER)
|
94 | if (parent) {
|
95 | const accordion = Accordion.getOrCreateInstance(parent)
|
96 | accordion.handleKeyDown(evt.key, toggle, evt)
|
97 | }
|
98 | })
|
99 | })
|
100 |
|
101 | export default Accordion
|