@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
@@ -1,417 +1,152 @@
1
1
  <script setup lang="ts">
2
- import type { PropType } from 'vue'
3
- import type { ConfigInterface } from './typings/config.interface'
4
- import type { DayInterface } from './typings/interfaces/day.interface'
5
- import type { EventInterface } from './typings/interfaces/event.interface'
6
- import type { PeriodInterface } from './typings/interfaces/period.interface'
7
- import type { ModeType } from './typings/types'
8
- import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
9
- import AppHeader from './components/header/Header.vue'
10
- import AgendaEvents from './components/month/AgendaEvents.vue'
11
- import Month from './components/month/Month.vue'
12
- import Week from './components/week/Week.vue'
13
- import Errors from './helpers/Errors'
14
- import Time from './helpers/Time'
15
-
16
- const props = defineProps({
17
- config: {
18
- type: Object as PropType<ConfigInterface>,
19
- default: () => ({}),
20
- },
21
- events: {
22
- type: Array as PropType<EventInterface[]>,
23
- default: () => [],
24
- },
25
- selectedDate: {
26
- type: Date,
27
- default: new Date(),
28
- },
29
- isLoading: {
30
- type: Boolean,
31
- default: false,
32
- },
2
+ import type { Component } from 'vue'
3
+ import type { CalendarEvent, CalendarView, WeekStart } from './CalendarTypes'
4
+ import { timeDelta, Btn, formatDate, ListItem, Dropdown } from '@bagelink/vue'
5
+ import { ref, computed } from 'vue'
6
+ import AgendaView from './views/AgendaView.vue'
7
+ import DayView from './views/DayView.vue'
8
+ import MonthView from './views/MonthView.vue'
9
+ import WeekView from './views/WeekView.vue'
10
+
11
+ interface CalendarProps {
12
+ events: CalendarEvent[]
13
+ startDate?: Date
14
+ view?: CalendarView
15
+ weekStart?: WeekStart
16
+ }
17
+
18
+ const props = withDefaults(defineProps<CalendarProps>(), {
19
+ startDate: () => new Date(),
20
+ view: 'Week',
21
+ weekStart: 'Sunday'
33
22
  })
34
23
 
35
- const emit = defineEmits([
36
- 'eventWasClicked',
37
- 'eventWasResized',
38
- 'eventWasDragged',
39
- 'updatedPeriod',
40
- 'updatedMode',
41
- 'editEvent',
42
- 'deleteEvent',
43
- 'intervalWasClicked',
44
- 'dayWasClicked', // TODO: remove with v4. day-was-clicked is deprecated
45
- 'dateWasClicked',
46
- 'datetimeWasClicked',
47
- ] as const)
48
-
49
- const wasInitialized = ref(0)
50
- const period = ref({
51
- start: new Date(),
52
- end: new Date(),
53
- selectedDate: props.selectedDate,
54
- })
55
- const mode = ref(props.config.defaultMode || ('week' as ModeType))
56
- const time = ref(new Time(props.config.week?.startsOn, props.config.locale || null, {
57
- start: setTimePointsFromDayBoundary(props.config.dayBoundaries?.start || 0),
58
- end: setTimePointsFromDayBoundary(props.config.dayBoundaries?.end || 24),
59
- }) as Time | any)
60
- // const UNUSED_fontFamily = computed(() => props.config.style?.fontFamily || '\'Verdana\', \'Open Sans\', serif')
61
- const eventRenderingKey = ref(0)
62
- const eventsDataProperty = ref(props.events)
63
- const isSmall = ref(false)
64
- const ErrorsHelper = Errors
65
- const appHeaderRef = ref()
66
-
67
- const enhancedConfig = computed((): ConfigInterface => {
68
- return { ...props.config, isSmall: isSmall.value }
69
- })
70
-
71
- watch(() => props.events, (newVal, oldVal) => {
72
- if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
73
- eventsDataProperty.value = newVal
74
- eventRenderingKey.value = eventRenderingKey.value + 1
24
+ const emit = defineEmits<{
25
+ (e: 'eventClick', event: CalendarEvent): void
26
+ (e: 'eventCreate', event: { start_time: Date, end_time: Date }): void
27
+ (e: 'eventUpdate', event: CalendarEvent): void
28
+ (e: 'eventDelete', eventId: string): void
29
+ (e: 'dateChange', date: Date): void
30
+ (e: 'viewChange', view: CalendarView): void
31
+ }>()
32
+
33
+ // State
34
+ const currentDate = ref(new Date(props.startDate))
35
+ const currentView = ref(props.view)
36
+
37
+ // Available view components
38
+ const views: Record<CalendarView, Component> = {
39
+ Week: WeekView,
40
+ Month: MonthView,
41
+ Day: DayView,
42
+ Agenda: AgendaView
43
+ }
44
+
45
+ // Calculate visible date range based on current view
46
+ const visibleDateRange = computed(() => {
47
+ const start = new Date(currentDate.value)
48
+ let end = new Date(currentDate.value)
49
+
50
+ if (currentView.value === 'Week') {
51
+ const dayOfWeek = start.getDay()
52
+ const weekStartOffset = props.weekStart === 'Sunday' ? 0 : 1
53
+ start.setDate(start.getDate() - dayOfWeek + weekStartOffset)
54
+ end = new Date(start)
55
+ end.setDate(end.getDate() + 6)
56
+ } else if (['Month', 'Agenda'].includes(currentView.value)) {
57
+ start.setDate(1)
58
+ start.setDate(start.getDate() - 7)
59
+ end = new Date(start.getFullYear(), start.getMonth() + 2, 7)
60
+ } else if (currentView.value === 'Day') {
61
+ end.setDate(end.getDate() + 1)
75
62
  }
76
- if (props.config.isSilent) return
77
-
78
- props.events.forEach((e) => { ErrorsHelper.checkEventProperties(e) })
79
- }, { deep: true, immediate: true })
80
-
81
- watch(() => props.config, (value: ConfigInterface) => {
82
- ErrorsHelper.checkConfig(value)
83
- }, { deep: true, immediate: true })
84
-
85
- onMounted(() => {
86
- setConfigOnMount()
87
- onCalendarResize()
88
- setPeriodOnMount()
89
- window.addEventListener('resize', onCalendarResize)
63
+ return { start, end }
90
64
  })
91
65
 
92
- onBeforeUnmount(() => {
93
- window.removeEventListener('resize', onCalendarResize)
94
- })
95
-
96
- function setConfigOnMount() {
97
- wasInitialized.value = 1
66
+ // Event handlers
67
+ function handleEventClick(event: CalendarEvent) {
68
+ emit('eventClick', event)
98
69
  }
99
70
 
100
- function handleUpdatedPeriod(
101
- value: PeriodInterface,
102
- leaveMonthMode = false
103
- ) {
104
- emit('updatedPeriod', { start: value.start, end: value.end })
105
- period.value = value
106
-
107
- if (leaveMonthMode) mode.value = 'day'
71
+ function handleEventCreate(event: { start_time: Date, end_time: Date }) {
72
+ emit('eventCreate', event)
108
73
  }
109
74
 
110
- function handleChangeMode(payload: ModeType) {
111
- if (payload === 'day') {
112
- period.value.start = period.value.selectedDate
113
- period.value.end = time.value.setDateToEndOfDay(period.value.selectedDate)
114
- }
115
-
116
- if (payload === 'week') {
117
- const week = time.value.getCalendarWeekDateObjects(period.value.selectedDate)
118
- period.value.start = week[0]
119
- period.value.end = time.value.setDateToEndOfDay(week[6])
120
- }
121
-
122
- if (payload === 'month') {
123
- const month = time.value.getCalendarMonthSplitInWeeks(
124
- period.value.selectedDate.getFullYear(),
125
- period.value.selectedDate.getMonth()
126
- )
127
-
128
- period.value.start = month[0][0]
129
- const lastWeek = month[month.length - 1]
130
- period.value.end = time.value.setDateToEndOfDay(lastWeek[lastWeek.length - 1])
131
- }
132
-
133
- mode.value = payload
134
- emit('updatedMode', { mode: payload, period: period.value })
135
- }
136
-
137
- const calendarRoot = ref<HTMLElement>()
138
-
139
- function onCalendarResize() {
140
- const documentRoot = document.documentElement
141
- const documentFontSize = +window
142
- .getComputedStyle(documentRoot)
143
- .fontSize
144
- .split('p')[0]
145
- const breakPointFor1RemEquals16px = 600
146
- const multiplier = 16 / documentFontSize
147
- const smallCalendarBreakpoint = breakPointFor1RemEquals16px / multiplier // For 16px root font-size, break point is at 43.75rem
148
-
149
- if (!calendarRoot.value) return
150
-
151
- isSmall.value = calendarRoot.value.clientWidth < smallCalendarBreakpoint
152
-
153
- if (isSmall.value && !['day', 'month'].includes(mode.value)) {
154
- mode.value = 'day'
155
- }
75
+ function handleEventUpdate(event: CalendarEvent) {
76
+ emit('eventUpdate', event)
156
77
  }
157
78
 
158
- function setPeriodOnMount() {
159
- if (mode.value === 'week') {
160
- const currentWeek = time.value.getCalendarWeekDateObjects(period.value.selectedDate)
161
- period.value.start = currentWeek[0]
162
- period.value.end = currentWeek[6]
163
- } else if (mode.value === 'month') {
164
- const month = time.value.getCalendarMonthSplitInWeeks(
165
- period.value.selectedDate.getFullYear(),
166
- period.value.selectedDate.getMonth()
167
- )
168
- period.value.start = month[0][0]
169
- const lastWeek = month[month.length - 1]
170
- period.value.end = lastWeek[lastWeek.length - 1]
171
- }
79
+ function handleEventDelete(eventId: string) {
80
+ emit('eventDelete', eventId)
172
81
  }
173
82
 
174
- function handleEventWasUpdated(
175
- calendarEvent: EventInterface,
176
- eventType: 'dragged' | 'resized'
177
- ) {
178
- const newEvents = eventsDataProperty.value.filter(
179
- e => e.id !== calendarEvent.id
180
- )
181
- eventsDataProperty.value = [calendarEvent, ...newEvents]
182
- emit(eventType === 'dragged' ? 'eventWasDragged' : 'eventWasResized', calendarEvent)
83
+ function handleDateChange(date: Date) {
84
+ currentDate.value = new Date(date)
85
+ emit('dateChange', currentDate.value)
183
86
  }
184
87
 
185
- function setTimePointsFromDayBoundary(boundary: number) {
186
- return Time.getTimePointsFromHour(boundary)
88
+ function handleViewChange(view: CalendarView) {
89
+ currentView.value = view
90
+ emit('viewChange', view)
187
91
  }
188
92
 
189
- const day = $computed(() => {
190
- return {
191
- dayName: time.value.getLocalizedNameOfWeekday(period.value.selectedDate, 'long'),
192
- dateTimeString: period.value.selectedDate.toLocaleDateString('en-US', {
193
- day: '2-digit',
194
- month: '2-digit',
195
- year: 'numeric',
196
- }),
197
- events: eventsDataProperty.value.filter((e) => {
198
- const eventDate = new Date(e.time.start)
199
- return eventDate >= period.value.start && eventDate <= period.value.end
200
- }),
201
- fullDayEvents: {
202
- date: period.value.selectedDate,
203
- events: eventsDataProperty.value.filter((e) => {
204
- const eventDate = new Date(e.time.start)
205
- return eventDate >= period.value.start && eventDate <= period.value.end
206
- }),
207
- }
208
- } as DayInterface
209
- })
210
-
211
- function handleDateWasClicked(payload: string) {
212
- emit('dayWasClicked', payload)
213
- emit('dateWasClicked', payload)
214
- }
93
+ defineExpose({ visibleDateRange })
215
94
  </script>
216
95
 
217
96
  <template>
218
- <div class="calendar-root-wrapper txt16">
219
- <div
220
- ref="calendarRoot"
221
- class="calendar-root"
222
- :class="{
223
- 'mode-is-day': mode === 'day',
224
- 'mode-is-week': mode === 'week',
225
- 'mode-is-month': mode === 'month',
226
- 'qalendar-is-small': isSmall,
227
- }"
228
- :data-lang="config?.locale?.substring(0, 2) || 'en'"
229
- >
230
- <Transition name="loading">
231
- <div
232
- v-if="isLoading"
233
- class="top-bar-loader"
234
- />
235
- </Transition>
236
-
237
- <AppHeader
238
- ref="appHeaderRef"
239
- :key="wasInitialized + mode"
240
- :config="config"
241
- :mode="mode"
242
- :time="time"
243
- :period="period"
244
- :is-small="isSmall"
245
- @change-mode="handleChangeMode"
246
- @updated-period="handleUpdatedPeriod"
247
- />
248
- <AgendaEvents
249
- v-if="mode === 'agenda'"
250
- :events-prop="eventsDataProperty"
251
- :period="period"
252
- :config="config"
253
- :time="time"
254
- :day="day"
255
- />
256
-
257
- <Week
258
- v-if="['week', 'day'].includes(mode)"
259
- :key="period.start.getTime() + period.end.getTime() + eventRenderingKey"
260
- :events-prop="eventsDataProperty"
261
- :period="period"
262
- :config="config"
263
- :mode-prop="mode"
264
- :time="time"
265
- @event-was-clicked="$emit('eventWasClicked', $event)"
266
- @event-was-resized="handleEventWasUpdated($event, 'resized')"
267
- @event-was-dragged="handleEventWasUpdated($event, 'dragged')"
268
- @edit-event="$emit('editEvent', $event)"
269
- @delete-event="$emit('deleteEvent', $event)"
270
- @interval-was-clicked="$emit('intervalWasClicked', $event)"
271
- @day-was-clicked="$emit('dayWasClicked', $event)"
272
- @datetime-was-clicked="$emit('datetimeWasClicked', $event)"
273
- >
274
- <template #weekDayEvent="p">
275
- <slot
276
- :event-data="p.eventData"
277
- name="weekDayEvent"
278
- />
279
- </template>
280
-
281
- <template #eventDialog="p">
282
- <slot
283
- name="eventDialog"
284
- :event-dialog-data="p.eventDialogData"
285
- :close-event-dialog="p.closeEventDialog"
97
+ <div class="calendar">
98
+ <div class="flex m_block m_pb-1">
99
+ <h3 class="txt-light my-0">
100
+ <b>{{ formatDate(currentDate, 'MMMM') }}</b>
101
+ {{ formatDate(currentDate, 'YYYY') }}
102
+ </h3>
103
+ <div class="ms-auto flex gap-025">
104
+ <Dropdown thin :value="currentView" iconEnd="keyboard_arrow_down" color="gray">
105
+ <ListItem
106
+ v-for="(_, key) in views"
107
+ :key="key"
108
+ :title="key"
109
+ @click="handleViewChange(key)"
286
110
  />
287
- </template>
288
-
289
- <template #customCurrentTime>
290
- <slot name="customCurrentTime" />
291
- </template>
292
- </Week>
293
-
294
- <Month
295
- v-if="mode === 'month'"
296
- :key="period.start.getTime() + period.end.getTime() + eventRenderingKey"
297
- :events-prop="eventsDataProperty"
298
- :time="time"
299
- :config="enhancedConfig"
300
- :period="period"
301
- @event-was-clicked="$emit('eventWasClicked', $event)"
302
- @date-was-clicked="handleDateWasClicked"
303
- @event-was-dragged="handleEventWasUpdated($event, 'dragged')"
304
- @updated-period="handleUpdatedPeriod($event, true)"
305
- @edit-event="$emit('editEvent', $event)"
306
- @delete-event="$emit('deleteEvent', $event)"
307
- >
308
- <template #eventDialog="p">
309
- <slot
310
- name="eventDialog"
311
- :event-dialog-data="p.eventDialogData"
312
- :close-event-dialog="p.closeEventDialog"
313
- />
314
- </template>
315
-
316
- <template #monthEvent="p">
317
- <slot
318
- :event-data="p.eventData"
319
- name="monthEvent"
320
- />
321
- </template>
322
-
323
- <template #dayCell="{ dayData }">
324
- <slot
325
- :day-data="dayData"
326
- name="dayCell"
327
- />
328
- </template>
329
- </Month>
111
+ </Dropdown>
112
+ <Btn icon="calendar" thin color="gray" value="Today" @click="handleDateChange(new Date())" />
113
+ <Btn
114
+ icon="chevron_left"
115
+ color="gray"
116
+ thin
117
+ @click="handleDateChange(timeDelta(currentDate, { [currentView]: -1 }))"
118
+ />
119
+ <Btn
120
+ icon="chevron_right"
121
+ color="gray"
122
+ thin
123
+ @click="handleDateChange(timeDelta(currentDate, { [currentView]: 1 }))"
124
+ />
125
+ </div>
330
126
  </div>
127
+ <component
128
+ :is="views[currentView]"
129
+ :events="events"
130
+ :start-date="currentDate"
131
+ :week-start="weekStart"
132
+ @event-click="handleEventClick"
133
+ @event-create="handleEventCreate"
134
+ @event-update="handleEventUpdate"
135
+ @event-delete="handleEventDelete"
136
+ @date-change="handleDateChange"
137
+ >
138
+ <template #eventContent="{ event }">
139
+ <slot name="eventContent" :event="event" />
140
+ </template>
141
+ </component>
331
142
  </div>
332
143
  </template>
333
144
 
334
- <!-- <style src="../../styles/_variables.css"></style> -->
335
- <style src="../calendar/styles/_variables.css"></style>
336
-
337
- <style>
338
- .calendar-root-wrapper {
339
- width: 100%;
340
- max-width: 100vw;
341
- height: 100%;
342
- display: flex;
343
- }
344
-
345
- .calendar-root-wrapper .calendar-root {
346
- flex: 1;
347
- border-radius: var(--qalendar-border-radius);
348
- position: relative;
349
- width: 100%;
350
- margin: 0 auto;
351
- display: flex;
352
- flex-flow: column;
353
- }
354
- .calendar-root-wrapper .calendar-root .calendar-month__weeks{
355
- border-inline: var(--qalendar-border-gray-thin);
356
- }
357
-
358
- .calendar-root-wrapper .calendar-root .top-bar-loader {
359
- position: absolute;
360
- top: 1px;
361
- left: 2px;
362
- width: calc(100% - 4px);
363
- height: 3px;
364
- background: rgba(241, 241, 241, 0.2);
365
- border-radius: 16px;
366
- overflow: hidden;
367
- }
368
-
369
- .calendar-root-wrapper .calendar-root .top-bar-loader:before {
370
- content: '';
371
- height: 4px;
372
- width: calc(100% - 4px);
373
- position: absolute;
374
- top: 1px;
375
- left: 2px;
376
- background: rgb(38, 132, 255);
377
- background: linear-gradient(
378
- 90deg,
379
- rgba(38, 132, 255, 1) 0%,
380
- rgba(38, 132, 255, 0.5088410364145659) 48%,
381
- rgba(38, 132, 255, 1) 100%
382
- );
383
- animation: load 1.8s infinite;
384
- border-radius: 16px;
385
- }
386
-
387
- @media (prefers-color-scheme: dark) {
388
- .calendar-root-wrapper .calendar-root .top-bar-loader:before {
389
- background: rgb(229, 224, 245);
390
- }
391
- }
392
-
393
- @keyframes load {
394
- 0% {
395
- width: 0;
396
- left: -100%;
397
- }
398
- 50% {
399
- left: 0;
400
- width: 100%;
401
- }
402
- 100% {
403
- width: 0;
404
- left: 100%;
405
- }
406
- }
407
-
408
- .calendar-root-wrapper .calendar-root .loading-enter-active,
409
- .calendar-root-wrapper .calendar-root .loading-leave-active {
410
- transition: background 0.5s ease;
411
- }
412
-
413
- .calendar-root-wrapper .calendar-root .loading-leave-to,
414
- .calendar-root-wrapper .calendar-root .loading-enter-from {
415
- background-color: rgba(255, 255, 255, 0);
145
+ <style scoped>
146
+ .calendar {
147
+ display: flex;
148
+ flex-direction: column;
149
+ height: 100%;
150
+ width: 100%;
416
151
  }
417
152
  </style>
@@ -0,0 +1,70 @@
1
+ import type { Ref } from 'vue'
2
+ import type { CalendarEvent } from './CalendarTypes'
3
+
4
+ export interface PopoverState {
5
+ activeEvent: Ref<CalendarEvent | null>
6
+ showPopover: Ref<boolean>
7
+ popoverPosition: Ref<{ top: number, left: number, width: number, height: number }>
8
+ popoverRef: Ref<HTMLElement | null>
9
+ }
10
+
11
+ /**
12
+ * Positions the popover to be centered on the target element and ensures it stays within viewport
13
+ */
14
+ export function calculatePopoverPosition(
15
+ target: HTMLElement,
16
+ popoverWidth = 300,
17
+ popoverHeight = 150,
18
+ ): { top: number, left: number, width: number, height: number } {
19
+ const rect = target.getBoundingClientRect()
20
+ const { scrollY, scrollX, innerHeight, innerWidth } = window
21
+
22
+ // Default position (centered to the left of the element)
23
+ let top = rect.top + scrollY + (rect.height / 2) - (popoverHeight / 2)
24
+ let left = rect.left + scrollX + rect.width + 10
25
+
26
+ // Keep popover within viewport bounds
27
+ top = Math.max(scrollY + 10, Math.min(top, innerHeight + scrollY - popoverHeight - 10))
28
+
29
+ // If it would go off right side, position to the left instead
30
+ if (left + popoverWidth > innerWidth + scrollX) {
31
+ left = rect.left + scrollX - popoverWidth - 10
32
+ }
33
+
34
+ // If it would go off left side, place it to the right
35
+ if (left < scrollX) {
36
+ left = rect.right + scrollX + 10
37
+ }
38
+
39
+ return { top, left, width: popoverWidth, height: popoverHeight }
40
+ }
41
+
42
+ /**
43
+ * Opens a popover for a calendar event
44
+ */
45
+ export function openPopover(
46
+ event: CalendarEvent,
47
+ e: MouseEvent,
48
+ state: PopoverState,
49
+ hasEventContentSlot: boolean,
50
+ emitEventClick: (event: CalendarEvent) => void
51
+ ) {
52
+ // Don't open if no slot provided
53
+ if (!hasEventContentSlot) {
54
+ emitEventClick(event)
55
+ return
56
+ }
57
+
58
+ // Set active event and calculate position
59
+ state.activeEvent.value = event
60
+ state.popoverPosition.value = calculatePopoverPosition(e.currentTarget as HTMLElement)
61
+ state.showPopover.value = true
62
+ }
63
+
64
+ /**
65
+ * Closes the popover
66
+ */
67
+ export function closePopover(state: PopoverState) {
68
+ state.showPopover.value = false
69
+ state.activeEvent.value = null
70
+ }