mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 08:22:07 +02:00
MDL-47053 gradereport_grader: Add a floating Footer Title cell
This issue is a part of the MDL-46658 Task. This issue is a part of the MDL-25544 Epic.
This commit is contained in:
parent
8a9335075e
commit
bc782f1f4c
@ -30,6 +30,7 @@ YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
|
||||
*/
|
||||
|
||||
var SELECTORS = {
|
||||
FOOTERTITLE: '#user-grades .avg .header',
|
||||
FOOTERCELLS: '#user-grades .avg .cell',
|
||||
FOOTERROW: '#user-grades .avg',
|
||||
GRADECELL: 'td.grade',
|
||||
@ -39,7 +40,8 @@ var SELECTORS = {
|
||||
HEADERCELL: '.gradebook-header-cell',
|
||||
HEADERROW: '#user-grades tr.heading',
|
||||
STUDENTHEADER: '#studentheader',
|
||||
USERCELL: '#user-grades .user.cell'
|
||||
USERCELL: '#user-grades .user.cell',
|
||||
USERMAIL: '#user-grades .useremail'
|
||||
},
|
||||
CSS = {
|
||||
OVERRIDDEN: 'overridden',
|
||||
@ -340,6 +342,27 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
firstUserCell: null,
|
||||
|
||||
/**
|
||||
* A Node representing the first cell which does not contain a user header.
|
||||
*
|
||||
* @property firstNonUserCell
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCell: null,
|
||||
|
||||
/**
|
||||
* The position of the left of the first non-header cell in a row - the one after the email address.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
* additional rows are loaded, or the window changes in some fashion.
|
||||
*
|
||||
* @property firstNonUserCellLeft
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCellLeft: 0,
|
||||
|
||||
|
||||
/**
|
||||
* A Node representing the original table footer row.
|
||||
*
|
||||
@ -387,6 +410,15 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
userColumn: null,
|
||||
|
||||
/**
|
||||
* A Node representing the floating footer title header. This is the header with the "Overage Average" title.
|
||||
*
|
||||
* @property floatingFooterTitleRow
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
floatingFooterTitleRow: null,
|
||||
|
||||
/**
|
||||
* The position of the bottom of the first user cell.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
@ -439,6 +471,7 @@ FloatingHeaders.prototype = {
|
||||
// Grab references to commonly used Nodes.
|
||||
this.firstUserCell = Y.one(SELECTORS.USERCELL);
|
||||
this.container = Y.one(SELECTORS.GRADEPARENT);
|
||||
this.firstNonUserCell = Y.one(SELECTORS.USERMAIL).next();
|
||||
|
||||
if (!this.firstUserCell) {
|
||||
// No need for floating elements, there are no users.
|
||||
@ -450,6 +483,7 @@ FloatingHeaders.prototype = {
|
||||
this._setupFloatingUserHeader();
|
||||
this._setupFloatingAssignmentHeaders();
|
||||
this._setupFloatingAssignmentFooter();
|
||||
this._setupFloatingAssignmentFooterTitle();
|
||||
|
||||
// Calculate the positions of edge cells. These are used for positioning of the floating headers.
|
||||
// This must be called after the floating headers are setup, but before the scroll event handler is invoked.
|
||||
@ -485,6 +519,9 @@ FloatingHeaders.prototype = {
|
||||
// The left of the user cells matches the left of the headerRow.
|
||||
this.firstUserCellLeft = this.firstUserCell.getX();
|
||||
|
||||
// The left of the user cells matches the left of the footer title.
|
||||
this.firstNonUserCellLeft = this.firstNonUserCell.getX();
|
||||
|
||||
if (userCellList.size() > 1) {
|
||||
// Use the top of the second cell for the bottom of the first cell.
|
||||
// This is used when scrolling to fix the footer to the top edge of the window.
|
||||
@ -666,7 +703,7 @@ FloatingHeaders.prototype = {
|
||||
var floatingUserHeaderRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater heading"></div>'),
|
||||
floatingUserHeaderCell = Y.Node.create('<div></div>'),
|
||||
nodepos = this._getRelativeXY(this.headerCell)[0],
|
||||
coordinates = this._getRelativeXY(this.headerRow);
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
gradeHeadersOffset = coordinates[0];
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
@ -811,6 +848,50 @@ FloatingHeaders.prototype = {
|
||||
this.footerRow = floatingGraderFooter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and setup the floating footer title cell.
|
||||
*
|
||||
* @method _setupFloatingAssignmentFooterTitle
|
||||
* @protected
|
||||
*/
|
||||
_setupFloatingAssignmentFooterTitle: function() {
|
||||
// We make various references to the header cells. Store it for later.
|
||||
this.footerCell = Y.one(SELECTORS.FOOTERTITLE);
|
||||
|
||||
// Create the floating row and cell.
|
||||
var floatingFooterRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater avg"></div>'),
|
||||
floatingFooterCell = Y.Node.create('<div></div>'),
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
width = this.firstUserCell.getComputedStyle(WIDTH),
|
||||
height = this.footerCell.get(OFFSETHEIGHT);
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
floatingFooterCell
|
||||
.set('innerHTML', this.footerCell.getHTML())
|
||||
.setAttribute('class', this.footerCell.getAttribute('class'))
|
||||
.setStyles({
|
||||
// The header is larger than the user cells, so we take the user cell.
|
||||
width: width
|
||||
});
|
||||
|
||||
// Style the floating row.
|
||||
floatingFooterRow
|
||||
.setStyles({
|
||||
position: 'absolute',
|
||||
left: coordinates[0] + 'px',
|
||||
bottom: '1px',
|
||||
height: height + 'px',
|
||||
width: width
|
||||
});
|
||||
|
||||
// Append the cell to the row, and finally to the region.
|
||||
floatingFooterRow.append(floatingFooterCell);
|
||||
this.graderRegion.append(floatingFooterRow);
|
||||
|
||||
// Store a reference to this for later - we use it in the event handlers.
|
||||
this.floatingFooterTitleRow = floatingFooterRow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process a Scroll Event on the window.
|
||||
*
|
||||
@ -827,13 +908,15 @@ FloatingHeaders.prototype = {
|
||||
userColumnHeaderStyles = {},
|
||||
userColumnStyles = {},
|
||||
footerStyles = {},
|
||||
footerTitleStyles = {},
|
||||
coord = 0,
|
||||
userCellWidth = 0,
|
||||
floatingUserTriggerPoint = 0, // The X position at which the floating should start.
|
||||
floatingUserRelativePoint = 0, // The point to use when calculating the new position.
|
||||
headerFloats = false,
|
||||
userFloats = false,
|
||||
footerFloats = false;
|
||||
footerFloats = false,
|
||||
footerTitleFloats = false;
|
||||
|
||||
// Header position.
|
||||
gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
|
||||
@ -856,20 +939,24 @@ FloatingHeaders.prototype = {
|
||||
}
|
||||
|
||||
// User column position.
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
if (right_to_left()) {
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint - userCellWidth;
|
||||
userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + userCellWidth);
|
||||
footerTitleFloats = floatingUserTriggerPoint < (this.firstNonUserCellLeft + userCellWidth);
|
||||
} else {
|
||||
floatingUserTriggerPoint = Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint;
|
||||
userFloats = floatingUserTriggerPoint > this.firstUserCellLeft;
|
||||
footerTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - userCellWidth);
|
||||
}
|
||||
|
||||
if (userFloats) {
|
||||
coord = this._getRelativeXFromX(floatingUserRelativePoint);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
userColumnHeaderStyles.left = coord + 'px';
|
||||
footerTitleStyles.left = userColumnStyles.left;
|
||||
} else {
|
||||
coord = this._getRelativeXFromX(this.firstUserCellLeft);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
@ -889,12 +976,15 @@ FloatingHeaders.prototype = {
|
||||
|
||||
if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
|
||||
// We have not scrolled below the footer, nor above the first row.
|
||||
footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerStyles.bottom = footerTitleStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerFloats = true;
|
||||
} else {
|
||||
// The footer should not float any more.
|
||||
footerStyles.bottom = 0;
|
||||
footerStyles.bottom = '1px';
|
||||
footerFloats = false;
|
||||
|
||||
// The footer title may float if the page scrolls right.
|
||||
footerTitleStyles.bottom = '1px';
|
||||
}
|
||||
}
|
||||
|
||||
@ -903,6 +993,7 @@ FloatingHeaders.prototype = {
|
||||
this.userColumnHeader.setStyles(userColumnHeaderStyles);
|
||||
this.userColumn.setStyles(userColumnStyles);
|
||||
this.footerRow.setStyles(footerStyles);
|
||||
this.floatingFooterTitleRow.setStyles(footerTitleStyles);
|
||||
|
||||
// Mark the elements as floating, or not.
|
||||
if (headerFloats) {
|
||||
@ -924,6 +1015,12 @@ FloatingHeaders.prototype = {
|
||||
} else {
|
||||
this.footerRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
|
||||
if (this.footerRow && footerTitleFloats) {
|
||||
this.floatingFooterTitleRow.addClass(CSS.FLOATING);
|
||||
} else {
|
||||
this.floatingFooterTitleRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -981,6 +1078,9 @@ FloatingHeaders.prototype = {
|
||||
};
|
||||
footercell.setStyles(styles);
|
||||
});
|
||||
|
||||
// Resize the title area too.
|
||||
this.floatingFooterTitleRow.one('div').setStyle('width', userWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -30,6 +30,7 @@ YUI.add('moodle-gradereport_grader-gradereporttable', function (Y, NAME) {
|
||||
*/
|
||||
|
||||
var SELECTORS = {
|
||||
FOOTERTITLE: '#user-grades .avg .header',
|
||||
FOOTERCELLS: '#user-grades .avg .cell',
|
||||
FOOTERROW: '#user-grades .avg',
|
||||
GRADECELL: 'td.grade',
|
||||
@ -39,7 +40,8 @@ var SELECTORS = {
|
||||
HEADERCELL: '.gradebook-header-cell',
|
||||
HEADERROW: '#user-grades tr.heading',
|
||||
STUDENTHEADER: '#studentheader',
|
||||
USERCELL: '#user-grades .user.cell'
|
||||
USERCELL: '#user-grades .user.cell',
|
||||
USERMAIL: '#user-grades .useremail'
|
||||
},
|
||||
CSS = {
|
||||
OVERRIDDEN: 'overridden',
|
||||
@ -340,6 +342,27 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
firstUserCell: null,
|
||||
|
||||
/**
|
||||
* A Node representing the first cell which does not contain a user header.
|
||||
*
|
||||
* @property firstNonUserCell
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCell: null,
|
||||
|
||||
/**
|
||||
* The position of the left of the first non-header cell in a row - the one after the email address.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
* additional rows are loaded, or the window changes in some fashion.
|
||||
*
|
||||
* @property firstNonUserCellLeft
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCellLeft: 0,
|
||||
|
||||
|
||||
/**
|
||||
* A Node representing the original table footer row.
|
||||
*
|
||||
@ -387,6 +410,15 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
userColumn: null,
|
||||
|
||||
/**
|
||||
* A Node representing the floating footer title header. This is the header with the "Overage Average" title.
|
||||
*
|
||||
* @property floatingFooterTitleRow
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
floatingFooterTitleRow: null,
|
||||
|
||||
/**
|
||||
* The position of the bottom of the first user cell.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
@ -439,6 +471,7 @@ FloatingHeaders.prototype = {
|
||||
// Grab references to commonly used Nodes.
|
||||
this.firstUserCell = Y.one(SELECTORS.USERCELL);
|
||||
this.container = Y.one(SELECTORS.GRADEPARENT);
|
||||
this.firstNonUserCell = Y.one(SELECTORS.USERMAIL).next();
|
||||
|
||||
if (!this.firstUserCell) {
|
||||
// No need for floating elements, there are no users.
|
||||
@ -450,6 +483,7 @@ FloatingHeaders.prototype = {
|
||||
this._setupFloatingUserHeader();
|
||||
this._setupFloatingAssignmentHeaders();
|
||||
this._setupFloatingAssignmentFooter();
|
||||
this._setupFloatingAssignmentFooterTitle();
|
||||
|
||||
// Calculate the positions of edge cells. These are used for positioning of the floating headers.
|
||||
// This must be called after the floating headers are setup, but before the scroll event handler is invoked.
|
||||
@ -485,6 +519,9 @@ FloatingHeaders.prototype = {
|
||||
// The left of the user cells matches the left of the headerRow.
|
||||
this.firstUserCellLeft = this.firstUserCell.getX();
|
||||
|
||||
// The left of the user cells matches the left of the footer title.
|
||||
this.firstNonUserCellLeft = this.firstNonUserCell.getX();
|
||||
|
||||
if (userCellList.size() > 1) {
|
||||
// Use the top of the second cell for the bottom of the first cell.
|
||||
// This is used when scrolling to fix the footer to the top edge of the window.
|
||||
@ -666,7 +703,7 @@ FloatingHeaders.prototype = {
|
||||
var floatingUserHeaderRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater heading"></div>'),
|
||||
floatingUserHeaderCell = Y.Node.create('<div></div>'),
|
||||
nodepos = this._getRelativeXY(this.headerCell)[0],
|
||||
coordinates = this._getRelativeXY(this.headerRow);
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
gradeHeadersOffset = coordinates[0];
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
@ -810,6 +847,50 @@ FloatingHeaders.prototype = {
|
||||
this.footerRow = floatingGraderFooter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and setup the floating footer title cell.
|
||||
*
|
||||
* @method _setupFloatingAssignmentFooterTitle
|
||||
* @protected
|
||||
*/
|
||||
_setupFloatingAssignmentFooterTitle: function() {
|
||||
// We make various references to the header cells. Store it for later.
|
||||
this.footerCell = Y.one(SELECTORS.FOOTERTITLE);
|
||||
|
||||
// Create the floating row and cell.
|
||||
var floatingFooterRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater avg"></div>'),
|
||||
floatingFooterCell = Y.Node.create('<div></div>'),
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
width = this.firstUserCell.getComputedStyle(WIDTH),
|
||||
height = this.footerCell.get(OFFSETHEIGHT);
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
floatingFooterCell
|
||||
.set('innerHTML', this.footerCell.getHTML())
|
||||
.setAttribute('class', this.footerCell.getAttribute('class'))
|
||||
.setStyles({
|
||||
// The header is larger than the user cells, so we take the user cell.
|
||||
width: width
|
||||
});
|
||||
|
||||
// Style the floating row.
|
||||
floatingFooterRow
|
||||
.setStyles({
|
||||
position: 'absolute',
|
||||
left: coordinates[0] + 'px',
|
||||
bottom: '1px',
|
||||
height: height + 'px',
|
||||
width: width
|
||||
});
|
||||
|
||||
// Append the cell to the row, and finally to the region.
|
||||
floatingFooterRow.append(floatingFooterCell);
|
||||
this.graderRegion.append(floatingFooterRow);
|
||||
|
||||
// Store a reference to this for later - we use it in the event handlers.
|
||||
this.floatingFooterTitleRow = floatingFooterRow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process a Scroll Event on the window.
|
||||
*
|
||||
@ -826,13 +907,15 @@ FloatingHeaders.prototype = {
|
||||
userColumnHeaderStyles = {},
|
||||
userColumnStyles = {},
|
||||
footerStyles = {},
|
||||
footerTitleStyles = {},
|
||||
coord = 0,
|
||||
userCellWidth = 0,
|
||||
floatingUserTriggerPoint = 0, // The X position at which the floating should start.
|
||||
floatingUserRelativePoint = 0, // The point to use when calculating the new position.
|
||||
headerFloats = false,
|
||||
userFloats = false,
|
||||
footerFloats = false;
|
||||
footerFloats = false,
|
||||
footerTitleFloats = false;
|
||||
|
||||
// Header position.
|
||||
gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
|
||||
@ -855,20 +938,24 @@ FloatingHeaders.prototype = {
|
||||
}
|
||||
|
||||
// User column position.
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
if (right_to_left()) {
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint - userCellWidth;
|
||||
userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + userCellWidth);
|
||||
footerTitleFloats = floatingUserTriggerPoint < (this.firstNonUserCellLeft + userCellWidth);
|
||||
} else {
|
||||
floatingUserTriggerPoint = Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint;
|
||||
userFloats = floatingUserTriggerPoint > this.firstUserCellLeft;
|
||||
footerTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - userCellWidth);
|
||||
}
|
||||
|
||||
if (userFloats) {
|
||||
coord = this._getRelativeXFromX(floatingUserRelativePoint);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
userColumnHeaderStyles.left = coord + 'px';
|
||||
footerTitleStyles.left = userColumnStyles.left;
|
||||
} else {
|
||||
coord = this._getRelativeXFromX(this.firstUserCellLeft);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
@ -888,12 +975,15 @@ FloatingHeaders.prototype = {
|
||||
|
||||
if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
|
||||
// We have not scrolled below the footer, nor above the first row.
|
||||
footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerStyles.bottom = footerTitleStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerFloats = true;
|
||||
} else {
|
||||
// The footer should not float any more.
|
||||
footerStyles.bottom = 0;
|
||||
footerStyles.bottom = '1px';
|
||||
footerFloats = false;
|
||||
|
||||
// The footer title may float if the page scrolls right.
|
||||
footerTitleStyles.bottom = '1px';
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,6 +992,7 @@ FloatingHeaders.prototype = {
|
||||
this.userColumnHeader.setStyles(userColumnHeaderStyles);
|
||||
this.userColumn.setStyles(userColumnStyles);
|
||||
this.footerRow.setStyles(footerStyles);
|
||||
this.floatingFooterTitleRow.setStyles(footerTitleStyles);
|
||||
|
||||
// Mark the elements as floating, or not.
|
||||
if (headerFloats) {
|
||||
@ -923,6 +1014,12 @@ FloatingHeaders.prototype = {
|
||||
} else {
|
||||
this.footerRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
|
||||
if (this.footerRow && footerTitleFloats) {
|
||||
this.floatingFooterTitleRow.addClass(CSS.FLOATING);
|
||||
} else {
|
||||
this.floatingFooterTitleRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -980,6 +1077,9 @@ FloatingHeaders.prototype = {
|
||||
};
|
||||
footercell.setStyles(styles);
|
||||
});
|
||||
|
||||
// Resize the title area too.
|
||||
this.floatingFooterTitleRow.one('div').setStyle('width', userWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,27 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
firstUserCell: null,
|
||||
|
||||
/**
|
||||
* A Node representing the first cell which does not contain a user header.
|
||||
*
|
||||
* @property firstNonUserCell
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCell: null,
|
||||
|
||||
/**
|
||||
* The position of the left of the first non-header cell in a row - the one after the email address.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
* additional rows are loaded, or the window changes in some fashion.
|
||||
*
|
||||
* @property firstNonUserCellLeft
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
firstNonUserCellLeft: 0,
|
||||
|
||||
|
||||
/**
|
||||
* A Node representing the original table footer row.
|
||||
*
|
||||
@ -137,6 +158,15 @@ FloatingHeaders.prototype = {
|
||||
*/
|
||||
userColumn: null,
|
||||
|
||||
/**
|
||||
* A Node representing the floating footer title header. This is the header with the "Overage Average" title.
|
||||
*
|
||||
* @property floatingFooterTitleRow
|
||||
* @type Node
|
||||
* @protected
|
||||
*/
|
||||
floatingFooterTitleRow: null,
|
||||
|
||||
/**
|
||||
* The position of the bottom of the first user cell.
|
||||
* This is used when processing the scroll event as an optimisation. It must be updated when
|
||||
@ -189,6 +219,7 @@ FloatingHeaders.prototype = {
|
||||
// Grab references to commonly used Nodes.
|
||||
this.firstUserCell = Y.one(SELECTORS.USERCELL);
|
||||
this.container = Y.one(SELECTORS.GRADEPARENT);
|
||||
this.firstNonUserCell = Y.one(SELECTORS.USERMAIL).next();
|
||||
|
||||
if (!this.firstUserCell) {
|
||||
// No need for floating elements, there are no users.
|
||||
@ -200,6 +231,7 @@ FloatingHeaders.prototype = {
|
||||
this._setupFloatingUserHeader();
|
||||
this._setupFloatingAssignmentHeaders();
|
||||
this._setupFloatingAssignmentFooter();
|
||||
this._setupFloatingAssignmentFooterTitle();
|
||||
|
||||
// Calculate the positions of edge cells. These are used for positioning of the floating headers.
|
||||
// This must be called after the floating headers are setup, but before the scroll event handler is invoked.
|
||||
@ -235,6 +267,9 @@ FloatingHeaders.prototype = {
|
||||
// The left of the user cells matches the left of the headerRow.
|
||||
this.firstUserCellLeft = this.firstUserCell.getX();
|
||||
|
||||
// The left of the user cells matches the left of the footer title.
|
||||
this.firstNonUserCellLeft = this.firstNonUserCell.getX();
|
||||
|
||||
if (userCellList.size() > 1) {
|
||||
// Use the top of the second cell for the bottom of the first cell.
|
||||
// This is used when scrolling to fix the footer to the top edge of the window.
|
||||
@ -416,7 +451,7 @@ FloatingHeaders.prototype = {
|
||||
var floatingUserHeaderRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater heading"></div>'),
|
||||
floatingUserHeaderCell = Y.Node.create('<div></div>'),
|
||||
nodepos = this._getRelativeXY(this.headerCell)[0],
|
||||
coordinates = this._getRelativeXY(this.headerRow);
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
gradeHeadersOffset = coordinates[0];
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
@ -561,6 +596,50 @@ FloatingHeaders.prototype = {
|
||||
this.footerRow = floatingGraderFooter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and setup the floating footer title cell.
|
||||
*
|
||||
* @method _setupFloatingAssignmentFooterTitle
|
||||
* @protected
|
||||
*/
|
||||
_setupFloatingAssignmentFooterTitle: function() {
|
||||
// We make various references to the header cells. Store it for later.
|
||||
this.footerCell = Y.one(SELECTORS.FOOTERTITLE);
|
||||
|
||||
// Create the floating row and cell.
|
||||
var floatingFooterRow = Y.Node.create('<div aria-hidden="true" role="presentation" class="floater avg"></div>'),
|
||||
floatingFooterCell = Y.Node.create('<div></div>'),
|
||||
coordinates = this._getRelativeXY(this.headerRow),
|
||||
width = this.firstUserCell.getComputedStyle(WIDTH),
|
||||
height = this.footerCell.get(OFFSETHEIGHT);
|
||||
|
||||
// Append the content and style to the floating cell.
|
||||
floatingFooterCell
|
||||
.set('innerHTML', this.footerCell.getHTML())
|
||||
.setAttribute('class', this.footerCell.getAttribute('class'))
|
||||
.setStyles({
|
||||
// The header is larger than the user cells, so we take the user cell.
|
||||
width: width
|
||||
});
|
||||
|
||||
// Style the floating row.
|
||||
floatingFooterRow
|
||||
.setStyles({
|
||||
position: 'absolute',
|
||||
left: coordinates[0] + 'px',
|
||||
bottom: '1px',
|
||||
height: height + 'px',
|
||||
width: width
|
||||
});
|
||||
|
||||
// Append the cell to the row, and finally to the region.
|
||||
floatingFooterRow.append(floatingFooterCell);
|
||||
this.graderRegion.append(floatingFooterRow);
|
||||
|
||||
// Store a reference to this for later - we use it in the event handlers.
|
||||
this.floatingFooterTitleRow = floatingFooterRow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process a Scroll Event on the window.
|
||||
*
|
||||
@ -577,13 +656,15 @@ FloatingHeaders.prototype = {
|
||||
userColumnHeaderStyles = {},
|
||||
userColumnStyles = {},
|
||||
footerStyles = {},
|
||||
footerTitleStyles = {},
|
||||
coord = 0,
|
||||
userCellWidth = 0,
|
||||
floatingUserTriggerPoint = 0, // The X position at which the floating should start.
|
||||
floatingUserRelativePoint = 0, // The point to use when calculating the new position.
|
||||
headerFloats = false,
|
||||
userFloats = false,
|
||||
footerFloats = false;
|
||||
footerFloats = false,
|
||||
footerTitleFloats = false;
|
||||
|
||||
// Header position.
|
||||
gradeItemHeadingContainerStyles.left = this._getRelativeXFromX(this.headerRow.getX());
|
||||
@ -606,20 +687,24 @@ FloatingHeaders.prototype = {
|
||||
}
|
||||
|
||||
// User column position.
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
if (right_to_left()) {
|
||||
userCellWidth = this.firstUserCell.get(OFFSETWIDTH);
|
||||
floatingUserTriggerPoint = Y.config.win.innerWidth + Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint - userCellWidth;
|
||||
userFloats = floatingUserTriggerPoint < (this.firstUserCellLeft + userCellWidth);
|
||||
footerTitleFloats = floatingUserTriggerPoint < (this.firstNonUserCellLeft + userCellWidth);
|
||||
} else {
|
||||
floatingUserTriggerPoint = Y.config.win.pageXOffset;
|
||||
floatingUserRelativePoint = floatingUserTriggerPoint;
|
||||
userFloats = floatingUserTriggerPoint > this.firstUserCellLeft;
|
||||
footerTitleFloats = floatingUserTriggerPoint > (this.firstNonUserCellLeft - userCellWidth);
|
||||
}
|
||||
|
||||
if (userFloats) {
|
||||
coord = this._getRelativeXFromX(floatingUserRelativePoint);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
userColumnHeaderStyles.left = coord + 'px';
|
||||
footerTitleStyles.left = userColumnStyles.left;
|
||||
} else {
|
||||
coord = this._getRelativeXFromX(this.firstUserCellLeft);
|
||||
userColumnStyles.left = coord + 'px';
|
||||
@ -639,12 +724,15 @@ FloatingHeaders.prototype = {
|
||||
|
||||
if (bottomScrollPosition < footerBottomPosition && bottomScrollPosition > this.firstUserCellBottom) {
|
||||
// We have not scrolled below the footer, nor above the first row.
|
||||
footerStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerStyles.bottom = footerTitleStyles.bottom = Math.ceil(footerBottomPosition - bottomScrollPosition) + 'px';
|
||||
footerFloats = true;
|
||||
} else {
|
||||
// The footer should not float any more.
|
||||
footerStyles.bottom = 0;
|
||||
footerStyles.bottom = '1px';
|
||||
footerFloats = false;
|
||||
|
||||
// The footer title may float if the page scrolls right.
|
||||
footerTitleStyles.bottom = '1px';
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,6 +741,7 @@ FloatingHeaders.prototype = {
|
||||
this.userColumnHeader.setStyles(userColumnHeaderStyles);
|
||||
this.userColumn.setStyles(userColumnStyles);
|
||||
this.footerRow.setStyles(footerStyles);
|
||||
this.floatingFooterTitleRow.setStyles(footerTitleStyles);
|
||||
|
||||
// Mark the elements as floating, or not.
|
||||
if (headerFloats) {
|
||||
@ -674,6 +763,12 @@ FloatingHeaders.prototype = {
|
||||
} else {
|
||||
this.footerRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
|
||||
if (this.footerRow && footerTitleFloats) {
|
||||
this.floatingFooterTitleRow.addClass(CSS.FLOATING);
|
||||
} else {
|
||||
this.floatingFooterTitleRow.removeClass(CSS.FLOATING);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -731,6 +826,9 @@ FloatingHeaders.prototype = {
|
||||
};
|
||||
footercell.setStyles(styles);
|
||||
});
|
||||
|
||||
// Resize the title area too.
|
||||
this.floatingFooterTitleRow.one('div').setStyle('width', userWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
*/
|
||||
|
||||
var SELECTORS = {
|
||||
FOOTERTITLE: '#user-grades .avg .header',
|
||||
FOOTERCELLS: '#user-grades .avg .cell',
|
||||
FOOTERROW: '#user-grades .avg',
|
||||
GRADECELL: 'td.grade',
|
||||
@ -37,7 +38,8 @@ var SELECTORS = {
|
||||
HEADERCELL: '.gradebook-header-cell',
|
||||
HEADERROW: '#user-grades tr.heading',
|
||||
STUDENTHEADER: '#studentheader',
|
||||
USERCELL: '#user-grades .user.cell'
|
||||
USERCELL: '#user-grades .user.cell',
|
||||
USERMAIL: '#user-grades .useremail'
|
||||
},
|
||||
CSS = {
|
||||
OVERRIDDEN: 'overridden',
|
||||
|
@ -244,8 +244,9 @@
|
||||
border-color: @tableBorder;
|
||||
}
|
||||
tr:nth-of-type(even) .cell,
|
||||
.floater .cell {
|
||||
.floater .cell,
|
||||
.avg {
|
||||
background-color: @tableBackgroundAccent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user