CollapsibleSection.vue 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. <template>
  2. <div class="border rounded-md mb-4">
  3. <button
  4. @click="toggleCollapse"
  5. 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"
  6. >
  7. <span>{{ title }}</span>
  8. <svg
  9. class="w-5 h-5 transform transition-transform duration-200"
  10. :class="{ 'rotate-180': !isCollapsed }"
  11. fill="none"
  12. stroke-linecap="round"
  13. stroke-linejoin="round"
  14. stroke-width="2"
  15. viewBox="0 0 24 24"
  16. stroke="currentColor"
  17. >
  18. <path d="M19 9l-7 7-7-7"></path>
  19. </svg>
  20. </button>
  21. <transition
  22. name="collapse"
  23. @enter="startTransition"
  24. @after-enter="endTransition"
  25. @before-leave="startTransition"
  26. @after-leave="endTransition"
  27. >
  28. <div v-show="!isCollapsed" class="overflow-hidden">
  29. <div class="p-4">
  30. <slot></slot>
  31. </div>
  32. </div>
  33. </transition>
  34. </div>
  35. </template>
  36. <script setup>
  37. import { ref } from 'vue';
  38. const props = defineProps({
  39. title: {
  40. type: String,
  41. required: true,
  42. },
  43. initiallyCollapsed: {
  44. type: Boolean,
  45. default: true,
  46. },
  47. });
  48. const isCollapsed = ref(props.initiallyCollapsed);
  49. const toggleCollapse = () => {
  50. isCollapsed.value = !isCollapsed.value;
  51. };
  52. const startTransition = (el) => {
  53. el.style.height = 'auto';
  54. const height = el.scrollHeight;
  55. el.style.height = '0px';
  56. el.offsetHeight; // force reflow
  57. el.style.height = height + 'px';
  58. };
  59. const endTransition = (el) => {
  60. el.style.height = '';
  61. };
  62. </script>
  63. <style scoped>
  64. .collapse-enter-active,
  65. .collapse-leave-active {
  66. transition: height 0.3s ease-out;
  67. overflow: hidden;
  68. }
  69. .collapse-enter-from,
  70. .collapse-leave-to {
  71. height: 0;
  72. }
  73. </style>