Open nkostadinov opened 8 years ago
hello, that is currently not possible, but would you be able to post a visual mockup of what you are talking about? I'm particularly interested in where the + will be positioned within the selection rectangle, and that is it guaranteed to be seen in all scenarios.
We build this in an older version of fullcalendar where we added resource columns functionality ourselves. It looked like this:
dayClick: function (date, jsEvent, view, resource) {
if (!$(jsEvent.target).parents('div').hasClass("addappointment")) {
// remove all previous addappointment elements
$("div.addappointment").remove();
// render new element to open popup
$(jsEvent.target).append($("<div class='addappointment'><div><span class='fa fa-plus'></span><spanAdd appointment</span></div></div>").click(function () {
// remove all previous addappointment elements
$(this).remove();
var resourceitem;
if (resource) {
resourceitem = getObjectByGuid(pageVM.resourceItems, resource.id);
}
// open appointment popup
window.location = "#addappointment";
}));
}
},
But after updating to the latest version of fullcalendar with resource support it hilights the complete row. We didn't dive into it yet to fix it. Probably the html structure changed a bit.
I want to create something like this:
The problem really is the HTML generated by fullcalendar. It doesn't have well defined "cells" but crossing rows and columns which makes it hard to implement. I'm currently looking at the cell selection feature and trying to implement something similar, another way would be to create some sort of "temporary" event when the mouse is over the cell and the delete it on mouse leave.
I'm in a similar situation where I need to style a specific cell on hover using the scheduler. My UI requirements are very similar to the mockup by @nkostadinov.
I need to insert a new event for a specific date for a particular resource but haven't found a way to highlight a single cell to indicate to the user where the new event will be placed. I can highlight the entire row via CSS, but the selection is more specific so I've resorted to no UI feedback for now which is less than ideal.
this is probably something that should be implemented in the core, so non-resource views can take advantage as well.
Can we make it not just hover but also selecting a single cell and adding event handlers to it? Click events etc. for example?
@sboele, having unlimited control over this area is a bit difficult because, as you pointed out, they are no discreet DOM nodes. This is done for optimization reasons.
If you would like to accept click nodes on the slots, use dayClick: http://fullcalendar.io/docs/mouse/dayClick/
If you want unlimited arbitrary control over mouse events, please subscribe to this issue: https://github.com/fullcalendar/fullcalendar/issues/2718
This is also my issue, you can use "dayRender" to customize day cell and "mouseenter" or "mouseleave" for customizing css.
References: http://fullcalendar.io/docs/display/dayRender/
same issue in core: https://github.com/fullcalendar/fullcalendar/issues/2513
I wanted to show the current time for the slot i was hovering, and also possibly change color, and I found this to be a good start. There are some side effects and currently only works in AgendaWeek, so any feedback would be great. Hope it helps someone!
$('.fc-widget-content').hover(function(){
if(!$(this).html()){
for(i=0;i<7;i++){
$(this).append('<td class="temp-cell" style="border: 0px; width:'+(Number($('.fc-day').width())+3)+'px"></td>');
}
$(this).children('td').each(function(){
$(this).hover(function(){
$(this).html('<div class=current-time>'+$(this).parent().parent().data('time').substring(0,5)+'</div>');
},function(){
$(this).html('');
});
});
}
},function(){
$(this).children('.temp-cell').remove();
});
Based on: http://stackoverflow.com/questions/10056107/jquery-fullcalendar-event-timeslot-hover-method
Any update on this?
+1
+1, very surprised to see that this isn't possible
any update on this.
In timeline view, I want to show bootstrap popover for the slot which got clicked.
+1 This would be a great UX improvement
I have found the solution to do it like nkostadinov
Understand how the fullcalender renders the component
Each row has :
<div class="fc-row fc-week fc-widget-content fc-rigid" >
a) <div class="fc-bg">
: the back part of the calender
b) <div class="fc-content-skeleton">
: The front part of the calender
The soultion is to add an extra tr and append it to the index of the row and the index of the day. And everytime the user hovers another tr, just remove it and append it again to the new one.
How to do this :
1) add disabled class to past dates use dayRender method of fullcalendar
dayRender: function(date, cell){
if (moment(date).format('x') < moment().subtract(1,'days').format('x')){
$(cell).addClass('disabled');
}
},
2) We have to target multiple classes on mouseenter, When you hover on a day block, F12 tools will tell you what fc-day, day-block, fc-day-top classes mean. They are basically the sub elements inside the day.
$(document).on('mouseenter', '.fc-day:not(.disabled), .day-block, .fc-day-top:not(.fc-past), .not-this-one', function(e) {
//Add button on hovering of the day
var container = $(this).parents().closest('.fc-bg').siblings('.fc-content-skeleton').find('table>tbody');
if(container.html() == undefined){
//Since we are targeting multiple classes above on mousenter lets search the container in a different position
container = $(this).parents().closest('table').last().children('tbody');
}
//Clear out any add button
$('#add-btn').remove();
//Start building html content for add button
var content = '<tr id="add-btn">', td = '';
//Add not-this-one class for days which are not the selected date
for (var i = 0; i < $(this).index(); i++) {
td = td + '<td class="not-this-one"></td>';
}
var contentText = '<td class="fc-event-container"><a class="whatever" onclick="doWhatever()"> ADD BTN HERE</a></td>';
content = content + td + contentText;
//Add not-this-one class for other cells
if($(this).index() < 6 && $('#calendar').fullCalendar('getView').type !== 'basicDay'){
for (var i = $(this).index(); i < 6; i++) {
content = content + '<td class="not-this-one"></td>';
}
}
content = content + '</tr>';
container.append(content);
});
Conclusion
This is basically it. You should be able to get add button on hover. Just remember to target the basic elements on mouseenter. Find out what is where and then append the extra <tr>
with <td class="whatever">
for siblings for the date you don't want selected and <td class="fc-event-container">
For the date you want. You should be able to customize based on this guide.
Is there really not a way to render things in the fc-content-skeleton rather than fc-bg with dayRender so that hacky workarounds aren't needed? Seems so unnecessarily restricting.
@IRCraziestTaxi - If there was a simple way to do that, the software creator/maintainers would have done it already. So yes, unfortunately, sometimes with software things are more difficult than you'd expect. They might seem simple as a feature, but that does not equal simplicity from a code/engineering standpoint.
No one sat down one day and thought, "I'm going to engineer this in a way that is unnecessarily restricting if the user wants to have the cells highlight on hover."
This is an open source library. People aren't getting paid to maintain it, so keep that in mind.
@nicmar I adapted your solution to add a hover button.
var cellSize = {
width: $('.fc-day').width() + 2, // count border pixels
heigth: $('.fc-slats > table > tbody > tr').height() - 1
};
var tmpCellCss = [
'border: 0px',
'width:' + (cellSize.width) + 'px',
'height:' + (cellSize.heigth) + 'px',
].join(';');
var hoverCss= [
'width:' + (cellSize.width - 6*2) + 'px', // 3px padding left, 3px padding right
'height:' + (cellSize.heigth - 4) + 'px', // 2px padding top, 2px padding bottom
'line-height:' + (cellSize.heigth - 4) + 'px' // center text vertically
].join(';');
var hoverHtml = '<div class="hover-button" style="'+ hoverCss +'">+</div>';
$('.fc-widget-content').hover(function(){
if (!$(this).html()){
for(var i = 0; i < 7; i++){
$(this).append('<td class="temp-cell" style="'+tmpCellCss+'"></td>');
}
$(this).children('td').each(function(){
$(this).hover(function(){
$(this).html(hoverHtml);
},function(){
$(this).html('');
});
});
}
}, function(){
$(this).children('.temp-cell').remove();
});
don't forget the css
.hover-button{
background-color: rgba(20, 20, 20, 0.10);
border-radius: 3px;
color: #9B9B9B;
position: relative;
top: 2px;
left: 5px;
font-size: 36px;
cursor: pointer;
}
I hope it helps. PS: this only works on the week view and the day view
Previous solution doesn't work well on day view, here is complete solution for week and day view:
eventAfterAllRender: function () {
$('.fc-widget-content').unbind('mouseenter mouseleave');
$('.fc-widget-content').hover(function () {
if (!$(this).html()) {
var slots = $('.fc-day');
for (i = 0; i < slots.length; i++) {
$(this).append('<td class="temp-cell" style="border: 0px; width:' + (Number(slots.width()) + 3) + 'px"></td>');
}
$(this).children('td').each(function () {
$(this).hover(function () {
$(this).html('<div class="current-time" style="padding-left: 5px">' + $(this).parent().parent().data('time').substring(0, 5) + '</div>');
}, function () {
$(this).html('');
});
});
}
}, function () {
$(this).children('.temp-cell').remove();
});
}
And css:
.current-time {
background-color: rgba(20, 20, 20, 0.10);
border-radius: 3px;
color: #9B9B9B;
position: relative;
top: 2px;
cursor: pointer;
}
This is for showing selected time on calendar.
There are some valiant attempts on this thread, but I haven't found anything that works well enough to use. @Thomas-Webber's version is close but I find it fires inconsistently, and misses certain time slots altogether. @Aca-jov's version may be more consistent, but it's splitting each time slot into two separate hover states. The 'slots' count seems to be double the number of columns in view. I wonder if this feature will be any easier in v4.
Any updates on this feature?
Based on what @nicmar suggested, I updated his code to also work in day/resource view.
Until now it works flawlessly on my side and I can't identify any drawbacks.
$('.fc-widget-content').hover(function(){
var cellWidth = $('.fc-day').width();
var columnCount = $(".fc-widget-header.fc-row thead tr").children().length-1;
if(!$(this).html()){
for(i=0;i<columnCount;i++){
$(this).append('<td class="temp-cell" style="border:0; width:'+(cellWidth+1)+'px"></td>');
}
$(this).children('td').each(function(){
$(this).hover(function(){
$(this).html('<div class="current-time text-center">'+$(this).parent().parent().data('time').substring(0,5)+'</div>');
},function(){
$(this).html('');
});
});
}
},function(){
$(this).children('.temp-cell').remove();
});
For anybody else who comes here from a Google search, I was able to achieve this in the monthly view with the following code using FullCalendar v4.0. https://fullcalendar.io/docs/dayRender
dayRender: function (info) {
console.group('dayRender(info) called', info);
var element = info.el;
var currentDate = new moment(info.date).format('YYYY-MM-DD');
var hoverDivs =
'<div class="fc-day-hover-container">' +
' <div class="fc-day-hover-button"><i class="far fa-calendar-plus"></i></div>' +
' <div class="fc-day-hover-helper"></div>' +
'</div>';
// Change background of a date cell upon hover and add a centralized "+" icon
$('td').find('[data-date="' + currentDate + '"]').hover(function () {
console.warn('find [data-date="' + currentDate + '"]', currentDate, $(element));
$('td [data-date="' + currentDate + '"]').addClass('fc-day-hover'); // Monthly view
$(element).append(hoverDivs);
}, function() {
$('[data-date="' + currentDate + '"]').removeClass('fc-day-hover');
$('.fc-day-hover-container').remove();
});
console.groupEnd();
}
css
#fcYearly .fc-day-hover { /* triggered when hovering over calendar date cells */
background-color: rgba(0, 0, 0, .05);
}
#fcYearly .fc-day-hover .fc-day-hover-container {
background-color: rgba(20, 20, 20, 0.10);
width: 100%;
height: 100%;
border-radius: 1rem;
color: #9B9B9B;
font-size: 20pt;
cursor: pointer;
text-align: center;
}
#fcYearly .fc-day-hover .fc-day-hover-container .fc-day-hover-button {
display: inline-block;
text-align: center;
vertical-align: middle;
white-space: normal;
padding-left: 5px;
}
#fcYearly .fc-day-hover .fc-day-hover-container .fc-day-hover-helper {
display: inline-block;
vertical-align: middle;
height: 100%;
}
Is there a tentative idea of when this will be added?
var clickelement={};
clickelement="";
$(document).on('mouseenter mouseleave', '#calendar .fc-day-grid-event tr td:not(.fc-event-container)', function(e) {
//Add button on hovering of the day
var container = $(this);
if(container.html() == undefined){
//Since we are targeting multiple classes above on mousenter lets search the container in a different position
container = $(this).parents().closest('table').last().children('tbody');
}
//Clear out any add button
$('#add-btn.temp').remove();
var dayIndex=$(this).context.cellIndex;
//or use the one below for geting the dayindex
//var dayIndex= this.cellIndex;
var row = $(this).closest(".fc-row");
// Get this date
var vdate = row.find('.fc-day').eq(dayIndex).attr('data-date');
console.log(vdate);
//Start building html content for add button
var content = '<div id="add-btn" class="temp">', td = ''
var contentText = '<a class="addevents" data-date="'+ vdate+ '"> <i class="fa fa-plus"></i></a>';
content = content + td + contentText;
content = content + '</tr>';
container.append(content);
});
You can do something like this
This is for V4 and day time grid view:
$(document).on({
mouseenter: function() {
var cellWidth = $('th.fc-day-header').width();
var cellHeight=$(this).height();
var columnCount = $('thead.fc-head th.fc-day-header').children().length;
if(!$(this).html()){
for(i=0;i<columnCount;i++){
$(this).append('<td class="temp-cell" style="border:0px; height:'+(cellHeight-1)+'px;width:'+(cellWidth+1)+'px"></td>');
}}
$(this).children('td').each(function(){
$(this).hover(function(){
$(this).html('<div class="current-time"></div>');
// $(this).html('<div class="current-time">'+$(this).parent().parent().data('time').substring(0,5)+'</div>');
},function(){
$(this).html('');
});
});
},
mouseleave:function(){
$(this).children('.temp-cell').remove();
}
}, 'div.fc-slats td.fc-widget-content:not(.fc-axis)');
And this one is for V5 and day time grid view: ` $(document).on({
mouseenter: function() {
//var cellWidth = $('th.fc-day-header').width();
var cellWidth = $('th.fc-col-header-cell').width();
var cellHeight=$(this).height();
var columnCount = $('thead table.fc-col-header th.fc-col-header-cell').children().length;
if(!$(this).html()){
for(i=0;i<columnCount;i++){
$(this).append('<td class="temp-cell" style="border:0px; height:'+(cellHeight-1)+'px;width:'+(cellWidth+1)+'px"></td>');
}}
$(this).children('td').each(function(){
$(this).hover(function(){
$(this).html('<div class="current-time"></div>');
},function(){
$(this).html('');
});
});
},
mouseleave:function(){
$(this).children('.temp-cell').remove();
}
}, 'td.fc-timegrid-slot.fc-timegrid-slot-lane');`
Hover effect on single cell in fullcalender.io => not for all columns in single row
is there any solution to highlight just single slot. Event DOM structure also have a single
[Jeder Alle(https://www.jederalle.com/)
I had this same issue when I was using my V4.5 code above with V5. Make sure you pay attention to the versions in my posts.
I need something similar. I was hoping I could attach listeners to single empty cells, but I can understand that those empty cells are optimized away. Thanks for all the provided workarounds, I will try those.
In the meantime, here is an idea to make this easier: What if there was a function which takes a position on the screen and returns the corresponding calendar data, like date, time, and optionally resource? That way, we could use a global mousemove event listener, in which we use this function to determine where the mouse is to perform our logic there. To change the DOM of the corresponding cell, probably some kind of handle or callback would be needed as well.
Something like this:
document.mousemove(e => {
const { start, end, resource, domHandle } = calendar.getCellHandleAt(e.x, e.y);
...
});
Essentially, getCellHandleAt
would abstract away the logic presented by the various workarounds. This would reduce the work of library users to implement it themselves, and also to maintain it with newer versions.
Im also surprised to find this isn't available out of the box, we are after the time to be highlighted in a single cell/column on hover, it included in many web calendars. Cant see it still in v5?
+1
+1
+1
Need this feature as well. +1
+1
Here is my solution on v5
for timeGridWeek
and timeGridDay
$(document).on({
mouseenter: function() {
let cellWidth = $('th.fc-col-header-cell').width();
let cellHeight = $(this).height();
let columnCount = $('thead table.fc-col-header th.fc-col-header-cell').children().length;
if (!$(this).html()) {
for (var i = 0; i < columnCount; i++) {
$(this).append('<td class="temp-cell" style="border:0px; height:' + (cellHeight - 1) + 'px;width:' + (cellWidth + 3) + 'px"></td>');
}
}
$(this).children('td').each(function() {
$(this).hover(function() {
let dtime = $(this).parent().data('time').slice(0, -3);
$(this).html('<div class="current-time">' + dtime + '</div>');
}, function() {
$(this).html('');
});
});
},
mouseleave: function() {
$(this).children('.temp-cell').remove();
}
}, 'td.fc-timegrid-slot.fc-timegrid-slot-lane');
With the following style
.current-time {
background-color: rgba(20, 20, 20, 0.10);
color: #181818;
position: relative;
cursor: pointer;
padding-right: 5px;
text-align: right;
}
That end up like that :)
Here is my solution on
v5
fortimeGridWeek
andtimeGridDay
$(document).on({ mouseenter: function() { let cellWidth = $('th.fc-col-header-cell').width(); let cellHeight = $(this).height(); let columnCount = $('thead table.fc-col-header th.fc-col-header-cell').children().length; if (!$(this).html()) { for (var i = 0; i < columnCount; i++) { $(this).append('<td class="temp-cell" style="border:0px; height:' + (cellHeight - 1) + 'px;width:' + (cellWidth + 3) + 'px"></td>'); } } $(this).children('td').each(function() { $(this).hover(function() { let dtime = $(this).parent().data('time').slice(0, -3); $(this).html('<div class="current-time">' + dtime + '</div>'); }, function() { $(this).html(''); }); }); }, mouseleave: function() { $(this).children('.temp-cell').remove(); } }, 'td.fc-timegrid-slot.fc-timegrid-slot-lane');
With the following style
.current-time { background-color: rgba(20, 20, 20, 0.10); color: #181818; position: relative; cursor: pointer; padding-right: 5px; text-align: right; }
That end up like that :)
Did you have an idea, how this can work with resources?
@t-poeschl What do you mean by ressources?
I have a custom view with 5 ressources (resourceTimeGridDay
) and that work fine
Here is my solution on
v5
fortimeGridWeek
andtimeGridDay
$(document).on({ mouseenter: function() { let cellWidth = $('th.fc-col-header-cell').width(); let cellHeight = $(this).height(); let columnCount = $('thead table.fc-col-header th.fc-col-header-cell').children().length; if (!$(this).html()) { for (var i = 0; i < columnCount; i++) { $(this).append('<td class="temp-cell" style="border:0px; height:' + (cellHeight - 1) + 'px;width:' + (cellWidth + 3) + 'px"></td>'); } } $(this).children('td').each(function() { $(this).hover(function() { let dtime = $(this).parent().data('time').slice(0, -3); $(this).html('<div class="current-time">' + dtime + '</div>'); }, function() { $(this).html(''); }); }); }, mouseleave: function() { $(this).children('.temp-cell').remove(); } }, 'td.fc-timegrid-slot.fc-timegrid-slot-lane');
With the following style
.current-time { background-color: rgba(20, 20, 20, 0.10); color: #181818; position: relative; cursor: pointer; padding-right: 5px; text-align: right; }
That end up like that :)
For v5 I use this updated version which prevents adding events in the past, not very clean but it works allright.
$(document).on({
mouseenter: function() {
let cellWidth = $('th.fc-col-header-cell').width();
let cellHeight = $(this).height();
let columnCount = $('table.fc-col-header th.fc-col-header-cell').children().length;
if (!$(this).html()) {
for (var i = 0; i < columnCount; i++) {
$(this).append('<td class="temp-cell" style="border:0px; height:' + (cellHeight ) + 'px;width:' + (cellWidth + 3) + 'px"></td>');
}
}
$(this).children('td').each(function() {
$(this).hover(function() {
// pokud vubec nema datum ve sloupci, neukazuj
coldate = $('table.fc-col-header th.fc-col-header-cell').eq($(this).index()).data('date');
if (coldate) {
// hodina
let dtime = $(this).parent().data('time').slice(0, -3).split(":");
coldate_split = coldate.split("-");
d = new Date(coldate_split[0], coldate_split[1]-1, coldate_split[2], dtime[0], dtime[1]);
now = new Date();
// pokud je to novejsi
if (d > now) {
$(this).html('<div class="current-time">+ Vytvorit</div>');
}
}
}, function() {
$(this).html('');
});
});
},
mouseleave: function() {
$(this).children('.temp-cell').remove();
}
}, 'td.fc-timegrid-slot.fc-timegrid-slot-lane');
any update?
I was able to achieve this in our resource-timeline
grid using the resourceLaneDidMount
hook, so when a resource lane is added to the grid, we add additional cells that highlight on hover.
JSX:
<FullCalendar resourceLaneDidMount={({ el }) => {
const cellHoverElementsContainer = document.createElement('div');
cellHoverElementsContainer.className = 'cell-hover-elements-container';
for (let i = 0; i < 7; i++) {
cellHoverElementsContainer.appendChild(document.createElement('div'));
}
el.querySelector('.fc-timeline-lane-frame')?.appendChild(cellHoverElementsContainer);
}}/>
SCSS
.cell-hover-elements-container {
position: absolute;
z-index: 1;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
> div {
flex: 1;
margin: 1px;
&:hover {
background-color: $cell-hover-background;
}
}
}
@splintor Thanks man, Your solution works for me
Here I'm sharing my solution which plays with multiple views in fullcalendar. I used useEffect hook because with resourceLaneDidMount it just rendered first time for my solution, After changing views it doesn't worked for me.
useEffect(() => {
setTimeout(() => {
document.querySelectorAll('.fc-timeline-lane-frame')?.forEach((e) => {
if (e) {
const cellHoverElementsContainer = document.createElement('div');
cellHoverElementsContainer.className = 'cell-hover-elements-container';
const count = currentView === 'resourceTimelineWeek' ? 7 : 24;
for (let i = 0; i < count; i += 1) {
cellHoverElementsContainer.appendChild(document.createElement('div'));
}
e?.appendChild(cellHoverElementsContainer);
}
});
}, [1000]);
}, [//Your dependencies here]);
.cell-hover-elements-container {
position: absolute;
z-index: 1;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
> div {
flex: 1;
margin: 1px;
&:hover::after {
content: "+";
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
font-size: $title;
color: $text-secondary-color;
cursor: pointer;
background-color: $main-bg-color;
}
}
}
@kevit-manish-andodariya Thanks for the update. I have two comments:
[1000]
) as the second parameter of setTimeout
? I checked and it works, but it is strange. Why not just pass a number (1000
).useEffect
and setTimeout
, you can use MutationObserver and get notified whenever a node is created in your FullCalendar
.Why is there still no such feature to easily implement this, as it appears it's useful for all of us and everyone would like to display X/Y/Z on hover...? Should be allowed for any framework though @arshaw
I made custom hover events for display hover time
could you show the code?
Hey,
we use resourceTimeGridDay
view and we decided to use the following simple code to add background color on a slot hover. We also show content (time) on hover event. Resources are used as columns so resources.length
div elements are created; alternatively, const values (e.g. 7 days - week view, 24 hours - day view)) may be use instead - depending on use case. This POC code have been written in JSX/React and it should be updated according to your project-specific coding rules and patterns.
<FullCalendar
initialView="resourceTimeGridDay"
resources={resources}
slotLaneContent={(args) => (
<div style={{ display: 'flex', width: '100%' }}>
{resources.map((resource) => (
<div className="slot-cell" key={resource.id}>
{args.date?.toLocaleTimeString(undefined, { timeStyle: 'short' })}
</div>
))}
</div>
)}
/>
.slot-cell {
flex: 1;
text-align: center;
opacity: 0;
&:hover {
background: rgba(0, 255, 0, 0.2);
cursor: pointer;
opacity: 1;
}
}
Is it possible to highlight current cell in Timeline view while mouse is passing over them and display some sort of hint like a plus sign '+' to hint the user that clicking the cell will add an event.