@bagelink/vue 1.2.15 → 1.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/components/calendar/CalendarTypes.d.ts +13 -0
  2. package/dist/components/calendar/CalendarTypes.d.ts.map +1 -0
  3. package/dist/components/calendar/Index.vue.d.ts +39 -507
  4. package/dist/components/calendar/Index.vue.d.ts.map +1 -1
  5. package/dist/components/calendar/utils.d.ts +31 -0
  6. package/dist/components/calendar/utils.d.ts.map +1 -0
  7. package/dist/components/calendar/views/AgendaView.vue.d.ts +16 -0
  8. package/dist/components/calendar/views/AgendaView.vue.d.ts.map +1 -0
  9. package/dist/components/calendar/views/DayView.vue.d.ts +50 -0
  10. package/dist/components/calendar/views/DayView.vue.d.ts.map +1 -0
  11. package/dist/components/calendar/views/MonthView.vue.d.ts +20 -0
  12. package/dist/components/calendar/views/MonthView.vue.d.ts.map +1 -0
  13. package/dist/components/calendar/views/WeekView.vue.d.ts +33 -0
  14. package/dist/components/calendar/views/WeekView.vue.d.ts.map +1 -0
  15. package/dist/components/form/BglMultiStepForm.vue.d.ts +63 -0
  16. package/dist/components/form/BglMultiStepForm.vue.d.ts.map +1 -0
  17. package/dist/components/form/index.d.ts +1 -0
  18. package/dist/components/form/index.d.ts.map +1 -1
  19. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -1
  20. package/dist/components/form/inputs/DateInput.vue.d.ts +3 -3
  21. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  22. package/dist/components/form/inputs/DatePicker.vue.d.ts +3 -3
  23. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  24. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  25. package/dist/index.cjs +2241 -3891
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.mjs +2242 -3892
  29. package/dist/style.css +567 -633
  30. package/dist/utils/BagelFormUtils.d.ts +4 -2
  31. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  32. package/dist/utils/calendar/EDate.d.ts +2 -0
  33. package/dist/utils/calendar/EDate.d.ts.map +1 -0
  34. package/dist/utils/calendar/Helpers.d.ts +19 -0
  35. package/dist/utils/calendar/Helpers.d.ts.map +1 -0
  36. package/dist/utils/calendar/constants.d.ts +3 -0
  37. package/dist/utils/calendar/constants.d.ts.map +1 -0
  38. package/dist/utils/calendar/dateUtils.d.ts +30 -0
  39. package/dist/utils/calendar/dateUtils.d.ts.map +1 -0
  40. package/dist/utils/calendar/event.interface.d.ts +32 -0
  41. package/dist/utils/calendar/event.interface.d.ts.map +1 -0
  42. package/dist/utils/calendar/time.d.ts +117 -0
  43. package/dist/utils/calendar/time.d.ts.map +1 -0
  44. package/dist/utils/calendar/types.d.ts +27 -0
  45. package/dist/utils/calendar/types.d.ts.map +1 -0
  46. package/dist/utils/calendar/typings.d.ts +87 -0
  47. package/dist/utils/calendar/typings.d.ts.map +1 -0
  48. package/dist/utils/calendar/week.d.ts +117 -0
  49. package/dist/utils/calendar/week.d.ts.map +1 -0
  50. package/package.json +1 -1
  51. package/src/components/calendar/CalendarTypes.ts +13 -0
  52. package/src/components/calendar/Index.vue +124 -389
  53. package/src/components/calendar/utils.ts +70 -0
  54. package/src/components/calendar/views/AgendaView.vue +263 -0
  55. package/src/components/calendar/views/DayView.vue +373 -0
  56. package/src/components/calendar/views/MonthView.vue +313 -0
  57. package/src/components/calendar/views/WeekView.vue +431 -0
  58. package/src/components/form/BglMultiStepForm.vue +383 -69
  59. package/src/components/form/index.ts +1 -0
  60. package/src/components/form/inputs/CodeEditor/Index.vue +11 -0
  61. package/src/components/form/inputs/DateInput.vue +3 -3
  62. package/src/components/form/inputs/DatePicker.vue +35 -30
  63. package/src/components/form/inputs/SelectInput.vue +2 -0
  64. package/src/components/form/inputs/Upload/upload.types.d.ts +0 -1
  65. package/src/index.ts +2 -2
  66. package/src/styles/inputs.css +138 -137
  67. package/src/styles/layout.css +3 -2
  68. package/src/styles/mobilLayout.css +4 -2
  69. package/src/utils/BagelFormUtils.ts +6 -2
  70. package/src/utils/calendar/EDate.ts +0 -0
  71. package/src/{components/calendar/helpers → utils/calendar}/Helpers.ts +6 -6
  72. package/src/utils/calendar/constants.ts +2 -0
  73. package/src/utils/{timeAgo.ts → calendar/dateUtils.ts} +38 -1
  74. package/src/utils/calendar/event.interface.ts +33 -0
  75. package/src/{components/calendar/helpers/Time.ts → utils/calendar/time.ts} +15 -15
  76. package/src/utils/calendar/types.ts +27 -0
  77. package/src/{components/calendar/typings/config.interface.ts → utils/calendar/typings.ts} +13 -6
  78. package/src/utils/calendar/week.ts +588 -0
  79. package/src/components/calendar/assets/base.css +0 -60
  80. package/src/components/calendar/components/header/Header.vue +0 -153
  81. package/src/components/calendar/components/month/AgendaEventTile.vue +0 -135
  82. package/src/components/calendar/components/month/AgendaEvents.vue +0 -72
  83. package/src/components/calendar/components/month/Day.vue +0 -256
  84. package/src/components/calendar/components/month/Event.vue +0 -164
  85. package/src/components/calendar/components/month/Month.vue +0 -241
  86. package/src/components/calendar/components/month/WeekDay.vue +0 -15
  87. package/src/components/calendar/components/partials/EventFlyout.vue +0 -430
  88. package/src/components/calendar/components/week/Day.vue +0 -198
  89. package/src/components/calendar/components/week/DayEvent.vue +0 -584
  90. package/src/components/calendar/components/week/DayTimeline.vue +0 -80
  91. package/src/components/calendar/components/week/FullDayEvent.vue +0 -121
  92. package/src/components/calendar/components/week/Week.vue +0 -414
  93. package/src/components/calendar/components/week/WeekTimeline.vue +0 -101
  94. package/src/components/calendar/constants.ts +0 -13
  95. package/src/components/calendar/helpers/DayIntervals.ts +0 -48
  96. package/src/components/calendar/helpers/EDate.ts +0 -18
  97. package/src/components/calendar/helpers/Errors.ts +0 -69
  98. package/src/components/calendar/helpers/EventChange.ts +0 -88
  99. package/src/components/calendar/helpers/EventConcurrency.ts +0 -69
  100. package/src/components/calendar/helpers/EventFlyoutPosition.ts +0 -96
  101. package/src/components/calendar/helpers/EventPosition.ts +0 -154
  102. package/src/components/calendar/helpers/EventsFilter.ts +0 -50
  103. package/src/components/calendar/helpers/Week.ts +0 -37
  104. package/src/components/calendar/language/index.ts +0 -41
  105. package/src/components/calendar/language/keys.ts +0 -99
  106. package/src/components/calendar/models/Event.ts +0 -112
  107. package/src/components/calendar/styles/_mixins.css +0 -21
  108. package/src/components/calendar/styles/_variables.css +0 -47
  109. package/src/components/calendar/typings/interfaces/day.interface.ts +0 -10
  110. package/src/components/calendar/typings/interfaces/event.interface.ts +0 -32
  111. package/src/components/calendar/typings/interfaces/full-day-events-week.type.ts +0 -8
  112. package/src/components/calendar/typings/interfaces/period.interface.ts +0 -5
  113. package/src/components/calendar/typings/interfaces/time-modes.ts +0 -11
  114. package/src/components/calendar/typings/types.ts +0 -27
@@ -0,0 +1,263 @@
1
+ <script setup lang="ts">
2
+ import type { CalendarEvent } from '../CalendarTypes'
3
+ import { formatDate } from '@bagelink/vue'
4
+ import { ref, computed, onMounted, onUnmounted } from 'vue'
5
+
6
+ interface AgendaViewEvent extends CalendarEvent {
7
+ dayLabel: string
8
+ isToday: boolean
9
+ height: number
10
+ }
11
+
12
+ const props = defineProps<{
13
+ events: CalendarEvent[]
14
+ startDate: Date
15
+ }>()
16
+
17
+ const emit = defineEmits<{
18
+ (e: 'eventClick', event: CalendarEvent): void
19
+ (e: 'dateChange', date: Date): void
20
+ }>()
21
+
22
+ // UI references
23
+ const containerRef = ref<HTMLElement | null>(null)
24
+ const currentDate = ref<Date | null>(null)
25
+
26
+ // Process events for agenda view
27
+ const processedEvents = computed(() => {
28
+ const events: AgendaViewEvent[] = []
29
+ const today = new Date().toDateString()
30
+
31
+ // Sort events by start time
32
+ const sortedEvents = [...props.events].sort(
33
+ (a, b) => new Date(a.start_time).getTime() - new Date(b.start_time).getTime()
34
+ )
35
+
36
+ // Group events by date
37
+ const eventsByDate = sortedEvents.reduce((acc, event) => {
38
+ const date = new Date(event.start_time).toDateString()
39
+ if (!acc[date]) acc[date] = []
40
+ acc[date].push(event)
41
+ return acc
42
+ }, {} as { [key: string]: CalendarEvent[] })
43
+
44
+ // Process each date's events
45
+ Object.entries(eventsByDate).forEach(([dateStr, dateEvents]) => {
46
+ const date = new Date(dateStr)
47
+ const isToday = date.toDateString() === today
48
+
49
+ dateEvents.forEach((event) => {
50
+ const duration = (new Date(event.end_time).getTime() - new Date(event.start_time).getTime()) / (1000 * 60)
51
+ // Limit height: 60px per 30 minutes, max 200px
52
+ const height = Math.min(duration / 30 * 60, 200)
53
+
54
+ events.push({
55
+ ...event,
56
+ dayLabel: date.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }),
57
+ isToday,
58
+ height
59
+ })
60
+ })
61
+ })
62
+
63
+ return events
64
+ })
65
+
66
+ // Handle scroll to determine visible date
67
+ function handleScroll() {
68
+ if (!containerRef.value) return
69
+
70
+ const container = containerRef.value
71
+ const eventElements = container.querySelectorAll('.event')
72
+ let mostVisibleEvent: Element | null = null
73
+ let maxVisibility = 0
74
+
75
+ // Find the event that's most visible in the viewport
76
+ eventElements.forEach((element) => {
77
+ const rect = element.getBoundingClientRect()
78
+ const containerRect = container.getBoundingClientRect()
79
+
80
+ // Calculate how much of the element is visible
81
+ const visibleTop = Math.max(rect.top, containerRect.top)
82
+ const visibleBottom = Math.min(rect.bottom, containerRect.bottom)
83
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop)
84
+ const visibility = visibleHeight / rect.height
85
+
86
+ if (visibility > maxVisibility) {
87
+ maxVisibility = visibility
88
+ mostVisibleEvent = element
89
+ }
90
+ })
91
+
92
+ // Update current date if a new date is most visible
93
+ if (mostVisibleEvent) {
94
+ const element = mostVisibleEvent as HTMLElement
95
+ const dateStr = element.dataset.date || element.getAttribute('data-date')
96
+ if (dateStr) {
97
+ const date = new Date(dateStr)
98
+ if (!currentDate.value || date.toDateString() !== currentDate.value.toDateString()) {
99
+ currentDate.value = date
100
+ emit('dateChange', date)
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ // Lifecycle hooks
107
+ onMounted(() => {
108
+ if (containerRef.value) {
109
+ containerRef.value.addEventListener('scroll', handleScroll)
110
+ }
111
+ })
112
+
113
+ onUnmounted(() => {
114
+ if (containerRef.value) {
115
+ containerRef.value.removeEventListener('scroll', handleScroll)
116
+ }
117
+ })
118
+ </script>
119
+
120
+ <template>
121
+ <div class="agenda-view">
122
+ <div class="agenda-header">
123
+ <div class="time-column">
124
+ Time
125
+ </div>
126
+ <div class="event-column">
127
+ Event
128
+ </div>
129
+ </div>
130
+ <div ref="containerRef" class="agenda-content">
131
+ <div
132
+ v-for="event in processedEvents"
133
+ :key="event.id"
134
+ class="event"
135
+ :style="{
136
+ height: `${event.height}px`,
137
+ backgroundColor: event.color || 'var(--bgl-primary)',
138
+ }"
139
+ :data-date="event.start_time"
140
+ @click.stop="emit('eventClick', event)"
141
+ >
142
+ <div class="event-content">
143
+ <div class="event-time">
144
+ {{ formatDate(event.start_time, 'HH:mm') }}
145
+ </div>
146
+ <div class="event-details">
147
+ <div class="event-title">
148
+ {{ event.title }}
149
+ </div>
150
+ <div class="event-day" :class="{ today: event.isToday }">
151
+ {{ event.dayLabel }}
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </template>
159
+
160
+ <style scoped>
161
+ .agenda-view {
162
+ display: flex;
163
+ flex-direction: column;
164
+ height: 100%;
165
+ overflow: hidden;
166
+ }
167
+
168
+ .agenda-header {
169
+ display: flex;
170
+ padding: 0.5rem;
171
+ border-bottom: 1px solid var(--border-color);
172
+ }
173
+
174
+ .time-column {
175
+ width: 100px;
176
+ flex-shrink: 0;
177
+ }
178
+
179
+ .event-column {
180
+ flex-grow: 1;
181
+ }
182
+
183
+ .agenda-content {
184
+ flex-grow: 1;
185
+ overflow-y: auto;
186
+ padding: 1rem;
187
+ }
188
+
189
+ .event {
190
+ margin-bottom: 1rem;
191
+ border-radius: 4px;
192
+ overflow: hidden;
193
+ cursor: pointer;
194
+ transition: all 0.2s ease;
195
+ }
196
+
197
+ .event:hover {
198
+ transform: scale(1.01);
199
+ }
200
+
201
+ .event-content {
202
+ display: flex;
203
+ height: 100%;
204
+ color: white;
205
+ }
206
+
207
+ .event-time {
208
+ width: 100px;
209
+ flex-shrink: 0;
210
+ padding: 0.5rem;
211
+ display: flex;
212
+ align-items: center;
213
+ font-size: 0.9rem;
214
+ }
215
+
216
+ .event-details {
217
+ flex-grow: 1;
218
+ padding: 0.5rem;
219
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
220
+ }
221
+
222
+ .event-title {
223
+ font-size: 1rem;
224
+ margin-bottom: 0.25rem;
225
+ }
226
+
227
+ .event-day {
228
+ font-size: 0.8rem;
229
+ opacity: 0.8;
230
+ }
231
+
232
+ .event-day.today {
233
+ color: var(--bgl-primary);
234
+ opacity: 1;
235
+ }
236
+
237
+ @media (max-width: 768px) {
238
+ .agenda-header {
239
+ padding: 0.5rem;
240
+ }
241
+
242
+ .time-column {
243
+ width: 80px;
244
+ }
245
+
246
+ .event-content {
247
+ font-size: 0.9rem;
248
+ }
249
+
250
+ .event-time {
251
+ width: 80px;
252
+ font-size: 0.8rem;
253
+ }
254
+
255
+ .event-title {
256
+ font-size: 0.9rem;
257
+ }
258
+
259
+ .event-day {
260
+ font-size: 0.7rem;
261
+ }
262
+ }
263
+ </style>
@@ -0,0 +1,373 @@
1
+ <script setup lang="ts">
2
+ import type { CalendarEvent } from '../CalendarTypes'
3
+ import type { PopoverState } from '../utils'
4
+ import { formatDate } from '@bagelink/vue'
5
+ import { ref, computed, onMounted, onUnmounted, useSlots } from 'vue'
6
+ import {
7
+ openPopover as openPopoverUtil,
8
+ closePopover as closePopoverUtil,
9
+ } from '../utils'
10
+
11
+ const props = defineProps<{
12
+ events: CalendarEvent[]
13
+ startDate: Date
14
+ }>()
15
+
16
+ const emit = defineEmits<{
17
+ (e: 'eventClick', event: CalendarEvent): void
18
+ (e: 'eventCreate', event: { start_time: Date, end_time: Date }): void
19
+ }>()
20
+
21
+ const slots = useSlots()
22
+
23
+ // Configuration constants
24
+ const slotHeight = 60
25
+ const slotDuration = 60
26
+ const timeRange = { start: 0, end: 24 }
27
+
28
+ // Drag state
29
+ const dragState = ref({
30
+ isDragging: false,
31
+ start: null as { x: number, y: number } | null,
32
+ end: null as { x: number, y: number } | null,
33
+ startTime: null as Date | null,
34
+ endTime: null as Date | null
35
+ })
36
+
37
+ // Time indicators
38
+ const currentTimeTop = ref(0)
39
+ const isToday = ref(false)
40
+ const currentTimeInterval = ref(null as any)
41
+
42
+ // Popover state
43
+ const activeEvent = ref<CalendarEvent | null>(null)
44
+ const showPopover = ref(false)
45
+ const popoverPosition = ref({ top: 0, left: 0, width: 0, height: 0 })
46
+ const popoverRef = ref<HTMLElement | null>(null)
47
+
48
+ const popoverState: PopoverState = {
49
+ activeEvent,
50
+ showPopover,
51
+ popoverPosition,
52
+ popoverRef
53
+ }
54
+
55
+ // Generate hourly time slots
56
+ const timeSlots = computed(() => {
57
+ const slots = []
58
+ for (let hour = timeRange.start; hour < timeRange.end; hour++) {
59
+ for (let minute = 0; minute < 60; minute += slotDuration) {
60
+ slots.push({
61
+ hour,
62
+ minute,
63
+ time: `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
64
+ })
65
+ }
66
+ }
67
+ return slots
68
+ })
69
+
70
+ // Process events for the current day
71
+ const processedEvents = computed(() => {
72
+ return props.events
73
+ .filter((event) => {
74
+ const eventDate = new Date(event.start_time)
75
+ return eventDate.toDateString() === props.startDate.toDateString()
76
+ })
77
+ .map((event) => {
78
+ const startDate = new Date(event.start_time)
79
+ const endDate = new Date(event.end_time)
80
+
81
+ const startMinutes = startDate.getHours() * 60 + startDate.getMinutes()
82
+ const endMinutes = endDate.getHours() * 60 + endDate.getMinutes()
83
+
84
+ const top = (startMinutes / slotDuration) * slotHeight
85
+ const height = Math.max(((endMinutes - startMinutes) / slotDuration) * slotHeight, 30)
86
+
87
+ return {
88
+ ...event,
89
+ top,
90
+ height
91
+ }
92
+ })
93
+ })
94
+
95
+ // Updates the current time indicator
96
+ function updateCurrentTimeIndicator() {
97
+ const now = new Date()
98
+
99
+ // Check if today is the selected day
100
+ isToday.value = now.toDateString() === props.startDate.toDateString()
101
+
102
+ // Calculate the position for the current time line
103
+ const minutes = now.getHours() * 60 + now.getMinutes()
104
+ currentTimeTop.value = (minutes / slotDuration) * slotHeight
105
+ }
106
+
107
+ // Event handlers for drag-to-create functionality
108
+ function handleMouseDown(e: MouseEvent) {
109
+ const target = e.target as HTMLElement
110
+ if (!target.closest('.time-slot')) return
111
+
112
+ dragState.value.isDragging = true
113
+ dragState.value.start = { x: e.clientX, y: e.clientY }
114
+ dragState.value.startTime = getTimeFromPosition(e.clientY)
115
+
116
+ document.addEventListener('mousemove', handleMouseMove)
117
+ document.addEventListener('mouseup', handleMouseUp)
118
+ }
119
+
120
+ function handleMouseMove(e: MouseEvent) {
121
+ if (!dragState.value.isDragging) return
122
+
123
+ dragState.value.end = { x: e.clientX, y: e.clientY }
124
+ dragState.value.endTime = getTimeFromPosition(e.clientY)
125
+ }
126
+
127
+ function handleMouseUp() {
128
+ if (!dragState.value.isDragging || !dragState.value.startTime || !dragState.value.endTime) return
129
+
130
+ let start = new Date(dragState.value.startTime)
131
+ let end = new Date(dragState.value.endTime)
132
+
133
+ // Ensure end time is after start time
134
+ if (end < start) [start, end] = [end, start]
135
+
136
+ emit('eventCreate', { start_time: start, end_time: end })
137
+
138
+ // Reset drag state
139
+ dragState.value = {
140
+ isDragging: false,
141
+ start: null,
142
+ end: null,
143
+ startTime: null,
144
+ endTime: null
145
+ }
146
+
147
+ document.removeEventListener('mousemove', handleMouseMove)
148
+ document.removeEventListener('mouseup', handleMouseUp)
149
+ }
150
+
151
+ function getTimeFromPosition(y: number): Date {
152
+ const rect = document.querySelector('.time-slots')?.getBoundingClientRect()
153
+ if (!rect) return new Date()
154
+
155
+ const minutes = Math.floor((y - rect.top) / slotHeight) * slotDuration
156
+ const date = new Date(props.startDate)
157
+ date.setHours(Math.floor(minutes / 60))
158
+ date.setMinutes(minutes % 60)
159
+
160
+ return date
161
+ }
162
+
163
+ // Popover handling functions
164
+ function openPopover(event: CalendarEvent, e: MouseEvent) {
165
+ openPopoverUtil(
166
+ event,
167
+ e,
168
+ popoverState,
169
+ !!slots.eventContent,
170
+ (evt) => { emit('eventClick', evt) }
171
+ )
172
+ }
173
+
174
+ function closePopover() {
175
+ closePopoverUtil(popoverState)
176
+ }
177
+
178
+ // Lifecycle hooks
179
+ onMounted(() => {
180
+ document.addEventListener('mousedown', handleMouseDown)
181
+
182
+ // Initialize current time indicator and set up interval to update it
183
+ updateCurrentTimeIndicator()
184
+ currentTimeInterval.value = setInterval(updateCurrentTimeIndicator, 60000)
185
+ })
186
+
187
+ onUnmounted(() => {
188
+ document.removeEventListener('mousedown', handleMouseDown)
189
+ document.removeEventListener('mousemove', handleMouseMove)
190
+ document.removeEventListener('mouseup', handleMouseUp)
191
+
192
+ if (currentTimeInterval.value) {
193
+ clearInterval(currentTimeInterval.value)
194
+ }
195
+ })
196
+ </script>
197
+
198
+ <template>
199
+ <div class="w-100p overflow-hidden m_overflow h-100p grid">
200
+ <div class="border-bottom me-1 txt-center p-05 dayGrid">
201
+ <div />
202
+ <div>
203
+ {{ formatDate(startDate) }}
204
+ </div>
205
+ </div>
206
+
207
+ <div class="overflow h-100p pe-05">
208
+ <div class="time-slots dayGrid border-end relative" @mousedown="handleMouseDown">
209
+ <div class="time-column">
210
+ <div
211
+ v-for="slot in timeSlots" :key="slot.time"
212
+ class="time-slot txt-light txt-12 color-gray bg-gray-light border-start"
213
+ :style="{ height: `${slotHeight}px` }"
214
+ >
215
+ {{ slot.time }}
216
+ </div>
217
+ </div>
218
+ <div class="events-column">
219
+ <!-- Current time indicator -->
220
+ <div v-if="isToday" class=" absolute end w-100p z-2 flex pointer-events-none" :style="{ top: `${currentTimeTop}px` }">
221
+ <div class="current-time-dot h-10px aspect-ratio-1 round bg-primary" />
222
+ <div class="current-time-line w-100p bg-primary" />
223
+ </div>
224
+
225
+ <div
226
+ v-for="event in processedEvents" :key="event.id"
227
+ class="event absolute radius-05 overflow-hidden pointer transition z-1"
228
+ :style="{
229
+ top: `${event.top}px`,
230
+ height: `${event.height}px`,
231
+ backgroundColor: event.color || 'var(--bgl-primary)',
232
+ }"
233
+ @click.stop="slots.eventContent ? openPopover(event, $event) : emit('eventClick', event)"
234
+ >
235
+ <div class="event-content">
236
+ <div class=" white-space ellipsis-1">
237
+ {{ event.title }}
238
+ </div>
239
+ <div class="event-time opacity-8">
240
+ {{ formatDate(new Date(event.start_time)) }}
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ <!-- Drag selection preview -->
247
+ <div
248
+ v-if="dragState.isDragging && dragState.start && dragState.end"
249
+ class="drag-preview absolute bg-primary pointer-events-none"
250
+ :style="{
251
+ top: `${Math.min(dragState.start.y, dragState.end.y)}px`,
252
+ height: `${Math.abs(dragState.end.y - dragState.start.y)}px`,
253
+ }"
254
+ />
255
+ </div>
256
+ </div>
257
+
258
+ <!-- Custom Popover -->
259
+ <div
260
+ v-if="showPopover && activeEvent"
261
+ ref="popoverRef"
262
+ v-click-outside="closePopover"
263
+ class="custom-popover"
264
+ :style="{
265
+ top: `${popoverPosition.top}px`,
266
+ left: `${popoverPosition.left}px`,
267
+ maxWidth: `${popoverPosition.width}px`,
268
+ }"
269
+ >
270
+ <slot name="eventContent" :event="activeEvent" />
271
+ </div>
272
+ <div class="border-bottom me-1" />
273
+ </div>
274
+ </template>
275
+
276
+ <style scoped>
277
+ .dayGrid{
278
+ display: grid;
279
+ grid-template-columns: 5rem 1fr;
280
+ }
281
+
282
+ .time-column {
283
+ width: 80px;
284
+ flex-shrink: 0;
285
+ }
286
+
287
+ .events-column {
288
+ flex-grow: 1;
289
+ position: relative;
290
+ border-inline-start: 1px solid var(--border-color);
291
+ }
292
+
293
+ .date-header {
294
+ padding: 0.5rem;
295
+ text-align: center;
296
+ }
297
+ .day-header .events-column {
298
+ border-inline-start: 1px solid transparent;
299
+ }
300
+
301
+ .time-slot {
302
+ height: 30px;
303
+ display: flex;
304
+ align-items: center;
305
+ justify-content: center;
306
+ }
307
+
308
+ .time-slots {
309
+ flex-grow: 1;
310
+ display: flex;
311
+ overflow: auto;
312
+ position: relative;
313
+ }
314
+
315
+ .event {
316
+ left: 10px;
317
+ right: 10px;
318
+ }
319
+
320
+ .event:hover {
321
+ z-index: 2;
322
+ transform: scale(1.02);
323
+ }
324
+
325
+ .event-content {
326
+ padding: 0.5rem;
327
+ color: white;
328
+ font-size: 0.9rem;
329
+ height: 100%;
330
+ overflow: hidden;
331
+ }
332
+
333
+ .event-title {
334
+ white-space: nowrap;
335
+ overflow: hidden;
336
+ text-overflow: ellipsis;
337
+ }
338
+
339
+ .event-time {
340
+ font-size: 0.8rem;
341
+ }
342
+
343
+ .drag-preview {
344
+ background-color: rgba(var(--bgl-primary-rgb), 0.1);
345
+ }
346
+
347
+ .current-time-line {
348
+ height: 2px;
349
+ }
350
+
351
+ .custom-popover {
352
+ position: fixed;
353
+ z-index: 1000;
354
+ min-width: 250px;
355
+ max-width: 350px;
356
+ background-color: white;
357
+ border-radius: 4px;
358
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
359
+ animation: fadeIn 0.2s ease;
360
+ transform-origin: center left;
361
+ }
362
+
363
+ @keyframes fadeIn {
364
+ from {
365
+ opacity: 0;
366
+ transform: scale(0.95);
367
+ }
368
+ to {
369
+ opacity: 1;
370
+ transform: scale(1);
371
+ }
372
+ }
373
+ </style>