diff --git a/calendar/js/app.js b/calendar/js/app.js index 4a3c08cb00..b2aa0a4c97 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -509,25 +509,28 @@ app.classes.calendar = AppJS.extend( } // We clone the nodes so we can animate the transition - var original = $j(widget.getDOMNode()); + var original = $j(widget.getDOMNode()).closest('.et2_grid'); var cloned = original.clone(true).attr("id","CLONE"); - var wrapper = $j(document.createElement("div")); - original.parent().append(wrapper); - // This is to hide the scrollbar - wrapper.wrap("
"); - wrapper.height(direction == "up" || direction == "down" ? 2 * original.outerHeight() : original.outerHeight()); - wrapper.width(direction == "left" || direction == "right" ? 2 * original.outerWidth() : original.outerWidth()); - - // Moving this stuff around breaks scroll to day start in Chrome + // Moving this stuff around scrolls things around too + // We need this later var scrollTop = $j('.calendar_calTimeGridScroll',original).scrollTop(); + // This is to hide the scrollbar + var wrapper = original.parent(); if(direction == "right" || direction == "left") { original.css({"display":"inline-block","width":original.width()+"px"}); cloned.css({"display":"inline-block","width":original.width()+"px"}); } - wrapper.append(original); + else + { + original.css("height",original.height() + "px"); + cloned.css("height",original.height() + "px"); + } + wrapper.parent().css({overflow:'hidden', height:original.outerHeight()+"px", width:original.outerWidth() + "px"}); + wrapper.height(direction == "up" || direction == "down" ? 2 * original.outerHeight() : original.outerHeight()); + wrapper.width(direction == "left" || direction == "right" ? 2 * original.outerWidth() : original.outerWidth()); // Re-scroll to previous to avoid "jumping" $j('.calendar_calTimeGridScroll',original).scrollTop(scrollTop); @@ -538,13 +541,17 @@ app.classes.calendar = AppJS.extend( // Scrolling up // Apply the reverse quickly, then let it animate as the changes are // removed, leaving things where they should be. + original.parent().append(cloned); - wrapper.css({"transform": direction == "up" ? "translateY(-50%)" : "translateX(-50%)"}); // Makes it jump to destination wrapper.css({ "transition-duration": "0s", - "transition-delay": "0s" + "transition-delay": "0s", + "transform": direction == "up" ? "translateY(-50%)" : "translateX(-50%)" }); + // Stop browser from caching style by forcing reflow + wrapper[0].offsetHeight; + wrapper.css({ "transition-duration": "", "transition-delay": "" @@ -567,21 +574,30 @@ app.classes.calendar = AppJS.extend( wrapper.css({"transform": translate}); window.setTimeout(function() { var scrollTop = $j('.calendar_calTimeGridScroll',original).scrollTop(); - // Clean up from animation + cloned.remove(); - var parent = wrapper.parent().parent(); - wrapper.parent().remove(); - original.appendTo(parent); + + // Makes it jump to destination + wrapper.css({ + "transition-duration": "0s", + "transition-delay": "0s" + }); + + // Clean up from animation + wrapper + .removeClass("calendar_slide") + .css({"transform": '',height: '', width:'',overflow:''}); + wrapper.parent().css({overflow: '', width: '', height: ''}); original.css("display",""); - // Re-attach events, if widget is still there - if(widget && widget.getDOMNode(widget)) - { - widget.attachToDOM(); - } + wrapper[0].offsetHeight; + wrapper.css({ + "transition-duration": "", + "transition-delay": "" + }); + // Re-scroll to start of day $j('.calendar_calTimeGridScroll',original).scrollTop(scrollTop); - window.setTimeout(function() { if(app.calendar) { diff --git a/calendar/js/et2_widget_daycol.js b/calendar/js/et2_widget_daycol.js index 9037102402..d54dfaedbb 100644 --- a/calendar/js/et2_widget_daycol.js +++ b/calendar/js/et2_widget_daycol.js @@ -441,6 +441,90 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea { this._children[c].set_value(events[c]); } + + // Apply styles to hidden events + this._out_of_view(); + }, + + /** + * Apply styles for out-of-view and partially hidden events + */ + _out_of_view: function() + { + // Reset + this.header.children('.hiddenEventBefore').remove(); + this.div.children('.hiddenEventAfter').remove(); + + var timegrid = this._parent; + + // elem is jquery div of event + function isHidden(elem) { + var docViewTop = timegrid.scrolling.scrollTop(), + docViewBottom = docViewTop + timegrid.scrolling.height(), + elemTop = elem.position().top, + elemBottom = elemTop + elem.outerHeight(); + if((elemBottom <= docViewBottom) && (elemTop >= docViewTop)) + { + // Entirely visible + return false; + } + var visible = { + hidden: elemTop > docViewTop ? 'bottom' : 'top', + completely: false + }; + visible.completely = visible.hidden == 'top' ? elemBottom < docViewTop : elemTop > docViewBottom; + return visible; + } + + // Check each event + this.iterateOver(function(event) { + // Skip whole day events and events missing value + if(!event.options || !event.options.value || event.options.value.whole_day) return; + + // Reset + event.title.css('top',''); + event.body.css('padding-top',''); + + var hidden = isHidden.call(this,event.div); + if(!hidden) + { + return; + } + // Only top is hidden, move label + // Bottom hidden is fine + if(hidden.hidden === 'top' && !hidden.completely) + { + event.title.css('top',timegrid.scrolling.scrollTop() - event.div.position().top); + event.body.css('padding-top',timegrid.scrolling.scrollTop() - event.div.position().top); + } + // Completely out of view, show indicator + else if (hidden.completely) + { + var indicator = ''; + if(hidden.hidden === 'top' && $j('.hiddenEventBefore',this.header).length == 0) + { + indicator = $j('
') + .appendTo(this.header); + } + else if(hidden.hidden === 'bottom') + + { + indicator = $j('.hiddenEventAfter',this.div); + if(indicator.length == 0) + { + indicator = $j('
'); + this.div.append(indicator); + } + indicator.css('top',timegrid.scrolling.height() + timegrid.scrolling.scrollTop()-indicator.height()); + } + // Match color to the event + if(indicator != '') + { + // Use border-top-color, Firefox doesn't give a value with border-color + indicator.css('border-color', event.div.css('border-top-color')); + } + } + }, this, et2_calendar_event); }, /** diff --git a/calendar/js/et2_widget_timegrid.js b/calendar/js/et2_widget_timegrid.js index 96662821e2..7e9982d136 100644 --- a/calendar/js/et2_widget_timegrid.js +++ b/calendar/js/et2_widget_timegrid.js @@ -532,7 +532,8 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz this.scrolling .css('height', (this.options.height - header_height)+'px') .appendTo(this.div) - .empty(); + .empty() + .off().on('scroll', jQuery.proxy(this._scroll, this)); // Percent var rowHeight = (100/rowsToDisplay).toFixed(1); @@ -666,6 +667,9 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz // Scroll to start of day this.scrolling.scrollTop(this._top_time); + + // Handle not fully visible elements + this._scroll(); // TODO: Figure out how to do this with detached nodes /* @@ -681,6 +685,21 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz */ }, + /** + * Update UI while scrolling within the selected time + * + * Toggles out of view indicators and adjusts not visible headers + * @param {Event} event Scroll event + */ + _scroll: function(event) + { + // Loop through days, let them deal with it + for(var day = 0; day < this.day_widgets.length; day++) + { + this.day_widgets[day]._out_of_view(); + } + }, + /** * Calculate a list of days between start and end date, skipping weekends if * desired. diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index 33e7f1d89d..5143ed1744 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -246,6 +246,28 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget text-align: center; border-bottom: 1px solid silver; } + +/* Indicators for offscreen events */ +.calendar_calDayColHeader .hiddenEventBefore, .calendar_calDayCol .hiddenEventAfter { + width: 80%; + height: 5px; + left: 10%; + position: absolute; + border-radius: 5px; + border: 5px solid; + border-left: none; + border-right: none; + z-index: 40; +} +.calendar_calDayColHeader .hiddenEventBefore { + bottom: -7px; + border-top: none; +} +.calendar_calDayCol .hiddenEventAfter { + border-bottom: none; +} + + .calendar_calDayColAllDay { display: flex; flex-wrap: wrap; @@ -318,6 +340,7 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget padding-left: 3px; z-index: 29; border-bottom: 1px solid silver; + min-height: 45px; } #calendar-view_view tbody.ui-sortable { cursor: default; diff --git a/calendar/templates/default/view.xet b/calendar/templates/default/view.xet index 799539057c..a61ed6f95f 100644 --- a/calendar/templates/default/view.xet +++ b/calendar/templates/default/view.xet @@ -13,7 +13,7 @@ Egroupware