detail page port
This commit is contained in:
73
components/event/list/Index.vue
Normal file
73
components/event/list/Index.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="event-list">
|
||||
<div class="filter-bar">
|
||||
<h3 class="h4">Upcoming Events</h3>
|
||||
|
||||
<div class="filters">
|
||||
<btn
|
||||
:class="{ active: filter === 'current' }"
|
||||
@click="onFilterClick('current')"
|
||||
>Current</btn
|
||||
>
|
||||
<btn
|
||||
:class="{ active: filter === 'past' }"
|
||||
@click="onFilterClick('past')"
|
||||
>Past</btn
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<event-list-item
|
||||
v-for="event in filteredEvents"
|
||||
:event="event"
|
||||
@hover="(e) => emit('eventHover', e)"
|
||||
@blur="(e) => emit('eventBlur', e)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({ events: Array })
|
||||
const emit = defineEmits(['eventHover', 'eventBlur'])
|
||||
|
||||
const { filter, filteredEvents } = useFilteredEvents(props.events)
|
||||
|
||||
const onFilterClick = (label) => {
|
||||
navigateTo({
|
||||
query: {
|
||||
filter: label,
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.event-list {
|
||||
margin-top: desktop-vw(78px);
|
||||
|
||||
.filter-bar {
|
||||
padding: desktop-vw(20px) var(--layout-margin);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: desktop-vw(15px);
|
||||
}
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
margin-top: mobile-vw(75px);
|
||||
|
||||
.filter-bar {
|
||||
padding: mobile-vw(20px) var(--layout-margin);
|
||||
display: block;
|
||||
|
||||
.filters {
|
||||
margin-top: mobile-vw(15px);
|
||||
gap: mobile-vw(15px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
207
components/event/list/Item.vue
Normal file
207
components/event/list/Item.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="event"
|
||||
:class="[
|
||||
`event-list-item type-${type}`,
|
||||
{ 'mobile-active': mobileActive },
|
||||
]"
|
||||
@mouseenter="emit('hover', event.id)"
|
||||
@mouseleave="emit('blur', event.id)"
|
||||
ref="container"
|
||||
>
|
||||
<prismic-media
|
||||
v-if="type === 'recirc'"
|
||||
class="preview-image"
|
||||
:image="event.preview_image"
|
||||
:video="event.preview_video"
|
||||
desktopSize="25vw"
|
||||
/>
|
||||
|
||||
<p class="title p-l">{{ event.title }}</p>
|
||||
<p class="date p-l">{{ date }}</p>
|
||||
|
||||
<prismic-rich-text
|
||||
v-if="type === 'row'"
|
||||
class="description entry"
|
||||
:field="event.description"
|
||||
/>
|
||||
|
||||
<div v-if="rsvpExpanded && type === 'row'" class="btn-wrap">
|
||||
<event-rsvp
|
||||
:event-name="event.title"
|
||||
:event-date="date"
|
||||
@success="rsvpExpanded = false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="btn-wrap">
|
||||
<btn
|
||||
v-if="eventType === 'rsvp' && type === 'row'"
|
||||
class="btn-rsvp"
|
||||
secondary
|
||||
:hover="false"
|
||||
@click="rsvpExpanded = true"
|
||||
>RSVP</btn
|
||||
>
|
||||
|
||||
<smart-link :field="link">
|
||||
<btn secondary :hover="false">{{ link.text || 'View' }}</btn>
|
||||
</smart-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { resolveEventType } from '~/libs/resolveEventType'
|
||||
import { format } from 'fecha'
|
||||
|
||||
const props = defineProps({
|
||||
event: Object,
|
||||
type: {
|
||||
type: String,
|
||||
default: () => 'row',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['hover', 'blur'])
|
||||
|
||||
const rsvpExpanded = ref(false)
|
||||
|
||||
const date = computed(() => {
|
||||
if (!props.event?.date) return ''
|
||||
const d = new Date(props.event.date)
|
||||
return format(d, 'MM.DD.YY')
|
||||
})
|
||||
const link = computed(() => {
|
||||
return {
|
||||
...props.event,
|
||||
type: 'event',
|
||||
link_type: 'Document',
|
||||
text: 'View Event',
|
||||
}
|
||||
})
|
||||
const eventType = computed(() => {
|
||||
const prismicType = props.event?.event_type?.trim()
|
||||
|
||||
return resolveEventType(prismicType)
|
||||
})
|
||||
|
||||
// Mobile active states
|
||||
const container = ref()
|
||||
const mobileActive = ref(false)
|
||||
useIntersectionObserver(
|
||||
container,
|
||||
([{ isIntersecting }]) => {
|
||||
if (isIntersecting) {
|
||||
emit('hover', props.event.id)
|
||||
mobileActive.value = true
|
||||
} else {
|
||||
emit('blur', props.event.id)
|
||||
mobileActive.value = false
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '-70% 0px -30% 0px',
|
||||
threshold: 0,
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.event-list-item {
|
||||
&.type-row {
|
||||
border-top: 1px solid var(--theme-fg);
|
||||
padding: desktop-vw(32px) var(--layout-margin);
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
display: grid;
|
||||
|
||||
.title {
|
||||
grid-column: 1 / span 2;
|
||||
width: desktop-vw(600px);
|
||||
}
|
||||
.btn-wrap {
|
||||
grid-column: 5 / span 2;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.smart-link {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.smart-link:not(.has-link) {
|
||||
pointer-events: none;
|
||||
}
|
||||
.btn-rsvp.btn:not(.hover),
|
||||
.has-link .btn:not(.hover) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-rsvp {
|
||||
margin-left: desktop-vw(15px);
|
||||
margin-right: desktop-vw(15px);
|
||||
}
|
||||
@include hover {
|
||||
background: var(--theme-fg);
|
||||
color: var(--theme-bg);
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
padding: mobile-vw(30px) var(--layout-margin);
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
.title {
|
||||
grid-column: 1;
|
||||
width: 100%;
|
||||
}
|
||||
.description {
|
||||
grid-column: 1/-1;
|
||||
margin-top: mobile-vw(31px);
|
||||
width: mobile-vw(280px);
|
||||
}
|
||||
.btn-wrap {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 3;
|
||||
justify-content: flex-start;
|
||||
margin-top: mobile-vw(31px);
|
||||
}
|
||||
&.mobile-active {
|
||||
background: var(--theme-fg);
|
||||
color: var(--theme-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.type-recirc {
|
||||
grid-template-columns: auto auto 1fr;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
column-gap: desktop-vw(20px);
|
||||
border-bottom: 1px solid var(--theme-fg);
|
||||
|
||||
.preview-image {
|
||||
grid-column: 1/-1;
|
||||
border-top: 1px solid var(--theme-fg);
|
||||
border-bottom: 1px solid var(--theme-fg);
|
||||
}
|
||||
p {
|
||||
@include p-xs;
|
||||
}
|
||||
.btn-wrap {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
|
||||
.btn {
|
||||
border-width: 0 1px 0 0;
|
||||
background: var(--theme-fg);
|
||||
color: var(--theme-bg);
|
||||
}
|
||||
}
|
||||
.date {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
.title {
|
||||
grid-column: 3;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user