Files
chrono/src/components/EventViewerPopup.vue
T
2026-02-17 03:12:12 -05:00

182 lines
5.5 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
event: any | null
position: { x: number; y: number }
visible: boolean
}>()
const emit = defineEmits<{
'click': [event: any]
}>()
const formatDateTime = (isoString: string | null | undefined): string => {
if (!isoString) return 'Not set'
const date = new Date(isoString)
return date.toLocaleString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit'
})
}
const formatTime = (isoString: string | null | undefined): string => {
if (!isoString) return 'Not set'
const date = new Date(isoString)
return date.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit'
})
}
const popupStyle = computed(() => {
return {
left: `${props.position.x}px`,
top: `${props.position.y}px`
}
})
const hasLocation = computed(() => {
return props.event?.properties?.locationsPhysical && Object.keys(props.event.properties.locationsPhysical).length > 0 ||
props.event?.properties?.locationsVirtual && Object.keys(props.event.properties.locationsVirtual).length > 0
})
const firstLocation = computed(() => {
if (props.event?.properties?.locationsPhysical && Object.keys(props.event.properties.locationsPhysical).length > 0) {
const key = Object.keys(props.event.properties.locationsPhysical)[0]
return props.event.properties.locationsPhysical[key]
}
if (props.event?.properties?.locationsVirtual && Object.keys(props.event.properties.locationsVirtual).length > 0) {
const key = Object.keys(props.event.properties.locationsVirtual)[0]
return props.event.properties.locationsVirtual[key]
}
return null
})
const participantCount = computed(() => {
if (!props.event?.properties?.participants) return 0
return Object.keys(props.event.properties.participants).length
})
</script>
<template>
<Transition name="fade">
<div
v-if="visible && event"
class="event-popup"
:style="popupStyle"
@click.stop="$emit('click', event)"
>
<v-card elevation="8" class="event-popup-card">
<v-card-text class="pa-3">
<div class="d-flex align-center mb-2">
<v-icon icon="mdi-calendar" size="small" class="mr-2" />
<div class="text-subtitle-1 font-weight-bold">{{ event.properties?.label || 'Untitled Event' }}</div>
</div>
<div class="event-popup-details">
<div class="d-flex align-center mb-1">
<v-icon icon="mdi-clock-outline" size="small" class="mr-2 text-grey" />
<div class="text-caption">
<div v-if="event.properties?.timeless">
All Day - {{ new Date(event.properties.startsOn).toLocaleDateString() }}
</div>
<div v-else>
{{ formatTime(event.properties?.startsOn) }} - {{ formatTime(event.properties?.endsOn) }}
</div>
</div>
</div>
<div v-if="hasLocation" class="d-flex align-center mb-1">
<v-icon icon="mdi-map-marker" size="small" class="mr-2 text-grey" />
<div class="text-caption text-truncate">
{{ firstLocation?.label || firstLocation?.location || 'Location' }}
</div>
</div>
<div v-if="event.properties?.organizer" class="d-flex align-center mb-1">
<v-icon icon="mdi-account" size="small" class="mr-2 text-grey" />
<div class="text-caption">
{{ event.properties.organizer.name || event.properties.organizer.address }}
</div>
</div>
<div v-if="participantCount > 0" class="d-flex align-center mb-1">
<v-icon icon="mdi-account-group" size="small" class="mr-2 text-grey" />
<div class="text-caption">
{{ participantCount }} participant{{ participantCount !== 1 ? 's' : '' }}
</div>
</div>
<div v-if="event.properties?.description" class="mt-2 pt-2 border-t">
<div class="text-caption text-grey">{{ event.properties.description }}</div>
</div>
<div v-if="event.properties?.tags && event.properties.tags.length > 0" class="mt-2">
<v-chip
v-for="(tag, index) in event.properties.tags.slice(0, 3)"
:key="index"
size="x-small"
class="mr-1"
variant="tonal"
>
{{ tag }}
</v-chip>
<span v-if="event.properties.tags.length > 3" class="text-caption text-grey">
+{{ event.properties.tags.length - 3 }} more
</span>
</div>
</div>
<div class="text-caption text-grey-lighten-1 mt-2 text-center">
Click to view details
</div>
</v-card-text>
</v-card>
</div>
</Transition>
</template>
<style scoped>
.event-popup {
position: fixed;
z-index: 9999;
pointer-events: auto;
transform: translate(10px, 10px);
}
.event-popup-card {
min-width: 280px;
max-width: 350px;
cursor: pointer;
transition: box-shadow 0.2s;
}
.event-popup-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important;
}
.event-popup-details {
font-size: 13px;
}
.border-t {
border-top: 1px solid rgb(var(--v-border-color));
}
/* Fade transition */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.15s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>