Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save JLynch7/1175470 to your computer and use it in GitHub Desktop.
Save JLynch7/1175470 to your computer and use it in GitHub Desktop.
Patch for 1.3.2 to add frozen column support. Courtesy of libory.
From 6e94bbbd9c190a51dd825afd74709d259d3d004b Mon Sep 17 00:00:00 2001
From: User <[email protected]>
Date: Sat, 27 Aug 2011 10:41:14 -0400
Subject: [PATCH] New Feature: Frozen Columns. Set using the frozenColumn
option
---
slick.grid.js | 341 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 300 insertions(+), 41 deletions(-)
diff --git a/slick.grid.js b/slick.grid.js
index a20cf39..b144087 100644
--- a/slick.grid.js
+++ b/slick.grid.js
@@ -12,7 +12,6 @@
* SlickGrid v1.3.2
*
* TODO:
- * - frozen columns
* - consistent events (EventHelper? jQuery events?)
*
*
@@ -37,7 +36,7 @@
* secondaryHeaderRowHeight - (default 25px) The height of the secondary header row.
* syncColumnCellResize - (default false) Synchronously resize column cells when column headers are resized
* rowCssClasses - (default null) A function which (given a row's data item as an argument) returns a space-delimited string of CSS classes that will be applied to the slick-row element. Note that this should be fast, as it is called every time a row is displayed.
- *
+ * frozenColumn - (default -1) If greater than Cero, then columns whose index are less than or equal to this number will be frozen
*
* COLUMN DEFINITION (columns) OPTIONS:
* id - Column ID.
@@ -235,7 +234,9 @@ if (!jQuery.fn.drag) {
showSecondaryHeaderRow: false,
secondaryHeaderRowHeight: 25,
syncColumnCellResize: false,
- enableAutoTooltips: true
+ enableAutoTooltips: true,
+ // @author Diego Boris (5/Jan/2011)
+ frozenColumn: -1
},
gridData, gridDataGetLength, gridDataGetItem;
@@ -296,6 +297,9 @@ if (!jQuery.fn.drag) {
var counter_rows_rendered = 0;
var counter_rows_removed = 0;
+ // appended for split funtionality
+ var canvas0, canvas1, headers0, headers1, viewp0,
+ uis = "dbhp" + Math.round(1000000 * Math.random());
//////////////////////////////////////////////////////////////////////////////////////////////
// Initialization
@@ -358,8 +362,16 @@ if (!jQuery.fn.drag) {
gridDataGetLength = gridData.getLength || defaultGetLength;
gridDataGetItem = gridData.getItem || defaultGetItem;
+ // @author DB 5/Jan/2011
+ // for frozen column
+ var $headerScroller1, $headers1, $secondaryHeaderScroller1, $secondaryHeaders1,
+ $viewport1, $canvas1, $scrollerContainer, totalFrozenWidth = 0;
scrollbarDimensions = scrollbarDimensions || measureScrollbar(); // skip measurement if already have dimensions
options = $.extend({},defaults,options);
+ // validate frozenColumn
+ // @author(modify)DB
+ options.frozenColumn = ( options.frozenColumn >= 0
+ && options.frozenColumn < columns.length ) ? parseInt(options.frozenColumn) : -1;
columnDefaults.width = options.defaultColumnWidth;
// validate loaded JavaScript modules against requested options
@@ -391,20 +403,70 @@ if (!jQuery.fn.drag) {
break;
}
- $headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
- $headers = $("<div class='slick-header-columns' style='width:100000px; left:-10000px' />").appendTo($headerScroller);
+ // modified (attribute 'id' appended)
+ // @author(modify)DB
+ $headerScroller = $("<div id='hs" + uis + "' class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
+ $headers = $("<div id='h" + uis + "' class='slick-header-columns' style='width:100000px; left:-10000px' />").appendTo($headerScroller);
+
+ // modified (attribute 'id' appended)
+ // @author(modify)DB
+ $secondaryHeaderScroller = $("<div id='shs" + uis + "' class='slick-header-secondary ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
+ $secondaryHeaders = $("<div id='sh" + uis + "' class='slick-header-columns-secondary' style='width:100000px' />").appendTo($secondaryHeaderScroller);
+
+ // split in two
+ // @author(modify)DB
+ if (options.frozenColumn > -1){
+ $scrollerContainer = $("<div id='splittedC" + uis + "' style='overflow:hidden;position:absolute;top:0px;' />");
+ $headerScroller1 = $("<div id='hs1" + uis + "' class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($scrollerContainer);
+ $headers1 = $("<div id='h1" + uis + "' class='slick-header-columns' style='width:100000px; left:-10000px' />").appendTo($headerScroller1);
+ $headers = $().add($headers).add($headers1);
+ // TODO REFACTORING
+ // @author DB
+ $secondaryHeaderScroller1 = $("<div id='shs1" + uis + "' class='slick-header-secondary ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($scrollerContainer);
+ $secondaryHeaders1 = $("<div id='sh1" + uis + "' class='slick-header-columns-secondary' style='width:100000px' />").appendTo($secondaryHeaderScroller1);
+ $secondaryHeaderScroller = $().add($secondaryHeaderScroller).add($secondaryHeaderScroller1);
+ $headerScroller = $().add($headerScroller).add($headerScroller1);
+ $secondaryHeaders = $().add($secondaryHeaders).add($secondaryHeaders1);
+ }
- $secondaryHeaderScroller = $("<div class='slick-header-secondary ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
- $secondaryHeaders = $("<div class='slick-header-columns-secondary' style='width:100000px' />").appendTo($secondaryHeaderScroller);
if (!options.showSecondaryHeaderRow) {
$secondaryHeaderScroller.hide();
}
- // with autoHeight, we can set the mainscroller's y-overflow to auto, since the scroll bar will not appear
- var msStyle = "width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;"
- $viewport = $("<div class='slick-viewport' tabIndex='0' hideFocus style='" + msStyle + "'>").appendTo($container);
- $canvas = $("<div class='grid-canvas' tabIndex='0' hideFocus style='overflow:hidden' />").appendTo($viewport);
+ // original code for msStyle was modified
+ // @author DB 27/Jan/2011
+ var msStyle = "";
+
+ if (options.frozenColumn > -1){
+ msStyle = "width:100%;outline:0;position:relative;overflow-x: scroll; overflow-y: hidden;"
+ }else {
+ // with autoHeight, we can set the mainscroller's y-overflow to auto, since the scroll bar will not appear
+ msStyle = "width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;"
+ }
+ // modified (attribute 'id' appended)
+ // @author(modify)DB
+ $viewport = $("<div id='v" + uis + "' class='slick-viewport' tabIndex='0' hideFocus style='" + msStyle + "'>").appendTo($container);
+ $canvas = $("<div id='c" + uis + "' class='grid-canvas' tabIndex='0' hideFocus style='overflow:hidden' />").appendTo($viewport);
+
+ // split in two
+ // @author(modify)DB
+ if (options.frozenColumn > -1){
+ var msStyle1 = "width:100%;overflow-x:scroll;overflow-y:auto;outline:0;position:relative;overflow-y:auto;";
+ $viewport1 = $("<div id='v1" + uis + "' class='slick-viewport' tabIndex='0' hideFocus style='" + msStyle1 + "'>").appendTo($scrollerContainer);
+ $canvas1 = $("<div id='c1" + uis + "' class='grid-canvas' tabIndex='0' hideFocus style='overflow:hidden' />").appendTo($viewport1);
+ $viewport = $().add($viewport).add($viewport1);
+ $canvas = $().add($canvas).add($canvas1);
+ $scrollerContainer.appendTo($container);
+ // canvas
+ canvas0 = $("#c" + uis, $container)[0];
+ canvas1 = $("#c1" + uis, $container)[0];
+ // headers
+ headers0 = $("#h" + uis, $container)[0];
+ headers1 = $("#h1" + uis, $container)[0];
+ // viewport
+ viewp0 = $("#v" + uis, $container)[0];
+ }
// header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?)
// calculate the diff so we can set consistent sizes
@@ -427,6 +489,21 @@ if (!jQuery.fn.drag) {
setupRowReordering();
createCssRules();
+ // appended
+ // @author DB
+ if (options.frozenColumn > -1){
+ // set width
+ // problems with cache whne recovering item by index
+ // e.g headers0 = $headers.get(0)
+ totalFrozenWidth = getWidthSplittedCanvas().widthFixed;
+ $("#hs" + uis , $container).width(totalFrozenWidth);
+ $("#shs" + uis, $container).width(totalFrozenWidth);
+ $("#v" + uis, $container).width(totalFrozenWidth);
+ var rightW = $container.width() - totalFrozenWidth;
+ $scrollerContainer.css("left", totalFrozenWidth);
+ //.width(rightW);
+ //$("#v1" + uis, $container).width(rightW);
+ }
resizeAndRender();
$viewport.bind("scroll", handleScroll);
@@ -441,6 +518,8 @@ if (!jQuery.fn.drag) {
function createColumnHeaders() {
var i;
+ // @author Diego Boris(modified for frozen Column) (6/Jan/2011)
+ var $headerTarget;
function hoverBegin() {
$(this).addClass('ui-state-hover');
@@ -453,11 +532,27 @@ if (!jQuery.fn.drag) {
var m = columns[i] = $.extend({},columnDefaults,columns[i]);
columnsById[m.id] = i;
+ // @author Diego Boris(modify for frozen Column) (7/Jan/2011)
+ $headerTarget = (options.frozenColumn > -1 ) ?
+ ((i <= options.frozenColumn) ? $(headers0): $(headers1)) : $headers;
+ if (options.frozenColumn > -1){
+ if (i <= options.frozenColumn){
+ $headerTarget = $(headers0);
+ // TODO..
+ // make it resizable
+ // @author DB
+ m.resizable = false;
+ }else{
+ $headerTarget = $(headers1);
+ }
+ }else{
+ $headerTarget = $headers;
+ }
var header = $("<div class='ui-state-default slick-header-column' cell=" + i + " id='" + m.id + "' />")
.html("<span class='slick-column-name'>" + m.name + "</span>")
.width(m.width - headerColumnWidthDiff)
.attr('title', m.toolTip || m.name)
- .appendTo($headers);
+ .appendTo($headerTarget);
if (m.hidden) {
header.css("display","none");
@@ -732,6 +827,8 @@ if (!jQuery.fn.drag) {
});
}
+ // TODO REFACTORING
+ // @author DB
function setupRowReordering() {
$canvas
.bind("beforedragstart", function(e) {
@@ -836,7 +933,14 @@ if (!jQuery.fn.drag) {
options.editorLock.cancelCurrentEdit();
if (self.onBeforeDestroy) { self.onBeforeDestroy(); }
- if ($headers.sortable) { $headers.sortable("destroy"); }
+ // changed
+ // @author DB
+ // if ($headers.sortable){$headers.sortable("destroy");}
+ $headers.each(function(){
+ if ($(this).sortable){
+ $(this).sortable("destroy");
+ }
+ });
$container.unbind("resize", resizeCanvas);
removeCssRules();
@@ -1029,6 +1133,8 @@ if (!jQuery.fn.drag) {
}
}
+ // TODO REFACTORING
+ // @author DB
function getSecondaryHeaderRow() {
return $secondaryHeaders[0];
}
@@ -1087,7 +1193,12 @@ if (!jQuery.fn.drag) {
}
function removeAllRows() {
- $canvas[0].innerHTML = "";
+ // modified
+ // @author DB
+ // $canvas[0].innerHTML = "";
+ $canvas.each(function(){
+ this.innerHTML = "";
+ });
rowsCache= {};
postProcessedRows = {};
counter_rows_removed += renderedRows;
@@ -1097,7 +1208,11 @@ if (!jQuery.fn.drag) {
function removeRowFromCache(row) {
var node = rowsCache[row];
if (!node) { return; }
- node.parentNode.removeChild(node);
+ // comment added and changed original code
+ // remove from DOM
+ // @auhor DB
+ // node.parentNode.removeChild(node);
+ $canvas.find(".slick-row[row=" + row + "]").detach();
delete rowsCache[row];
delete postProcessedRows[row];
renderedRows--;
@@ -1178,7 +1293,10 @@ if (!jQuery.fn.drag) {
$container.innerHeight() -
$headerScroller.outerHeight() -
(options.showSecondaryHeaderRow ? $secondaryHeaderScroller.outerHeight() : 0)); }
-
+ // FIXME.. although
+ // when read, resizeCanvas() method is always invoked
+ // so it appears it gives no problem
+ // @author DB
viewportW = $viewport.innerWidth();
viewportH = $viewport.innerHeight();
BUFFER = numVisibleRows = Math.ceil(viewportH / options.rowHeight);
@@ -1190,6 +1308,14 @@ if (!jQuery.fn.drag) {
});
$canvas.width(totalWidth);
+ // modified to support split functionality
+ // @author DB
+ if(options.frozenColumn > -1 ){
+ var o = getWidthSplittedCanvas();
+ $(canvas0).width(o.widthFixed);
+ $(canvas1).width(o.widthScrolled);
+ $("#splittedC" + uis, $container).width($container.width() - o.widthFixed);
+ }
// browsers sometimes do not adjust scrollTop/scrollHeight when the height of contained objects changes
newHeight = Math.max(newHeight, viewportH - scrollbarDimensions.height);
if ($viewport.scrollTop() > newHeight - $viewport.height() + scrollbarDimensions.height) {
@@ -1238,30 +1364,66 @@ if (!jQuery.fn.drag) {
};
}
+ // modified to support the split funcionality
+ // @author DB 17/Jan/2011
function renderRows(from,to) {
var i, l,
parentNode = $canvas[0],
rowsBefore = renderedRows,
stringArray = [],
+ // appended
+ // @author DB
+ stringArrayRight = [],
rows =[],
startTimestamp = new Date(),
needToReselectCell = false;
- for (i = from; i <= to; i++) {
- if (rowsCache[i]) { continue; }
- renderedRows++;
- rows.push(i);
- appendRowHtml(stringArray,i);
- if (currentCellNode && currentRow === i)
- needToReselectCell = true;
- counter_rows_rendered++;
- }
+ if(options.frozenColumn > -1 ){
+
+ for (i = from; i <= to; i++) {
+ if (rowsCache[i]) {
+ continue;
+ }
+ renderedRows++;
+ rows.push(i);
+ appendSplitedHtmlRow(stringArray, stringArrayRight, i);
+ if (currentCellNode && currentRow === i)
+ needToReselectCell = true;
+ counter_rows_rendered++;
+ }
+
+ var x = document.createElement("div"),
+ xRight = document.createElement("div");
+
+ x.innerHTML = stringArray.join("");
+ xRight.innerHTML = stringArrayRight.join("");
+
+ for (i = 0, l = x.childNodes.length; i < l; i++) {
+ rowsCache[rows[i]] = $().add($(x.firstChild).appendTo(canvas0))
+ .add($(xRight.firstChild).appendTo(canvas1));
+ }
+
+ }else{
+
+ for (i = from; i <= to; i++) {
+ if (rowsCache[i]) {
+ continue;
+ }
+ renderedRows++;
+ rows.push(i);
+ appendRowHtml(stringArray,i);
+ if (currentCellNode && currentRow === i)
+ needToReselectCell = true;
+ counter_rows_rendered++;
+ }
- var x = document.createElement("div");
- x.innerHTML = stringArray.join("");
+ x = document.createElement("div");
+ x.innerHTML = stringArray.join("");
+
+ for (i = 0, l = x.childNodes.length; i < l; i++) {
+ rowsCache[rows[i]] = parentNode.appendChild(x.firstChild);
+ }
- for (i = 0, l = x.childNodes.length; i < l; i++) {
- rowsCache[rows[i]] = parentNode.appendChild(x.firstChild);
}
if (needToReselectCell) {
@@ -1274,6 +1436,78 @@ if (!jQuery.fn.drag) {
}
}
+ /**
+ * Split htmlRow into the appropiate canvas according
+ * to options.frozenColumn value 26/Jan/2011
+ * @author DB
+ */
+ function appendSplitedHtmlRow(stringArray, stringArrayRight, row) {
+
+ var d = gridDataGetItem(row);
+ var dataLoading = row < gridDataGetLength() && !d;
+ var css = "slick-row " +
+ (dataLoading ? " loading" : "") +
+ (selectedRowsLookup[row] ? " selected ui-state-active" : "") +
+ (row % 2 == 1 ? ' odd' : ' even');
+
+ // if the user has specified a function to provide additional per-row css classes, call it here
+ if (options.rowCssClasses) {
+ css += ' ' + options.rowCssClasses(d);
+ }
+
+ stringArray.push("<div class='ui-widget-content " + css + "' row='" + row + "' style='top:" + (options.rowHeight*row) + "px'>");
+ stringArrayRight.push("<div class='ui-widget-content " + css + "' row='" + row + "' style='top:" + (options.rowHeight*row) + "px'>");
+
+ // left row
+ for (var i=0, cols=options.frozenColumn + 1; i<cols; i++) {
+ var m = columns[i];
+ if (m.hidden) continue;
+
+ stringArray.push("<div " + (m.unselectable ? "tabIndex=-1 " : "hideFocus tabIndex=0 ") + "class='slick-cell c" + i + (m.cssClass ? " " + m.cssClass : "") + "' cell=" + i + ">");
+
+ // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
+ if (d && row < gridDataGetLength()) {
+ stringArray.push(m.formatter(row, i, d[m.field], m, d));
+ }
+
+ stringArray.push("</div>");
+ }
+ stringArray.push("</div>");
+
+ // right row
+ for (i=options.frozenColumn + 1, cols=columns.length; i<cols; i++) {
+ m = columns[i];
+ if (m.hidden) continue;
+
+ stringArrayRight.push("<div " + (m.unselectable ? "tabIndex=-1 " : "hideFocus tabIndex=0 ") + "class='slick-cell c" + i + (m.cssClass ? " " + m.cssClass : "") + "' cell=" + i + ">");
+
+ // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
+ if (d && row < gridDataGetLength()) {
+ stringArrayRight.push(m.formatter(row, i, d[m.field], m, d));
+ }
+
+ stringArrayRight.push("</div>");
+ }
+ stringArrayRight.push("</div>");
+
+ }
+
+ // Return an object containing the two newly created canvas' width
+ // @author DB 28/Jan/2011
+ function getWidthSplittedCanvas(){
+ var widthFixed = 0, widthScrolled = 0;
+
+ $(headers0).find('.slick-header-column:visible').each(function () {
+ widthFixed += $(this).outerWidth();
+ });
+
+ $(headers1).find('.slick-header-column:visible').each(function () {
+ widthScrolled += $(this).outerWidth();
+ });
+
+ return {widthFixed : widthFixed, widthScrolled : widthScrolled};
+ }
+
function startPostProcessing() {
if (!options.enableAsyncPostRender) { return; }
clearTimeout(h_postrender);
@@ -1315,18 +1549,31 @@ if (!jQuery.fn.drag) {
render();
}
+ // modified to support frozen column functionality
+ // @author DB 28/Jan/2011
function handleScroll() {
- currentScrollTop = $viewport[0].scrollTop;
+ var sufix = ((options.frozenColumn > -1 ) ? "1" : "") + uis;
+ var vwp = $("#v" + sufix, $container)[0];
+ // changed
+ currentScrollTop = vwp.scrollTop;
+ var scrollLeft = vwp.scrollLeft;
var scrollDistance = Math.abs(lastRenderedScrollTop - currentScrollTop);
- var scrollLeft = $viewport[0].scrollLeft;
if (scrollLeft !== currentScrollLeft) {
- $headerScroller[0].scrollLeft = currentScrollLeft = scrollLeft;
- $secondaryHeaderScroller[0].scrollLeft = currentScrollLeft = scrollLeft;
+ // changed
+ $("#hs" + sufix, $container)[0].scrollLeft = currentScrollLeft = scrollLeft;
+ $("#shs" + sufix, $container)[0].scrollLeft = currentScrollLeft = scrollLeft;
+ }
+
+ // appended
+ if (options.frozenColumn > -1 ){
+ viewp0.scrollTop = currentScrollTop;
}
// min scroll distance = 25% of the viewport or MIN_BUFFER rows (whichever is smaller)
- if (scrollDistance < Math.min(viewportH/4, MIN_BUFFER*options.rowHeight)) { return; }
+ if (scrollDistance < Math.min(viewportH/4, MIN_BUFFER*options.rowHeight)) {
+ return;
+ }
if (lastRenderedScrollTop === currentScrollTop) {
scrollDir = 0;
@@ -1360,7 +1607,10 @@ if (!jQuery.fn.drag) {
var rowNode = rowsCache[row];
if (!rowNode || postProcessedRows[row] || row>=gridDataGetLength()) { continue; }
- var d = gridDataGetItem(row), cellNodes = rowNode.childNodes;
+ // modified
+ // @author DB
+ //var d = gridDataGetItem(row), cellNodes = rowNode.childNodes;
+ var d = gridDataGetItem(row), cellNodes = $(rowNode).children();
for (var i=0, j=0, l=columns.length; i<l; ++i) {
var m = columns[i];
if (m.hidden) { continue; }
@@ -1788,22 +2038,31 @@ if (!jQuery.fn.drag) {
return currentCellNode;
}
+ // modified to support frozen column functionality
+ // @author DB 28/Jan/2011
function scrollRowIntoView(row, doPaging) {
- var scrollTop = $viewport[0].scrollTop;
+
+ var sufix = ((options.frozenColumn > -1 ) ? "1" : "") + uis;
+ var vwp = $("#v" + sufix, $container)[0];
+ // changed
+ var scrollTop = vwp.scrollTop;
// need to page down?
if ((row + 1) * options.rowHeight > scrollTop + viewportH) {
- $viewport[0].scrollTop = doPaging
+ // changed
+ // $viewport[0].scrollTop
+ $viewport.attr("scrollTop", doPaging
? row * options.rowHeight
- : (row + 1) * options.rowHeight - viewportH;
+ : (row + 1) * options.rowHeight - viewportH);
handleScroll();
}
// or page up?
else if (row * options.rowHeight < scrollTop) {
- $viewport[0].scrollTop = doPaging
+ // changed
+ $viewport.attr("scrollTop", doPaging
? (row + 1) * options.rowHeight - viewportH
- : row * options.rowHeight;
+ : row * options.rowHeight);
handleScroll();
}
}
@@ -2063,4 +2322,4 @@ if (!jQuery.fn.drag) {
GlobalEditorLock: new EditorLock()
}
});
-}(jQuery));
+}(jQuery));
\ No newline at end of file
--
1.7.6.msysgit.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment