Could you please share the full JS code for your POC so we can inspect & debug?
Support Forum
<script setup lang="ts">
// @ts-nocheck
import '@bryntum/calendar/calendar.stockholm.css'
import { ref, toRefs, computed, onMounted } from 'vue'
import { BryntumCalendar } from '@bryntum/calendar-vue-3'
import { Calendar, DateHelper } from '@bryntum/calendar'
import { PresetManager, StringHelper } from '@bryntum/schedulerpro'
interface iSpectoraCalendarProps {
config: any
events: any[]
resources: any[]
settings: any
me: any
}
const convertTime12to24 = (time12h) => {
const [time, modifier] = time12h.split(' ')
let [hours, minutes] = time.split(':')
if (hours === '12') {
hours = '00'
}
if (modifier === 'PM') {
hours = parseInt(hours, 10) + 12
}
return Number(hours)
}
const useCalendarConfig = () => {
const profileSettings = props.settings.profile.attributes.settings || {}
const dayStart = convertTime12to24(profileSettings.calendar_start_time)
const dayEnd = convertTime12to24(profileSettings.calendar_end_time)
// this.queryParams.type ||
// this.profileSettings.calendar_default_view ||
// 'resourceTimelineDay'
// // if (this.isMobile) initialView = 'listWeek'
// const slotMinTime = this.profileSettings.calendar_start_time
// const slotMaxTime = this.profileSettings.calendar_end_time
// const offsetWeeks = this.profileSettings.calendar_offset_weeks
// const offsetDays = this.profileSettings.calendar_offset_days
const baseTimelineConfig = {
type: 'scheduler',
displayName: 'Timeline',
showAvatars: true,
// eventStyle: 'calendar',
workingTime: {
fromHour: dayStart,
toHour: dayEnd,
},
features: {
nonWorkingTime: true,
},
columns: [
{
type: 'resourceInfo',
field: 'name',
text: 'Inspectors',
width: 175,
//
// Custom rendering
// https://bryntum.com/products/schedulerpro/examples/conflicts/
//
// htmlEncode: false,
// // Renderer that returns a DOM config object, a more performant way than returning a html string, allows
// // reusing elements as cells are re-rendered
// renderer: ({ record }) => ({
// children: [
// // <i> tag with the icon
// record.icon
// ? {
// tag: 'i',
// className: `b-fa b-fa-fw ${record.icon}`,
// style: 'margin-right: .5em',
// }
// : null,
// // text node with the name
// record.name,
// ],
// }),
},
],
}
return {
sidebar: props.config.sidebar,
mode: 'week',
features: {
eventTooltip: {
// Tooltip configs can be used here
align: 'l-r', // Align left to right,
// Custom content
renderer: (data) => {
console.log(data, 'abc...')
return 'I am a tooltip!'
},
},
},
modes: {
day: {
...baseTimelineConfig,
stepUnit: 'day',
displayName: 'Day',
descriptionRenderer() {
return DateHelper.format(this.startDate, 'dddd, MMMM Do, YYYY')
},
// https://www.bryntum.com/products/scheduler/docs/api/Scheduler/preset/ViewPreset
viewPreset: {
base: 'hourAndDay',
headers: [
{
unit: 'hour',
dateFormat: 'hA',
},
],
},
},
week: {
...baseTimelineConfig,
stepUnit: 'week',
displayName: 'Week',
viewPreset: {
base: 'hourAndDay',
tickWidth: 45,
headers: [
{
unit: 'day',
dateFormat: 'ddd MM/DD',
},
{
unit: 'hour',
dateFormat: 'hA',
},
],
},
},
month: {
showResourceAvatars: 'last',
autoRowHeight: true,
},
year: false,
},
}
}
// const calendar = computed(() => calendarRef.value.instance.value)
const props = withDefaults(defineProps<iSpectoraCalendarProps>(), {
config: () => ({}),
settings: () => ({}),
me: () => ({}),
resources: () => [],
events: () => [],
})
const calendarRef = ref(null)
const calendarConfig = ref(useCalendarConfig())
function onAutoRowHeightChanged({ checked }) {
const calendar = calendarRef.value.instance.value
calendar.modes.month.autoRowHeight = checked
}
// Called as the resourceFilterFilter's onChange handler
function onResourceFilterFilterChange({ value }) {
const calendar = calendarRef.value.instance.value
// A filter with an id replaces any previous filter with that id.
// Leave any other filters which may be in use in place.
calendar.widgetMap.resourceFilter.store.filter({
id: 'resourceFilterFilter',
filterBy: (r) => r.name.toLowerCase().startsWith(value.toLowerCase()), // a function which returns true to include the record
})
}
const handleEventClick = (options) => {
console.log('event clicked:', options)
}
const handleBeforeEventEdit = (options) => {
console.log('handleBeforeEventEdit', options)
// By returning false from a listener for an event documented as preventable the action that would otherwise be executed after the event is prevented. These events names are usually prefixed with before.
// return false
}
const handleBeforeDragMoveEnd = (options) => {
console.log('handleBeforeDragMoveEnd', options)
return new Promise((resolve, reject) => {
setTimeout(() => {
// Trigger reschedule confirmation modal and pass it the promise resolver
// If confirmed, you can resolve. If rejected you reject
// https://jmp.sh/15dKSBDi
if (confirm('Are you sure you want to move this event?')) {
console.log('confirmed...')
resolve(true)
} else {
console.log('rejected...')
reject()
}
}, 3000)
})
}
const handleDragMoveEnd = (options) => {
console.log('handleDragMoveEnd', options)
return true
}
const handleBeforeDragResizeEnd = (options) => {
console.log('handleBeforeDragResizeEnd', options)
}
const handleBeforeDragCreateEnd = (options) => {
console.log('handleBeforeDragCreateEnd', options)
}
// beforeDragMoveEnd : async({ eventRecord }) => {
// const result = await MessageDialog.confirm({
// title : 'Please confirm',
// message : 'Is this the start time you wanted?'
// });
// // Return true to accept the drop or false to reject it
// return result === MessageDialog.yesButton;
// },
// beforeDragResizeEnd : async({ eventRecord }) => {
// const result = await MessageDialog.confirm({
// title : 'Please confirm',
// message : 'Is this the duration you wanted?'
// });
// // Return true to accept the drop or false to reject it
// return result === MessageDialog.yesButton;
// },
// beforeDragCreateEnd : async({ eventRecord }) => {
// const result = await MessageDialog.confirm({
// title : 'Please confirm',
// message : 'Want to create this event?'
// });
// // Return true to accept the drop or false to reject it
// return result === MessageDialog.yesButton;
// }
onMounted(() => {
const calendar = calendarRef.value.instance.value
// calendar.widgetMap.autoRowHeight.on({ change: onAutoRowHeightChanged })
// calendar.widgetMap.resourceFilterFilter.on({
// change: onResourceFilterFilterChange,
// })
})
</script>
<template>
<bryntum-calendar
ref="calendarRef"
:events="events"
:resources="resources"
v-bind="calendarConfig"
@eventclick="handleEventClick"
@beforeDragMoveEnd="handleBeforeDragMoveEnd"
@dragMoveEnd="handleDragMoveEnd"
/>
</template>
This is in the context of the Scheduler being owned by a Calendar, and the Calendar's tooltip take over.
renderer
is correct. It is missing from the docs, and I'll get that fixed ASAP
We deprecate usage of "templates". Creating a complex HTML string, integrating conditional classes and styles is extremely ugly. You should see some of the legacy "templates" in the system that we are working to eliminate!
Usage of strings containing "<tag>" is really only useful for transfer of a representation of DOM structure over the network.
In reality, the DOM is an object tree, so it is more efficient to represent it as such. Objects can be merged, so a default configuration may be mutated easily. (Consider a default HTML string and your code has to add a class or style to it - you'd have to parse it, snip it up and do horrible string manipulation!). Finally, it's easier to convert it to real DOM.
Newer classes such as https://bryntum.com/products/calendar/docs/api/Calendar/widget/EventTip which is what is in use here use renderer
, and that may produce an object based representation known as a `DomConfig: https://bryntum.com/products/calendar/docs/api/Core/helper/DomHelper#typedef-DomConfig
Example:
{
tag : 'ul", // May be omitted if you want a DIV
// Conditionally added classes are much easier by just setting properties.
// If the value of the property is truthy, the class is set
className : {
'b-selected' : this.isSelected(),
// this.addedCls may be set, this allows you to add it if it exists
[this.addedCls] : this.addedCls
},
style : {
left : 0
},
children : [{
tag : 'li',
text : 'Text node'
}]
}
````