@bagelink/vue 1.4.109 → 1.4.115

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 (87) hide show
  1. package/bin/generateFormSchema.ts +12 -12
  2. package/dist/components/Card.vue.d.ts.map +1 -1
  3. package/dist/components/ImportData.vue.d.ts.map +1 -1
  4. package/dist/components/ListItem.vue.d.ts +6 -1
  5. package/dist/components/ListItem.vue.d.ts.map +1 -1
  6. package/dist/components/analytics/BarChart.vue.d.ts +39 -0
  7. package/dist/components/analytics/BarChart.vue.d.ts.map +1 -0
  8. package/dist/components/analytics/KpiCard.vue.d.ts +24 -0
  9. package/dist/components/analytics/KpiCard.vue.d.ts.map +1 -0
  10. package/dist/components/analytics/LineChart.vue.d.ts +26 -0
  11. package/dist/components/analytics/LineChart.vue.d.ts.map +1 -0
  12. package/dist/components/analytics/PieChart.vue.d.ts +24 -0
  13. package/dist/components/analytics/PieChart.vue.d.ts.map +1 -0
  14. package/dist/components/analytics/index.d.ts +5 -0
  15. package/dist/components/analytics/index.d.ts.map +1 -0
  16. package/dist/components/calendar/Index.vue.d.ts.map +1 -1
  17. package/dist/components/calendar/index.d.ts +2 -0
  18. package/dist/components/calendar/index.d.ts.map +1 -0
  19. package/dist/components/calendar/views/MonthView.vue.d.ts.map +1 -1
  20. package/dist/components/calendar/views/WeekView.vue.d.ts.map +1 -1
  21. package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
  22. package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
  23. package/dist/components/form/inputs/DatePicker.vue.d.ts +2 -0
  24. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  25. package/dist/components/form/inputs/RadioGroup.vue.d.ts +6 -10
  26. package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
  27. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
  28. package/dist/components/form/inputs/SelectInput.vue.d.ts +2 -2
  29. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  30. package/dist/components/layout/AppContent.vue.d.ts +34 -0
  31. package/dist/components/layout/AppContent.vue.d.ts.map +1 -0
  32. package/dist/components/layout/AppLayout.vue.d.ts +27 -0
  33. package/dist/components/layout/AppLayout.vue.d.ts.map +1 -0
  34. package/dist/components/layout/AppSidebar.vue.d.ts +44 -0
  35. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -0
  36. package/dist/components/layout/index.d.ts +3 -0
  37. package/dist/components/layout/index.d.ts.map +1 -1
  38. package/dist/composables/useFormField.d.ts.map +1 -1
  39. package/dist/composables/useSchemaField.d.ts +2 -2
  40. package/dist/composables/useSchemaField.d.ts.map +1 -1
  41. package/dist/index.cjs +19 -19
  42. package/dist/index.mjs +10 -10
  43. package/dist/style.css +1 -1
  44. package/dist/types/BagelForm.d.ts +25 -13
  45. package/dist/types/BagelForm.d.ts.map +1 -1
  46. package/dist/utils/BagelFormUtils.d.ts +11 -8
  47. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  48. package/dist/utils/calendar/dateUtils.d.ts +21 -0
  49. package/dist/utils/calendar/dateUtils.d.ts.map +1 -1
  50. package/dist/utils/elementUtils.d.ts +5 -0
  51. package/dist/utils/elementUtils.d.ts.map +1 -1
  52. package/dist/utils/useSearch.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/Card.vue +1 -2
  55. package/src/components/DataPreview.vue +1 -1
  56. package/src/components/ImportData.vue +94 -88
  57. package/src/components/ListItem.vue +32 -24
  58. package/src/components/analytics/BarChart.vue +153 -0
  59. package/src/components/analytics/KpiCard.vue +84 -0
  60. package/src/components/analytics/LineChart.vue +267 -0
  61. package/src/components/analytics/PieChart.vue +183 -0
  62. package/src/components/analytics/index.ts +4 -0
  63. package/src/components/calendar/Index.vue +15 -35
  64. package/src/components/calendar/views/MonthView.vue +84 -88
  65. package/src/components/calendar/views/WeekView.vue +143 -89
  66. package/src/components/dataTable/DataTable.vue +2 -3
  67. package/src/components/form/BagelForm.vue +27 -6
  68. package/src/components/form/inputs/DateInput.vue +2 -2
  69. package/src/components/form/inputs/DatePicker.vue +42 -48
  70. package/src/components/form/inputs/RadioGroup.vue +60 -35
  71. package/src/components/form/inputs/RichText/utils/media.ts +1 -2
  72. package/src/components/form/inputs/SelectInput.vue +94 -101
  73. package/src/components/form/inputs/Upload/upload.css +135 -138
  74. package/src/components/layout/AppContent.vue +125 -0
  75. package/src/components/layout/AppLayout.vue +124 -0
  76. package/src/components/layout/AppSidebar.vue +271 -0
  77. package/src/components/layout/index.ts +5 -0
  78. package/src/composables/useFormField.ts +6 -0
  79. package/src/composables/useSchemaField.ts +38 -10
  80. package/src/styles/inputs.css +9 -0
  81. package/src/styles/theme.css +2 -2
  82. package/src/types/BagelForm.ts +68 -13
  83. package/src/utils/BagelFormUtils.ts +49 -52
  84. package/src/utils/calendar/dateUtils.ts +71 -17
  85. package/src/utils/elementUtils.ts +23 -4
  86. package/src/utils/useSearch.ts +14 -7
  87. /package/src/components/{dialog → calendar}/index.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { SetupContext } from 'vue'
3
3
  import type { CalendarEvent } from '../CalendarTypes'
4
- import { fmtDate } from '@bagelink/vue'
4
+ import { formatDate } from '@bagelink/vue'
5
5
  import { computed, useSlots } from 'vue'
6
6
 
7
7
  interface MonthViewEvent {
@@ -115,17 +115,14 @@ function handleEventSelection(event: CalendarEvent, domEvent?: MouseEvent) {
115
115
  </div>
116
116
  <div class="month-grid">
117
117
  <div
118
- v-for="day in currentMonth"
119
- :key="day.date.toISOString()"
120
- class="day-cell"
121
- :class="{
118
+ v-for="day in currentMonth" :key="day.date.toISOString()" class="day-cell" :class="{
122
119
  'current-month': day.isCurrentMonth,
123
120
  'today': day.isToday,
124
121
  'other-month': !day.isCurrentMonth,
125
122
  }"
126
123
  >
127
124
  <div class="day-number">
128
- {{ fmtDate(day.date, { fmt: 'DD' }) }}
125
+ {{ formatDate(day.date, { fmt: 'DD' }) }}
129
126
  </div>
130
127
  <div class="day-events">
131
128
  <template v-if="isMobile">
@@ -133,9 +130,7 @@ function handleEventSelection(event: CalendarEvent, domEvent?: MouseEvent) {
133
130
  </template>
134
131
  <template v-else>
135
132
  <div
136
- v-for="event in day.events"
137
- :key="event.id"
138
- class="event-item"
133
+ v-for="event in day.events" :key="event.id" class="event-item"
139
134
  :style="{ backgroundColor: event.color || 'var(--bgl-primary)' }"
140
135
  @click.stop="handleEventSelection(event, $event)"
141
136
  >
@@ -143,7 +138,7 @@ function handleEventSelection(event: CalendarEvent, domEvent?: MouseEvent) {
143
138
  {{ event.title }}
144
139
  </div>
145
140
  <div class="event-time">
146
- {{ fmtDate(event.start_time, { fmt: 'HH:mm' }) }}
141
+ {{ formatDate(event.start_time, 'HH:mm') }}
147
142
  </div>
148
143
  </div>
149
144
  </template>
@@ -155,132 +150,133 @@ function handleEventSelection(event: CalendarEvent, domEvent?: MouseEvent) {
155
150
 
156
151
  <style scoped>
157
152
  .month-view {
158
- display: flex;
159
- flex-direction: column;
160
- height: 100%;
161
- overflow: hidden;
153
+ display: flex;
154
+ flex-direction: column;
155
+ height: 100%;
156
+ overflow: hidden;
162
157
  }
163
158
 
164
159
  .month-header {
165
- display: grid;
166
- grid-template-columns: repeat(7, 1fr);
167
- border-bottom: 1px solid var(--border-color);
160
+ display: grid;
161
+ grid-template-columns: repeat(7, 1fr);
162
+ border-bottom: 1px solid var(--border-color);
168
163
  }
169
164
 
170
165
  .weekday {
171
- padding: 0.5rem;
172
- text-align: center;
173
- color: var(--text-muted);
166
+ padding: 0.5rem;
167
+ text-align: center;
168
+ color: var(--text-muted);
174
169
  }
175
170
 
176
171
  .month-grid {
177
- display: grid;
178
- grid-template-columns: repeat(7, 1fr);
179
- grid-template-rows: repeat(6, 1fr);
180
- flex-grow: 1;
181
- overflow: auto;
182
- border-inline-end: 1px solid var(--border-color);
172
+ display: grid;
173
+ grid-template-columns: repeat(7, 1fr);
174
+ grid-template-rows: repeat(6, 1fr);
175
+ flex-grow: 1;
176
+ overflow: auto;
177
+ border-inline-end: 1px solid var(--border-color);
183
178
 
184
179
  }
185
180
 
186
181
  .day-cell {
187
- border-inline-start: 1px solid var(--border-color);
188
- border-bottom: 1px solid var(--border-color);
189
- padding: 0.5rem;
190
- min-height: 100px;
191
- display: flex;
192
- flex-direction: column;
182
+ border-inline-start: 1px solid var(--border-color);
183
+ border-bottom: 1px solid var(--border-color);
184
+ padding: 0.5rem;
185
+ min-height: 100px;
186
+ display: flex;
187
+ flex-direction: column;
193
188
  }
194
189
 
195
190
  .day-number {
196
- margin-bottom: 0.5rem;
191
+ margin-bottom: 0.5rem;
197
192
  }
198
193
 
199
194
  .other-month {
200
- background-color: var(--bgl-gray-light);
201
- color: var(--bgl-gray);
195
+ background-color: var(--bgl-gray-light);
196
+ color: var(--bgl-gray);
202
197
  }
203
198
 
204
199
  .today {
205
- background-color: var(--bgl-primary-light);
200
+ background-color: var(--bgl-primary-light);
206
201
  }
207
202
 
208
203
  .today .day-number {
209
- color: var(--bgl-primary);
204
+ color: var(--bgl-primary);
210
205
  }
211
206
 
212
207
  .day-events {
213
- flex-grow: 1;
214
- overflow-y: auto;
208
+ flex-grow: 1;
209
+ overflow-y: auto;
215
210
  }
216
211
 
217
212
  .event-item {
218
- margin-bottom: 0.25rem;
219
- padding: 0.25rem;
220
- border-radius: 4px;
221
- color: white;
222
- font-size: 0.8rem;
223
- cursor: pointer;
224
- transition: all 0.2s ease;
213
+ margin-bottom: 0.25rem;
214
+ padding: 0.25rem;
215
+ border-radius: 4px;
216
+ color: white;
217
+ font-size: 0.8rem;
218
+ cursor: pointer;
219
+ transition: all 0.2s ease;
225
220
  }
226
221
 
227
222
  .event-title {
228
- font-weight: 500;
229
- white-space: nowrap;
230
- overflow: hidden;
231
- text-overflow: ellipsis;
223
+ font-weight: 500;
224
+ white-space: nowrap;
225
+ overflow: hidden;
226
+ text-overflow: ellipsis;
232
227
  }
233
228
 
234
229
  .event-time {
235
- font-size: 0.7rem;
236
- opacity: 0.8;
230
+ font-size: 0.7rem;
231
+ opacity: 0.8;
237
232
  }
238
233
 
239
234
  .event-dot {
240
- width: 6px;
241
- height: 6px;
242
- background-color: var(--bgl-primary);
243
- border-radius: 50%;
244
- margin: 0.25rem auto;
235
+ width: 6px;
236
+ height: 6px;
237
+ background-color: var(--bgl-primary);
238
+ border-radius: 50%;
239
+ margin: 0.25rem auto;
245
240
  }
246
241
 
247
242
  .custom-popover {
248
- position: fixed;
249
- z-index: 1000;
250
- min-width: 250px;
251
- max-width: 350px;
252
- background-color: white;
253
- border-radius: var(--btn-border-radius);
254
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
255
- animation: fadeIn 0.2s ease;
256
- transform-origin: center left;
243
+ position: fixed;
244
+ z-index: 1000;
245
+ min-width: 250px;
246
+ max-width: 350px;
247
+ background-color: white;
248
+ border-radius: var(--btn-border-radius);
249
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
250
+ animation: fadeIn 0.2s ease;
251
+ transform-origin: center left;
257
252
  }
258
253
 
259
254
  @keyframes fadeIn {
260
- from {
261
- opacity: 0;
262
- transform: scale(0.95);
263
- }
264
- to {
265
- opacity: 1;
266
- transform: scale(1);
267
- }
255
+ from {
256
+ opacity: 0;
257
+ transform: scale(0.95);
258
+ }
259
+
260
+ to {
261
+ opacity: 1;
262
+ transform: scale(1);
263
+ }
268
264
  }
269
265
 
270
266
  @media (max-width: 768px) {
271
- .day-cell {
272
- min-height: 60px;
273
- padding: 0.25rem;
274
- }
275
-
276
- .day-number {
277
- font-size: 0.8rem;
278
- margin-bottom: 0.25rem;
279
- }
280
-
281
- .weekday {
282
- font-size: 0.8rem;
283
- padding: 0.25rem;
284
- }
267
+ .day-cell {
268
+ min-height: 60px;
269
+ padding: 0.25rem;
270
+ }
271
+
272
+ .day-number {
273
+ font-size: 0.8rem;
274
+ margin-bottom: 0.25rem;
275
+ }
276
+
277
+ .weekday {
278
+ font-size: 0.8rem;
279
+ padding: 0.25rem;
280
+ }
285
281
  }
286
282
  </style>
@@ -36,6 +36,11 @@ const slotHeight = 60
36
36
  const slotDuration = 60
37
37
  const timeRange = { start: 0, end: 24 }
38
38
 
39
+ // Check if drag-to-create is enabled based on whether eventCreate handler is provided
40
+ const isDragToCreateEnabled = computed(() => {
41
+ return !!slots.eventCreate || emit.hasOwnProperty('eventCreate')
42
+ })
43
+
39
44
  // Drag state
40
45
  const dragState = ref({
41
46
  isDragging: false,
@@ -92,11 +97,28 @@ const timeSlots = computed(() => {
92
97
  return slots
93
98
  })
94
99
 
100
+ // Calculate drag preview positioning using the same formula as events
101
+ const dragPreviewPosition = computed(() => {
102
+ if (!dragState.value.isDragging || !dragState.value.startTime || !dragState.value.endTime) {
103
+ return null
104
+ }
105
+
106
+ // Use the exact same calculation as events
107
+ const startMinutes = dragState.value.startTime.getHours() * 60 + dragState.value.startTime.getMinutes()
108
+ const endMinutes = dragState.value.endTime.getHours() * 60 + dragState.value.endTime.getMinutes()
109
+
110
+ const top = (startMinutes / slotDuration) * slotHeight
111
+ const height = Math.max(((endMinutes - startMinutes) / slotDuration) * slotHeight, 30)
112
+
113
+ return { top, height }
114
+ })
115
+
95
116
  // Process events for display with positioning calculations
96
117
  const processedEvents = computed(() => {
97
118
  const events: WeekViewEvent[] = []
98
- const overlappingGroups: { [key: string]: WeekViewEvent[] } = {}
119
+ const dayEvents: { [dayIndex: number]: WeekViewEvent[] } = {}
99
120
 
121
+ // First pass: group events by day and calculate basic positioning
100
122
  props.events.forEach((event) => {
101
123
  const startDate = new Date(event.start_time)
102
124
  const endDate = new Date(event.end_time)
@@ -109,7 +131,7 @@ const processedEvents = computed(() => {
109
131
 
110
132
  if (dayIndex === -1) return
111
133
 
112
- // Calculate vertical positioning - make sure to use the same formula everywhere
134
+ // Calculate vertical positioning
113
135
  const startMinutes = startDate.getHours() * 60 + startDate.getMinutes()
114
136
  const endMinutes = endDate.getHours() * 60 + endDate.getMinutes()
115
137
  const top = (startMinutes / slotDuration) * slotHeight
@@ -125,26 +147,69 @@ const processedEvents = computed(() => {
125
147
  position: 0
126
148
  }
127
149
 
128
- // Group by day and time range for overlapping calculation
129
- const key = `${dayIndex}-${Math.floor(startMinutes / 30)}`
130
- if (!overlappingGroups[key]) {
131
- overlappingGroups[key] = []
150
+ if (!dayEvents[dayIndex]) {
151
+ dayEvents[dayIndex] = []
132
152
  }
133
- overlappingGroups[key].push(weekEvent)
153
+ dayEvents[dayIndex].push(weekEvent)
134
154
  })
135
155
 
136
- // Adjust overlapping events
137
- Object.values(overlappingGroups).forEach((group) => {
138
- if (group.length > 1) {
139
- const groupWidth = 100 / group.length
140
- group.forEach((event, index) => {
141
- event.overlappingEvents = group.length
142
- event.position = index
143
- event.width = groupWidth
144
- event.left = index * groupWidth
145
- })
156
+ // Second pass: detect and handle overlapping events for each day
157
+ Object.entries(dayEvents).forEach(([_, dayEventList]) => {
158
+ // Sort events by start time
159
+ dayEventList.sort((a, b) => new Date(a.start_time).getTime() - new Date(b.start_time).getTime())
160
+
161
+ // Group overlapping events
162
+ const overlappingGroups: WeekViewEvent[][] = []
163
+ let currentGroup: WeekViewEvent[] = []
164
+
165
+ dayEventList.forEach((event) => {
166
+ if (currentGroup.length === 0) {
167
+ currentGroup = [event]
168
+ } else {
169
+ // Check if this event overlaps with any event in the current group
170
+ const overlaps = currentGroup.some((groupEvent) => {
171
+ const eventStart = new Date(event.start_time).getTime()
172
+ const eventEnd = new Date(event.end_time).getTime()
173
+ const groupEventStart = new Date(groupEvent.start_time).getTime()
174
+ const groupEventEnd = new Date(groupEvent.end_time).getTime()
175
+
176
+ return !(eventEnd <= groupEventStart || eventStart >= groupEventEnd)
177
+ })
178
+
179
+ if (overlaps) {
180
+ currentGroup.push(event)
181
+ } else {
182
+ // No overlap, finalize current group and start new one
183
+ if (currentGroup.length > 0) {
184
+ overlappingGroups.push([...currentGroup])
185
+ }
186
+ currentGroup = [event]
187
+ }
188
+ }
189
+ })
190
+
191
+ // Don't forget the last group
192
+ if (currentGroup.length > 0) {
193
+ overlappingGroups.push(currentGroup)
146
194
  }
147
- events.push(...group)
195
+
196
+ // Position overlapping events with elegant spacing
197
+ overlappingGroups.forEach((group) => {
198
+ if (group.length > 1) {
199
+ // Add some spacing between overlapping events (2% gap)
200
+ const gap = 2
201
+ const availableWidth = 100 - (gap * (group.length - 1))
202
+ const eventWidth = availableWidth / group.length
203
+
204
+ group.forEach((event, index) => {
205
+ event.overlappingEvents = group.length
206
+ event.position = index
207
+ event.width = eventWidth
208
+ event.left = index * (eventWidth + gap)
209
+ })
210
+ }
211
+ events.push(...group)
212
+ })
148
213
  })
149
214
 
150
215
  return events
@@ -152,6 +217,9 @@ const processedEvents = computed(() => {
152
217
 
153
218
  // Mouse event handlers for drag-to-create functionality
154
219
  function handleMouseDown(e: MouseEvent, day: Date) {
220
+ // Only allow drag-to-create if the feature is enabled
221
+ if (!isDragToCreateEnabled.value) return
222
+
155
223
  if (!calendarGrid.value || !dayColumnsContainer.value) return
156
224
 
157
225
  // Find the clicked day column
@@ -185,11 +253,8 @@ function handleMouseDown(e: MouseEvent, day: Date) {
185
253
  y: e.clientY - dayColumnsRect.top
186
254
  }
187
255
 
188
- // Create a clean copy of the day
189
- const cleanDay = new Date(day)
190
- cleanDay.setHours(0, 0, 0, 0)
191
-
192
- const startTime = getTimeFromPosition(e.clientY, cleanDay)
256
+ // Use the same positioning calculation as events for consistency
257
+ const startTime = getTimeFromPositionConsistent(e.clientY, day)
193
258
  dragState.value.startTime = startTime
194
259
 
195
260
  document.addEventListener('mousemove', handleMouseMove)
@@ -197,7 +262,7 @@ function handleMouseDown(e: MouseEvent, day: Date) {
197
262
  }
198
263
 
199
264
  function handleMouseMove(e: MouseEvent) {
200
- if (!dragState.value.isDragging || !dragState.value.start || !dayColumns.value.length || !calendarGrid.value || !dayColumnsContainer.value) return
265
+ if (!isDragToCreateEnabled.value || !dragState.value.isDragging || !dragState.value.start || !dayColumns.value.length || !calendarGrid.value || !dayColumnsContainer.value) return
201
266
 
202
267
  // Find which day column we're hovering over
203
268
  let targetDayIndex = -1
@@ -215,50 +280,34 @@ function handleMouseMove(e: MouseEvent) {
215
280
  if (targetDayIndex === -1) return
216
281
 
217
282
  // Update which days are selected
218
- const dayColumnsRect = dayColumnsContainer.value.getBoundingClientRect()
219
- const minDayIndex = Math.min(dragState.value.startDay, targetDayIndex)
220
- const maxDayIndex = Math.max(dragState.value.startDay, targetDayIndex)
221
-
222
- // Get the left edge of the leftmost selected day
223
- const leftDayEl = dayColumns.value[minDayIndex]
224
- if (!leftDayEl) return
225
- const leftDayRect = leftDayEl.getBoundingClientRect()
226
- const leftEdge = leftDayRect.left - dayColumnsRect.left
227
-
228
- // Get the right edge of the rightmost selected day
229
- const rightDayEl = dayColumns.value[maxDayIndex]
230
- if (!rightDayEl) return
231
- const rightDayRect = rightDayEl.getBoundingClientRect()
232
- const rightEdge = rightDayRect.right - dayColumnsRect.left
233
-
234
283
  dragState.value.endDay = targetDayIndex
235
284
  dragState.value.end = {
236
- x: rightEdge,
237
- y: e.clientY - dayColumnsRect.top
285
+ x: dragState.value.start!.x, // Keep x position bound to starting day column
286
+ y: e.clientY - dayColumnsContainer.value.getBoundingClientRect().top
238
287
  }
239
288
 
240
- // Ensure the selection spans the full width of all selected days
241
- dragState.value.start.x = leftEdge
242
-
243
289
  if (targetDayIndex >= 0 && targetDayIndex < weekDays.value.length) {
244
- // Create a clean copy of the day
245
- const targetDay = new Date(weekDays.value[targetDayIndex])
246
- targetDay.setHours(0, 0, 0, 0)
290
+ // Always use the starting day as the base date for the event
291
+ const startDay = new Date(weekDays.value[dragState.value.startDay])
292
+ startDay.setHours(0, 0, 0, 0)
247
293
 
248
- const endTime = getTimeFromPosition(e.clientY, targetDay)
294
+ // Calculate end time based on mouse position but on the starting day
295
+ // Use the consistent positioning function for perfect alignment
296
+ const endTime = getTimeFromPositionConsistent(e.clientY, startDay)
249
297
  dragState.value.endTime = endTime
250
298
  }
251
299
  }
252
300
 
253
301
  function handleMouseUp() {
254
- if (!dragState.value.isDragging || !dragState.value.startTime || !dragState.value.endTime) return
302
+ if (!isDragToCreateEnabled.value || !dragState.value.isDragging || !dragState.value.startTime || !dragState.value.endTime) return
255
303
 
256
304
  let start = new Date(dragState.value.startTime)
257
305
  let end = new Date(dragState.value.endTime)
258
306
 
259
- // Ensure end time is after start time - handle multi-day and reversed selections
260
- if (dragState.value.startDay > dragState.value.endDay
261
- || (dragState.value.startDay === dragState.value.endDay && end < start)) {
307
+ // Always use the starting day for the event, but allow time selection across days
308
+ // The event will be created on the starting day with the selected time range
309
+ if (end < start) {
310
+ // If end time is before start time, swap them but keep the starting day
262
311
  [start, end] = [end, start]
263
312
  }
264
313
 
@@ -279,21 +328,22 @@ function handleMouseUp() {
279
328
  document.removeEventListener('mouseup', handleMouseUp)
280
329
  }
281
330
 
282
- function getTimeFromPosition(y: number, day: Date): Date {
283
- if (!calendarGrid.value || !timeSlotsContainer.value) return new Date(day)
331
+ // Consistent time positioning function that uses the same coordinate system as events
332
+ function getTimeFromPositionConsistent(y: number, day: Date): Date {
333
+ if (!dayColumnsContainer.value) return new Date(day)
284
334
 
285
- const gridRect = calendarGrid.value.getBoundingClientRect()
335
+ const containerRect = dayColumnsContainer.value.getBoundingClientRect()
286
336
 
287
- // Calculate relative Y position
288
- const relativeY = y - gridRect.top
337
+ // Calculate relative Y position from the container (same as events use)
338
+ const relativeY = y - containerRect.top
289
339
 
290
- // Use the same formula as for events
340
+ // Use the exact same formula as events
291
341
  const minutesPerSlot = slotDuration
292
342
  const slotsFromTop = relativeY / slotHeight
293
343
  const minutes = Math.floor(slotsFromTop * minutesPerSlot)
294
344
 
295
345
  const date = new Date(day)
296
- date.setHours(Math.floor(minutes / 60) - 1)
346
+ date.setHours(Math.floor(minutes / 60))
297
347
  const roundedMinutes = Math.round((minutes % 60) / 5) * 5
298
348
  date.setMinutes(roundedMinutes)
299
349
  date.setSeconds(0)
@@ -371,14 +421,10 @@ onUnmounted(() => {
371
421
  <div class="w-100p overflow-hidden m_overflow h-100p grid">
372
422
  <div ref="calendarGrid" class="weekGrid border-bottom me-1">
373
423
  <div />
374
- <div
375
- v-for="day in weekDays" :key="day.toISOString()"
376
- class="day-header p-05 txt-center"
377
- >
424
+ <div v-for="day in weekDays" :key="day.toISOString()" class="day-header p-05 txt-center">
378
425
  {{ formatDate(day, { fmt: 'DDD' }) }}
379
426
  <span
380
- class="txt-12 round p-025"
381
- :class="{
427
+ class="txt-12 round p-025" :class="{
382
428
  'color-gray': day.toDateString() !== new Date().toDateString(),
383
429
  'bg-primary color-white': day.toDateString() === new Date().toDateString(),
384
430
  }"
@@ -390,28 +436,31 @@ onUnmounted(() => {
390
436
  <div class="overflow h-100p pe-05">
391
437
  <div ref="dayColumnsContainer" class="weekGrid border-end relative">
392
438
  <div ref="timeSlotsContainer">
393
- <div v-for="slot in timeSlots" :key="slot.time" class="txt-light txt-12 color-gray flex justify-content-center bg-gray-light border-start" :style="{ height: `${slotHeight}px` }">
439
+ <div
440
+ v-for="slot in timeSlots" :key="slot.time"
441
+ class="txt-light txt-12 color-gray flex justify-content-center bg-gray-light border-start"
442
+ :style="{ height: `${slotHeight}px` }"
443
+ >
394
444
  {{ slot.time }}
395
445
  </div>
396
446
  </div>
397
447
 
398
448
  <!-- Current time indicator - ensure it aligns with the time slots and events -->
399
- <div v-if="isToday" class="absolute end w-100p z-2 flex pointer-events-none ps-4-5" :style="{ top: `${currentTimeTop}px` }">
449
+ <div
450
+ v-if="isToday" class="absolute end w-100p z-2 flex pointer-events-none ps-4-5"
451
+ :style="{ top: `${currentTimeTop}px` }"
452
+ >
400
453
  <div class="current-time-dot h-10px aspect-ratio-1 round bg-primary" />
401
454
  <div class="current-time-line w-100p bg-primary" />
402
455
  </div>
403
456
 
404
457
  <div
405
- v-for="(day, index) in weekDays"
406
- :key="day.toISOString()"
407
- :ref="el => setDayColumnRef(el, index)"
408
- class="day-column top bottom border-start relative"
409
- @mousedown="handleMouseDown($event, day)"
458
+ v-for="(day, index) in weekDays" :key="day.toISOString()" :ref="el => setDayColumnRef(el, index)"
459
+ class="day-column top bottom border-start relative" @mousedown="handleMouseDown($event, day)"
410
460
  >
411
461
  <template
412
462
  v-for="event in processedEvents.filter(e =>
413
- new Date(e.start_time).toDateString() === day.toDateString())"
414
- :key="event.id"
463
+ new Date(e.start_time).toDateString() === day.toDateString())" :key="event.id"
415
464
  >
416
465
  <div
417
466
  class="event absolute radius-05 overflow-hidden pointer transition z-1 opacity-8 border"
@@ -421,16 +470,15 @@ onUnmounted(() => {
421
470
  left: `${event.left}%`,
422
471
  width: `${event.width}%`,
423
472
  backgroundColor: event.color || 'var(--bgl-primary)',
424
- }"
425
- @mousedown.stop
426
- @click.stop="handleEventSelection(event, $event)"
473
+ }" @mousedown.stop @click.stop="handleEventSelection(event, $event)"
427
474
  >
428
475
  <div class="overflow-hidden color-white p-025 txt12 h-100p">
429
476
  <div class="white-space ellipsis-1">
430
477
  {{ event.title }}
431
478
  </div>
432
479
  <div class="txt10 opacity-8 user-select-none">
433
- {{ formatDate(event.start_time, { fmt: 'HH:mm' }) }} - {{ formatDate(event.end_time, { fmt: 'HH:mm' }) }}
480
+ {{ formatDate(event.start_time, { fmt: 'HH:mm' }) }} - {{ formatDate(event.end_time,
481
+ { fmt: 'HH:mm' }) }}
434
482
  </div>
435
483
  </div>
436
484
  </div>
@@ -439,17 +487,19 @@ onUnmounted(() => {
439
487
 
440
488
  <!-- Drag selection preview - ensure it aligns with time slots and events -->
441
489
  <div
442
- v-if="dragState.isDragging && dragState.start && dragState.end"
490
+ v-if="isDragToCreateEnabled && dragState.isDragging && dragState.start && dragState.end && dragPreviewPosition"
443
491
  class="drag-preview absolute bg-primary-tint pointer-events-none overflow-hidden opacity-7 z-1"
444
492
  :style="{
445
- left: `${Math.min(dragState.start.x, dragState.end.x)}px`,
446
- top: `${Math.min(dragState.start.y, dragState.end.y)}px`,
447
- width: `${Math.abs(dragState.end.x - dragState.start.x)}px`,
448
- height: `${Math.abs(dragState.end.y - dragState.start.y)}px`,
493
+ left: `${dragState.start.x}px`,
494
+ top: `${dragPreviewPosition.top}px`,
495
+ width: `${dayColumns[dragState.startDay]?.offsetWidth || 100}px`,
496
+ height: `${dragPreviewPosition.height}px`,
449
497
  }"
450
498
  >
451
499
  <div class="color-primary txt-12 p-025">
452
- {{ dragState.startTime ? formatDate(dragState.startTime, { fmt: 'HH:mm' }) : '' }} - {{ dragState.endTime ? formatDate(dragState.endTime, { fmt: 'HH:mm' }) : '' }}
500
+ {{ dragState.startTime ? formatDate(dragState.startTime, { fmt: 'HH:mm' }) : '' }} - {{
501
+ dragState.endTime
502
+ ? formatDate(dragState.endTime, { fmt: 'HH:mm' }) : '' }}
453
503
  </div>
454
504
  </div>
455
505
  </div>
@@ -471,8 +521,10 @@ onUnmounted(() => {
471
521
 
472
522
  .day-column {
473
523
  flex: 1;
474
- position: relative; /* Ensure absolute positioning works properly */
475
- min-height: 100%; /* Ensure column fills the full height */
524
+ position: relative;
525
+ /* Ensure absolute positioning works properly */
526
+ min-height: 100%;
527
+ /* Ensure column fills the full height */
476
528
  }
477
529
 
478
530
  /* Add consistent styling for the time slots */
@@ -496,7 +548,8 @@ onUnmounted(() => {
496
548
  .drag-preview {
497
549
  border: 1px solid var(--bgl-primary);
498
550
  background-color: rgba(var(--bgl-primary-rgb), 0.1);
499
- box-sizing: border-box; /* Ensure consistent sizing */
551
+ box-sizing: border-box;
552
+ /* Ensure consistent sizing */
500
553
  }
501
554
 
502
555
  .current-time-line {
@@ -506,7 +559,8 @@ onUnmounted(() => {
506
559
 
507
560
  .current-time-dot {
508
561
  background-color: var(--bgl-primary);
509
- margin-right: -5px; /* Adjust dot position */
562
+ margin-right: -5px;
563
+ /* Adjust dot position */
510
564
  }
511
565
 
512
566
  /* This ensures all grid content aligns properly */
@@ -79,12 +79,11 @@ const {
79
79
  })
80
80
 
81
81
  function renderFieldForRow(field: Field<T>, row: T) {
82
- const { renderField } = useSchemaField<T, P>({
82
+ const { renderField } = useSchemaField<T>({
83
83
  mode: 'table',
84
84
  getFormData: () => row,
85
- onUpdateModelValue: undefined
86
85
  })
87
- return renderField({ ...field, label: '' } as BaseBagelField<T, P>, slots as unknown as BaseBagelField<T, P>['slots'])
86
+ return renderField({ ...field, label: '' } as BaseBagelField<T, Path<T>>, slots as unknown as BaseBagelField<T, Path<T>>['slots'])
88
87
  }
89
88
 
90
89
  const computedItemHeight = $computed(() => `${itemHeight.value}px`)