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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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