EventCard.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <template>
  2. <article class="event-card">
  3. <button
  4. type="button"
  5. class="event-header"
  6. :aria-expanded="isOpen"
  7. @click="isOpen = !isOpen"
  8. >
  9. <div class="min-w-0 flex-1 text-left">
  10. <span class="event-badge">
  11. <i :class="`mdi ${typeIcon}`" style="font-size: 0.875rem;"/>
  12. {{ eventTypeLabel(event.type) }}
  13. </span>
  14. <h3 class="text-subtitle-1 font-semibold text-text mt-2 mb-1 truncate">
  15. {{ event.title }}
  16. </h3>
  17. <p v-if="dateTimeLabel" class="text-body-2 text-text-2">
  18. {{ dateTimeLabel }}
  19. </p>
  20. </div>
  21. <span
  22. class="material-icons text-violet-normal shrink-0 transition-transform duration-300"
  23. :class="{ 'rotate-180': isOpen }"
  24. >
  25. expand_more
  26. </span>
  27. </button>
  28. <div class="event-body" :class="isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'">
  29. <div class="overflow-hidden">
  30. <div class="px-6 pb-6 pt-1">
  31. <img
  32. v-if="event.cover_url"
  33. :src="event.cover_url"
  34. :alt="event.title"
  35. class="event-cover"
  36. :class="{ 'mb-5': paragraphs.length }"
  37. loading="lazy"
  38. >
  39. <p
  40. v-for="(paragraph, index) in paragraphs"
  41. :key="index"
  42. lang="pt-BR"
  43. class="text-body-2 text-text-2 text-justify hyphens-auto event-text"
  44. :class="{ 'mt-3': index > 0 }"
  45. >
  46. {{ paragraph }}
  47. </p>
  48. <EventCarousel
  49. v-if="photos.length"
  50. :photos="photos"
  51. class="mt-4"
  52. />
  53. </div>
  54. </div>
  55. </div>
  56. </article>
  57. </template>
  58. <script setup lang="ts">
  59. import type { CompanyEvent } from '~/composables/useEvents'
  60. const { event } = defineProps<{ event: CompanyEvent }>()
  61. const isOpen = ref(true)
  62. const typeIcon = computed(() => (event.type === 'esporte' ? 'mdi-soccer' : 'mdi-palette-outline'))
  63. const dateTimeLabel = computed(() => formatEventDateTime(event))
  64. const photos = computed(() => (event.media ?? []).filter((photo) => !!photo.url))
  65. const paragraphs = computed(() =>
  66. (event.description ?? '')
  67. .split(/\r?\n/)
  68. .map((line) => line.trim())
  69. .filter(Boolean)
  70. )
  71. </script>
  72. <style scoped>
  73. .event-card {
  74. background-color: #ffffff;
  75. border-radius: 1rem;
  76. box-shadow: 0 4px 24px rgba(102, 29, 117, 0.14);
  77. transition: box-shadow 0.2s;
  78. overflow: hidden;
  79. height: 100%;
  80. }
  81. .event-card:hover {
  82. box-shadow: 0 8px 36px rgba(102, 29, 117, 0.22);
  83. }
  84. .event-header {
  85. display: flex;
  86. align-items: center;
  87. gap: 1rem;
  88. width: 100%;
  89. padding: 1.25rem 1.5rem;
  90. cursor: pointer;
  91. text-align: left;
  92. }
  93. .event-badge {
  94. display: inline-flex;
  95. align-items: center;
  96. gap: 0.25rem;
  97. background-color: #f0e8f1;
  98. color: #661d75;
  99. font-family: "Nunito", sans-serif;
  100. font-size: 0.6875rem;
  101. font-weight: 600;
  102. letter-spacing: 0.5px;
  103. text-transform: uppercase;
  104. padding: 0.125rem 0.5rem;
  105. border-radius: 9999px;
  106. }
  107. .event-body {
  108. display: grid;
  109. transition: grid-template-rows 0.35s ease;
  110. }
  111. .event-text {
  112. line-height: 1.7;
  113. }
  114. .event-cover {
  115. width: 100%;
  116. height: 240px;
  117. object-fit: cover;
  118. border-radius: 0.75rem;
  119. display: block;
  120. }
  121. @media (min-width: 1024px) {
  122. .event-cover {
  123. height: 280px;
  124. }
  125. }
  126. </style>