initial commit

This commit is contained in:
Zoe
2023-05-30 15:04:51 -05:00
commit 539805dacb
20 changed files with 14788 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<script setup>
const item = inject('accordionItem');
const contentHeight = ref(0);
const content = ref(null);
watch(item.hidden, () => {
if (!item.hidden.value) {
setTimeout(() => {
contentHeight.value = content.value.offsetHeight;
})
}
})
</script>
<template>
<div :id="`vueless-${item.index}`" role="region" :aria-labelledby="`vueless-${item.index}`" class="vl-accordion-content"
:style="`--vueless-accordion-content-height: ${contentHeight}px`"
:data-state="(item.isOpen.value) ? 'open' : 'closed'"
@animationend="(!item.isOpen.value) ? item.hidden.value = true : ''" :hidden="item.hidden.value">
<div ref="content">
<slot />
</div>
</div>
</template>

View File

@@ -0,0 +1,194 @@
<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>

View File

@@ -0,0 +1,19 @@
<script setup>
const props = defineProps({
value: {
type: String,
default: "",
}
})
const accordion = inject('accordion')
const item = accordion.registerAccordionItem(props.value);
provide('accordionItem', item);
</script>
<template>
<div class="vl-accordion-item" :data-state="(item.isOpen.value) ? 'open' : 'closed'">
<slot />
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
const item = inject('accordionItem');
const { toggleAccordion } = inject('accordion');
</script>
<template>
<h3 class="vl-accordion-header">
<button :id="`vueless-${item.index}`" :aria-controls="`vueless-${item.index}`" :aria-expanded="item.isOpen.value"
@click="toggleAccordion(item.index)">
<slot />
</button>
</h3>
</template>