• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tomosterlund / qalendar / 13467125422

21 Feb 2025 11:41PM UTC coverage: 97.288%. Remained the same
13467125422

Pull #269

github

web-flow
Merge 8ec4f6867 into 1e81b86d4
Pull Request #269: chore(deps): update dependency @types/node to v22

914 of 979 branches covered (93.36%)

Branch coverage included in aggregate %.

6439 of 6579 relevant lines covered (97.87%)

87.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.01
/src/components/week/DayEvent.vue
1
<template>
128✔
2
  <div
1✔
3
    class="calendar-week__event is-event"
1✔
4
    :class="{
1✔
5
      'is-editable': isEditable,
1✔
6
      'has-disabled-dnd': hasDisabledDragAndDrop,
1✔
7
    }"
1✔
8
    :style="{
1✔
9
      ...requiredStyles,
1✔
10
      border: getBorderRule,
1✔
11
    }"
1✔
12
    :data-ref="'event-' + event.id"
1✔
13
    @click="handleClickOnEvent"
1✔
14
    @mouseenter="showResizeElements = isEditable && !hasDisabledResize"
1✔
15
    @mouseleave="showResizeElements = false"
1✔
16
    @mousedown="initDrag"
1✔
17
    @touchstart="initDrag"
1✔
18
  >
1✔
19
    <div
1✔
20
      v-if="!isCustomEvent"
1✔
21
      class="calendar-week__event-info-wrapper"
129✔
22
      :style="{
129✔
23
        color: eventColor,
129✔
24
        width: '100%',
129✔
25
        height: '100%',
129✔
26
        backgroundColor: eventBackgroundColor,
129✔
27
      }"
129✔
28
    >
129✔
29
      <div class="calendar-week__event-row is-title">
129✔
30
        {{ event.title }}
129✔
31
      </div>
129✔
32

129✔
33
      <div class="calendar-week__event-row is-time">
129✔
34
        <font-awesome-icon
129✔
35
          :icon="icons.clock"
129✔
36
          class="calendar-week__event-icon"
129✔
37
        />
129✔
38
        <span>{{ getEventTime }}</span>
129✔
39
      </div>
129✔
40

129✔
41
      <div
129✔
42
        v-if="event.location"
129✔
43
        class="calendar-week__event-row is-location"
2✔
44
      >
2✔
45
        <font-awesome-icon
2✔
46
          :icon="icons.location"
2✔
47
          class="calendar-week__event-icon"
2✔
48
        />
2✔
49
        <span>{{ event.location }}</span>
2✔
50
      </div>
129✔
51

129✔
52
      <div
129✔
53
        v-if="event.with"
129✔
54
        class="calendar-week__event-row is-with"
2✔
55
      >
2✔
56
        <font-awesome-icon
2✔
57
          :icon="icons.user"
2✔
58
          class="calendar-week__event-icon"
2✔
59
        />
2✔
60
        <span>{{ event.with }}</span>
2✔
61
      </div>
129✔
62

129✔
63
      <div
129✔
64
        v-if="event.topic"
129✔
65
        class="calendar-week__event-row is-topic"
2✔
66
      >
2✔
67
        <font-awesome-icon
2✔
68
          :icon="icons.topic"
2✔
69
          class="calendar-week__event-icon"
2✔
70
        />
2✔
71
        <span>{{ event.topic }}</span>
2✔
72
      </div>
129✔
73

129✔
74
      <div
129✔
75
        v-if="event.description"
129✔
76
        class="calendar-week__event-row is-description"
2✔
77
      >
2✔
78
        <font-awesome-icon
2✔
79
          :icon="icons.description"
2✔
80
          class="calendar-week__event-icon"
2✔
81
        />
2✔
82
        <!-- eslint-disable vue/no-v-html -->
2✔
83
        <p v-html="event.description" />
2✔
84
        <!--eslint-enable-->
2✔
85
      </div>
129✔
86

129✔
87
      <div
129✔
88
        v-if="eventIsLongerThan30Minutes"
129✔
89
        class="calendar-week__event-blend-out"
129✔
90
        :style="{
129✔
91
          backgroundImage:
129✔
92
            'linear-gradient(to bottom, transparent, ' +
129✔
93
            eventBackgroundColor +
129✔
94
            ')',
129✔
95
        }"
129✔
96
      />
129✔
97
    </div>
129✔
98

129✔
99
    <slot
129✔
100
      v-else
5✔
101
      name="weekDayEvent"
5✔
102
      :event-data="event"
5✔
103
    />
1✔
104

1✔
105
    <div
1✔
106
      v-if="showResizeElements"
1✔
107
      class="calendar-week__event-resize calendar-week__event-resize-up"
1✔
108
      @mousedown="resizeEvent('up')"
1✔
109
    />
1✔
110

1✔
111
    <div
1✔
112
      v-if="showResizeElements"
1✔
113
      class="calendar-week__event-resize calendar-week__event-resize-down"
1✔
114
      @mousedown="resizeEvent('down')"
1✔
115
    />
1✔
116
  </div>
1✔
117
</template>
1✔
118

1✔
119
<script lang="ts">
1✔
120
import {defineComponent, type PropType} from 'vue';
1✔
121
import {type eventInterface} from '../../typings/interfaces/event.interface';
1✔
122
import {
1✔
123
  faClock,
1✔
124
  faComment,
1✔
125
  faMapMarkerAlt,
1✔
126
  faQuestionCircle,
1✔
127
  faUser,
1✔
128
} from '@fortawesome/free-solid-svg-icons';
1✔
129
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome';
1✔
130
import Time from '../../helpers/Time';
1✔
131
import {type configInterface} from '../../typings/config.interface';
1✔
132
import {EVENT_COLORS} from '../../constants';
1✔
133
import {type DayInfo, DRAG_N_RESIZE_DIRECTION, type modeType} from '../../typings/types';
1✔
134
import { EventChange } from '../../helpers/EventChange';
1✔
135
import Helpers from "../../helpers/Helpers";
1✔
136

1✔
137
export default defineComponent({
1✔
138
  name: 'DayEvent',
1✔
139

1✔
140
  components: {
1✔
141
    FontAwesomeIcon,
1✔
142
  },
1✔
143

1✔
144
  props: {
1✔
145
    eventProp: {
1✔
146
      type: Object as PropType<eventInterface>,
1✔
147
      required: true,
1✔
148
    },
1✔
149
    time: {
1✔
150
      type: Object as PropType<Time>,
1✔
151
      required: true,
1✔
152
    },
1✔
153
    config: {
1✔
154
      type: Object as PropType<configInterface>,
1✔
155
      required: true,
1✔
156
    },
1✔
157
    dayInfo: {
1✔
158
      type: Object as PropType<DayInfo>,
1✔
159
      required: true,
1✔
160
    },
1✔
161
    mode: {
1✔
162
      type: String as PropType<modeType>,
1✔
163
      required: true,
1✔
164
    },
1✔
165
  },
1✔
166

1✔
167
  emits: ['event-was-clicked', 'event-was-resized', 'event-was-dragged', 'drag-start', 'drag-end'],
1✔
168

1✔
169
  data() {
1✔
170
    return {
64✔
171
      event: this.eventProp,
64✔
172
      icons: {
64✔
173
        clock: faClock,
64✔
174
        user: faUser,
64✔
175
        description: faComment,
64✔
176
        location: faMapMarkerAlt,
64✔
177
        topic: faQuestionCircle,
64✔
178
      },
64✔
179
      showResizeElements: false,
64✔
180
      eventTransformValue: 'initial',
64✔
181
      eventZIndexValue: 'initial' as 'initial' | number,
64✔
182
      dayElement: document.querySelector('.calendar-week__day'),
64✔
183
      dayBoundariesDateTimeStrings: this.time.getDateTimeStringDayBoundariesFrom(this.dayInfo.dateTimeString),
64✔
184

64✔
185
      // Resizing events
64✔
186
      resizingStartingPoint: undefined as undefined | number,
64✔
187
      resizingStartingPointEndOfTime: this.eventProp.time.end,
64✔
188
      resizingStartingPointStartOfTime: this.eventProp.time.start,
64✔
189
      resizingDirection: '',
64✔
190
      changeInQuarterHoursEventStart: 0,
64✔
191
      changeInQuarterHoursEventEnd: 0,
64✔
192
      isEditable: this.eventProp.isEditable || false,
64✔
193
      colors: EVENT_COLORS as { [key: string]: string },
64✔
194
      eventColor: '#fff',
64✔
195
      eventBackgroundColor: '',
64✔
196
      isResizing: false,
64✔
197

64✔
198
      // Dragging events
64✔
199
      canDrag: false, // set to true on mousedown and false on mouseup
64✔
200
      clientYDragStart: null as null | number,
64✔
201
      clientXDragStart: null as null | number,
64✔
202
      changeInQuartersOnDrag: 0,
64✔
203
      changeInDaysOnDrag: 0,
64✔
204
      isDragging: false,
64✔
205
      timeStartDragStart: this.eventProp.time.start,
64✔
206
      timeEndDragStart: this.eventProp.time.end,
64✔
207

64✔
208
      dragMoveListenerNameAndCallbacks: [
64✔
209
        ['mousemove', this.handleDrag],
64✔
210
        ['touchmove', this.handleDrag],
64✔
211
        ['mouseup', this.onMouseUpWhenDragging],
64✔
212
        ['touchend', this.onMouseUpWhenDragging],
64✔
213
      ] as ReadonlyArray<[string, any]>,
64✔
214
    };
64✔
215
  },
64✔
216

1✔
217
  computed: {
1✔
218
    eventChangeHelper() {
1✔
219
      const eventCurrentDay = this.time.addDaysToDateTimeString(this.changeInDaysOnDrag, this.dayInfo.dateTimeString)
4✔
220

4✔
221
      return new EventChange(this.time, this.time.dateStringFrom(eventCurrentDay))
4✔
222
    },
4✔
223

1✔
224
    isCustomEvent(): boolean {
1✔
225
      if (Array.isArray(this.eventProp.isCustom)) {
64✔
226
        return this.eventProp.isCustom.includes(this.mode);
1✔
227
      }
1✔
228

63✔
229
      return this.eventProp.isCustom || false;
64✔
230
    },
64✔
231

1✔
232
    getEventTime() {
1✔
233
      return (
67✔
234
        this.time.getLocalizedTime(this.event.time.start) +
67✔
235
        ' - ' +
67✔
236
        this.time.getLocalizedTime(this.event.time.end)
67✔
237
      );
67✔
238
    },
67✔
239

1✔
240
    timePointsInDay() {
1✔
241
      return this.time.HOURS_PER_DAY * 100;
2✔
242
    },
2✔
243

1✔
244
    timePointsInOneMinute() {
1✔
245
      return 100 / 60;
1✔
246
    },
1✔
247

1✔
248
    getLeftRule() {
1✔
249
      if (
68✔
250
        !this.event.totalConcurrentEvents ||
68✔
251
        !this.event.nOfPreviousConcurrentEvents
27✔
252
      )
68✔
253
        return 0;
68✔
254

11✔
255
      return (
11✔
256
        (this.event.nOfPreviousConcurrentEvents /
11✔
257
          this.event.totalConcurrentEvents) *
11✔
258
        100
11✔
259
      );
68✔
260
    },
68✔
261

1✔
262
    getWidthRule() {
1✔
263
      return 100 - this.getLeftRule;
67✔
264
    },
67✔
265

1✔
266
    getBorderRule() {
1✔
267
      if (!this.event.nOfPreviousConcurrentEvents) return 'none';
67✔
268

12✔
269
      return '1px solid #fff';
12✔
270
    },
67✔
271

1✔
272
    eventIsLongerThan30Minutes() {
1✔
273
      const { hour: startHour, minutes: startMinutes } =
67✔
274
        this.time.getAllVariablesFromDateTimeString(this.event.time.start);
67✔
275
      const { hour: endHour, minutes: endMinutes } =
67✔
276
        this.time.getAllVariablesFromDateTimeString(this.event.time.end);
67✔
277
      const startDateMS = new Date(0, 0, 0, startHour, startMinutes).getTime();
67✔
278
      const endDateMS = new Date(0, 0, 0, endHour, endMinutes).getTime();
67✔
279

67✔
280
      return endDateMS - startDateMS >= 1800000;
67✔
281
    },
67✔
282

1✔
283
    hasDisabledDragAndDrop() {
1✔
284
      return !!(
64✔
285
        this.eventProp.disableDnD &&
64✔
286
        this.eventProp.disableDnD.includes(this.mode)
2✔
287
      );
64✔
288
    },
64✔
289

1✔
290
    hasDisabledResize() {
1✔
291
      return !!(
5✔
292
        this.eventProp.disableResize &&
5✔
293
        this.eventProp.disableResize.includes(this.mode)
1✔
294
      );
5✔
295
    },
5✔
296

1✔
297
    requiredStyles() {
1✔
298
      return {
77✔
299
        top: this.getPositionInDay(this.event.time.start),
77✔
300
        height: this.getLengthOfEvent(
77✔
301
          this.event.time.start,
77✔
302
          this.event.time.end
77✔
303
        ),
77✔
304
        left: this.getLeftRule + '%',
77✔
305
        width: this.getWidthRule + '%',
77✔
306
        transform: this.eventTransformValue,
77✔
307
        zIndex: this.eventZIndexValue,
77✔
308
      };
77✔
309
    },
77✔
310
  },
1✔
311

1✔
312
  watch: {
1✔
313
    changeInQuarterHoursEventStart(newValue, oldValue) {
1✔
314
      const newStartOfTimeDateTimeString = this.time.addMinutesToDateTimeString(
1✔
315
        15 * newValue,
1✔
316
        this.resizingStartingPointStartOfTime
1✔
317
      );
1✔
318
      const direction = newValue > oldValue ? DRAG_N_RESIZE_DIRECTION.FORWARDS : DRAG_N_RESIZE_DIRECTION.BACKWARDS;
1!
319
      const eventCanBeResizedFurther = this.eventChangeHelper.canEventBeMoved(
1✔
320
        this.event,
1✔
321
        direction
1✔
322
      )
1✔
323

1✔
324
      // Only set the new start time, if it's earlier than the end time of the event
1✔
325
      if (newStartOfTimeDateTimeString < this.event.time.end && eventCanBeResizedFurther) {
1✔
326
        this.event.time.start = newStartOfTimeDateTimeString;
1✔
327
      }
1✔
328
    },
1✔
329

1✔
330
    changeInQuarterHoursEventEnd(newValue, oldValue) {
1✔
331
      const newEndOfTimeDateTimeString = this.time.addMinutesToDateTimeString(
1✔
332
        15 * newValue,
1✔
333
        this.resizingStartingPointEndOfTime
1✔
334
      )
1✔
335
      const direction = newValue > oldValue ? DRAG_N_RESIZE_DIRECTION.FORWARDS : DRAG_N_RESIZE_DIRECTION.BACKWARDS;
1!
336
      const eventCanBeResizedFurther = this.eventChangeHelper.canEventBeMoved(
1✔
337
        this.event,
1✔
338
        direction
1✔
339
      )
1✔
340

1✔
341
      // Only set the new end time, if it's later than the start time of the event
1✔
342
      if (newEndOfTimeDateTimeString > this.event.time.start && eventCanBeResizedFurther) {
1✔
343
        this.event.time.end = newEndOfTimeDateTimeString;
1✔
344
      }
1✔
345
    },
1✔
346

1✔
347
    changeInQuartersOnDrag(newValue, oldValue) {
1✔
348
      const direction = newValue > oldValue ? DRAG_N_RESIZE_DIRECTION.FORWARDS : DRAG_N_RESIZE_DIRECTION.BACKWARDS;
2!
349

2✔
350
      const eventCanBeDraggedFurther = this.eventChangeHelper.canEventBeMoved(
2✔
351
        this.event,
2✔
352
        direction
2✔
353
      )
2✔
354

2✔
355
      if (!eventCanBeDraggedFurther) return;
2!
356

2✔
357
      this.updatePositionOnDrag();
2✔
358
    },
2✔
359

1✔
360
    changeInDaysOnDrag(newValue) {
1✔
361
      if (!this.dayElement) return;
3✔
362
      // +1 to account for the zero indexing vs length
2✔
363
      const upcomingDaysInWeek = this.dayInfo.daysTotalN - (this.dayInfo.thisDayIndex + 1);
2✔
364
      const previousDaysInWeek = 0 - this.dayInfo.thisDayIndex;
2✔
365
      if (newValue > upcomingDaysInWeek || newValue < previousDaysInWeek)
2✔
366
        return;
3!
367

2✔
368
      const pixelsToTransform = newValue * this.dayElement.clientWidth;
2✔
369
      this.eventTransformValue = `translateX(${pixelsToTransform}px)`;
2✔
370
      this.updatePositionOnDrag();
2✔
371
    },
3✔
372
  },
1✔
373

1✔
374
  mounted() {
1✔
375
    this.setColors();
64✔
376
  },
64✔
377

1✔
378
  methods: {
1✔
379
    getPositionInDay(dateTimeString: string) {
1✔
380
      return (
77✔
381
        this.time
77✔
382
          .getPercentageOfDayFromDateTimeString(
77✔
383
            dateTimeString,
77✔
384
            this.time.DAY_START,
77✔
385
            this.time.DAY_END,
77✔
386
          )
77✔
387
          .toString() + '%'
77✔
388
      );
77✔
389
    },
77✔
390

1✔
391
    getLengthOfEvent(start: string, end: string) {
1✔
392
      const startOfEvent =
77✔
393
        this.time.getPercentageOfDayFromDateTimeString(
77✔
394
          start,
77✔
395
          this.time.DAY_START,
77✔
396
          this.time.DAY_END
77✔
397
        );
77✔
398
      const endOfEvent =
77✔
399
        this.time.getPercentageOfDayFromDateTimeString(
77✔
400
          end,
77✔
401
          this.time.DAY_START,
77✔
402
          this.time.DAY_END
77✔
403
        );
77✔
404
      const length = Math.abs(endOfEvent - startOfEvent);
77✔
405

77✔
406
      return length + '%';
77✔
407
    },
77✔
408

1✔
409
    handleClickOnEvent(event: any) {
1✔
410
      const eventElement = this.getEventElementFromChildElement(event);
2✔
411

2✔
412
      if (!eventElement) return;
2!
413

2✔
414
      this.$emit('event-was-clicked', {
2✔
415
        clickedEvent: this.event,
2✔
416
        eventElement,
2✔
417
      });
2✔
418
    },
2✔
419

1✔
420
    /**
1✔
421
     * When a child element of the event is clicked, return the parent event element
1✔
422
     * */
1✔
423
    getEventElementFromChildElement(event: any) {
1✔
424
      const eventTarget = event.target;
2✔
425
      if (!eventTarget || typeof eventTarget.className.includes !== 'function')
2✔
426
        return null;
2!
427

2✔
428
      if (eventTarget.className.includes('.calendar-week__event'))
2✔
429
        return event.target;
2!
430

2✔
431
      return eventTarget.closest('.calendar-week__event');
2✔
432
    },
2✔
433

1✔
434
    /**
1✔
435
     * Handle mousemove-events, while the event is being resized
1✔
436
     * */
1✔
437
    onMouseMoveResize(event: MouseEvent) {
1✔
438
      const eventsContainer = document.querySelector('.calendar-week__events');
×
439

×
440
      if (!eventsContainer) return;
×
441

×
442
      if (typeof this.resizingStartingPoint === 'undefined') {
×
443
        this.resizingStartingPoint = event.clientY;
×
444
      }
×
445

×
446
      const cursorPositionY = event.clientY;
×
447
      const nOfPixelsDistance = cursorPositionY - this.resizingStartingPoint;
×
448
      const eventsContainerHeight = eventsContainer.clientHeight;
×
449
      const percentageOfDayChanged = (nOfPixelsDistance / eventsContainerHeight) * 100;
×
450
      const changeInTimePoints = (this.timePointsInDay / 100) * percentageOfDayChanged;
×
451
      const changeInMinutes = this.getMinutesFromTimePoints(changeInTimePoints);
×
452

×
453
      // Count how many quarters have changed, since the event will only be updated
×
454
      // for every quarter that is added or subtracted
×
455
      if (this.resizingDirection === 'down') {
×
456
        this.changeInQuarterHoursEventEnd = Math.floor(changeInMinutes / 15);
×
457
      } else {
×
458
        this.changeInQuarterHoursEventStart = Math.floor(changeInMinutes / 15);
×
459
      }
×
460
    },
×
461

1✔
462
    /**
1✔
463
     * Handle mouseup-events, for when an event stops being resized
1✔
464
     * */
1✔
465
    onMouseUpWhenResizing() {
1✔
466
      this.stopResizing();
×
467
    },
×
468

1✔
469
    resizeEvent(direction: 'down' | 'up') {
1✔
470
      this.isResizing = true;
2✔
471
      this.resizingDirection = direction;
2✔
472
      document.addEventListener('mousemove', this.onMouseMoveResize);
2✔
473
      document.addEventListener('mouseup', this.onMouseUpWhenResizing);
2✔
474
    },
2✔
475

1✔
476
    stopResizing() {
1✔
477
      document.removeEventListener('mousemove', this.onMouseMoveResize);
1✔
478
      document.removeEventListener('mouseup', this.onMouseUpWhenResizing);
1✔
479
      this.resetResizingValues();
1✔
480
      this.$emit('event-was-resized', this.event);
1✔
481
      this.isResizing = false;
1✔
482
    },
1✔
483

1✔
484
    /**
1✔
485
     * Reset values used for resizing an event, to prepare for upcoming resizing events
1✔
486
     * */
1✔
487
    resetResizingValues() {
1✔
488
      this.resizingStartingPoint = undefined;
1✔
489
      this.resizingStartingPointStartOfTime = this.eventProp.time.start;
1✔
490
      this.resizingStartingPointEndOfTime = this.eventProp.time.end;
1✔
491
      this.changeInQuarterHoursEventEnd = 0;
1✔
492
    },
1✔
493

1✔
494
    /**
1✔
495
     * Calculate the change of an event in minutes, based on the number of time points that changed
1✔
496
     * */
1✔
497
    getMinutesFromTimePoints(timePoints: number) {
1✔
498
      return timePoints / this.timePointsInOneMinute;
×
499
    },
×
500

1✔
501
    setColors() {
1✔
502
      // First, if the event has a customColorScheme, and the name of that
67✔
503
      if (
67✔
504
        this.event?.colorScheme &&
67✔
505
        this.config.style?.colorSchemes &&
2✔
506
        this.config.style.colorSchemes[this.event.colorScheme]
2✔
507
      ) {
67✔
508
        this.eventColor =
2✔
509
          this.config.style.colorSchemes[this.event.colorScheme].color;
2✔
510
        return (this.eventBackgroundColor =
2✔
511
          this.config.style.colorSchemes[
2✔
512
            this.event.colorScheme
2✔
513
          ].backgroundColor);
2✔
514
      }
2✔
515

65✔
516
      if (this.event?.color) {
67✔
517
        this.eventColor = '#fff';
4✔
518
        this.eventBackgroundColor = this.colors[this.event.color];
4✔
519

4✔
520
        return;
4✔
521
      }
4✔
522

61✔
523
      this.eventBackgroundColor = this.colors.blue;
61✔
524
    },
67✔
525

1✔
526
    initDrag(domEvent: UIEvent) {
1✔
527
      // Do not allow drag & drop, if event is not editable
6✔
528
      if (!this.event.isEditable || this.hasDisabledDragAndDrop) return;
6✔
529

4✔
530
      this.$emit('drag-start');
4✔
531

4✔
532
      this.dragMoveListenerNameAndCallbacks.forEach(([name, callback]) => {
4✔
533
        document.addEventListener(name, callback);
16✔
534
      });
4✔
535

4✔
536
      if (Helpers.isUIEventTouchEvent(domEvent)) {
6✔
537
        this.setInitialDragValues(
1✔
538
          (domEvent as TouchEvent).touches[0]?.clientX,
1!
539
          (domEvent as TouchEvent).touches[0]?.clientY
1!
540
        );
1✔
541
      } else {
6✔
542
        this.setInitialDragValues((domEvent as MouseEvent).clientX, (domEvent as MouseEvent).clientY);
3✔
543
      }
3✔
544
    },
6✔
545

1✔
546
    setInitialDragValues(clientX: number, clientY: number) {
1✔
547
      this.canDrag = true;
4✔
548
      this.eventZIndexValue = 10;
4✔
549
      this.clientYDragStart = clientY;
4✔
550
      this.clientXDragStart = clientX;
4✔
551
      this.timeStartDragStart = this.event.time.start;
4✔
552
      this.timeEndDragStart = this.event.time.end;
4✔
553
    },
4✔
554

1✔
555
    onMouseUpWhenDragging() {
1✔
556
      this.$emit('drag-end');
1✔
557
      this.handleDragEnd();
1✔
558
    },
1✔
559

1✔
560
    handleDragEnd() {
1✔
561
      this.canDrag = false;
2✔
562
      this.eventZIndexValue = 'initial';
2✔
563
      this.dragMoveListenerNameAndCallbacks.forEach(([name, callback]) => {
2✔
564
        document.removeEventListener(name, callback);
8✔
565
      });
2✔
566
      const dayChanged = this.changeInDaysOnDrag <= -1 || this.changeInDaysOnDrag > 0;
2✔
567
      const timeChanged = this.changeInQuartersOnDrag <= -1 || this.changeInQuartersOnDrag > 0;
2✔
568
      if (dayChanged || timeChanged) this.$emit('event-was-dragged', this.event);
2✔
569
    },
2✔
570

1✔
571
    handleDrag(mouseEvent: UIEvent) {
1✔
572
      // Do not run the drag & drop algorithms, under the following conditions:
×
573
      if (this.isResizing || !this.canDrag || !this.clientYDragStart) return;
×
574

×
575
      if (Helpers.isUIEventTouchEvent(mouseEvent)) {
×
576
        this.handleVerticalDrag((mouseEvent as TouchEvent).touches[0].clientY);
×
577
        this.handleHorizontalDrag((mouseEvent as TouchEvent).touches[0].clientX);
×
578
      } else {
×
579
        this.handleVerticalDrag((mouseEvent as MouseEvent).clientY);
×
580
        this.handleHorizontalDrag((mouseEvent as MouseEvent).clientX);
×
581
      }
×
582
    },
×
583

1✔
584
    /**
1✔
585
     * Handle dragging within days
1✔
586
     * */
1✔
587
    handleVerticalDrag(clientY: number) {
1✔
588
      const eventsContainer = document.querySelector('.calendar-week__events');
×
589
      if (!eventsContainer || !this.clientYDragStart) return;
×
590

×
591
      const nOfPixelsDistance = clientY - this.clientYDragStart;
×
592
      const eventsContainerHeight = eventsContainer.clientHeight;
×
593
      const percentageOfDayChanged =
×
594
        (nOfPixelsDistance / eventsContainerHeight) * 100;
×
595
      const changeInTimePoints =
×
596
        (this.timePointsInDay / 100) * percentageOfDayChanged;
×
597
      const changeInMinutes = this.getMinutesFromTimePoints(changeInTimePoints);
×
598
      this.changeInQuartersOnDrag =
×
599
        changeInMinutes < 0
×
600
          ? Math.ceil(changeInMinutes / 15)
×
601
          : Math.floor(changeInMinutes / 15);
×
602
    },
×
603

1✔
604
    /**
1✔
605
     * Handle dragging between days
1✔
606
     * */
1✔
607
    handleHorizontalDrag(clientX: number) {
1✔
608
      if (!this.dayElement || !this.clientXDragStart) return;
×
609

×
610
      const dayWidth = this.dayElement.clientWidth;
×
611
      const changeInPixelsX = clientX - this.clientXDragStart;
×
612
      this.changeInDaysOnDrag =
×
613
        changeInPixelsX < 0
×
614
          ? Math.ceil(changeInPixelsX / dayWidth)
×
615
          : Math.floor(changeInPixelsX / dayWidth);
×
616
    },
×
617

1✔
618
    updatePositionOnDrag() {
1✔
619
      const minutesChangedVertically = this.changeInQuartersOnDrag * 15;
4✔
620
      const minutesChangedHorizontally = this.changeInDaysOnDrag * 1440;
4✔
621

4✔
622
      this.event.time.start = this.time.addMinutesToDateTimeString(
4✔
623
        minutesChangedVertically + minutesChangedHorizontally,
4✔
624
        this.timeStartDragStart
4✔
625
      );
4✔
626

4✔
627
      this.event.time.end = this.time.addMinutesToDateTimeString(
4✔
628
        minutesChangedVertically + minutesChangedHorizontally,
4✔
629
        this.timeEndDragStart
4✔
630
      );
4✔
631
    },
4✔
632
  },
1✔
633
});
1✔
634
</script>
1✔
635

1✔
636
<style scoped lang="scss">
1✔
637
.calendar-week__event {
1✔
638
  position: absolute;
1✔
639
  width: 100%;
1✔
640
  border-radius: 4px;
1✔
641
  cursor: pointer;
1✔
642
  box-sizing: content-box;
1✔
643
  user-select: none;
1✔
644
  overflow: hidden;
1✔
645

1✔
646
  &.is-editable {
1✔
647
    cursor: grab;
1✔
648
  }
1✔
649

1✔
650
  &.has-disabled-dnd {
1✔
651
    cursor: initial;
1✔
652
  }
1✔
653

1✔
654
  .calendar-week__event-row {
1✔
655
    display: flex;
1✔
656
    align-items: flex-start;
1✔
657
    margin-bottom: 0.25em;
1✔
658

1✔
659
    p {
1✔
660
      margin: 0;
1✔
661
      padding: 0;
1✔
662
    }
1✔
663
  }
1✔
664

1✔
665
  .calendar-week__event-info-wrapper {
1✔
666
    position: relative;
1✔
667
    padding: var(--qalendar-spacing-half);
1✔
668
    font-size: var(--qalendar-font-xs);
1✔
669
    height: 100%;
1✔
670
    box-sizing: border-box;
1✔
671
    user-select: none;
1✔
672
  }
1✔
673

1✔
674
  .calendar-week__event-blend-out {
1✔
675
    position: absolute;
1✔
676
    bottom: 0;
1✔
677
    height: 20px;
1✔
678
    width: 100%;
1✔
679
    transform: translateX(calc(var(--qalendar-spacing-half) * -1));
1✔
680
  }
1✔
681

1✔
682
  .calendar-week__event-icon {
1✔
683
    margin: 2px 4px 0 0;
1✔
684
    font-size: var(--qalendar-font-xs);
1✔
685
  }
1✔
686

1✔
687
  .calendar-week__event-resize {
1✔
688
    position: absolute;
1✔
689
    width: 100%;
1✔
690
    cursor: ns-resize;
1✔
691
    height: 5px;
1✔
692
  }
1✔
693

1✔
694
  .calendar-week__event-resize-up {
1✔
695
    top: 0;
1✔
696
  }
1✔
697

1✔
698
  .calendar-week__event-resize-down {
1✔
699
    bottom: 0;
1✔
700
  }
1✔
701
}
1✔
702
</style>
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc