// Styles
import "../../../../src/components/VCalendar/mixins/calendar-with-events.sass"; // Directives
import ripple from '../../../directives/ripple'; // Mixins
import CalendarBase from './calendar-base'; // Helpers
import { escapeHTML } from '../../../util/helpers'; // Util
import props from '../util/props';
import { CalendarEventOverlapModes } from '../modes';
import { getDayIdentifier, diffMinutes } from '../util/timestamp';
import { parseEvent, isEventStart, isEventOn, isEventOverlapping } from '../util/events';
const WIDTH_FULL = 100;
const WIDTH_START = 95;
const MINUTES_IN_DAY = 1440;
/* @vue/component */
export default CalendarBase.extend({
name: 'calendar-with-events',
directives: {
ripple
},
props: props.events,
computed: {
noEvents() {
return this.events.length === 0;
},
parsedEvents() {
return this.events.map(this.parseEvent);
},
parsedEventOverlapThreshold() {
return parseInt(this.eventOverlapThreshold);
},
eventColorFunction() {
return typeof this.eventColor === 'function' ? this.eventColor : () => this.eventColor;
},
eventTimedFunction() {
return typeof this.eventTimed === 'function' ? this.eventTimed : event => !!event[this.eventTimed];
},
eventCategoryFunction() {
return typeof this.eventCategory === 'function' ? this.eventCategory : event => event[this.eventCategory];
},
eventTextColorFunction() {
return typeof this.eventTextColor === 'function' ? this.eventTextColor : () => this.eventTextColor;
},
eventNameFunction() {
return typeof this.eventName === 'function' ? this.eventName : (event, timedEvent) => escapeHTML(event.input[this.eventName]);
},
eventModeFunction() {
return typeof this.eventOverlapMode === 'function' ? this.eventOverlapMode : CalendarEventOverlapModes[this.eventOverlapMode];
},
eventWeekdays() {
return this.parsedWeekdays;
},
categoryMode() {
return false;
}
},
methods: {
parseEvent(input, index = 0) {
return parseEvent(input, index, this.eventStart, this.eventEnd, this.eventTimedFunction(input), this.categoryMode ? this.eventCategoryFunction(input) : false);
},
formatTime(withTime, ampm) {
const formatter = this.getFormatter({
timeZone: 'UTC',
hour: 'numeric',
minute: withTime.minute > 0 ? 'numeric' : undefined
});
return formatter(withTime, true);
},
updateEventVisibility() {
if (this.noEvents || !this.eventMore) {
return;
}
const eventHeight = this.eventHeight;
const eventsMap = this.getEventsMap();
for (const date in eventsMap) {
const {
parent,
events,
more
} = eventsMap[date];
if (!more) {
break;
}
const parentBounds = parent.getBoundingClientRect();
const last = events.length - 1;
let hide = false;
let hidden = 0;
for (let i = 0; i <= last; i++) {
if (!hide) {
const eventBounds = events[i].getBoundingClientRect();
hide = i === last ? eventBounds.bottom > parentBounds.bottom : eventBounds.bottom + eventHeight > parentBounds.bottom;
}
if (hide) {
events[i].style.display = 'none';
hidden++;
}
}
if (hide) {
more.style.display = '';
more.innerHTML = this.$vuetify.lang.t(this.eventMoreText, hidden);
} else {
more.style.display = 'none';
}
}
},
getEventsMap() {
const eventsMap = {};
const elements = this.$refs.events;
if (!elements || !elements.forEach) {
return eventsMap;
}
elements.forEach(el => {
const date = el.getAttribute('data-date');
if (el.parentElement && date) {
if (!(date in eventsMap)) {
eventsMap[date] = {
parent: el.parentElement,
more: null,
events: []
};
}
if (el.getAttribute('data-more')) {
eventsMap[date].more = el;
} else {
eventsMap[date].events.push(el);
el.style.display = '';
}
}
});
return eventsMap;
},
genDayEvent({
event
}, day) {
const eventHeight = this.eventHeight;
const eventMarginBottom = this.eventMarginBottom;
const dayIdentifier = getDayIdentifier(day);
const week = day.week;
const start = dayIdentifier === event.startIdentifier;
let end = dayIdentifier === event.endIdentifier;
let width = WIDTH_START;
if (!this.categoryMode) {
for (let i = day.index + 1; i < week.length; i++) {
const weekdayIdentifier = getDayIdentifier(week[i]);
if (event.endIdentifier >= weekdayIdentifier) {
width += WIDTH_FULL;
end = end || weekdayIdentifier === event.endIdentifier;
} else {
end = true;
break;
}
}
}
const scope = {
eventParsed: event,
day,
start,
end,
timed: false
};
return this.genEvent(event, scope, false, {
staticClass: 'v-event',
class: {
'v-event-start': start,
'v-event-end': end
},
style: {
height: `${eventHeight}px`,
width: `${width}%`,
'margin-bottom': `${eventMarginBottom}px`
},
attrs: {
'data-date': day.date
},
key: event.index,
ref: 'events',
refInFor: true
});
},
genTimedEvent({
event,
left,
width
}, day) {
if (day.timeDelta(event.end) <= 0 || day.timeDelta(event.start) >= 1) {
return false;
}
const dayIdentifier = getDayIdentifier(day);
const start = event.startIdentifier >= dayIdentifier;
const end = event.endIdentifier > dayIdentifier;
const top = start ? day.timeToY(event.start) : 0;
const bottom = end ? day.timeToY(MINUTES_IN_DAY) : day.timeToY(event.end);
const height = Math.max(this.eventHeight, bottom - top);
const scope = {
eventParsed: event,
day,
start,
end,
timed: true
};
return this.genEvent(event, scope, true, {
staticClass: 'v-event-timed',
style: {
top: `${top}px`,
height: `${height}px`,
left: `${left}%`,
width: `${width}%`
}
});
},
genEvent(event, scopeInput, timedEvent, data) {
const slot = this.$scopedSlots.event;
const text = this.eventTextColorFunction(event.input);
const background = this.eventColorFunction(event.input);
const overlapsNoon = event.start.hour < 12 && event.end.hour >= 12;
const singline = diffMinutes(event.start, event.end) <= this.parsedEventOverlapThreshold;
const formatTime = this.formatTime;
const timeSummary = () => formatTime(event.start, overlapsNoon) + ' - ' + formatTime(event.end, true);
const eventSummary = () => {
const name = this.eventNameFunction(event, timedEvent);
if (event.start.hasTime) {
if (timedEvent) {
const time = timeSummary();
const delimiter = singline ? ', ' : '
';
return `${name}${delimiter}${time}`;
} else {
const time = formatTime(event.start, true);
return `${time} ${name}`;
}
}
return name;
};
const scope = { ...scopeInput,
event: event.input,
outside: scopeInput.day.outside,
singline,
overlapsNoon,
formatTime,
timeSummary,
eventSummary
};
return this.$createElement('div', this.setTextColor(text, this.setBackgroundColor(background, {
on: this.getDefaultMouseEventHandlers(':event', nativeEvent => ({ ...scope,
nativeEvent
})),
directives: [{
name: 'ripple',
value: this.eventRipple != null ? this.eventRipple : true
}],
...data
})), slot ? slot(scope) : [this.genName(eventSummary)]);
},
genName(eventSummary) {
return this.$createElement('div', {
staticClass: 'pl-1',
domProps: {
innerHTML: eventSummary()
}
});
},
genPlaceholder(day) {
const height = this.eventHeight + this.eventMarginBottom;
return this.$createElement('div', {
style: {
height: `${height}px`
},
attrs: {
'data-date': day.date
},
ref: 'events',
refInFor: true
});
},
genMore(day) {
const eventHeight = this.eventHeight;
const eventMarginBottom = this.eventMarginBottom;
return this.$createElement('div', {
staticClass: 'v-event-more pl-1',
class: {
'v-outside': day.outside
},
attrs: {
'data-date': day.date,
'data-more': 1
},
directives: [{
name: 'ripple',
value: this.eventRipple != null ? this.eventRipple : true
}],
on: {
click: () => this.$emit('click:more', day)
},
style: {
display: 'none',
height: `${eventHeight}px`,
'margin-bottom': `${eventMarginBottom}px`
},
ref: 'events',
refInFor: true
});
},
getVisibleEvents() {
const start = getDayIdentifier(this.days[0]);
const end = getDayIdentifier(this.days[this.days.length - 1]);
return this.parsedEvents.filter(event => isEventOverlapping(event, start, end));
},
isEventForCategory(event, category) {
return !this.categoryMode || category === event.category || typeof event.category !== 'string' && category === null;
},
getEventsForDay(day) {
const identifier = getDayIdentifier(day);
const firstWeekday = this.eventWeekdays[0];
return this.parsedEvents.filter(event => isEventStart(event, day, identifier, firstWeekday));
},
getEventsForDayAll(day) {
const identifier = getDayIdentifier(day);
const firstWeekday = this.eventWeekdays[0];
return this.parsedEvents.filter(event => event.allDay && (this.categoryMode ? isEventOn(event, identifier) : isEventStart(event, day, identifier, firstWeekday)) && this.isEventForCategory(event, day.category));
},
getEventsForDayTimed(day) {
const identifier = getDayIdentifier(day);
return this.parsedEvents.filter(event => !event.allDay && isEventOn(event, identifier) && this.isEventForCategory(event, day.category));
},
getScopedSlots() {
if (this.noEvents) {
return { ...this.$scopedSlots
};
}
const mode = this.eventModeFunction(this.parsedEvents, this.eventWeekdays[0], this.parsedEventOverlapThreshold);
const isNode = input => !!input;
const getSlotChildren = (day, getter, mapper, timed) => {
const events = getter(day);
const visuals = mode(day, events, timed, this.categoryMode);
if (timed) {
return visuals.map(visual => mapper(visual, day)).filter(isNode);
}
const children = [];
visuals.forEach((visual, index) => {
while (children.length < visual.column) {
children.push(this.genPlaceholder(day));
}
const mapped = mapper(visual, day);
if (mapped) {
children.push(mapped);
}
});
return children;
};
const slots = this.$scopedSlots;
const slotDay = slots.day;
const slotDayHeader = slots['day-header'];
const slotDayBody = slots['day-body'];
return { ...slots,
day: day => {
let children = getSlotChildren(day, this.getEventsForDay, this.genDayEvent, false);
if (children && children.length > 0 && this.eventMore) {
children.push(this.genMore(day));
}
if (slotDay) {
const slot = slotDay(day);
if (slot) {
children = children ? children.concat(slot) : slot;
}
}
return children;
},
'day-header': day => {
let children = getSlotChildren(day, this.getEventsForDayAll, this.genDayEvent, false);
if (slotDayHeader) {
const slot = slotDayHeader(day);
if (slot) {
children = children ? children.concat(slot) : slot;
}
}
return children;
},
'day-body': day => {
const events = getSlotChildren(day, this.getEventsForDayTimed, this.genTimedEvent, true);
let children = [this.$createElement('div', {
staticClass: 'v-event-timed-container'
}, events)];
if (slotDayBody) {
const slot = slotDayBody(day);
if (slot) {
children = children.concat(slot);
}
}
return children;
}
};
}
}
});
//# sourceMappingURL=calendar-with-events.js.map