@bagelink/vue 1.2.13 → 1.2.18

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 (131) 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/FieldArray.vue.d.ts.map +1 -1
  18. package/dist/components/form/index.d.ts +1 -0
  19. package/dist/components/form/index.d.ts.map +1 -1
  20. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -1
  21. package/dist/components/form/inputs/DateInput.vue.d.ts +3 -3
  22. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  23. package/dist/components/form/inputs/DatePicker.vue.d.ts +3 -3
  24. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  25. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
  26. package/dist/components/form/inputs/RichText/components/gridBox.vue.d.ts +6 -3
  27. package/dist/components/form/inputs/RichText/components/gridBox.vue.d.ts.map +1 -1
  28. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
  29. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
  30. package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
  31. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
  32. package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
  33. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  34. package/dist/index.cjs +2364 -3769
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.mjs +2365 -3770
  38. package/dist/style.css +607 -631
  39. package/dist/utils/BagelFormUtils.d.ts +4 -2
  40. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  41. package/dist/utils/calendar/EDate.d.ts +2 -0
  42. package/dist/utils/calendar/EDate.d.ts.map +1 -0
  43. package/dist/utils/calendar/Helpers.d.ts +19 -0
  44. package/dist/utils/calendar/Helpers.d.ts.map +1 -0
  45. package/dist/utils/calendar/constants.d.ts +3 -0
  46. package/dist/utils/calendar/constants.d.ts.map +1 -0
  47. package/dist/utils/calendar/dateUtils.d.ts +30 -0
  48. package/dist/utils/calendar/dateUtils.d.ts.map +1 -0
  49. package/dist/utils/calendar/event.interface.d.ts +32 -0
  50. package/dist/utils/calendar/event.interface.d.ts.map +1 -0
  51. package/dist/utils/calendar/time.d.ts +117 -0
  52. package/dist/utils/calendar/time.d.ts.map +1 -0
  53. package/dist/utils/calendar/types.d.ts +27 -0
  54. package/dist/utils/calendar/types.d.ts.map +1 -0
  55. package/dist/utils/calendar/typings.d.ts +87 -0
  56. package/dist/utils/calendar/typings.d.ts.map +1 -0
  57. package/dist/utils/calendar/week.d.ts +117 -0
  58. package/dist/utils/calendar/week.d.ts.map +1 -0
  59. package/package.json +1 -1
  60. package/src/components/calendar/CalendarTypes.ts +13 -0
  61. package/src/components/calendar/Index.vue +124 -389
  62. package/src/components/calendar/utils.ts +70 -0
  63. package/src/components/calendar/views/AgendaView.vue +263 -0
  64. package/src/components/calendar/views/DayView.vue +417 -0
  65. package/src/components/calendar/views/MonthView.vue +313 -0
  66. package/src/components/calendar/views/WeekView.vue +432 -0
  67. package/src/components/form/BglMultiStepForm.vue +323 -70
  68. package/src/components/form/FieldArray.vue +1 -1
  69. package/src/components/form/index.ts +1 -0
  70. package/src/components/form/inputs/CodeEditor/Index.vue +11 -0
  71. package/src/components/form/inputs/DateInput.vue +3 -3
  72. package/src/components/form/inputs/DatePicker.vue +3 -3
  73. package/src/components/form/inputs/RichText/components/EditorToolbar.vue +3 -4
  74. package/src/components/form/inputs/RichText/components/gridBox.vue +4 -1
  75. package/src/components/form/inputs/RichText/composables/useCommands.ts +22 -3
  76. package/src/components/form/inputs/RichText/config.ts +9 -7
  77. package/src/components/form/inputs/RichText/utils/commands.ts +307 -23
  78. package/src/components/form/inputs/RichText/utils/formatting.ts +117 -17
  79. package/src/components/form/inputs/RichText/utils/table.ts +36 -1
  80. package/src/components/form/inputs/SelectInput.vue +2 -0
  81. package/src/components/form/inputs/Upload/upload.types.d.ts +0 -1
  82. package/src/index.ts +2 -2
  83. package/src/styles/inputs.css +138 -137
  84. package/src/styles/layout.css +3 -2
  85. package/src/styles/mobilLayout.css +4 -2
  86. package/src/utils/BagelFormUtils.ts +6 -2
  87. package/src/utils/calendar/EDate.ts +0 -0
  88. package/src/{components/calendar/helpers → utils/calendar}/Helpers.ts +6 -6
  89. package/src/utils/calendar/constants.ts +2 -0
  90. package/src/utils/{timeAgo.ts → calendar/dateUtils.ts} +38 -1
  91. package/src/utils/calendar/event.interface.ts +33 -0
  92. package/src/{components/calendar/helpers/Time.ts → utils/calendar/time.ts} +15 -15
  93. package/src/utils/calendar/types.ts +27 -0
  94. package/src/{components/calendar/typings/config.interface.ts → utils/calendar/typings.ts} +13 -6
  95. package/src/utils/calendar/week.ts +588 -0
  96. package/src/components/calendar/assets/base.css +0 -60
  97. package/src/components/calendar/components/header/Header.vue +0 -153
  98. package/src/components/calendar/components/month/AgendaEventTile.vue +0 -135
  99. package/src/components/calendar/components/month/AgendaEvents.vue +0 -72
  100. package/src/components/calendar/components/month/Day.vue +0 -256
  101. package/src/components/calendar/components/month/Event.vue +0 -164
  102. package/src/components/calendar/components/month/Month.vue +0 -241
  103. package/src/components/calendar/components/month/WeekDay.vue +0 -15
  104. package/src/components/calendar/components/partials/EventFlyout.vue +0 -430
  105. package/src/components/calendar/components/week/Day.vue +0 -198
  106. package/src/components/calendar/components/week/DayEvent.vue +0 -584
  107. package/src/components/calendar/components/week/DayTimeline.vue +0 -80
  108. package/src/components/calendar/components/week/FullDayEvent.vue +0 -121
  109. package/src/components/calendar/components/week/Week.vue +0 -414
  110. package/src/components/calendar/components/week/WeekTimeline.vue +0 -101
  111. package/src/components/calendar/constants.ts +0 -13
  112. package/src/components/calendar/helpers/DayIntervals.ts +0 -48
  113. package/src/components/calendar/helpers/EDate.ts +0 -18
  114. package/src/components/calendar/helpers/Errors.ts +0 -69
  115. package/src/components/calendar/helpers/EventChange.ts +0 -88
  116. package/src/components/calendar/helpers/EventConcurrency.ts +0 -69
  117. package/src/components/calendar/helpers/EventFlyoutPosition.ts +0 -96
  118. package/src/components/calendar/helpers/EventPosition.ts +0 -154
  119. package/src/components/calendar/helpers/EventsFilter.ts +0 -50
  120. package/src/components/calendar/helpers/Week.ts +0 -37
  121. package/src/components/calendar/language/index.ts +0 -41
  122. package/src/components/calendar/language/keys.ts +0 -99
  123. package/src/components/calendar/models/Event.ts +0 -112
  124. package/src/components/calendar/styles/_mixins.css +0 -21
  125. package/src/components/calendar/styles/_variables.css +0 -47
  126. package/src/components/calendar/typings/interfaces/day.interface.ts +0 -10
  127. package/src/components/calendar/typings/interfaces/event.interface.ts +0 -32
  128. package/src/components/calendar/typings/interfaces/full-day-events-week.type.ts +0 -8
  129. package/src/components/calendar/typings/interfaces/period.interface.ts +0 -5
  130. package/src/components/calendar/typings/interfaces/time-modes.ts +0 -11
  131. 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,417 @@
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="day-view">
200
+ <div class="day-header">
201
+ <div class="time-column">
202
+ <div class="time-slot" />
203
+ </div>
204
+ <div class="events-column">
205
+ <div class="date-header">
206
+ {{ formatDate(startDate) }}
207
+ </div>
208
+ </div>
209
+ </div>
210
+ <div class="time-slots" @mousedown="handleMouseDown">
211
+ <div class="time-column">
212
+ <div
213
+ v-for="slot in timeSlots" :key="slot.time"
214
+ class="time-slot txt-light txt-12 color-gray"
215
+ :style="{ height: `${slotHeight}px` }"
216
+ >
217
+ {{ slot.time }}
218
+ </div>
219
+ </div>
220
+ <div class="events-column">
221
+ <!-- Current time indicator -->
222
+ <div v-if="isToday" class="current-time-indicator" :style="{ top: `${currentTimeTop}px` }">
223
+ <div class="current-time-dot" />
224
+ <div class="current-time-line" />
225
+ </div>
226
+
227
+ <div
228
+ v-for="event in processedEvents" :key="event.id"
229
+ class="event"
230
+ :style="{
231
+ top: `${event.top}px`,
232
+ height: `${event.height}px`,
233
+ backgroundColor: event.color || 'var(--bgl-primary)',
234
+ }"
235
+ @click.stop="slots.eventContent ? openPopover(event, $event) : emit('eventClick', event)"
236
+ >
237
+ <div class="event-content">
238
+ <div class="event-title">
239
+ {{ event.title }}
240
+ </div>
241
+ <div class="event-time">
242
+ {{ formatDate(new Date(event.start_time)) }}
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+
248
+ <!-- Drag selection preview -->
249
+ <div
250
+ v-if="dragState.isDragging && dragState.start && dragState.end"
251
+ class="drag-preview"
252
+ :style="{
253
+ top: `${Math.min(dragState.start.y, dragState.end.y)}px`,
254
+ height: `${Math.abs(dragState.end.y - dragState.start.y)}px`,
255
+ }"
256
+ />
257
+ </div>
258
+
259
+ <!-- Custom Popover -->
260
+ <div
261
+ v-if="showPopover && activeEvent"
262
+ ref="popoverRef"
263
+ v-click-outside="closePopover"
264
+ class="custom-popover"
265
+ :style="{
266
+ top: `${popoverPosition.top}px`,
267
+ left: `${popoverPosition.left}px`,
268
+ maxWidth: `${popoverPosition.width}px`,
269
+ }"
270
+ >
271
+ <slot name="eventContent" :event="activeEvent" />
272
+ </div>
273
+ </div>
274
+ </template>
275
+
276
+ <style scoped>
277
+ .day-view {
278
+ display: flex;
279
+ flex-direction: column;
280
+ height: 100%;
281
+ overflow: hidden;
282
+ }
283
+
284
+ .day-header {
285
+ display: flex;
286
+ border-bottom: 1px solid var(--border-color);
287
+ flex-shrink: 0;
288
+ }
289
+
290
+ .time-column {
291
+ width: 80px;
292
+ flex-shrink: 0;
293
+ }
294
+
295
+ .events-column {
296
+ flex-grow: 1;
297
+ position: relative;
298
+ border-inline-start: 1px solid var(--border-color);
299
+ }
300
+
301
+ .date-header {
302
+ padding: 0.5rem;
303
+ text-align: center;
304
+ }
305
+ .day-header .events-column {
306
+ border-inline-start: 1px solid transparent;
307
+ }
308
+
309
+ .time-slot {
310
+ height: 30px;
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: center;
314
+ font-size: 0.8rem;
315
+ }
316
+
317
+ .time-slots {
318
+ flex-grow: 1;
319
+ display: flex;
320
+ overflow: auto;
321
+ position: relative;
322
+ }
323
+
324
+ .event {
325
+ position: absolute;
326
+ left: 10px;
327
+ right: 10px;
328
+ border-radius: 4px;
329
+ overflow: hidden;
330
+ cursor: pointer;
331
+ transition: all 0.2s ease;
332
+ z-index: 1;
333
+ }
334
+
335
+ .event:hover {
336
+ z-index: 2;
337
+ transform: scale(1.02);
338
+ }
339
+
340
+ .event-content {
341
+ padding: 0.5rem;
342
+ color: white;
343
+ font-size: 0.9rem;
344
+ height: 100%;
345
+ overflow: hidden;
346
+ }
347
+
348
+ .event-title {
349
+ font-weight: 500;
350
+ white-space: nowrap;
351
+ overflow: hidden;
352
+ text-overflow: ellipsis;
353
+ }
354
+
355
+ .event-time {
356
+ font-size: 0.8rem;
357
+ opacity: 0.8;
358
+ }
359
+
360
+ .drag-preview {
361
+ position: absolute;
362
+ left: 80px;
363
+ right: 0;
364
+ background-color: rgba(var(--bgl-primary-rgb), 0.1);
365
+ border: 2px solid var(--bgl-primary);
366
+ pointer-events: none;
367
+ z-index: 1000;
368
+ }
369
+
370
+ .current-time-indicator {
371
+ position: absolute;
372
+ width: 100%;
373
+ z-index: 2;
374
+ display: flex;
375
+ align-items: center;
376
+ pointer-events: none;
377
+ }
378
+
379
+ .current-time-dot {
380
+ width: 12px;
381
+ height: 12px;
382
+ border-radius: 50%;
383
+ background-color: var(--bgl-primary, red);
384
+ position: absolute;
385
+ left: -6px;
386
+ transform: translateX(-50%);
387
+ }
388
+
389
+ .current-time-line {
390
+ height: 2px;
391
+ background-color: var(--bgl-primary, red);
392
+ width: 100%;
393
+ }
394
+
395
+ .custom-popover {
396
+ position: fixed;
397
+ z-index: 1000;
398
+ min-width: 250px;
399
+ max-width: 350px;
400
+ background-color: white;
401
+ border-radius: 4px;
402
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
403
+ animation: fadeIn 0.2s ease;
404
+ transform-origin: center left;
405
+ }
406
+
407
+ @keyframes fadeIn {
408
+ from {
409
+ opacity: 0;
410
+ transform: scale(0.95);
411
+ }
412
+ to {
413
+ opacity: 1;
414
+ transform: scale(1);
415
+ }
416
+ }
417
+ </style>