-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
Accordion.astro
126 lines (98 loc) · 4.71 KB
/
Accordion.astro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
---
import type { AccordionItem } from '.'
interface Props {
children: AccordionItem | AccordionItem[]
}
---
<div class="accordion">
<ul class="accordion__wrapper">
<slot />
</ul>
</div>
<script type="module">
// variables
const accordionItems = [...document.querySelectorAll('.accordion__item')]
// functions
const getPanelHeight = (accordionItem) => {
const accordionInner = accordionItem.querySelector('.panel__inner')
return accordionInner.getBoundingClientRect().height
}
const openAccordionItem = (accordionItem) => {
const accordionItemHeader = accordionItem.querySelector('.accordion__header')
const accordionToggleIndicator = accordionItem.querySelector('.header__toggle-indicator')
const accordionPanel = accordionItem.querySelector('.accordion__panel')
accordionPanel.style.height = `${getPanelHeight(accordionItem)}px`
accordionItem.classList.add('is-active')
accordionItemHeader.setAttribute('aria-expanded', true)
accordionToggleIndicator.innerHTML = `<svg class="header__toggle-indicator" aria-hidden="true" data-prefix="fas" data-icon="minus" class="svg-inline--fa fa-minus fa-w-14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M416 208H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"/></svg>`
}
const closeAccordionItem = (accordionItem) => {
const accordionItemHeader = accordionItem.querySelector('.accordion__header')
const accordionToggleIndicator = accordionItem.querySelector('.header__toggle-indicator')
const accordionPanel = accordionItem.querySelector('.accordion__panel')
accordionItem.classList.remove('is-active')
accordionPanel.style.height = 0
accordionItemHeader.focus()
accordionItemHeader.setAttribute('aria-expanded', false)
accordionToggleIndicator.innerHTML = `<svg class="header__toggle-indicator" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"/></svg>`
}
const isAccordionOpen = (accordionItem) => {
return accordionItem.classList.contains('is-active')
}
function toggleAccordionItem() {
const accordionItem = event.target.closest('.accordion__item')
if (!accordionItem || event.target.closest('.accordion__panel')) return
isAccordionOpen(accordionItem) ? closeAccordionItem(accordionItem) : openAccordionItem(accordionItem)
}
function recalculateHeight() {
const toggledAccordionItems = accordionItems.filter((element) => element.classList.contains('is-active'))
toggledAccordionItems.forEach((toggledAccordionItem) => {
const accordionPanel = toggledAccordionItem.querySelector('.accordion__panel')
accordionPanel.style.height = `${getPanelHeight(toggledAccordionItem)}px`
})
}
// execution
accordionItems.forEach((item, index) => {
const accordionItemHeader = item.querySelector('.accordion__header')
const accordionItemPanel = item.querySelector('.accordion__panel')
accordionItemHeader.setAttribute('id', `accordion-item${index + 1}`)
accordionItemPanel.setAttribute('id', `item${index + 1}`)
accordionItemHeader.setAttribute('aria-controls', `item${index + 1}`)
accordionItemPanel.setAttribute('aria-labelledby', `accordion-item${index + 1}`)
})
document.addEventListener('keydown', (event) => {
const accordionItem = event.target.closest('.accordion__item')
if (event.key !== 'Escape') return
if (!accordionItem) return
if (isAccordionOpen(accordionItem)) {
closeAccordionItem(accordionItem)
}
})
document.addEventListener('keydown', (event) => {
if (!event.target.closest('.accordion__header')) return
const accordionWrapper = event.target.closest('.accordion__wrapper')
const accordionItem = event.target.closest('.accordion__item')
const accordionItems = [...accordionWrapper.querySelectorAll('.accordion__item')]
const index = accordionItems.findIndex((element) => element === accordionItem)
const { key } = event
let targetItem
if (key === 'ArrowDown') {
targetItem = accordionItems[index + 1]
}
if (key === 'ArrowUp') {
targetItem = accordionItems[index - 1]
}
if (targetItem) {
event.preventDefault()
targetItem.querySelector('.accordion__header').focus()
}
})
window.toggleAccordionItem = toggleAccordionItem
window.onresize = recalculateHeight
</script>
<style is:global>
.accordion__wrapper {
list-style: none;
padding: 0;
}
</style>