mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
fix(calendar): removed condition to show "create task modal" only when clicking to working hours
This commit is contained in:
1 parent
1e97ec472c
commit
128d53788e
1 file changed
+317
-212
@@ -62,10 +62,10 @@ transition-group.vuecal__cell(
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Event from './event.vue'
|
import Event from './event.vue';
|
||||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
import tmUtils from '@/plugins/tm@_utils/tm.utils';
|
||||||
import moment from 'moment'
|
import moment from 'moment';
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['vuecal', 'utils', 'modules', 'view', 'domEvents'],
|
inject: ['vuecal', 'utils', 'modules', 'view', 'domEvents'],
|
||||||
@@ -79,77 +79,85 @@ export default {
|
|||||||
minTimestamp: { type: [Number, null], default: null },
|
minTimestamp: { type: [Number, null], default: null },
|
||||||
maxTimestamp: { type: [Number, null], default: null },
|
maxTimestamp: { type: [Number, null], default: null },
|
||||||
cellWidth: { type: [Number, Boolean], default: false },
|
cellWidth: { type: [Number, Boolean], default: false },
|
||||||
allDay: { type: Boolean, default: false }
|
allDay: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
tmUtils,
|
tmUtils,
|
||||||
cellOverlaps: {},
|
cellOverlaps: {},
|
||||||
cellOverlapsStreak: 1, // Largest amount of simultaneous events in cell.
|
cellOverlapsStreak: 1, // Largest amount of simultaneous events in cell.
|
||||||
// On mouse down, save the time at cursor so it can be reused on cell focus event
|
// On mouse down, save the time at cursor so it can be reused on cell focus event
|
||||||
// where there is no cursor coords.
|
// where there is no cursor coords.
|
||||||
timeAtCursor: null,
|
timeAtCursor: null,
|
||||||
highlighted: false, // On event drag over.
|
highlighted: false, // On event drag over.
|
||||||
highlightedSplit: null
|
highlightedSplit: null,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
getSplitAtCursor ({ target }) {
|
getSplitAtCursor({ target }) {
|
||||||
const targetIsSplit = target.classList.contains('vuecal__cell-split')
|
const targetIsSplit = target.classList.contains('vuecal__cell-split');
|
||||||
let split = targetIsSplit ? target : this.vuecal.findAncestor(target, 'vuecal__cell-split')
|
let split = targetIsSplit
|
||||||
|
? target
|
||||||
|
: this.vuecal.findAncestor(target, 'vuecal__cell-split');
|
||||||
|
|
||||||
if (split) {
|
if (split) {
|
||||||
split = split.attributes['data-split'].value
|
split = split.attributes['data-split'].value;
|
||||||
// Convert to a numeric value if split id is a number.
|
// Convert to a numeric value if split id is a number.
|
||||||
if (parseInt(split).toString() === split.toString()) split = parseInt(split)
|
if (parseInt(split).toString() === split.toString())
|
||||||
|
split = parseInt(split);
|
||||||
}
|
}
|
||||||
return split || null
|
return split || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
splitClasses (split) {
|
splitClasses(split) {
|
||||||
return {
|
return {
|
||||||
'vuecal__cell-split': true,
|
'vuecal__cell-split': true,
|
||||||
'vuecal__cell-split--highlighted': this.highlightedSplit === split.id,
|
'vuecal__cell-split--highlighted': this.highlightedSplit === split.id,
|
||||||
'vuecal__cell-split--weekend': split.isWeekend,
|
'vuecal__cell-split--weekend': split.isWeekend,
|
||||||
[split.class]: !!split.class
|
[split.class]: !!split.class,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
checkCellOverlappingEvents () {
|
checkCellOverlappingEvents() {
|
||||||
// If splits, checkCellOverlappingEvents() is called from within computed splits.
|
// If splits, checkCellOverlappingEvents() is called from within computed splits.
|
||||||
if (this.options.time && this.eventsCount && !this.splitsCount) {
|
if (this.options.time && this.eventsCount && !this.splitsCount) {
|
||||||
if (this.eventsCount === 1) {
|
if (this.eventsCount === 1) {
|
||||||
this.cellOverlaps = []
|
this.cellOverlaps = [];
|
||||||
this.cellOverlapsStreak = 1
|
this.cellOverlapsStreak = 1;
|
||||||
}
|
}
|
||||||
// If only 1 event remains re-init the overlaps.
|
// If only 1 event remains re-init the overlaps.
|
||||||
else [this.cellOverlaps, this.cellOverlapsStreak] = this.utils.event.checkCellOverlappingEvents(this.events, this.options)
|
else
|
||||||
|
[this.cellOverlaps, this.cellOverlapsStreak] =
|
||||||
|
this.utils.event.checkCellOverlappingEvents(
|
||||||
|
this.events,
|
||||||
|
this.options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isDOMElementAnEvent (el) {
|
isDOMElementAnEvent(el) {
|
||||||
return this.vuecal.isDOMElementAnEvent(el)
|
return this.vuecal.isDOMElementAnEvent(el);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a cell and go to narrower view on double click or single click according to vuecalProps option.
|
* Select a cell and go to narrower view on double click or single click according to vuecalProps option.
|
||||||
*/
|
*/
|
||||||
selectCell (DOMEvent, force = false) {
|
selectCell(DOMEvent, force = false) {
|
||||||
// If splitting days, also return the clicked split on cell click when emitting event.
|
// If splitting days, also return the clicked split on cell click when emitting event.
|
||||||
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null;
|
||||||
|
|
||||||
this.utils.cell.selectCell(force, this.timeAtCursor, split)
|
this.utils.cell.selectCell(force, this.timeAtCursor, split);
|
||||||
this.timeAtCursor = null
|
this.timeAtCursor = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellkeyPressEnter (DOMEvent) {
|
onCellkeyPressEnter(DOMEvent) {
|
||||||
if (!this.isSelected) this.onCellFocus(DOMEvent)
|
if (!this.isSelected) this.onCellFocus(DOMEvent);
|
||||||
|
|
||||||
// If splitting days, also return the clicked split on cell keypress when emitting event.
|
// If splitting days, also return the clicked split on cell keypress when emitting event.
|
||||||
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null;
|
||||||
|
|
||||||
this.utils.cell.keyPressEnterCell(this.timeAtCursor, split)
|
this.utils.cell.keyPressEnterCell(this.timeAtCursor, split);
|
||||||
this.timeAtCursor = null
|
this.timeAtCursor = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,288 +165,350 @@ export default {
|
|||||||
* the cell-focus event with the date of the cell start when focusing from tab or
|
* the cell-focus event with the date of the cell start when focusing from tab or
|
||||||
* the date & time at cursor if click/touch.
|
* the date & time at cursor if click/touch.
|
||||||
*/
|
*/
|
||||||
onCellFocus (DOMEvent) {
|
onCellFocus(DOMEvent) {
|
||||||
if (!this.isSelected && !this.isDisabled) {
|
if (!this.isSelected && !this.isDisabled) {
|
||||||
this.isSelected = this.data.startDate // Highlight the cell.
|
this.isSelected = this.data.startDate; // Highlight the cell.
|
||||||
|
|
||||||
// If splitting days, also return the clicked split on cell focus when emitting event.
|
// If splitting days, also return the clicked split on cell focus when emitting event.
|
||||||
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null;
|
||||||
|
|
||||||
// Cell-focus event returns the cell start date (at midnight) if triggered from tab key,
|
// Cell-focus event returns the cell start date (at midnight) if triggered from tab key,
|
||||||
// or cursor coords time if clicked.
|
// or cursor coords time if clicked.
|
||||||
const date = this.timeAtCursor || this.data.startDate
|
const date = this.timeAtCursor || this.data.startDate;
|
||||||
this.vuecal.$emit('cell-focus', split ? { date, split } : date)
|
this.vuecal.$emit('cell-focus', split ? { date, split } : date);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellMouseDown (DOMEvent, split = null, touch = false) {
|
onCellMouseDown(DOMEvent, split = null, touch = false) {
|
||||||
// Prevent a double mouse down on touch devices.
|
// Prevent a double mouse down on touch devices.
|
||||||
if ('ontouchstart' in window && !touch) return false
|
if ('ontouchstart' in window && !touch) return false;
|
||||||
|
|
||||||
if (!this.isSelected) this.onCellFocus(DOMEvent)
|
if (!this.isSelected) this.onCellFocus(DOMEvent);
|
||||||
|
|
||||||
const { clickHoldACell, focusAnEvent } = this.domEvents
|
const { clickHoldACell, focusAnEvent } = this.domEvents;
|
||||||
// Reinit the click trigger on each mousedown.
|
// Reinit the click trigger on each mousedown.
|
||||||
// In some cases we explicitly set this flag to prevent the click event to trigger,
|
// In some cases we explicitly set this flag to prevent the click event to trigger,
|
||||||
// and cancel event creation.
|
// and cancel event creation.
|
||||||
this.domEvents.cancelClickEventCreation = false
|
this.domEvents.cancelClickEventCreation = false;
|
||||||
// Also reinit this var on each mousedown.
|
// Also reinit this var on each mousedown.
|
||||||
clickHoldACell.eventCreated = false
|
clickHoldACell.eventCreated = false;
|
||||||
|
|
||||||
this.timeAtCursor = new Date(this.data.startDate)
|
this.timeAtCursor = new Date(this.data.startDate);
|
||||||
const { minutes, cursorCoords: { y } } = this.vuecal.minutesAtCursor(DOMEvent)
|
const {
|
||||||
this.timeAtCursor.setMinutes(minutes)
|
minutes,
|
||||||
|
cursorCoords: { y },
|
||||||
|
} = this.vuecal.minutesAtCursor(DOMEvent);
|
||||||
|
this.timeAtCursor.setMinutes(minutes);
|
||||||
|
|
||||||
const mouseDownOnEvent = this.isDOMElementAnEvent(DOMEvent.target)
|
const mouseDownOnEvent = this.isDOMElementAnEvent(DOMEvent.target);
|
||||||
// Unfocus an event if any is focused and clicking on cell outside of an event.
|
// Unfocus an event if any is focused and clicking on cell outside of an event.
|
||||||
if (!mouseDownOnEvent && focusAnEvent._eid) {
|
if (!mouseDownOnEvent && focusAnEvent._eid) {
|
||||||
(this.view.events.find(e => e._eid === focusAnEvent._eid) || {}).focused = false
|
(
|
||||||
|
this.view.events.find((e) => e._eid === focusAnEvent._eid) || {}
|
||||||
|
).focused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only if event creation is allowed and mousedown is on a cell (not on event).
|
// Only if event creation is allowed and mousedown is on a cell (not on event).
|
||||||
if (this.editEvents.create && !mouseDownOnEvent) this.setUpEventCreation(DOMEvent, y)
|
if (this.editEvents.create && !mouseDownOnEvent)
|
||||||
|
this.setUpEventCreation(DOMEvent, y);
|
||||||
},
|
},
|
||||||
|
|
||||||
setUpEventCreation (DOMEvent, startCursorY) {
|
setUpEventCreation(DOMEvent, startCursorY) {
|
||||||
// If dragToCreateEvent is true, start the event creation from dragging
|
// If dragToCreateEvent is true, start the event creation from dragging
|
||||||
// only on week and day views (doesn't make sense on month view).
|
// only on week and day views (doesn't make sense on month view).
|
||||||
if (this.options.dragToCreateEvent && ['week', 'day'].includes(this.view.id)) {
|
if (
|
||||||
const { dragCreateAnEvent } = this.domEvents
|
this.options.dragToCreateEvent &&
|
||||||
dragCreateAnEvent.startCursorY = startCursorY
|
['week', 'day'].includes(this.view.id)
|
||||||
|
) {
|
||||||
|
const { dragCreateAnEvent } = this.domEvents;
|
||||||
|
dragCreateAnEvent.startCursorY = startCursorY;
|
||||||
|
|
||||||
// If splitting days, store the clicked split to create an event in it from the global
|
// If splitting days, store the clicked split to create an event in it from the global
|
||||||
// mousemove handler in index.vue.
|
// mousemove handler in index.vue.
|
||||||
dragCreateAnEvent.split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
dragCreateAnEvent.split = this.splitsCount
|
||||||
|
? this.getSplitAtCursor(DOMEvent)
|
||||||
|
: null;
|
||||||
|
|
||||||
// Save the time at cursor on initial mousedown.
|
// Save the time at cursor on initial mousedown.
|
||||||
dragCreateAnEvent.start = this.timeAtCursor
|
dragCreateAnEvent.start = this.timeAtCursor;
|
||||||
|
|
||||||
// If snapToTime, set the start to the closest intervaled number.
|
// If snapToTime, set the start to the closest intervaled number.
|
||||||
if (this.options.snapToTime) {
|
if (this.options.snapToTime) {
|
||||||
let timeMinutes = this.timeAtCursor.getHours() * 60 + this.timeAtCursor.getMinutes()
|
let timeMinutes =
|
||||||
const plusHalfSnapTime = timeMinutes + this.options.snapToTime / 2
|
this.timeAtCursor.getHours() * 60 + this.timeAtCursor.getMinutes();
|
||||||
timeMinutes = plusHalfSnapTime - (plusHalfSnapTime % this.options.snapToTime)
|
const plusHalfSnapTime = timeMinutes + this.options.snapToTime / 2;
|
||||||
|
timeMinutes =
|
||||||
|
plusHalfSnapTime - (plusHalfSnapTime % this.options.snapToTime);
|
||||||
|
|
||||||
dragCreateAnEvent.start.setHours(0, timeMinutes, 0, 0)
|
dragCreateAnEvent.start.setHours(0, timeMinutes, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cellClickHold option is true and not mousedown on an event, click & hold to create an event.
|
// If the cellClickHold option is true and not mousedown on an event, click & hold to create an event.
|
||||||
else if (this.options.cellClickHold && ['month', 'week', 'day'].includes(this.view.id)) {
|
else if (
|
||||||
this.setUpCellHoldTimer(DOMEvent)
|
this.options.cellClickHold &&
|
||||||
|
['month', 'week', 'day'].includes(this.view.id)
|
||||||
|
) {
|
||||||
|
this.setUpCellHoldTimer(DOMEvent);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// When click & holding a cell, and if allowed, set a timeout to create an event (can be cancelled).
|
// When click & holding a cell, and if allowed, set a timeout to create an event (can be cancelled).
|
||||||
setUpCellHoldTimer (DOMEvent) {
|
setUpCellHoldTimer(DOMEvent) {
|
||||||
const { clickHoldACell } = this.domEvents
|
const { clickHoldACell } = this.domEvents;
|
||||||
clickHoldACell.cellId = `${this.vuecal._.uid}_${this.data.formattedDate}`
|
clickHoldACell.cellId = `${this.vuecal._.uid}_${this.data.formattedDate}`;
|
||||||
// If splitting days, store the clicked split to create an event in it from the global
|
// If splitting days, store the clicked split to create an event in it from the global
|
||||||
// mousemove handler in index.vue.
|
// mousemove handler in index.vue.
|
||||||
clickHoldACell.split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
clickHoldACell.split = this.splitsCount
|
||||||
|
? this.getSplitAtCursor(DOMEvent)
|
||||||
|
: null;
|
||||||
|
|
||||||
clickHoldACell.timeoutId = setTimeout(() => {
|
clickHoldACell.timeoutId = setTimeout(() => {
|
||||||
if (clickHoldACell.cellId && !this.domEvents.cancelClickEventCreation) {
|
if (clickHoldACell.cellId && !this.domEvents.cancelClickEventCreation) {
|
||||||
const { _eid } = this.utils.event.createAnEvent(
|
const { _eid } = this.utils.event.createAnEvent(
|
||||||
this.timeAtCursor,
|
this.timeAtCursor,
|
||||||
null,
|
null,
|
||||||
clickHoldACell.split ? { split: clickHoldACell.split } : {}
|
clickHoldACell.split ? { split: clickHoldACell.split } : {},
|
||||||
)
|
);
|
||||||
|
|
||||||
clickHoldACell.eventCreated = _eid
|
clickHoldACell.eventCreated = _eid;
|
||||||
}
|
}
|
||||||
}, clickHoldACell.timeout)
|
}, clickHoldACell.timeout);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellTouchStart (DOMEvent, split = null) {
|
onCellTouchStart(DOMEvent, split = null) {
|
||||||
// If not mousedown on an event.
|
// If not mousedown on an event.
|
||||||
this.onCellMouseDown(DOMEvent, split, true)
|
this.onCellMouseDown(DOMEvent, split, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellClick(DOMEvent, split) {
|
onCellClick(DOMEvent, split) {
|
||||||
if (DOMEvent.offsetY > 950 && DOMEvent.offsetY < 1700) {
|
const dateFrom = split.date;
|
||||||
const dateFrom = split.date
|
dateFrom.setHours(Math.floor(DOMEvent.offsetY / 100), 0, 0, 0);
|
||||||
dateFrom.setHours(Math.floor(DOMEvent.offsetY / 100), 0, 0, 0)
|
const dateTo = new Date(dateFrom);
|
||||||
const dateTo = new Date(dateFrom)
|
dateTo.setHours(dateFrom.getHours() + 1);
|
||||||
dateTo.setHours(dateFrom.getHours() + 1)
|
|
||||||
|
|
||||||
const time = moment(dateFrom).format('HH:mm')
|
const time = moment(dateFrom).format('HH:mm');
|
||||||
|
|
||||||
const isTeamCalendar = this.$route.name === 'Dashboard - Teams'
|
const isTeamCalendar = this.$route.name === 'Dashboard - Teams';
|
||||||
const employee = isTeamCalendar ? split.employee_id : Number(localStorage.getItem('selectedEmployee') || this.user.id)
|
const employee = isTeamCalendar
|
||||||
|
? split.employee_id
|
||||||
|
: Number(localStorage.getItem('selectedEmployee') || this.user.id);
|
||||||
|
|
||||||
this.$events.emit('show-menu-modal', {
|
this.$events.emit('show-menu-modal', {
|
||||||
time,
|
time,
|
||||||
date: tmUtils.formatDateTimeForBackend(dateFrom),
|
date: tmUtils.formatDateTimeForBackend(dateFrom),
|
||||||
employee
|
employee,
|
||||||
})
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$events.emit('add-timeblock', {
|
this.$events.emit('add-timeblock', {
|
||||||
id: null,
|
id: null,
|
||||||
start: tmUtils.formatDateTimeForBackend(dateFrom),
|
start: tmUtils.formatDateTimeForBackend(dateFrom),
|
||||||
end: tmUtils.formatDateTimeForBackend(dateTo)
|
end: tmUtils.formatDateTimeForBackend(dateTo),
|
||||||
})
|
});
|
||||||
}, 150)
|
}, 150);
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isDOMElementAnEvent(DOMEvent.target)) this.selectCell(DOMEvent)
|
if (!this.isDOMElementAnEvent(DOMEvent.target)) this.selectCell(DOMEvent);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellDblClick (DOMEvent) {
|
onCellDblClick(DOMEvent) {
|
||||||
const date = new Date(this.data.startDate)
|
const date = new Date(this.data.startDate);
|
||||||
date.setMinutes(this.vuecal.minutesAtCursor(DOMEvent).minutes)
|
date.setMinutes(this.vuecal.minutesAtCursor(DOMEvent).minutes);
|
||||||
|
|
||||||
// If splitting days, also return the clicked split on cell dblclick when emitting event.
|
// If splitting days, also return the clicked split on cell dblclick when emitting event.
|
||||||
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null;
|
||||||
|
|
||||||
this.vuecal.$emit('cell-dblclick', split ? { date, split } : date)
|
this.vuecal.$emit('cell-dblclick', split ? { date, split } : date);
|
||||||
|
|
||||||
if (this.options.dblclickToNavigate) this.vuecal.switchToNarrowerView()
|
if (this.options.dblclickToNavigate) this.vuecal.switchToNarrowerView();
|
||||||
},
|
},
|
||||||
|
|
||||||
onCellContextMenu (DOMEvent) {
|
onCellContextMenu(DOMEvent) {
|
||||||
DOMEvent.stopPropagation()
|
DOMEvent.stopPropagation();
|
||||||
DOMEvent.preventDefault()
|
DOMEvent.preventDefault();
|
||||||
|
|
||||||
const date = new Date(this.data.startDate)
|
const date = new Date(this.data.startDate);
|
||||||
const { cursorCoords, minutes } = this.vuecal.minutesAtCursor(DOMEvent)
|
const { cursorCoords, minutes } = this.vuecal.minutesAtCursor(DOMEvent);
|
||||||
date.setMinutes(minutes)
|
date.setMinutes(minutes);
|
||||||
|
|
||||||
// If splitting days, also return the clicked split on cell contextmenu when emitting event.
|
// If splitting days, also return the clicked split on cell contextmenu when emitting event.
|
||||||
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null
|
const split = this.splitsCount ? this.getSplitAtCursor(DOMEvent) : null;
|
||||||
|
|
||||||
this.vuecal.$emit('cell-contextmenu', { date, ...cursorCoords, ...(split || {}), e: DOMEvent })
|
this.vuecal.$emit('cell-contextmenu', {
|
||||||
}
|
date,
|
||||||
|
...cursorCoords,
|
||||||
|
...(split || {}),
|
||||||
|
e: DOMEvent,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters("auth", ["user"]),
|
...mapGetters('auth', ['user']),
|
||||||
// Drag & drop module.
|
// Drag & drop module.
|
||||||
dnd () {
|
dnd() {
|
||||||
return this.modules.dnd
|
return this.modules.dnd;
|
||||||
},
|
},
|
||||||
nowInMinutes () {
|
nowInMinutes() {
|
||||||
return this.utils.date.dateToMinutes(this.vuecal.now)
|
return this.utils.date.dateToMinutes(this.vuecal.now);
|
||||||
},
|
},
|
||||||
isBeforeMinDate () {
|
isBeforeMinDate() {
|
||||||
return this.minTimestamp !== null && this.minTimestamp > this.data.endDate.getTime()
|
return (
|
||||||
|
this.minTimestamp !== null &&
|
||||||
|
this.minTimestamp > this.data.endDate.getTime()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
isAfterMaxDate () {
|
isAfterMaxDate() {
|
||||||
return this.maxTimestamp && this.maxTimestamp < this.data.startDate.getTime()
|
return (
|
||||||
|
this.maxTimestamp && this.maxTimestamp < this.data.startDate.getTime()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
// Is the current cell disabled or not.
|
// Is the current cell disabled or not.
|
||||||
isDisabled () {
|
isDisabled() {
|
||||||
const { disableDays } = this.options
|
const { disableDays } = this.options;
|
||||||
const { isYearsOrYearView } = this.vuecal
|
const { isYearsOrYearView } = this.vuecal;
|
||||||
if (disableDays.length && disableDays.includes(this.data.formattedDate) && !isYearsOrYearView) return true
|
if (
|
||||||
return this.isBeforeMinDate || this.isAfterMaxDate
|
disableDays.length &&
|
||||||
|
disableDays.includes(this.data.formattedDate) &&
|
||||||
|
!isYearsOrYearView
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
return this.isBeforeMinDate || this.isAfterMaxDate;
|
||||||
},
|
},
|
||||||
// Is the current cell selected or not.
|
// Is the current cell selected or not.
|
||||||
isSelected: {
|
isSelected: {
|
||||||
get () {
|
get() {
|
||||||
let selected = false
|
let selected = false;
|
||||||
const { selectedDate } = this.view
|
const { selectedDate } = this.view;
|
||||||
|
|
||||||
if (this.view.id === 'years') {
|
if (this.view.id === 'years') {
|
||||||
selected = selectedDate.getFullYear() === this.data.startDate.getFullYear()
|
selected =
|
||||||
}
|
selectedDate.getFullYear() === this.data.startDate.getFullYear();
|
||||||
else if (this.view.id === 'year') {
|
} else if (this.view.id === 'year') {
|
||||||
selected = (selectedDate.getFullYear() === this.data.startDate.getFullYear() &&
|
selected =
|
||||||
selectedDate.getMonth() === this.data.startDate.getMonth())
|
selectedDate.getFullYear() === this.data.startDate.getFullYear() &&
|
||||||
}
|
selectedDate.getMonth() === this.data.startDate.getMonth();
|
||||||
else selected = selectedDate.getTime() === this.data.startDate.getTime()
|
} else
|
||||||
|
selected = selectedDate.getTime() === this.data.startDate.getTime();
|
||||||
|
|
||||||
return selected
|
return selected;
|
||||||
|
},
|
||||||
|
set(date) {
|
||||||
|
this.view.selectedDate = date;
|
||||||
|
this.vuecal.$emit('update:selected-date', this.view.selectedDate);
|
||||||
},
|
},
|
||||||
set (date) {
|
|
||||||
this.view.selectedDate = date
|
|
||||||
this.vuecal.$emit('update:selected-date', this.view.selectedDate)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Cache result for performance.
|
// Cache result for performance.
|
||||||
isWeekOrDayView () {
|
isWeekOrDayView() {
|
||||||
return ['week', 'day'].includes(this.view.id)
|
return ['week', 'day'].includes(this.view.id);
|
||||||
},
|
},
|
||||||
transitionDirection () {
|
transitionDirection() {
|
||||||
return this.vuecal.transitionDirection
|
return this.vuecal.transitionDirection;
|
||||||
},
|
},
|
||||||
specialHours () {
|
specialHours() {
|
||||||
// this.data.specialHours is always an array, but may be empty.
|
// this.data.specialHours is always an array, but may be empty.
|
||||||
return this.data.specialHours.map(block => {
|
return this.data.specialHours.map((block) => {
|
||||||
let { from, to } = block
|
let { from, to } = block;
|
||||||
from = Math.max(from, this.options.timeFrom)
|
from = Math.max(from, this.options.timeFrom);
|
||||||
to = Math.min(to, this.options.timeTo)
|
to = Math.min(to, this.options.timeTo);
|
||||||
return {
|
return {
|
||||||
...block,
|
...block,
|
||||||
height: (to - from) * this.timeScale,
|
height: (to - from) * this.timeScale,
|
||||||
top: (from - this.options.timeFrom) * this.timeScale
|
top: (from - this.options.timeFrom) * this.timeScale,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
events () {
|
events() {
|
||||||
const { startDate: cellStart, endDate: cellEnd } = this.data
|
const { startDate: cellStart, endDate: cellEnd } = this.data;
|
||||||
let events = []
|
let events = [];
|
||||||
|
|
||||||
// Calculate events on month/week/day views or years/year if eventsCountOnYearView.
|
// Calculate events on month/week/day views or years/year if eventsCountOnYearView.
|
||||||
if (!(['years', 'year'].includes(this.view.id) && !this.options.eventsCountOnYearView)) {
|
if (
|
||||||
|
!(
|
||||||
|
['years', 'year'].includes(this.view.id) &&
|
||||||
|
!this.options.eventsCountOnYearView
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Means that when vuecal.view.events changes all the cells will be looking up new value. :/
|
// Means that when vuecal.view.events changes all the cells will be looking up new value. :/
|
||||||
// Also clone array to prevent modifying original.
|
// Also clone array to prevent modifying original.
|
||||||
events = this.view.events.slice(0)
|
events = this.view.events.slice(0);
|
||||||
|
|
||||||
if (this.view.id === 'month') {
|
if (this.view.id === 'month') {
|
||||||
events.push(...this.view.outOfScopeEvents)
|
events.push(...this.view.outOfScopeEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only keep events in cell time range.
|
// Only keep events in cell time range.
|
||||||
events = events.filter(e => this.utils.event.eventInRange(e, cellStart, cellEnd))
|
events = events.filter((e) =>
|
||||||
|
this.utils.event.eventInRange(e, cellStart, cellEnd),
|
||||||
|
);
|
||||||
|
|
||||||
if (this.options.showAllDayEvents && this.view.id !== 'month') events = events.filter(e => !!e.allDay === this.allDay)
|
if (this.options.showAllDayEvents && this.view.id !== 'month')
|
||||||
|
events = events.filter((e) => !!e.allDay === this.allDay);
|
||||||
|
|
||||||
// From events in view, filter the ones that are out of `time-from`-`time-to` range in this cell.
|
// From events in view, filter the ones that are out of `time-from`-`time-to` range in this cell.
|
||||||
if (this.options.time && this.isWeekOrDayView && !this.allDay) {
|
if (this.options.time && this.isWeekOrDayView && !this.allDay) {
|
||||||
const { timeFrom, timeTo } = this.options
|
const { timeFrom, timeTo } = this.options;
|
||||||
|
|
||||||
events = events.filter(e => {
|
events = events.filter((e) => {
|
||||||
const segment = (e.daysCount > 1 && e.segments[this.data.formattedDate]) || {}
|
const segment =
|
||||||
const singleDayInRange = e.daysCount === 1 && e.startTimeMinutes < timeTo && e.endTimeMinutes > timeFrom
|
(e.daysCount > 1 && e.segments[this.data.formattedDate]) || {};
|
||||||
const multipleDayInRange = e.daysCount > 1 && (segment.startTimeMinutes < timeTo && segment.endTimeMinutes > timeFrom)
|
const singleDayInRange =
|
||||||
const recurrMultDayInRange = false // e.daysCount > 1 && e.repeat && recurringEventInRange(e, cellStart, cellEnd)
|
e.daysCount === 1 &&
|
||||||
return (e.allDay || singleDayInRange || multipleDayInRange || recurrMultDayInRange)
|
e.startTimeMinutes < timeTo &&
|
||||||
})
|
e.endTimeMinutes > timeFrom;
|
||||||
|
const multipleDayInRange =
|
||||||
|
e.daysCount > 1 &&
|
||||||
|
segment.startTimeMinutes < timeTo &&
|
||||||
|
segment.endTimeMinutes > timeFrom;
|
||||||
|
const recurrMultDayInRange = false; // e.daysCount > 1 && e.repeat && recurringEventInRange(e, cellStart, cellEnd)
|
||||||
|
return (
|
||||||
|
e.allDay ||
|
||||||
|
singleDayInRange ||
|
||||||
|
multipleDayInRange ||
|
||||||
|
recurrMultDayInRange
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position events with time in the timeline when there is a timeline and not in allDay slot.
|
// Position events with time in the timeline when there is a timeline and not in allDay slot.
|
||||||
if (this.options.time && this.isWeekOrDayView && !(this.options.showAllDayEvents && this.allDay)) {
|
if (
|
||||||
|
this.options.time &&
|
||||||
|
this.isWeekOrDayView &&
|
||||||
|
!(this.options.showAllDayEvents && this.allDay)
|
||||||
|
) {
|
||||||
// Sort events in chronological order.
|
// Sort events in chronological order.
|
||||||
events.sort((a, b) => a.start < b.start ? -1 : 1)
|
events.sort((a, b) => (a.start < b.start ? -1 : 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If splits, checkCellOverlappingEvents() is called from within computed splits.
|
// If splits, checkCellOverlappingEvents() is called from within computed splits.
|
||||||
if (!this.cellSplits.length) this.$nextTick(this.checkCellOverlappingEvents)
|
if (!this.cellSplits.length)
|
||||||
|
this.$nextTick(this.checkCellOverlappingEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
return events
|
return events;
|
||||||
},
|
},
|
||||||
eventsCount () {
|
eventsCount() {
|
||||||
return this.events.length
|
return this.events.length;
|
||||||
},
|
},
|
||||||
splits () {
|
splits() {
|
||||||
return this.cellSplits.map((item, i) => {
|
return this.cellSplits.map((item, i) => {
|
||||||
const events = this.events.filter(e => e.split === item.id)
|
const events = this.events.filter((e) => e.split === item.id);
|
||||||
const [overlaps, streak] = this.utils.event.checkCellOverlappingEvents(events.filter(e => !e.background && !e.allDay), this.options)
|
const [overlaps, streak] = this.utils.event.checkCellOverlappingEvents(
|
||||||
|
events.filter((e) => !e.background && !e.allDay),
|
||||||
|
this.options,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
overlaps,
|
overlaps,
|
||||||
overlapsStreak: streak,
|
overlapsStreak: streak,
|
||||||
events
|
events,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
splitsCount () {
|
splitsCount() {
|
||||||
return this.splits.length
|
return this.splits.length;
|
||||||
},
|
},
|
||||||
cellClasses () {
|
cellClasses() {
|
||||||
return {
|
return {
|
||||||
[this.data.class]: !!this.data.class,
|
[this.data.class]: !!this.data.class,
|
||||||
'vuecal__cell--current': this.data.current, // E.g. Current year in years view.
|
'vuecal__cell--current': this.data.current, // E.g. Current year in years view.
|
||||||
@@ -450,31 +520,37 @@ export default {
|
|||||||
'vuecal__cell--selected': this.isSelected,
|
'vuecal__cell--selected': this.isSelected,
|
||||||
'vuecal__cell--highlighted': this.highlighted,
|
'vuecal__cell--highlighted': this.highlighted,
|
||||||
'vuecal__cell--has-splits': this.splitsCount,
|
'vuecal__cell--has-splits': this.splitsCount,
|
||||||
'vuecal__cell--has-events': this.eventsCount
|
'vuecal__cell--has-events': this.eventsCount,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
cellStyles () {
|
cellStyles() {
|
||||||
return {
|
return {
|
||||||
// cellWidth is only applied when hiding weekdays on month and week views.
|
// cellWidth is only applied when hiding weekdays on month and week views.
|
||||||
...(this.cellWidth ? { width: `${this.cellWidth}%` } : {})
|
...(this.cellWidth ? { width: `${this.cellWidth}%` } : {}),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
timelineVisible () {
|
timelineVisible() {
|
||||||
const { time, timeTo } = this.options
|
const { time, timeTo } = this.options;
|
||||||
return this.data.today && this.isWeekOrDayView && time && !this.allDay && this.nowInMinutes <= timeTo
|
return (
|
||||||
|
this.data.today &&
|
||||||
|
this.isWeekOrDayView &&
|
||||||
|
time &&
|
||||||
|
!this.allDay &&
|
||||||
|
this.nowInMinutes <= timeTo
|
||||||
|
);
|
||||||
},
|
},
|
||||||
todaysTimePosition () {
|
todaysTimePosition() {
|
||||||
// Skip the Maths if not relevant.
|
// Skip the Maths if not relevant.
|
||||||
if (!this.data.today || !this.options.time) return
|
if (!this.data.today || !this.options.time) return;
|
||||||
|
|
||||||
const minutesFromTop = this.nowInMinutes - this.options.timeFrom
|
const minutesFromTop = this.nowInMinutes - this.options.timeFrom;
|
||||||
return Math.round(minutesFromTop * this.timeScale)
|
return Math.round(minutesFromTop * this.timeScale);
|
||||||
},
|
},
|
||||||
timeScale () {
|
timeScale() {
|
||||||
return this.options.timeCellHeight / this.options.timeStep
|
return this.options.timeCellHeight / this.options.timeStep;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -499,15 +575,27 @@ export default {
|
|||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vuecal__cells.years-view & {width: 20%;}
|
.vuecal__cells.years-view & {
|
||||||
.vuecal__cells.year-view & {width: 33.33%;}
|
width: 20%;
|
||||||
.vuecal__cells.day-view & {flex: 1;}
|
}
|
||||||
.vuecal--overflow-x.vuecal--day-view & {width: auto;}
|
.vuecal__cells.year-view & {
|
||||||
|
width: 33.33%;
|
||||||
|
}
|
||||||
|
.vuecal__cells.day-view & {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.vuecal--overflow-x.vuecal--day-view & {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.vuecal--click-to-navigate &:not(&--disabled) {cursor: pointer;}
|
.vuecal--click-to-navigate &:not(&--disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.vuecal--view-with-time &,
|
.vuecal--view-with-time &,
|
||||||
.vuecal--week-view.vuecal--no-time &:not(.vuecal__cell--has-splits),
|
.vuecal--week-view.vuecal--no-time &:not(.vuecal__cell--has-splits),
|
||||||
.vuecal--day-view.vuecal--no-time &:not(.vuecal__cell--has-splits) {display: block;}
|
.vuecal--day-view.vuecal--no-time &:not(.vuecal__cell--has-splits) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
&.vuecal__cell--has-splits {
|
&.vuecal__cell--has-splits {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -524,7 +612,9 @@ export default {
|
|||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
border: 1px solid rgba(196, 196, 196, 0.25);
|
border: 1px solid rgba(196, 196, 196, 0.25);
|
||||||
}
|
}
|
||||||
.vuecal--overflow-x.vuecal--day-view &:before {bottom: 0;}
|
.vuecal--overflow-x.vuecal--day-view &:before {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&--today,
|
&--today,
|
||||||
&--current {
|
&--current {
|
||||||
@@ -536,14 +626,22 @@ export default {
|
|||||||
background-color: rgba(235, 255, 245, 0.4);
|
background-color: rgba(235, 255, 245, 0.4);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
.vuecal--day-view & {background: none;}
|
.vuecal--day-view & {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--out-of-scope {color: rgba(0, 0, 0, 0.25);}
|
&--out-of-scope {
|
||||||
&--disabled {color: rgba(0, 0, 0, 0.25);cursor: not-allowed;}
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
&--disabled {
|
||||||
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
// Cells/splits get highlighted when dragging an event over it.
|
// Cells/splits get highlighted when dragging an event over it.
|
||||||
&--highlighted:not(.vuecal__cell--has-splits), &-split.vuecal__cell-split--highlighted {
|
&--highlighted:not(.vuecal__cell--has-splits),
|
||||||
|
&-split.vuecal__cell-split--highlighted {
|
||||||
background-color: rgba(0, 0, 0, 0.04);
|
background-color: rgba(0, 0, 0, 0.04);
|
||||||
// Drag over feedback must be fast. Then it can fade away with longer duration.
|
// Drag over feedback must be fast. Then it can fade away with longer duration.
|
||||||
transition-duration: 5ms;
|
transition-duration: 5ms;
|
||||||
@@ -558,7 +656,9 @@ export default {
|
|||||||
|
|
||||||
.vuecal--years-view &,
|
.vuecal--years-view &,
|
||||||
.vuecal--year-view &,
|
.vuecal--year-view &,
|
||||||
.vuecal--month-view & {justify-content: center;}
|
.vuecal--month-view & {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell-time-labels {
|
.cell-time-labels {
|
||||||
@@ -587,7 +687,9 @@ export default {
|
|||||||
transition: 0.15s ease-in-out background-color;
|
transition: 0.15s ease-in-out background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-events {width: 100%;}
|
&-events {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&-events-count {
|
&-events-count {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -613,7 +715,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vuecal--overflow-x.vuecal--week-view .vuecal__cell, .vuecal__cell-split {
|
.vuecal--overflow-x.vuecal--week-view .vuecal__cell,
|
||||||
|
.vuecal__cell-split {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +727,9 @@ export default {
|
|||||||
margin-bottom: auto; // Vertical align top within flex column and align center.
|
margin-bottom: auto; // Vertical align top within flex column and align center.
|
||||||
}
|
}
|
||||||
|
|
||||||
.vuecal__all-day .vuecal__no-event {display: none;}
|
.vuecal__all-day .vuecal__no-event {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.vuecal__now-line {
|
.vuecal__now-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -637,7 +742,7 @@ export default {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
Reference in new issue
Block a user