123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- <template>
- <div class="border rounded-md mb-4">
- <button
- @click="toggleCollapse"
- class="flex justify-between items-center w-full px-4 py-2 text-left text-gray-700 font-medium bg-gray-100 hover:bg-gray-200 focus:outline-none"
- >
- <span>{{ title }}</span>
- <svg
- class="w-5 h-5 transform transition-transform duration-200"
- :class="{ 'rotate-180': !isCollapsed }"
- fill="none"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- viewBox="0 0 24 24"
- stroke="currentColor"
- >
- <path d="M19 9l-7 7-7-7"></path>
- </svg>
- </button>
- <transition
- name="collapse"
- @enter="startTransition"
- @after-enter="endTransition"
- @before-leave="startTransition"
- @after-leave="endTransition"
- >
- <div v-show="!isCollapsed" class="overflow-hidden">
- <div class="p-4">
- <slot></slot>
- </div>
- </div>
- </transition>
- </div>
- </template>
-
- <script setup>
- import { ref } from 'vue';
-
- const props = defineProps({
- title: {
- type: String,
- required: true,
- },
- initiallyCollapsed: {
- type: Boolean,
- default: true,
- },
- });
-
- const isCollapsed = ref(props.initiallyCollapsed);
-
- const toggleCollapse = () => {
- isCollapsed.value = !isCollapsed.value;
- };
-
- const startTransition = (el) => {
- el.style.height = 'auto';
- const height = el.scrollHeight;
- el.style.height = '0px';
- el.offsetHeight; // force reflow
- el.style.height = height + 'px';
- };
-
- const endTransition = (el) => {
- el.style.height = '';
- };
- </script>
-
- <style scoped>
- .collapse-enter-active,
- .collapse-leave-active {
- transition: height 0.3s ease-out;
- overflow: hidden;
- }
-
- .collapse-enter-from,
- .collapse-leave-to {
- height: 0;
- }
- </style>
|