Calendar scrolling

- prevent fast scrolling with a touchpad
- animation while changing scrolled dates
This commit is contained in:
Nathan Gray 2015-10-21 19:53:19 +00:00
parent 804d558e68
commit 7e5873eefa
6 changed files with 206 additions and 103 deletions

View File

@ -464,17 +464,22 @@ app.classes.calendar = AppJS.extend(
.on('wheel.calendar','.et2_container .calendar_calTimeGrid, .et2_container .calendar_plannerWidget',
function(e)
{
var at_bottom = true;
var at_top = true;
$j(this).children().each(function() {
// Ignore if they're going the other way
var direction = e.originalEvent.deltaY > 0 ? 1 : -1;
var at_bottom = direction !== -1;
var at_top = direction !== 1;
$j(this).children(":not(.calendar_calGridHeader)").each(function() {
at_bottom = at_bottom && this.scrollTop === (this.scrollHeight - this.offsetHeight)
}).each(function() {
at_top = at_top && this.scrollTop === 0;
});
if(!at_bottom && !at_top) return;
// Scrolling too fast?
if(app.calendar._scroll_disabled) return;
e.preventDefault();
var direction = e.originalEvent.deltaY > 0 ? 1 : -1;
var delta = 1;
var start = new Date(app.calendar.state.date);
var end = null;
@ -485,6 +490,80 @@ app.classes.calendar = AppJS.extend(
var template = etemplate2.getById(id);
if(!template) return;
// Prevent scrolling too fast
app.calendar._scroll_disabled = true;
// Animate the transition, if possible
var widget = null
template.widgetContainer.iterateOver(function(w) {
if (w.getDOMNode() == this) widget = w;
},this,et2_widget);
if(widget == null) return;
// We apply the reverse quickly, then let it animate as the changes are
// removed, leaving things where they should be.
var original = $j(this);
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("<div style='overflow:hidden; height:"+original.outerHeight()+"px'></div>");
wrapper.height(original.outerHeight() + cloned.outerHeight());
wrapper.append(original);
if(direction == -1)
{
// Scrolling up
original.parent().append(cloned);
wrapper.css({"transform": "translateY(" + (direction * 50) + "%)"});
// Makes it jump to destination
wrapper.css({
"transition-duration": "0s",
"transition-delay": "0s"
});
wrapper.css({
"transition-duration": "",
"transition-delay": ""
});
}
else
{
// Scrolling down
$j(this).parent().prepend(cloned);
$j('.calendar_calTimeGridScroll',cloned).scrollTop(10000);
}
// Remove
var remove = function() {
// Starting animation
wrapper.addClass("calendar_slide");
wrapper.css({"transform": direction == 1 ? "translateY(-50%)" : ""});
window.setTimeout(function() {
// Clean up from animation
cloned.remove();
// Moving this stuff around breaks scroll to day start in Chrome
var scrollTop = $j('.calendar_calTimeGridScroll',original).scrollTop();
var parent = wrapper.parent().parent();
wrapper.parent().remove();
original.appendTo(parent);
// Also detaches events
if(widget)
{
widget.attachToDOM();
}
// Re-scroll to start of day
$j('.calendar_calTimeGridScroll',original).scrollTop(scrollTop);
window.setTimeout(function() {
if(app.calendar)
{
app.calendar._scroll_disabled = false;
}
}, 500);
},1000);
}
// If detecting the transition end worked, we wouldn't need to use a timeout.
window.setTimeout(remove,100);
// Get the view to calculate
var view = app.classes.calendar.views[app.calendar.state.view] || false;
@ -498,6 +577,7 @@ app.classes.calendar = AppJS.extend(
// Home - always 1 week
// TODO
return false;
/*
var widget = [];
var value = [];
template.widgetContainer.iterateOver(function(w) {
@ -523,6 +603,7 @@ app.classes.calendar = AppJS.extend(
app.calendar._need_data([value[i]], state);
widget[i].set_value(value[i]);
}
*/
}
return false;
@ -1419,13 +1500,15 @@ app.classes.calendar = AppJS.extend(
}
// Hide other views
var view = app.classes.calendar.views[state.state.view];
for(var _view in app.classes.calendar.views)
{
if(state.state.view != _view && app.classes.calendar.views[_view])
{
for(var i = 0; i < app.classes.calendar.views[_view].etemplates.length; i++)
{
if(typeof app.classes.calendar.views[_view].etemplates[i] !== 'string')
if(typeof app.classes.calendar.views[_view].etemplates[i] !== 'string' &&
view.etemplates.indexOf(app.classes.calendar.views[_view].etemplates[i]) == -1)
{
$j(app.classes.calendar.views[_view].etemplates[i].DOMContainer).hide();
}
@ -1469,8 +1552,6 @@ app.classes.calendar = AppJS.extend(
// cause infinite loops.
this.state_update_in_progress = true;
var view = app.classes.calendar.views[state.state.view];
// Sanitize owner so it's always an array
if(state.state.owner === null)
{
@ -1632,11 +1713,12 @@ app.classes.calendar = AppJS.extend(
$j(view.etemplates[i].DOMContainer).show();
}
// Toggle todos
if(state.state.view == 'day')
if(state.state.view == 'day' || this.state.view == 'day')
{
if(state.state.owner.length === 1 && !isNaN(state.state.owner) && state.state.owner[0] > 0)
if(state.state.view == 'day' && state.state.owner.length === 1 && !isNaN(state.state.owner) && state.state.owner[0] > 0)
{
view.etemplates[0].widgetContainer.set_width("70%");
view.etemplates[0].DOMContainer.style.width = "70%";
$j(view.etemplates[1].DOMContainer).css("left","69%");
// TODO: Maybe some caching here
this.egw.jsonq('calendar_uiviews::ajax_get_todos', [state.state.date, state.state.owner[0]], function(data) {
this.getWidgetById('label').set_value(data.label||'');
@ -1645,13 +1727,17 @@ app.classes.calendar = AppJS.extend(
}
else
{
$j(view.etemplates[1].DOMContainer).hide();
view.etemplates[0].widgetContainer.set_width("");
$j(app.classes.calendar.views.day.etemplates[1].DOMContainer).show();
$j(app.classes.calendar.views.day.etemplates[1].DOMContainer).css("left","100%");
window.setTimeout(jQuery.proxy(function() {
$j(this).hide();
},app.classes.calendar.views.day.etemplates[1].DOMContainer),1000);
$j(app.classes.calendar.views.day.etemplates[0].DOMContainer).css("width","100%");
}
}
else
{
view.etemplates[0].widgetContainer.set_width("");
$j(view.etemplates[0].DOMContainer).css("width","100%");
}
this.state = jQuery.extend({},state.state);

View File

@ -64,12 +64,15 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea
// Main container
this.div = $j(document.createElement("div"))
.addClass("calendar_calDayCol")
.css('width',this.options.width);
.css('width',this.options.width)
.css('left', this.options.left);
this.header = $j(document.createElement('div'))
.addClass("calendar_calDayColHeader");
.addClass("calendar_calDayColHeader")
.css('width',this.options.width)
.css('left', this.options.left);
this.title = $j(document.createElement('div'))
.appendTo(this.header);
this.setDOMNode(this.div[0]);
// Used for its date calculations - note this is a datetime, parent
@ -132,7 +135,24 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea
this.display_settings.wd_start = 60*this._parent.options.day_start;
this.display_settings.wd_end = 60*this._parent.options.day_end;
this.display_settings.granularity = this._parent.options.granularity;
this._parent.dayHeader.append(this.header);
var header = this._parent.dayHeader.children();
// Figure out insert index
var idx = 0;
var siblings = this._parent.getDOMNode(this).childNodes
while(idx < siblings.length && siblings[idx] != this.getDOMNode())
{
idx++;
}
// Stick header in the right place
if(idx == 0)
{
this._parent.dayHeader.prepend(this.header);
}
else if(header.length)
{
header.eq(Math.min(header.length,idx)-1).after(this.header);
}
}
this.display_settings.rowsToDisplay = ((this.display_settings.wd_end - this.display_settings.wd_start)/this.display_settings.granularity);
@ -295,16 +315,25 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea
},
set_left: function(left) {
this.div.css('left',left);
// Maybe?
window.setTimeout(jQuery.proxy(function() {
this.header.css('left',left);
if(this.div)
{
this.div.css('left',left);
this.header.css('left',left);
}
},this),1);
},
set_width: function(width) {
this._super.apply(this, arguments);
this.header.width(width);
this.options.width = width;
window.setTimeout(jQuery.proxy(function() {
if(this.div)
{
this.div.width(this.options.width);
this.header.width(this.options.width);
}
},this),1);
},
/**
@ -528,8 +557,7 @@ var et2_calendar_daycol = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResizea
if(columns[c][i].options.value.whole_day_on_top)
{
columns[c][i].div
.appendTo(this.header)
.css('position', 'relative');
.appendTo(this.header);
continue;
}
else

View File

@ -578,35 +578,61 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
this.day_list = this._calculate_day_list(this.options.start_date, this.options.end_date, this.options.show_weekend);
}
var day_width = (100/this.day_list.length).toFixed(2);
// Create any needed widgets - otherwise, we'll just recycle
// Add any needed day widgets (now showing more days)
var add_index = 0;
var before = true;
while(this.day_list.length > this.day_widgets.length)
{
var existing_index = this.day_widgets[add_index] ? this.day_list.indexOf(this.day_widgets[add_index].options.date) : -1;
before = existing_index > add_index;
var day = et2_createWidget('calendar-daycol',{
owner: this.options.owner
owner: this.options.owner,
width: (before ? 0 : day_width) + "%"
},this);
if(this.isInTree())
{
day.doLoadingFinished();
}
this.day_widgets.push(day);
if(existing_index != -1 && parseInt(this.day_list[add_index]) < parseInt(this.day_list[existing_index]))
{
this.day_widgets.unshift(day);
$j(this.getDOMNode(day)).prepend(day.getDOMNode(day));
}
else
{
this.day_widgets.push(day);
}
add_index++;
}
// Remove any extra day widgets (now showing less)
var delete_index = this.day_widgets.length - 1;
before = false;
while(this.day_widgets.length > this.day_list.length)
{
// If we're going down to an existing one, just keep it for cool CSS animation
while(this.day_list.indexOf(this.day_widgets[delete_index].options.date) > -1)
{
delete_index--;
before = true;
}
// Wait until any animations or other timeouts are done
window.setTimeout(jQuery.proxy(function() {
this.free();
},this.day_widgets[delete_index]),1000);
// Widgets that are before our date shrink, after just get pushed out
if(before)
{
this.day_widgets[delete_index].set_width('0px');
}
this.day_widgets[delete_index].set_width('0px');
this.day_widgets[delete_index].free();
this.day_widgets.splice(delete_index--,1);
}
// Create / update day widgets with dates and data
var day_width = (100/this.day_list.length).toFixed(2);
for(var i = 0; i < this.day_list.length; i++)
{
day = this.day_widgets[i];
@ -619,7 +645,7 @@ var et2_calendar_timegrid = et2_valueWidget.extend([et2_IDetachedDOM, et2_IResiz
day.set_id(this.day_list[i]);
day.set_width(day_width + '%');
}
// Don't hold on to value any longer, use the data cache for best info
this.value = {};

View File

@ -36,8 +36,16 @@
}
#calendar-todo {
float: right;
position: absolute;
width: 30%;
/* Nice transition when changing days in a week */
transition: 1s ease-in-out;
}
#calendar-view {
width: 100%;
transition: width 1s ease-in-out;
}
/* Header classes */
tr.dialogHeader td, tr.dialogHeader2 td, tr.dialogHeader3 td, tr.dialogHeader4 td,
@ -189,6 +197,7 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
height: 100%;
left: 45px;
right: 0px;
white-space: nowrap;
}
/* 12h timeformat with am/pm
*/
@ -235,12 +244,21 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
line-height: 16px;
}
.calendar_calDayColHeader {
vertical-align: top;
height: 100%;
/* Nice transition when changing days in a week */
transition: width 1s ease-in-out;
z-index:30;
}
.calendar_calDayColHeader > div[data-date] {
height: 16px;
}
.calendar_calGridHeader > div {
position: absolute;
left: 45px;
right: 15px;
width: initial;
white-space: nowrap;
}
.calendar_calDayColHeader img {
vertical-align: middle;
@ -250,15 +268,6 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
left: 0px;
width: 100%;
right: 0px;
/* does not work in IE, but looks better in other browsers then width:100% */
text-align: center;
font-size: 100%;
white-space: nowrap;
border-bottom: 1px solid silver;
border-right: 1px solid silver;
height: 16px;
line-height: 12px;
z-index: 30;
}
.calendar_calWeekNavHeader,.calendar_calMonthNavHeader img {
vertical-align: middle;
@ -397,6 +406,13 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
border-width: 1px;
}
/**
* Events in the header (all day)
*/
.calendar_calDayColHeader .calendar_calEvent {
position: static;
display: inline-block;
}
.calendar_calEventTooltip{
border-radius: 6px;
-moz-border-radius: 6px;
@ -792,6 +808,14 @@ img.calendar_print_button, img.calendar_print_appicon {
display: inline-block;
}
/**
* View animation stuff
*/
.calendar_slide {
transition-duration: 1s;
transition-delay: 100ms;
}
/**
* Home page portlets
*/
@ -814,4 +838,4 @@ img.calendar_print_button, img.calendar_print_appicon {
border-top: 0;
margin: 0;
padding-right: 3px;
}
}

View File

@ -581,9 +581,6 @@ div.calendar {
#calendar-edit #calendar-edit_calendar-edit-links span.et2_label {
padding: 1em;
}
#calendar-edit #calendar-edit_calendar-edit-links div.delete {
background-image: url("https:://192.168.1.100/egroupware2/pixelegg/images/delete.png");
}
#calendar-edit #calendar-edit_calendar-edit-alarms tbody {
display: table;
width: 100%;
@ -721,19 +718,8 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
*/
.calendar_calDayColHeader,
.calendar_calGridHeader {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
right: 0px;
/* does not work in IE, but looks better in other browsers then width:100% */
text-align: center;
font-size: 100%;
white-space: nowrap;
border-bottom: 1px solid silver;
border-right: 1px solid silver;
height: 16px;
line-height: 12px;
}
.calendar_calDayColHeader a img,
.calendar_calGridHeader a img {
@ -802,27 +788,10 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
text-align: left;
padding-left: 3px;
}
/* contains (multiple) events's
*/
.calendar_calEventCol {
position: absolute;
top: 0px;
/* bottom: 0px; does NOT work in IE, IE needs height: 100%! */
height: 100%;
/* set via inline style on runtime:
* left:
* width:
*/
}
/* contains one event: header-row & -body
*/
.calendar_calEvent,
.calendar_calEventPrivate {
position: absolute;
left: 0px;
right: 0px;
overflow: hidden;
z-index: 20;
border-width: 1px;
border-radius: 6px;
-moz-border-radius: 6px;

View File

@ -661,8 +661,6 @@ div.calendar { position: relative; }
}
span.et2_label {padding: 1em;}
div.delete {background-image: url("https:://192.168.1.100/egroupware2/pixelegg/images/delete.png");}
}
/*###########################################*/
@ -767,7 +765,7 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
}
.calendar_calWeek{
// background: #ffffcc;
background-color : @gray_0;
background-color : @gray_0;
width:auto;
margin:0 auto;
text-align:center;
@ -834,18 +832,8 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
/* header for the dayCol
*/
.calendar_calDayColHeader,.calendar_calGridHeader{
position: absolute;
top: 0px;
left: 0px;
width: 100%;
right: 0px; /* does not work in IE, but looks better in other browsers then width:100% */
text-align: center;
font-size: 100%;
white-space: nowrap;
border-bottom: 1px solid silver;
border-right: 1px solid silver;
.dimension_height_s;
line-height: 12px;
a {
img {
@ -927,27 +915,9 @@ e.g. the div with class calendar_calTimeGrid is generated by the timeGridWidget
padding-left: 3px;
}
/* contains (multiple) events's
*/
.calendar_calEventCol{
position: absolute;
top: 0px;
/* bottom: 0px; does NOT work in IE, IE needs height: 100%! */
height: 100%;
/* set via inline style on runtime:
* left:
* width:
*/
}
/* contains one event: header-row & -body
*/
.calendar_calEvent,.calendar_calEventPrivate{
position: absolute;
left: 0px;
right: 0px;
overflow: hidden;
z-index: 20;
border-width: 1px;
border-radius: 6px;
-moz-border-radius: 6px;