208 lines
6.3 KiB
JavaScript
208 lines
6.3 KiB
JavaScript
// Composables
|
|
import { useCalendarBase } from "./calendarBase.js"; // Utilities
|
|
import { computed, shallowRef } from 'vue';
|
|
import { copyTimestamp, createDayList, createIntervalList, createNativeLocaleFormatter, getDayIdentifier, MINUTES_IN_DAY, parseTime, updateMinutes, validateNumber, validateTime } from "../util/timestamp.js";
|
|
import { propsFactory } from "../../../util/index.js";
|
|
import { Box, getTargetBox } from "../../../util/box.js"; // Types
|
|
export const makeCalendarWithIntervalsProps = propsFactory({
|
|
maxDays: {
|
|
type: Number,
|
|
default: 7
|
|
},
|
|
intervalHeight: {
|
|
type: [Number, String],
|
|
default: 48,
|
|
validate: validateNumber
|
|
},
|
|
intervalWidth: {
|
|
type: [Number, String],
|
|
default: 60,
|
|
validate: validateNumber
|
|
},
|
|
intervalMinutes: {
|
|
type: [Number, String],
|
|
default: 60,
|
|
validate: validateNumber
|
|
},
|
|
firstInterval: {
|
|
type: [Number, String],
|
|
default: 0,
|
|
validate: validateNumber
|
|
},
|
|
firstTime: {
|
|
type: [Number, String, Object],
|
|
validate: validateTime
|
|
},
|
|
intervalCount: {
|
|
type: [Number, String],
|
|
default: 24,
|
|
validate: validateNumber
|
|
},
|
|
intervalFormat: {
|
|
type: Function,
|
|
default: null
|
|
},
|
|
intervalStyle: {
|
|
type: Function,
|
|
default: null
|
|
},
|
|
showIntervalLabel: {
|
|
type: Function,
|
|
default: null
|
|
}
|
|
}, 'VCalendar-intervals');
|
|
export function useCalendarWithIntervals(props) {
|
|
const base = useCalendarBase(props);
|
|
const scrollAreaRef = shallowRef();
|
|
const parsedFirstInterval = computed(() => {
|
|
return parseInt(String(props.firstInterval || 0));
|
|
});
|
|
const parsedIntervalMinutes = computed(() => {
|
|
return parseInt(String(props.intervalMinutes || 60));
|
|
});
|
|
const parsedIntervalCount = computed(() => {
|
|
return parseInt(String(props.intervalCount || 24));
|
|
});
|
|
const parsedIntervalHeight = computed(() => {
|
|
return parseFloat(String(props.intervalHeight || 48));
|
|
});
|
|
const parsedFirstTime = computed(() => {
|
|
return parseTime(props.firstTime);
|
|
});
|
|
const firstMinute = computed(() => {
|
|
const time = parsedFirstTime.value;
|
|
return time !== false && time >= 0 && time <= MINUTES_IN_DAY ? time : parsedFirstInterval.value * parsedIntervalMinutes.value;
|
|
});
|
|
const bodyHeight = computed(() => {
|
|
return parsedIntervalCount.value * parsedIntervalHeight.value;
|
|
});
|
|
const days = computed(() => {
|
|
return createDayList(base.parsedStart.value, base.parsedEnd.value, base.times.today, base.weekdaySkips.value, props.maxDays);
|
|
});
|
|
const intervals = computed(() => {
|
|
const daysValue = days.value;
|
|
const first = firstMinute.value;
|
|
const minutes = parsedIntervalMinutes.value;
|
|
const count = parsedIntervalCount.value;
|
|
const now = base.times.now;
|
|
return daysValue.map(d => createIntervalList(d, first, minutes, count, now));
|
|
});
|
|
const intervalFormatter = computed(() => {
|
|
if (props.intervalFormat) {
|
|
return props.intervalFormat;
|
|
}
|
|
return createNativeLocaleFormatter(base.locale.current.value, (tms, short) => !short ? {
|
|
timeZone: 'UTC',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
} : tms.minute === 0 ? {
|
|
timeZone: 'UTC',
|
|
hour: 'numeric'
|
|
} : {
|
|
timeZone: 'UTC',
|
|
hour: 'numeric',
|
|
minute: '2-digit'
|
|
});
|
|
});
|
|
function showIntervalLabelDefault(interval) {
|
|
const first = intervals.value[0][0];
|
|
const isFirst = first.hour === interval.hour && first.minute === interval.minute;
|
|
return !isFirst;
|
|
}
|
|
function intervalStyleDefault(_interval) {
|
|
return undefined;
|
|
}
|
|
function getTimestampAtEvent(e, day) {
|
|
const timestamp = copyTimestamp(day);
|
|
const bounds = new Box(e.currentTarget);
|
|
const baseMinutes = firstMinute.value;
|
|
const touchEvent = e;
|
|
const mouseEvent = e;
|
|
const touches = touchEvent.changedTouches || touchEvent.touches;
|
|
const target = touches && touches[0] ? touches[0] : mouseEvent;
|
|
const point = getTargetBox([target.clientX, target.clientY]);
|
|
const addIntervals = (point.y - bounds.top) / parsedIntervalHeight.value;
|
|
const addMinutes = Math.floor(addIntervals * parsedIntervalMinutes.value);
|
|
const minutes = baseMinutes + addMinutes;
|
|
return updateMinutes(timestamp, minutes, base.times.now);
|
|
}
|
|
function getSlotScope(timestamp) {
|
|
const scope = copyTimestamp(timestamp);
|
|
scope.timeToY = timeToY;
|
|
scope.timeDelta = timeDelta;
|
|
scope.minutesToPixels = minutesToPixels;
|
|
scope.week = days.value;
|
|
scope.intervalRange = [firstMinute.value, firstMinute.value + parsedIntervalCount.value * parsedIntervalMinutes.value];
|
|
return scope;
|
|
}
|
|
function scrollToTime(time) {
|
|
const y = timeToY(time);
|
|
const pane = scrollAreaRef.value;
|
|
if (y === false || !pane) {
|
|
return false;
|
|
}
|
|
pane.scrollTop = y;
|
|
return true;
|
|
}
|
|
function minutesToPixels(minutes) {
|
|
return minutes / parsedIntervalMinutes.value * parsedIntervalHeight.value;
|
|
}
|
|
function timeToY(time, targetDateOrClamp = false) {
|
|
const clamp = targetDateOrClamp !== false;
|
|
const targetDate = typeof targetDateOrClamp !== 'boolean' ? targetDateOrClamp : undefined;
|
|
let y = timeDelta(time, targetDate);
|
|
if (y === false) return y;
|
|
y *= bodyHeight.value;
|
|
if (clamp) {
|
|
if (y < 0) {
|
|
y = 0;
|
|
} else if (y > bodyHeight.value) {
|
|
y = bodyHeight.value;
|
|
}
|
|
} else {
|
|
if (y < 0) {
|
|
y = y + bodyHeight.value;
|
|
} else if (y > bodyHeight.value) {
|
|
y = y - bodyHeight.value;
|
|
}
|
|
}
|
|
return y;
|
|
}
|
|
function timeDelta(time, targetDate) {
|
|
let minutes = parseTime(time);
|
|
if (minutes === false) {
|
|
return false;
|
|
}
|
|
const gap = parsedIntervalCount.value * parsedIntervalMinutes.value;
|
|
if (targetDate && typeof time === 'object' && 'day' in time) {
|
|
const a = getDayIdentifier(time);
|
|
const b = getDayIdentifier(targetDate);
|
|
minutes += (a - b) * gap;
|
|
}
|
|
const min = firstMinute.value;
|
|
return (minutes - min) / gap;
|
|
}
|
|
return {
|
|
...base,
|
|
scrollAreaRef,
|
|
parsedFirstInterval,
|
|
parsedIntervalMinutes,
|
|
parsedIntervalCount,
|
|
parsedIntervalHeight,
|
|
parsedFirstTime,
|
|
firstMinute,
|
|
bodyHeight,
|
|
days,
|
|
intervals,
|
|
intervalFormatter,
|
|
showIntervalLabelDefault,
|
|
intervalStyleDefault,
|
|
getTimestampAtEvent,
|
|
getSlotScope,
|
|
scrollToTime,
|
|
minutesToPixels,
|
|
timeToY,
|
|
timeDelta
|
|
};
|
|
}
|
|
//# sourceMappingURL=calendarWithIntervals.js.map
|