194 lines
4.0 KiB
Vue
194 lines
4.0 KiB
Vue
<script setup>
|
|
const props = defineProps({
|
|
type: {
|
|
type: String,
|
|
default: "multiple"
|
|
},
|
|
defaultValue: {
|
|
type: String
|
|
}
|
|
})
|
|
|
|
const accordion = ref(null);
|
|
|
|
const accordionItems = ref([])
|
|
|
|
function toggleAccordion(index) {
|
|
const item = accordionItems.value[index];
|
|
if (props.type === "single") {
|
|
// close everything but the one we just opened
|
|
accordionItems.value.forEach((item, i) => {
|
|
if (i === index) return;
|
|
item.isOpen = false;
|
|
})
|
|
}
|
|
if (item.hidden) {
|
|
item.hidden = false;
|
|
}
|
|
item.isOpen = !item.isOpen;
|
|
}
|
|
|
|
const registerAccordionItem = (value) => {
|
|
const item = { isOpen: ref(false), hidden: ref(true), value }
|
|
accordionItems.value.push(item);
|
|
|
|
return { index: accordionItems.value.indexOf(item), isOpen: item.isOpen, hidden: item.hidden, value };
|
|
};
|
|
|
|
const unregisterAccordionItem = (index) => {
|
|
accordionItems.value.splice(index, 1);
|
|
};
|
|
|
|
provide('accordion', { registerAccordionItem, unregisterAccordionItem, toggleAccordion })
|
|
|
|
function keydown(event) {
|
|
const headers = Array.from(accordion.value.querySelectorAll(".vl-accordion-header"));
|
|
|
|
if (event.key === "ArrowUp") {
|
|
event.preventDefault();
|
|
const focusedElement = document.activeElement;
|
|
const currentIndex = headers.indexOf(focusedElement.parentElement);
|
|
const nextIndex = currentIndex > 0 ? currentIndex - 1 : headers.length - 1;
|
|
const nextButton = headers[nextIndex].querySelector("button");
|
|
nextButton.focus();
|
|
return;
|
|
}
|
|
|
|
if (event.key === "ArrowDown") {
|
|
event.preventDefault();
|
|
const focusedElement = document.activeElement;
|
|
const currentIndex = headers.indexOf(focusedElement.parentElement);
|
|
const nextIndex = currentIndex < headers.length - 1 ? currentIndex + 1 : 0;
|
|
const nextButton = headers[nextIndex].querySelector("button");
|
|
nextButton.focus();
|
|
return;
|
|
}
|
|
|
|
if (event.key === "End") {
|
|
event.preventDefault();
|
|
return headers[headers.length - 1].querySelector("button").focus();
|
|
}
|
|
|
|
if (event.key === "Home") {
|
|
event.preventDefault();
|
|
return headers[0].querySelector("button").focus();
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (props.defaultValue) {
|
|
const item = accordionItems.value.filter(item => item.value === props.defaultValue)[0];
|
|
item.isOpen = true;
|
|
item.hidden = false;
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="vl-accordion" ref="accordion" @keydown="keydown($event)">
|
|
<slot />
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.vl-accordion {
|
|
border-radius: 6px;
|
|
background-color: #1D1E1F;
|
|
box-shadow: 0 0 16px 0 #07070738;
|
|
width: 70%;
|
|
user-select: none;
|
|
cursor: default;
|
|
}
|
|
|
|
.vl-accordion-content {
|
|
width: var(--vueless-accordion-content-width);
|
|
background-color: #0B0C0D;
|
|
overflow: hidden;
|
|
transform-origin: top center;
|
|
height: 0;
|
|
}
|
|
|
|
.vl-accordion-item:focus-within {
|
|
position: relative;
|
|
z-index: 1;
|
|
box-shadow: #4e367e 0px 0px 0px 2px
|
|
}
|
|
|
|
.vl-accordion-content div {
|
|
padding: 15px 20px;
|
|
}
|
|
|
|
.vl-accordion-content[data-state="closed"] {
|
|
animation: 300ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards running closeAccordion;
|
|
}
|
|
|
|
.vl-accordion-content[data-state="open"] {
|
|
animation: 300ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards running openAccordion;
|
|
}
|
|
|
|
.vl-accordion-item h3 button svg {
|
|
transition: transform 300ms ease;
|
|
}
|
|
|
|
.vl-accordion-item[data-state="open"] h3 button svg {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.vl-accordion-item {
|
|
background-color: #161718;
|
|
height: 100%;
|
|
margin-top: 1px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.vl-accordion-item:first-child {
|
|
margin-top: 0px;
|
|
border-radius: 6px 6px 0 0;
|
|
}
|
|
|
|
.vl-accordion-item:last-child {
|
|
border-radius: 0 0 6px 6px;
|
|
}
|
|
|
|
.vl-accordion-header:hover {
|
|
background-color: #131415;
|
|
}
|
|
|
|
.vl-accordion-header button {
|
|
all: unset;
|
|
background-color: transparent;
|
|
height: 45px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
line-height: 1;
|
|
padding: 0px 20px;
|
|
flex: 1 1 0%;
|
|
box-shadow: #1D1E1F 0px 1px 0px;
|
|
}
|
|
|
|
.vl-accordion-header {
|
|
all: unset;
|
|
display: flex;
|
|
}
|
|
|
|
@keyframes closeAccordion {
|
|
0% {
|
|
height: var(--vueless-accordion-content-height);
|
|
}
|
|
|
|
100% {
|
|
height: 0px;
|
|
}
|
|
}
|
|
|
|
@keyframes openAccordion {
|
|
0% {
|
|
height: 0px;
|
|
}
|
|
|
|
100% {
|
|
height: var(--vueless-accordion-content-height);
|
|
}
|
|
}
|
|
</style> |