From 1cc8df020dd621c9a820103d2543f370233adf50 Mon Sep 17 00:00:00 2001 From: Frederick Ros Date: Sun, 14 Oct 2012 20:31:23 +0200 Subject: [PATCH] The slickgrid view can now be edited. --- src/view.slickgrid.js | 61 ++++++++- test/view.slickgrid.test.js | 46 ++++++- vendor/slickgrid/2.0.1/README.txt | 2 +- vendor/slickgrid/2.0.1/images/calendar.gif | Bin 0 -> 1035 bytes vendor/slickgrid/2.0.1/slick.grid.min.js | 144 +++++++++++---------- 5 files changed, 179 insertions(+), 74 deletions(-) create mode 100644 vendor/slickgrid/2.0.1/images/calendar.gif diff --git a/src/view.slickgrid.js b/src/view.slickgrid.js index 75c56b76..c751c83f 100644 --- a/src/view.slickgrid.js +++ b/src/view.slickgrid.js @@ -12,7 +12,24 @@ this.recline.View = this.recline.View || {}; // // Initialize it with a `recline.Model.Dataset`. // -// NB: you need an explicit height on the element for slickgrid to work +// Additional options to drive SlickGrid grid can be given through state. +// The following keys allow for customization: +// * gridOptions: to add options at grid level +// * columnsEditor: to add editor for editable columns +// +// For example: +// var grid = new recline.View.SlickGrid({ +// model: dataset, +// el: $el, +// state: { +// gridOptions: {editable: true}, +// columnsEditor: [ +// {column: 'date', editor: Slick.Editor.Date }, +// {column: 'title', editor: Slick.Editor.Text} +// ] +// } +// }); +//// NB: you need an explicit height on the element for slickgrid to work my.SlickGrid = Backbone.View.extend({ initialize: function(modelEtc) { var self = this; @@ -28,9 +45,13 @@ my.SlickGrid = Backbone.View.extend({ columnsOrder: [], columnsSort: {}, columnsWidth: [], + columnsEditor: [], + options: {}, fitColumns: false }, modelEtc.state + ); +// this.grid_options = modelEtc.options; this.state = new recline.Model.ObjectState(state); }, @@ -40,13 +61,13 @@ my.SlickGrid = Backbone.View.extend({ render: function() { var self = this; - var options = { + var options = _.extend({ enableCellNavigation: true, enableColumnReorder: true, explicitInitialization: true, syncColumnCellResize: true, forceFitColumns: this.state.get('fitColumns') - }; + }, self.state.get('gridOptions')); // We need all columns, even the hidden ones, to show on the column picker var columns = []; @@ -76,6 +97,10 @@ my.SlickGrid = Backbone.View.extend({ column['width'] = widthInfo.width; } + var editInfo = _.find(self.state.get('columnsEditor'),function(c){return c.column == field.id}); + if (editInfo){ + column['editor'] = editInfo.editor; + } columns.push(column); }); @@ -104,14 +129,29 @@ my.SlickGrid = Backbone.View.extend({ } columns = columns.concat(tempHiddenColumns); - var data = []; + function RowSet() { + var models = []; + var rows = []; + + this.push = function(model, row) { + models.push(model); + rows.push(row); + } + + this.getLength = function() { return rows.length; } + this.getItem = function(index) { return rows[index];} + this.getItemMetadata= function(index) { return {};} + this.getModel= function(index) { return models[index]; } + }; + + var data = new RowSet(); this.model.records.each(function(doc){ var row = {}; self.model.fields.each(function(field){ row[field.id] = doc.getFieldValueUnrendered(field); }); - data.push(row); + data.push(doc, row); }); this.grid = new Slick.Grid(this.el, data, visibleColumns, options); @@ -149,6 +189,17 @@ my.SlickGrid = Backbone.View.extend({ self.state.set({columnsWidth:columnsWidth}); }); + this.grid.onCellChange.subscribe(function (e, args) { + // We need to change the model associated value + // + var grid = args.grid; + var model = data.getModel(args.row); + var field = grid.getColumns()[args.cell]['id']; + var v = {}; + v[field] = args.item[field]; + model.set(v); + }); + var columnpicker = new Slick.Controls.ColumnPicker(columns, this.grid, _.extend(options,{state:this.state})); diff --git a/test/view.slickgrid.test.js b/test/view.slickgrid.test.js index 4f1a5315..f3cf05f3 100644 --- a/test/view.slickgrid.test.js +++ b/test/view.slickgrid.test.js @@ -31,7 +31,9 @@ test('state', function () { columnsOrder:['lon','id','z','date', 'y', 'country'], columnsWidth:[ {column:'id',width: 250} - ] + ], + gridOptions: {editable: true}, + columnsEditor: [{column: 'country', editor: Slick.Editors.Text}] } }); $('.fixtures .test-datatable').append(view.el); @@ -54,9 +56,51 @@ test('state', function () { // Column width equal($('.slick-header-column[title="id"]').width(),250); + // Editable grid + equal(true, view.grid.getOptions().editable); + + // Editor on 'country' column + var countryColumn = _.find(view.grid.getColumns(), function (c) { return c.field == 'country'; }); + equal(Slick.Editors.Text, countryColumn.editor); + view.remove(); }); +test('editable', function () { + var dataset = Fixture.getDataset(); + var view = new recline.View.SlickGrid({ + model: dataset, + state: { + hiddenColumns:['x','lat','title'], + columnsOrder:['lon','id','z','date', 'y', 'country'], + columnsWidth:[ + {column:'id',width: 250} + ], + gridOptions: {editable: true}, + columnsEditor: [{column: 'country', editor: Slick.Editors.Text}] + } + }); + + $('.fixtures .test-datatable').append(view.el); + view.render(); + view.grid.init(); + + var new_item = {lon: "foo", id: 1, z: 23, date: "12", y: 3, country: 'FR'}; + + dataset.records.on('change', function(record){ + equal(new_item['lon'], record.get('lon')); + }); + + // Be sure a cell change triggers a change of the model + e = new Slick.EventData(); + return view.grid.onCellChange.notify({ + row: 1, + cell: 0, + item: new_item, + grid: view.grid + }, e, view.grid); +}); + test('renderers', function () { var dataset = Fixture.getDataset(); diff --git a/vendor/slickgrid/2.0.1/README.txt b/vendor/slickgrid/2.0.1/README.txt index 90448433..c043a290 100644 --- a/vendor/slickgrid/2.0.1/README.txt +++ b/vendor/slickgrid/2.0.1/README.txt @@ -1,7 +1,7 @@ This compiled version of SlickGrid has been obtained with the Google Closure Compiler, using the following command: -java -jar compiler.jar --js=slick.core.js --js=slick.grid.js --js_output_file=slick.grid.min.js +java -jar compiler.jar --js=slick.core.js --js=slick.grid.js --js=slick.editors.js --js_output_file=slick.grid.min.js There are two other files required for the SlickGrid view to work properly: diff --git a/vendor/slickgrid/2.0.1/images/calendar.gif b/vendor/slickgrid/2.0.1/images/calendar.gif new file mode 100644 index 0000000000000000000000000000000000000000..90fd2e17fe247d3252977a0fb5b9e15aa0a513fb GIT binary patch literal 1035 zcmZ?wbhEHb6krfw_|5)@S9f&o11MQ?xO!cw>Iaru@>)1r=M0E4P+ZZ7mII zIULeKgDU;7-PP>>t`%1~&E2Rss zmoK_ev+zRIlAE$=f(b?AOC-O$p6(*{}&|uUzqrRQPTg#DgT!MQR@E+qCNbq%HSb);wrm_poFAqn?eAdp14k-|}?A)@PHqKcBko;gs#qr|o(M^svF)$&Z_>+Z^f#E-c4#;{?o?zg3!SJ6`#$&^S1A0Mo zOd^|}cuZ7utZDF2x^QZKw}f%r8-))i=S!QW?1-3n$c0l#`Co>>jVJxRayDfzPBbdB z@d(x%aio0O$kr>VAGIN%@zD`^9;T^(W-@hfEzoLM$MA{iMIURU+pmy=lUPqr(`R8} zQ7}$#a!{(>!NA06dGg`n cVfH2m1!<$04Gs?(n;)OBm5A{(P++hI0EaJh%K!iX literal 0 HcmV?d00001 diff --git a/vendor/slickgrid/2.0.1/slick.grid.min.js b/vendor/slickgrid/2.0.1/slick.grid.min.js index edf0208f..35e7d85b 100644 --- a/vendor/slickgrid/2.0.1/slick.grid.min.js +++ b/vendor/slickgrid/2.0.1/slick.grid.min.js @@ -1,9 +1,9 @@ -(function(e){function G(){var d=!1,e=!1;this.stopPropagation=function(){d=!0};this.isPropagationStopped=function(){return d};this.stopImmediatePropagation=function(){e=!0};this.isImmediatePropagationStopped=function(){return e}}function A(){this.__nonDataRow=!0}function ba(){this.__group=!0;this.__updated=!1;this.count=0;this.title=this.value=null;this.collapsed=!1;this.totals=null}function g(){this.__groupTotals=!0;this.group=null}function f(){var d=null;this.isActive=function(e){return e?d===e: -null!==d};this.activate=function(e){if(e!==d){if(null!==d)throw"SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController";if(!e.commitCurrentEdit)throw"SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";if(!e.cancelCurrentEdit)throw"SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";d=e}};this.deactivate=function(e){if(d!==e)throw"SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; -d=null};this.commitCurrentEdit=function(){return d?d.commitCurrentEdit():!0};this.cancelCurrentEdit=function(){return d?d.cancelCurrentEdit():!0}}e.extend(!0,window,{Slick:{Event:function(){var d=[];this.subscribe=function(e){d.push(e)};this.unsubscribe=function(e){for(var f=d.length-1;f>=0;f--)d[f]===e&&d.splice(f,1)};this.notify=function(e,f,g){for(var f=f||new G,g=g||this,A,ma=0;ma=this.fromRow&&d<=this.toRow&&e>=this.fromCell&&e<=this.toCell};this.toString=function(){return this.isSingleCell()?"("+this.fromRow+":"+this.fromCell+")":"("+this.fromRow+":"+this.fromCell+" - "+this.toRow+":"+this.toCell+")"}},NonDataRow:A,Group:ba,GroupTotals:g,EditorLock:f,GlobalEditorLock:new f}});ba.prototype= -new A;ba.prototype.equals=function(d){return this.value===d.value&&this.count===d.count&&this.collapsed===d.collapsed};g.prototype=new A})(jQuery);/* +(function(c){function g(){var d=!1,c=!1;this.stopPropagation=function(){d=!0};this.isPropagationStopped=function(){return d};this.stopImmediatePropagation=function(){c=!0};this.isImmediatePropagationStopped=function(){return c}}function e(){this.__nonDataRow=!0}function i(){this.__group=!0;this.__updated=!1;this.count=0;this.title=this.value=null;this.collapsed=!1;this.totals=null}function j(){this.__groupTotals=!0;this.group=null}function h(){var d=null;this.isActive=function(c){return c?d===c:null!== +d};this.activate=function(c){if(c!==d){if(null!==d)throw"SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController";if(!c.commitCurrentEdit)throw"SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";if(!c.cancelCurrentEdit)throw"SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";d=c}};this.deactivate=function(c){if(d!==c)throw"SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; +d=null};this.commitCurrentEdit=function(){return d?d.commitCurrentEdit():!0};this.cancelCurrentEdit=function(){return d?d.cancelCurrentEdit():!0}}c.extend(!0,window,{Slick:{Event:function(){var d=[];this.subscribe=function(c){d.push(c)};this.unsubscribe=function(c){for(var h=d.length-1;0<=h;h--)d[h]===c&&d.splice(h,1)};this.notify=function(c,h,e){for(var h=h||new g,e=e||this,j,i=0;i=this.fromRow&&d<=this.toRow&&c>=this.fromCell&&c<=this.toCell};this.toString=function(){return this.isSingleCell()?"("+this.fromRow+":"+this.fromCell+")":"("+this.fromRow+":"+this.fromCell+" - "+this.toRow+":"+this.toCell+")"}},NonDataRow:e,Group:i,GroupTotals:j,EditorLock:h,GlobalEditorLock:new h}});i.prototype=new e;i.prototype.equals= +function(d){return this.value===d.value&&this.count===d.count&&this.collapsed===d.collapsed};j.prototype=new e})(jQuery);/* (c) 2009-2012 Michael Leibman michael{dot}leibman{at}gmail{dot}com @@ -21,64 +21,74 @@ new A;ba.prototype.equals=function(d){return this.value===d.value&&this.count=== and do proper cleanup. */ if("undefined"===typeof jQuery)throw"SlickGrid requires jquery module to be loaded";if(!jQuery.fn.drag)throw"SlickGrid requires jquery.event.drag module to be loaded";if("undefined"===typeof Slick)throw"slick.core.js not loaded"; -(function(e){e.extend(!0,window,{Slick:{Grid:function(ba,g,f,d){function fb(){if(!H){H=true;U=parseFloat(e.css(n[0],"width",true));Vb();ma(t);d.enableTextSelectionOnCells||v.bind("selectstart.ui",function(a){return e(a.target).is("input,textarea")});hb();Wb();ib();K();Xb();n.bind("resize.slickgrid",K);v.bind("scroll.slickgrid",jb);ca.bind("contextmenu.slickgrid",Yb).bind("click.slickgrid",Zb);Ka.bind("keydown.slickgrid",kb);B.bind("keydown.slickgrid",kb).bind("click.slickgrid",$b).bind("dblclick.slickgrid", -ac).bind("contextmenu.slickgrid",bc).bind("draginit",cc).bind("dragstart",dc).bind("drag",ec).bind("dragend",fc).delegate(".slick-cell","mouseenter",gc).delegate(".slick-cell","mouseleave",hc)}}function gb(a){for(var b=O.length;b>=0;b--)if(O[b]===a){O[b].destroy&&O[b].destroy();O.splice(b,1);break}}function Ub(){var a=e("
").appendTo("body"),b={width:a.width()-a[0].clientWidth,height:a.height()- -a[0].clientHeight};a.remove();return b}function Ja(a){for(var b=P,c=na?U-G.width:U,j=0,e=f.length;e--;)j=j+(f[e].width||La.width);P=d.fullWidthRows?Math.max(j,c):j;if(P!=b){B.width(P);da.width(P);lb=P>U-G.width}(P!=b||a)&&Ma()}function ma(a){a&&a.jquery&&a.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})}function ic(){for(var a=1E6,b=e.browser.mozilla?5E6:1E9,c=e("
").appendTo(document.body);a<=b;){c.css("height",a+1E6); -if(c.height()!==a+1E6)break;else a=a+1E6}c.remove();return a}function Xb(){for(var a=B[0];(a=a.parentNode)!=document.body&&a!=null;)(a==v[0]||a.scrollWidth!=a.clientWidth||a.scrollHeight!=a.clientHeight)&&e(a).bind("scroll.slickgrid",mb)}function hb(){function a(){e(this).addClass("ui-state-hover")}function b(){e(this).removeClass("ui-state-hover")}t.empty();da.empty();I={};for(var c=0;c").html(""+j.name+"").width(j.width-V).attr("title",j.toolTip||j.name||"").data("fieldId",j.id).addClass(j.headerCssClass||"").appendTo(t);(d.enableColumnReorder||j.sortable)&&oa.hover(a,b);j.sortable&&oa.append("");d.showHeaderRow&&e("
").appendTo(da)}d.showHeaderRow&&e("
").appendTo(Q); -ta(r);nb();d.enableColumnReorder&&jc()}function Wb(){t.click(function(a){a.metaKey=a.metaKey||a.ctrlKey;if(!e(a.target).hasClass("slick-resizable-handle")){var b=e(a.target).closest(".slick-header-column");if(b.length){b=f[Na(b.data("fieldId"))];if(b.sortable&&p().commitCurrentEdit()){for(var c=null,j=0;j=g)){e(m);e("
").appendTo(m).bind("dragstart",function(g){if(!p().commitCurrentEdit())return false;c=g.pageX;e(this).parent().addClass("slick-header-column-active");var h=g=null;j.each(function(a,b){f[a].previousWidth=e(b).outerWidth()});if(d.forceFitColumns){h=g=0;for(a=k+1;a=0;a--){b=f[a];if(b.resizable){e=Math.max(b.minWidth||0,R);if(h&&b.previousWidth+h=0;a--){b=f[a];if(b.resizable)if(h&&b.maxWidth&&b.maxWidth-b.previousWidth
").appendTo(t);V=Pa=0;e.each(b,function(b,c){V=V+(parseFloat(a.css(c))||0)});e.each(c,function(b,c){Pa=Pa+(parseFloat(a.css(c))||0)});a.remove();var j=e("
").appendTo(B);a=e("").appendTo(j); -ua=va=0;e.each(b,function(b,c){ua=ua+(parseFloat(a.css(c))||0)});e.each(c,function(b,c){va=va+(parseFloat(a.css(c))||0)});j.remove();R=Math.max(V,ua)}function ib(){X=e("