A suggested way to resize an item on the Day view #375
Replies: 3 comments 3 replies
-
Nice work @flowt-au -- I really love this and may use it in an example so it can be better discovered. |
Beta Was this translation helpful? Give feedback.
-
Here is an update that allows items to be moved with the mouse wheel as well as resized. I have set up my Day calendar to be able to be configured for custom intervals as needed. These affect the mouse wheel handler so I am mentioning them here first. First the :interval-start=intervalStart
:interval-minutes=intervalMinutes
:interval-count=intervalCount
@wheel="onPanelWheel" <!-- more on this below ---> and the @wheel="onMouseWheel($event, item)" Next the vm properties: emits: ['item-block-changed']
// These define the actual hours / intervals to use for this instance of the calendar
// 8am to 12pm in this case:
intervalRangeStart: '08:00', // '00:00' = midnight start
intervalRangeEnd: '12:00', // '24:00' = midnight end
intervalMinutes: 15, // ie 4 intervals per hour Next some computed props computed: {
/**
* Take the this.intervalRangeStart eg '06:00'
* and get the hour integer
*/
intervalRangeStartHour () {
const ts = parsed(this.selectedDate + ' ' + this.intervalRangeStart)
return ts.hour
},
/**
* Take the this.intervalRangeEnd eg '20:00'
* and get the hour integer
*/
intervalRangeEndHour () {
const ts = parsed(this.selectedDate + ' ' + this.intervalRangeEnd)
return ts.hour
},
intervalCount () {
// eg if minutes is 15, this = 60 / 15 = 4 * 24 hours = 96 periods
// And if start calendar at 8am you need to deduct that many intervals: 8 * 4 = 32
// Same for end hour.
const intervalsPerHour = Math.round(60 / this.intervalMinutes)
const hoursPerDay = 24 - this.intervalRangeStartHour - (24 - this.intervalRangeEndHour)
return intervalsPerHour * hoursPerDay
},
intervalStart () {
// eq if intervalRangeStartHour = 2 ie 2am
return (this.intervalRangeStartHour * (60 / this.intervalMinutes))
}
} Two methods: /**
* Suspend ctrl + mouse wheel and alt + mouse wheel on this Calendar Day panel.
* We are using ctrl wheel and alt wheel in our onMouseWheel() below.
* The reason is when you ctrl + mouse wheel on Chrome (and others?) it ZOOMs the whole browser.
* Very annoying! And Alt + wheel scrolls the panel when we dont want it to.
* If you are mouse wheeling over your calendar item, and you move it out from under your mouse pointer,
* you get those unhelpful "panel level" and "browser level" side effects!
* So, prevent that.
*/
onPanelWheel (evt) {
if (evt.ctrlKey || evt.altKey) {
evt.preventDefault()
}
}, /**
* Handler for when we mouse wheel on a calendar item.
* We use:
* Ctrl + wheel: to resize the item by moving the end of the item
* down or up 1 minute (ie change the duration of the item ie the height)
* Shift + wheel: same as Ctrl + wheel but resize in 5 minute increments
* Alt + wheel: to move the item up or down
*
* We make sure we stay in bounds as configured by the vm props above.
* We debounce and emit 'item-block-changed' so the parent component
* can persist the changes or whatever else you might need to do.
*/
onMouseWheel (evt, item) {
// Clear any existing debounce timeout
clearTimeout(this.itemResizeTimeout)
// Preemtively capture this wheel event
// while still allowing the normal mouse wheel scroll of the panel,
// even when over a calendar item.
if (evt.altKey || evt.ctrlKey || evt.shiftKey) {
evt.preventDefault()
evt.stopPropagation()
} else {
// ie Let the normal scroll by mouse wheel event response happen
return
}
// How many minutes to move?
// Approx 100 "twirps" = "1 wheel increment" = 1 minute
const mins = Math.round(evt.deltaY / 100)
if (evt.altKey) {
// MOVING the item...
// Save the item date before you mutate it.
// You use this to check your boundaries and if you go out of bounds, you can restore it.
const savedDate = item.date
// Increment / decrement one minute at a time so you can fine-tune the position
const newStart = addToDate(parsed(item.date + ' ' + item.time), { minute: mins })
// Recalculate the end date so we can check boundaries below
const newEnd = addToDate(newStart, { minute: item.duration })
// Check our boundaries...
if (newStart.date < savedDate || newStart.time < this.intervalRangeStart || newEnd.date > savedDate || newEnd.time > this.intervalRangeEnd) {
// We will go out of bounds, so dont move it.
if (newEnd.date > savedDate) {
// This is only an issue if this.intervalRangeEnd = '24:00'.
// Here we are into the next day so we are hitting the 24:00 === 00:00 issue.
// Just recalculate the start of the item backwards from 24:00,
// otherwise we will never get the item to the correct start.
// eg a 60 minute item where the day boundary is midnight starts at 23:00, not 22:59
const newTs = addToDate(parsed(savedDate + ' 24:00'), { minute: -item.duration })
item.time = newTs.time
} else {
// Dont change the time
}
} else {
// We are in bounds, so assign the new start time
item.time = newStart.time
}
} else {
// RESIZING...
if (evt.shiftKey) {
// Use 5 minute increments if the user uses shift key + scroll wheel.
// item.duration is the reactive prop in QCalendar
item.duration += mins * 5
} else {
// ie ctrlKey
item.duration += mins
}
// Some basic range enforcement. Whatever makes sense to you.
if (item.duration < 5) item.duration = 5
if (item.duration > 300) item.duration = 300
// I have a q-tooltip using a custom item property 'tooltip'
// so this displays the actual number of minutes
// while this operation is active.
item.tooltip = `${item.duration} mins`
}
// Debounce before persisting (or whatever you need to do)
this.itemResizeTimeout = setTimeout(() => {
// Reinstate the tooltip
item.tooltip = item.title
// In my case this triggers a persist of the object that
// the calendar item represents.
this.$emit('item-block-changed', item)
}, 1000)
}
}, I am sure there are optimisations and better ways to do this, so please suggest! :-) Cheers, |
Beta Was this translation helpful? Give feedback.
-
And, you can also zoom the height of the intervals. /**
* Capture ctrl+wheel and alt+wheel on the calendar panel.
* For ctrl+alt+wheel, zoom the interval height.
*/
onPanelWheel (evt) {
if (evt.ctrlKey || evt.altKey) {
evt.preventDefault()
evt.stopPropagation()
}
// Use BOTH mofifiers so when you are doing an item mousewheel operation
// as per onMouseWheel(), and your mouse pointer moves off the item,
// you dont start Zooming the whole panel.
if (evt.ctrlKey && evt.altKey) {
const min = 6
const max = 28
const px = Math.round(evt.deltaY / 100)
const h = this.intervalHeight + px
if (h < min || h > max) {
// Out of bounds, no change
} else {
// Within bounds, so change the height
this.intervalHeight = h
}
}
}, |
Beta Was this translation helpful? Give feedback.
-
Hi,
I wanted users to be able to resize the items on a Day view by using the mouse wheel.
So I am just sharing a pattern that seems to work well. I haven't fully tested it on all browsers and OS etc but it is a start.
Maybe helpful for others?
Cheers,
Murray
Beta Was this translation helpful? Give feedback.
All reactions