import { h, computed, defineComponent, getCurrentInstance, onBeforeUpdate, onMounted, // nextTick, reactive, ref, Transition, watch, withDirectives, CSSProperties, SetupContext, VNode, } from 'vue' // Utility import { getDayIdentifier, parsed, parseTimestamp, type Timestamp, today } from '../utils/Timestamp' import { convertToUnit, minCharWidth } from '../utils/helpers' import useMouse, { getRawMouseEvents } from '../composables/useMouse' import useCalendar from '../composables/useCalendar' import useCommon, { useCommonProps } from '../composables/useCommon' import useTask, { useTaskProps, type Task } from '../composables/useTask' import useTimes, { useTimesProps } from '../composables/useTimes' import useRenderValues from '../composables/useRenderValues' import useMove, { useMoveEmits } from '../composables/useMove' import useEmitListeners from '../composables/useEmitListeners' import useButton from '../composables/useButton' import useFocusHelper from '../composables/useFocusHelper' import useCheckChange, { useCheckChangeEmits } from '../composables/useCheckChange' import useEvents from '../composables/useEvents' import useKeyboard, { useNavigationProps } from '../composables/useKeyboard' import { /*useCellWidth,*/ useCellWidthProps } from '../composables/useCellWidth' // Directives import ResizeObserver from '../directives/ResizeObserver' interface Size { width: number height: number } const { renderButton } = useButton() export default defineComponent({ name: 'QCalendarTask', directives: { ResizeObserver }, props: { ...useTimesProps, ...useNavigationProps, ...useCellWidthProps, ...useCommonProps, ...useTaskProps, // last for any overrides }, emits: [ 'update:model-value', 'update:model-tasks', 'update:model-title', 'update:model-footer', 'task-expanded', ...useCheckChangeEmits, ...useMoveEmits, ...getRawMouseEvents('-date'), ...getRawMouseEvents('-day'), ...getRawMouseEvents('-head-day'), ], setup(props, { slots, emit, expose }: SetupContext) { const scrollArea = ref(null), pane = ref(null), direction = ref<'next' | 'prev'>('next'), startDate = ref(props.modelValue || today()), endDate = ref('0000-00-00'), maxDaysRendered = ref(0), // always 0 emittedValue = ref(props.modelValue), focusRef = ref(props.modelValue || today()), focusValue = ref(parsed(props.modelValue || today()) as Timestamp), datesRef = ref>({}), // taskRef = ref(null), // weekEventRef = ref([]), // weekRef = ref([]), // headerColumnRef = ref(null), size = reactive({ width: 0, height: 0 }), dragOverHeadDayRef = ref(''), dragOverResource = ref(''), // keep track of last seen start and end dates lastStart = ref(null), lastEnd = ref(null) watch( () => props.view, () => { // reset maxDaysRendered maxDaysRendered.value = 0 }, ) const parsedView = computed(() => { if (props.view === 'month') { return 'month-interval' } return props.view as string }) const vm = getCurrentInstance() if (vm === null) { throw new Error('current instance is null') } // initialize emit listeners const { emitListeners } = useEmitListeners(vm) const { times, setCurrent, updateCurrent } = useTimes(props) // update dates updateCurrent() setCurrent() const { // computed parsedStart, // parsedEnd, dayFormatter, weekdayFormatter, ariaDateFormatter, // methods dayStyleDefault, getRelativeClasses, } = useCommon(props, { startDate, endDate, times }) // const { isSticky } = useCellWidth(props) const parsedValue = computed(() => { return parseTimestamp(props.modelValue, times.now) || parsedStart.value || times.today }) focusValue.value = parsedValue.value focusRef.value = parsedValue.value.date // const computedStyles = computed(() => { // const style: CSSProperties = {} // style.minWidth = computedWidth.value // style.maxWidth = computedWidth.value // style.width = computedWidth.value // return style // }) /// @ts-expect-error fix later const { renderValues } = useRenderValues(props, { parsedView, times, parsedValue, }) const { rootRef, __initCalendar, __renderCalendar } = useCalendar(props, __renderTask, { scrollArea, pane, }) const { // computed days, parsedStartDate, parsedEndDate, // methods /// @ts-expect-error fix later } = useTask(props, emit, { times, }) const { move } = useMove(props, { parsedView, parsedValue, direction, maxDays: maxDaysRendered, times, emittedValue, emit, }) const { getDefaultMouseEventHandlers } = useMouse(emit, emitListeners) const { checkChange } = useCheckChange(emit, { days, lastStart, lastEnd }) const { isKeyCode } = useEvents() /// @ts-expect-error fix later const { tryFocus } = useKeyboard(props, { rootRef, focusRef, focusValue, datesRef, days, parsedView, parsedValue, emittedValue, direction, times, }) // const parsedColumnCount = computed(() => { // return days.value.length // }) // const borderWidth = computed(() => { // if (rootRef.value) { // const calendarBorderWidth = getComputedStyle(rootRef.value).getPropertyValue('--calendar-border') // const parts = calendarBorderWidth.split(' ') // const part = parts.filter(part => part.indexOf('px') > -1) // return parseInt(part[ 0 ], 0) // } // return 0 // }) const isSticky = ref(true) const parsedCellWidth = computed(() => { if (props.cellWidth !== undefined) { return parseInt(String(props.cellWidth), 10) } return 150 // default when not specified }) // const computedWidth = computed(() => { // if (rootRef.value) { // const width = size.width || rootRef.value.getBoundingClientRect().width // if (width && parsedColumnCount.value) { // return (width / parsedColumnCount.value) + 'px' // } // } // return (100 / parsedColumnCount.value) + '%' // }) const isDayFocusable = computed(() => { return props.focusable === true && props.focusType.includes('day') }) const isDateFocusable = computed(() => { return ( props.focusable === true && props.focusType.includes('date') && isDayFocusable.value !== true ) }) const isWeekdayFocusable = computed(() => { return props.focusable === true && props.focusType.includes('weekday') }) const parsedHeight = computed(() => { return parseInt(String(props.dayHeight), 10) }) const parsedMinHeight = computed(() => { return parseInt(String(props.dayMinHeight), 10) }) watch([days], checkChange, { deep: true, immediate: true }) watch( () => props.modelValue, (val, oldVal) => { if (emittedValue.value !== val) { if (props.animated === true) { const v1 = getDayIdentifier(parsed(val) as Timestamp) const v2 = getDayIdentifier(parsed(oldVal) as Timestamp) direction.value = v1 >= v2 ? 'next' : 'prev' } emittedValue.value = val } focusRef.value = val }, ) watch(emittedValue, (val, oldVal) => { if (emittedValue.value !== props.modelValue) { if (props.animated === true) { const v1 = getDayIdentifier(parsed(val) as Timestamp) const v2 = getDayIdentifier(parsed(oldVal) as Timestamp) direction.value = v1 >= v2 ? 'next' : 'prev' } emit('update:model-value', val) } }) watch(focusRef, (val) => { if (val) { focusValue.value = parseTimestamp(val) as Timestamp } }) watch(focusValue, () => { if (datesRef.value[focusRef.value]) { datesRef.value[focusRef.value].focus() } else { // if focusRef is not in the list of current dates of dateRef, // then assume month is changing tryFocus() } }) onBeforeUpdate(() => { datesRef.value = {} // weekEventRef.value = [] // weekRef.value = [] }) onMounted(() => { __initCalendar() }) // public functions function moveToToday(): void { emittedValue.value = today() } function next(amount = 1): void { move(amount) } function prev(amount = 1): void { move(-amount) } // private functions function __onResize({ width, height }: Size): void { size.width = width size.height = height } function __isActiveDate(day: Timestamp): boolean { return day.date === emittedValue.value } /** * Renders the given day with the associated task * @param {Timestamp} day Timestamp representing the day * @param {any} task The Task * @param {number} taskIndex The task index * @returns VNode */ function __renderTaskDay(day: Timestamp, task: Task, taskIndex: number): VNode { const slot = slots.day const styler = props.dayStyle || dayStyleDefault const activeDate = props.noActiveDate !== true && parsedValue.value.date === day.date const dragValue = task[props.taskKey] const scope = { timestamp: day, task, taskIndex, activeDate, droppable: dragOverResource.value === dragValue, } const width = convertToUnit(parsedCellWidth.value) const style: CSSProperties = { width, minWidth: width, maxWidth: width, ...styler({ scope }), } const dayClass = typeof props.dayClass === 'function' ? props.dayClass({ scope }) : {} // const key = day.date + '-' + task.id return h( 'div', { // key, // ref: (el) => { // if (isDayFocusable.value === true) { // datesRef.value[ key ] = el // } // }, tabindex: isDayFocusable.value === true ? 0 : -1, class: { 'q-calendar-task__task--day': true, ...dayClass, ...getRelativeClasses(day), 'q-active-date': activeDate === true, 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isDayFocusable.value === true, }, style, onFocus: () => { if (isDayFocusable.value === true) { // focusRef.value = key } }, ...getDefaultMouseEventHandlers('-day', (event) => { return { scope, event } }), onDragenter: (e: DragEvent) => { if (props.dragEnterFunc !== undefined && typeof props.dragEnterFunc === 'function') { props.dragEnterFunc(e, 'day', { scope }) === true ? (dragOverResource.value = dragValue) : (dragOverResource.value = '') } }, onDragover: (e: DragEvent) => { if (props.dragOverFunc !== undefined && typeof props.dragOverFunc === 'function') { props.dragOverFunc(e, 'day', { scope }) === true ? (dragOverResource.value = dragValue) : (dragOverResource.value = '') } }, onDragleave: (e: DragEvent) => { if (props.dragLeaveFunc !== undefined && typeof props.dragLeaveFunc === 'function') { props.dragLeaveFunc(e, 'day', { scope }) === true ? (dragOverResource.value = dragValue) : (dragOverResource.value = '') } }, onDrop: (e: DragEvent) => { if (props.dropFunc !== undefined && typeof props.dropFunc === 'function') { props.dropFunc(e, 'day', { scope }) === true ? (dragOverResource.value = dragValue) : (dragOverResource.value = '') } }, }, [slot && slot({ scope }), useFocusHelper()], ) } function __renderTaskDays(task: Task, taskIndex: number): VNode[] { return days.value.map((day) => __renderTaskDay(day, task, taskIndex)) } function __renderTaskDaysRow(task: Task, taskIndex: number): VNode { const slot = slots.days const scope = { timestamps: days.value, days: days.value, // deprecated task, taskIndex, cellWidth: parsedCellWidth.value, } return h( 'div', { class: 'q-calendar-task__task--days-row', }, [__renderTaskDays(task, taskIndex), slot && slot({ scope })], ) } function __renderTaskItem( task: Task, taskIndex: number, indentLevel = 0, expanded = true, ): VNode { const slot = indentLevel === 0 ? slots.task : slots.subtask const scope = { start: parsedStartDate.value, end: parsedEndDate.value, task, taskIndex, expanded, } const width = convertToUnit(props.taskWidth) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } const isFocusable = props.focusable === true && props.focusType.includes('task') return h( 'div', { class: { 'q-calendar-task__task--item': true, 'q-calendar__sticky': isSticky.value === true, 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isFocusable === true, }, style, }, [ h( 'div', { style: { flexDirection: 'column', justifyContent: 'center', alignItems: 'center', width: 10 + 10 * indentLevel + 'px', }, }, [ h('div', { class: { 'q-calendar__parent': task.children !== undefined, 'q-calendar__parent--expanded': task.children !== undefined && task.expanded === true, 'q-calendar__parent--collapsed': task.children !== undefined && task.expanded !== true, }, onClick: (e) => { e.stopPropagation() task.expanded = !task.expanded // emit('update:model-tasks', props.modelTasks) emit('task-expanded', { expanded: task.expanded, scope }) }, }), ], ), slot && slot({ scope }), useFocusHelper(), ], ) } function __renderTaskRow( task: Task, taskIndex: number, indentLevel = 0, expanded = true, ): VNode[] { const height = task.height !== void 0 ? convertToUnit(parseInt(task.height, 10)) : parsedHeight.value > 0 ? convertToUnit(parsedHeight.value) : 'auto' const minHeight = parsedMinHeight.value > 0 ? convertToUnit(parsedMinHeight.value) : void 0 const style: CSSProperties = { height, } if (minHeight !== void 0) { style.minHeight = minHeight } const taskRow = h( 'div', { key: task[props.taskKey] + '-' + taskIndex, class: { 'q-calendar-task__task': indentLevel === 0, 'q-calendar-task__task--section': indentLevel !== 0, }, style, }, [ __renderTaskItem(task, taskIndex, indentLevel, expanded), __renderTaskDaysRow(task, taskIndex), ], ) if (task.children !== undefined) { return [ taskRow, h( 'div', { class: { 'q-calendar__child': true, 'q-calendar__child--expanded': expanded === true, 'q-calendar__child--collapsed': expanded !== true, }, }, [ __renderTasks( task.children, indentLevel + 1, expanded === false ? expanded : task.expanded, ), ], ), ] } return [taskRow] } function __renderTasks( tasks: Task[] | void = undefined, indentLevel = 0, expanded = true, ): VNode[] { if (tasks === undefined) { tasks = props.modelTasks } return (tasks as Array) .map((task, taskIndex) => { return __renderTaskRow( task, taskIndex, indentLevel, task.children !== undefined ? task.expanded : expanded, ) }) .flat() } function __renderTasksContainer(): VNode { return h( 'div', { class: { 'q-calendar-task__task--container': true, 'q-calendar__sticky': isSticky.value === true, }, }, [__renderTasks()], ) } function __renderFooterTask(task: Task, index: number): VNode { const slot = slots['footer-task'] const scope = { start: parsedStartDate.value, end: parsedEndDate.value, footer: task, index, } const width = convertToUnit(props.taskWidth) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } return h( 'div', { class: { 'q-calendar-task__footer--task': true, 'q-calendar__sticky': isSticky.value === true, }, style, }, [slot && slot({ scope })], ) } function __renderFooterDay(day: Timestamp, task: Task, index: number): VNode { const slot = slots['footer-day'] const scope = { timestamp: day, footer: task, index, } const width = convertToUnit(parsedCellWidth.value) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } const footerDayClass = typeof props.footerDayClass === 'function' ? props.footerDayClass({ scope }) : {} return h( 'div', { class: { 'q-calendar-task__footer--day': true, ...footerDayClass, ...getRelativeClasses(day), 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isDayFocusable.value === true, }, style, // onFocus: (e) => { // if (isDayFocusable.value === true) { // focusRef.value = day.date // } // } }, [slot && slot({ scope })], ) } function __renderFooterDays(task: Task, index: number): VNode { return h( 'div', { class: 'q-calendar-task__footer--day-wrapper', }, [days.value.map((day) => __renderFooterDay(day, task, index))], ) } function __renderFooterRows(): VNode[] { const isFocusable = props.focusable === true && props.focusType.includes('task') return props.modelFooter.map((task, index) => { return h( 'div', { class: { 'q-calendar-task__footer--wrapper': true, 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isFocusable === true, }, }, { default: () => [__renderFooterTask(task, index), __renderFooterDays(task, index)], }, ) }) } function __renderFooter(): VNode { return h( 'div', { class: { 'q-calendar-task__footer': true, 'q-calendar__sticky': isSticky.value === true, }, }, __renderFooterRows(), ) } function __renderContainer(): VNode { return h( 'div', { class: { 'q-calendar-task__container': true, }, }, [props.noHeader !== true && __renderHead(), __renderTasksContainer(), __renderFooter()], ) } function __renderHeadTask(): VNode { const slot = slots['head-tasks'] const scope = { start: parsedStartDate.value, end: parsedEndDate.value, } const width = convertToUnit(parseInt(String(props.taskWidth), 10)) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } return h( 'div', { class: { 'q-calendar-task__head--tasks': true, 'q-calendar__sticky': isSticky.value === true, }, style, }, [slot && slot({ scope })], ) } function __renderTitleTask(title: string, index: number): VNode { const slot = slots['title-task'] const width = convertToUnit(parseInt(String(props.taskWidth), 10)) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } const scope = { start: parsedStartDate.value, end: parsedEndDate.value, cellWidth: width, title, index, } return h( 'div', { class: { 'q-calendar-task__title--task': true, 'q-calendar__sticky': isSticky.value === true, }, style, }, [slot && slot({ scope })], ) } function __renderHeadWeekday(day: Timestamp): VNode { const slot = slots['head-weekday-label'] const activeDate = props.noActiveDate !== true && __isActiveDate(day) const scope = { activeDate, timestamp: day, disabled: props.disabledWeekdays ? props.disabledWeekdays.includes(Number(day.weekday)) : false, } const data: Record = { class: { 'q-calendar-task__head--weekday': true, ['q-calendar__' + props.weekdayAlign]: true, 'q-calendar__ellipsis': true, }, } return h( 'div', data, (slot && slot({ scope })) || __renderHeadWeekdayLabel(day, props.shortWeekdayLabel), ) } function __renderHeadWeekdayLabel(day: Timestamp, shortWeekdayLabel: boolean): VNode { const weekdayLabel = weekdayFormatter.value( day, shortWeekdayLabel || (props.weekdayBreakpoints[0] > 0 && parsedCellWidth.value <= props.weekdayBreakpoints[0]), ) return h( 'span', { class: 'q-calendar__ellipsis', }, props.weekdayBreakpoints[1] > 0 && parsedCellWidth.value <= props.weekdayBreakpoints[1] ? minCharWidth(weekdayLabel, Number(props.minWeekdayLabel)) : weekdayLabel, ) } function __renderHeadDayDate(day: Timestamp): VNode { const data: Record = { class: { 'q-calendar-task__head--date': true, ['q-calendar__' + props.dateAlign]: true, }, } return h('div', data, __renderHeadDayBtn(day)) } function __renderHeadDayBtn(day: Timestamp): VNode | VNode[] { const activeDate = props.noActiveDate !== true && __isActiveDate(day) const dayLabel = dayFormatter.value(day, false) const headDayLabelSlot = slots['head-day-label'] const headDayButtonSlot = slots['head-day-button'] const scope = { dayLabel, timestamp: day, activeDate } const key = day.date const data: Record = { key, tabindex: isDateFocusable.value === true ? 0 : -1, class: { 'q-calendar-task__head--day__label': true, 'q-calendar__button': true, 'q-calendar__button--round': props.dateType === 'round', 'q-calendar__button--rounded': props.dateType === 'rounded', 'q-calendar__button--bordered': day.current === true, 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isDateFocusable.value === true, }, disabled: day.disabled, onKeydown: (e: KeyboardEvent) => { if (day.disabled !== true && isKeyCode(e, [13, 32])) { e.stopPropagation() e.preventDefault() } }, onKeyup: (e: KeyboardEvent) => { // allow selection of date via Enter or Space keys if (day.disabled !== true && isKeyCode(e, [13, 32])) { emittedValue.value = day.date if (emitListeners.value.onClickDate !== undefined) { emit('click-date', { scope }) } } }, ...getDefaultMouseEventHandlers('-date', (event, eventName) => { if (eventName === 'click-date' || eventName === 'contextmenu-date') { emittedValue.value = day.date if (eventName === 'click-date') { event.preventDefault() } } return { scope, event } }), } if (props.noAria !== true) { data.ariaLabel = ariaDateFormatter.value(day, false) } return headDayButtonSlot ? headDayButtonSlot({ scope }) : renderButton(props, data, headDayLabelSlot ? headDayLabelSlot({ scope }) : dayLabel) } function __renderDateHeader(day: Timestamp): VNode | VNode[] | void { if (props.dateHeader === 'stacked') { return [ props.noDefaultHeaderText !== true && __renderHeadWeekday(day), props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), ].filter((v): v is VNode => v !== false) } else if (props.dateHeader === 'inline') { if (props.weekdayAlign === 'left' && props.dateAlign === 'right') { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderText !== true && __renderHeadWeekday(day), props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), ], ) } else if (props.weekdayAlign === 'right' && props.dateAlign === 'left') { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderText !== true && __renderHeadWeekday(day), props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), ], ) } else { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderText !== true && __renderHeadWeekday(day), props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), ], ) } } else if (props.dateHeader === 'inverted') { if (props.weekdayAlign === 'left' && props.dateAlign === 'right') { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), props.noDefaultHeaderText !== true && __renderHeadWeekday(day), ], ) } else if (props.weekdayAlign === 'right' && props.dateAlign === 'left') { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), props.noDefaultHeaderText !== true && __renderHeadWeekday(day), ], ) } else { return h( 'div', { class: 'q-calendar__header--inline', }, [ props.noDefaultHeaderBtn !== true && __renderHeadDayDate(day), props.noDefaultHeaderText !== true && __renderHeadWeekday(day), ], ) } } } /** * Renders the given day with the associated task * @param {Timestamp} day Timestamp representing the day * @param {sting} title The Title * @param {number} index The task index * @returns VNode */ function __renderTitleDay(day: Timestamp, title: string, index: number): VNode { const slot = slots['title-day'] const width = convertToUnit(parsedCellWidth.value) const style: CSSProperties = { width, minWidth: width, maxWidth: width, } const scope = { timestamp: day, title, index, cellWidth: parsedCellWidth.value, } const dayClass = typeof props.dayClass === 'function' ? props.dayClass({ scope }) : {} const isFocusable = props.focusable === true && props.focusType.includes('day') return h( 'div', { class: { 'q-calendar-task__title--day': true, ...dayClass, ...getRelativeClasses(day), 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isFocusable === true, }, style, }, [slot && slot({ scope }), useFocusHelper()], ) } function __renderHeadDay(day: Timestamp): VNode { const headDaySlot = slots['head-day'] const headDateSlot = slots['head-date'] const activeDate = props.noActiveDate !== true && __isActiveDate(day) const scope = { timestamp: day, activeDate, droppable: (dragOverHeadDayRef.value = day.date), disabled: props.disabledWeekdays ? props.disabledWeekdays.includes(Number(day.weekday)) : false, } const styler = props.weekdayStyle || dayStyleDefault const weekdayClass = typeof props.weekdayClass === 'function' ? props.weekdayClass({ scope }) : {} const width = convertToUnit(parsedCellWidth.value) const style: CSSProperties = { width, minWidth: width, maxWidth: width, ...styler({ scope }), } const key = day.date const data: Record = { key, ref: (el: HTMLElement) => { datesRef.value[key] = el }, tabindex: isWeekdayFocusable.value === true ? 0 : -1, class: { 'q-calendar-task__head--day': true, ...weekdayClass, ...getRelativeClasses(day), 'q-active-date': activeDate, 'q-calendar__hoverable': props.hoverable === true, 'q-calendar__focusable': isWeekdayFocusable.value === true, }, style, onFocus: () => { if (isWeekdayFocusable.value === true) { focusRef.value = key } }, onKeydown: (e: KeyboardEvent) => { if (day.disabled !== true && isKeyCode(e, [13, 32])) { e.stopPropagation() e.preventDefault() } }, onKeyup: (e: KeyboardEvent) => { // allow selection of date via Enter or Space keys if (day.disabled !== true && isKeyCode(e, [13, 32])) { emittedValue.value = day.date } }, ...getDefaultMouseEventHandlers('-head-day', (event) => { return { scope, event } }), onDragenter: (e: DragEvent) => { if (props.dragEnterFunc !== undefined && typeof props.dragEnterFunc === 'function') { props.dragEnterFunc(e, 'head-day', { scope }) === true ? (dragOverHeadDayRef.value = day.date) : (dragOverHeadDayRef.value = '') } }, onDragover: (e: DragEvent) => { if (props.dragOverFunc !== undefined && typeof props.dragOverFunc === 'function') { props.dragOverFunc(e, 'head-day', { scope }) === true ? (dragOverHeadDayRef.value = day.date) : (dragOverHeadDayRef.value = '') } }, onDragleave: (e: DragEvent) => { if (props.dragLeaveFunc !== undefined && typeof props.dragLeaveFunc === 'function') { props.dragLeaveFunc(e, 'head-day', { scope }) === true ? (dragOverHeadDayRef.value = day.date) : (dragOverHeadDayRef.value = '') } }, onDrop: (e: DragEvent) => { if (props.dropFunc !== undefined && typeof props.dropFunc === 'function') { props.dropFunc(e, 'head-day', { scope }) === true ? (dragOverHeadDayRef.value = day.date) : (dragOverHeadDayRef.value = '') } }, } return h('div', data, [ // head-day slot replaces everything below it headDaySlot !== undefined && headDaySlot({ scope }), headDaySlot === undefined && __renderDateHeader(day), headDaySlot === undefined && headDateSlot && headDateSlot({ scope }), useFocusHelper(), ]) } function __renderHeadDays(): VNode[] { return days.value.map((day) => __renderHeadDay(day)) } function __renderTitleDays(title: string, index: number): VNode[] { return days.value.map((day) => __renderTitleDay(day, title, index)) } function __renderHeadDaysRow(): VNode { return h( 'div', { class: { 'q-calendar-task__head--days': true, }, }, [...__renderHeadDays()], ) } function __renderTitleDaysRow(title: string, index: number): VNode { return h( 'div', { class: { 'q-calendar-task__title--days': true, }, }, [...__renderTitleDays(title, index)], ) } // ---- function __renderHead(): VNode { return h( 'div', { roll: 'presentation', class: { 'q-calendar-task__head': true, 'q-calendar__sticky': isSticky.value === true, }, style: {}, }, [ h( 'div', { style: { position: 'relative', display: 'flex', }, }, [__renderHeadTask(), __renderHeadDaysRow()], ), props.modelTitle.map((title, index) => h( 'div', { class: 'q-calendar-task__title', style: { position: 'relative', display: 'flex', }, }, [__renderTitleTask(title, index), __renderTitleDaysRow(title, index)], ), ), ], ) } function __renderBody(): VNode { return h( 'div', { class: 'q-calendar-task__body', }, [__renderScrollArea()], ) } function __renderScrollArea(): VNode { return h( 'div', { ref: scrollArea, class: { 'q-calendar-task__scroll-area': true, 'q-calendar__scroll': true, }, }, [__renderContainer()], ) } function __renderTask(): VNode { const { start, end } = renderValues.value startDate.value = start.date endDate.value = end.date const hasWidth = size.width > 0 const weekly = withDirectives( h( 'div', { key: startDate.value, class: 'q-calendar-task', }, [hasWidth === true && __renderBody()], ), [[ResizeObserver, __onResize]], ) if (props.animated === true) { const transition = 'q-calendar--' + (direction.value === 'prev' ? props.transitionPrev : props.transitionNext) return h( Transition, { name: transition, appear: true, }, () => weekly, ) } return weekly } // expose public methods expose({ prev, next, move, moveToToday, updateCurrent, }) // Object.assign(vm.proxy, { // prev, // next, // move, // moveToToday, // updateCurrent // }) return (): VNode => __renderCalendar() }, })