From 1b3acc94155595c2612c41fbf8e3e519d04687f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Fri, 16 May 2014 09:21:20 +0400 Subject: [PATCH 01/13] Big update: Resizable columns, minor bug fixes. --- bbGrid.js | 196 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 30 deletions(-) diff --git a/bbGrid.js b/bbGrid.js index 6e10f0e..089396d 100644 --- a/bbGrid.js +++ b/bbGrid.js @@ -18,6 +18,15 @@ this.lang = lang; } } + }, + parseWidth = function (node) { + return parseFloat(node.style.width.replace('%', '')); + }, + pointerX = function (e) { + if (e.type.indexOf('touch') === 0) { + return (e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]).pageX; + } + return e.pageX; }; bbGrid.Dict = { @@ -45,7 +54,7 @@ 'enableSearch', 'multiselect', 'rows', 'rowList', 'selectedRows', 'subgrid', 'subgridControl', 'subgridAccordion', 'onRowClick', 'onRowDblClick', 'onReady', 'onBeforeRender', 'onBeforeCollectionRequest', 'onRowExpanded', - 'onRowCollapsed', 'events', 'searchList']; + 'onRowCollapsed', 'events', 'searchList', 'sortSequence', 'resizable', 'resizeFromBody']; bbGrid.RowView = function (options) { this.events = { @@ -72,7 +81,10 @@ \ \ <%} _.each(values, function (row) {%>\ - class="bbGrid-actions-cell"<%}%>>\ + class="<%=row.className%>"<% } %> contenteditable="<%=row.editable || false%>"\ + <% _.each(row.attributes, function (value, key) { %>\ + <%=key%>="<%=value%>" \ + <% }); %>>\ <%=row.value%>\ \ <%})%>', null, templateSettings @@ -177,12 +189,15 @@ values: _.map(cols, function (col) { if (col.actions) { col.name = 'bbGrid-actions-cell'; + col.className = col.name; if (_.isFunction(col.actions)) { col.value = col.actions.call(self, self.model.id, self.model.attributes, self.view); } else { col.value = self.view.actions[col.actions].call(self, self.model.id, self.model.attributes, self.view); } } else { + col.attributes = _.omit(col, 'name', 'value', 'className', 'title', 'editable', 'width', 'index', + 'hidden', 'sorttype', 'filter', 'filterType', 'sortOrder', 'filterColName', 'resizable', 'attributes'); col.value = self.getPropByStr(self.model.attributes, col.name); } return col; @@ -192,7 +207,7 @@ this.selected = true; this.$el.addClass('warning'); } - this.$el.html(html); + this.$el.html(html).attr('data-cid', this.model.cid); return this; } }); @@ -217,10 +232,10 @@ '
\ \
\ @@ -253,6 +268,7 @@ this.view.render(); }, onPageChanged: function (event) { + event.preventDefault(); this.view.trigger('pageChanged', event); }, initPager: function () { @@ -298,12 +314,17 @@ bbGrid.PagerView.extend = Backbone.View.extend; bbGrid.TheadView = function (options) { + var self = this; this.events = { 'click th': 'onSort', 'click input[type=checkbox]': 'onAllCheckbox' }; Backbone.View.apply(this, [options]); this.view = options.view; + _.bindAll(this, 'handleTrigger'); + $(window).on('resize.rshandle', (function() { + self.refreshHandles(); + })); }; _.extend(bbGrid.TheadView.prototype, Backbone.View.prototype, { @@ -311,11 +332,12 @@ className: 'bbGrid-grid-head', template: _.template( '<% if (isMultiselect) {%>\ - \ + \ <%} if (isContainSubgrid) {%>\ - \ + \ <%} _.each(cols, function (col) {%>\ - style="width:<%=col.width%>"<%}%>><%=col.title%> data-noresize <% } %>\ + <% if (col.width) {%>style="width:<%=col.width%>"<%}%>><%=col.title%>class="icon-chevron-up"<%} else \ if (col.sortOrder === "desc" ) {%>class="icon-chevron-down"<% } %>/>\ <%})%>', null, templateSettings @@ -326,27 +348,127 @@ onSort: function (event) { this.view.trigger('sort', event); }, - render: function () { - var cols, theadHtml; + render: function (options) { + options || (options = {}); + var cols, theadHtml, grid = this.view; if (!this.$headHolder) { this.$headHolder = $('', {'class': 'bbGrid-grid-head-holder'}); this.$el.append(this.$headHolder); } - cols = _.filter(this.view.colModel, function (col) {return !col.hidden; }); - cols = _.map(cols, function (col) { col.title = col.title || col.name; return col; }); - this.view.colLength = cols.length + (this.view.multiselect ? 1 : 0) + (this.view.subgrid && this.view.subgridControl ? 1 : 0); + cols = _.filter(grid.colModel, function (col) {return !col.hidden; }); + this.cols = cols = _.map(cols, function (col) { + col.resizable = (col.resizable === undefined) ? grid.resizable : col.resizable; + col.title = col.title || col.name; + return col; + }); + this.isResizable = grid.resizable || _.chain(cols).pluck('resizable').compact().value().length > 0; + grid.colLength = cols.length + (grid.multiselect ? 1 : 0) + (grid.subgrid && grid.subgridControl ? 1 : 0); theadHtml = this.template({ - isMultiselect: this.view.multiselect, - isContainSubgrid: this.view.subgrid && this.view.subgridControl, + isMultiselect: grid.multiselect, + isContainSubgrid: grid.subgrid && grid.subgridControl, cols: cols }); this.$headHolder.html(theadHtml); - if (!this.view.$filterBar && this.view.enableFilter) { - this.view.filterBar = new this.view.entities.FilterView({ view: this.view }); - this.view.$filterBar = this.view.filterBar.render(); - this.$el.append(this.view.$filterBar); + if (!grid.$filterBar && grid.enableFilter) { + grid.filterBar = new grid.entities.FilterView({ view: grid }); + grid.$filterBar = grid.filterBar.render(); + this.$el.append(grid.$filterBar); + } + if (this.isResizable) { + this.$headers = this.$('.bbGrid-grid-head-holder th:visible'); + if (!options.silent) { + this.setHeadersWidthsInPerc(); + } + this.createHandles(); } return this.$el; + }, + setHeadersWidthsInPerc: function () { + // important! first calculate all widths, then -> set. + var self = this, calculatedArray = [], grid = this.view; + this.$headers.each(function (index, el) { + var $el = $(el); + calculatedArray.push({ + dom: $el[0], + width: $el.outerWidth() / grid.$grid.width() * 100, + index: index + }); + }); + _(calculatedArray).each(function (el) { + self.setWidth(el.dom, el.width, el.index); + }); + }, + createHandles: function () { + var _ref, self = this, grid = this.view; + if ((_ref = this.$handleContainer) != null) { + _ref.remove(); + } + grid.$grid.before((this.$handleContainer = $('
'))); + this.$headers.each(function (i, el) { + var $handle; + if (self.$headers.eq(i + 1).length === 0 || (self.$headers.eq(i).attr('data-noresize') != null)) { + return; + } + $handle = $("
"); + $handle.data('th', $(el)); + return $handle.appendTo(self.$handleContainer); + }); + this.$handleContainer.on('mousedown touchstart', '.bbGrid-rshandle', this.handleTrigger); + this.refreshHandles(); + }, + refreshHandles: function () { + var self = this, grid = this.view; + if (!this.isResizable) { + return; + } + this.$handleContainer.width(grid.$grid.width()).find('.bbGrid-rshandle').each(function(_, el) { + var $el = $(el); + if (!$el.data('th')) { + return $el; + } + return $el.css({ + top: grid.$grid.find('> caption').height(), + left: $el.data('th').outerWidth() + ($el.data('th').offset().left - self.$handleContainer.offset().left), + height: grid.resizeFromBody ? grid.$grid.height() : self.$el.height() + }); + }); + }, + handleTrigger: function (e) { + var $currentGrip, $leftColumn, $rightColumn, startPosition, widths, leftColumnIndex, + rightColumnIndex, self = this, grid = this.view; + e.preventDefault(); + startPosition = pointerX(e); + $currentGrip = $(e.currentTarget); + $leftColumn = $currentGrip.data('th'); + leftColumnIndex = this.$headers.index($leftColumn); + $rightColumn = this.$headers.eq(leftColumnIndex + 1); + rightColumnIndex = this.$headers.index($rightColumn); + widths = { + left: parseWidth($leftColumn[0]), + right: parseWidth($rightColumn[0]) + }; + grid.$grid.addClass('bbGrid-table-resizing'); + $(document).on('mousemove.rshandle touchmove.rshandle', function(e) { + var difference; + difference = (pointerX(e) - startPosition) / grid.$grid.width() * 100; + self.setWidth($rightColumn[0], widths.right - difference, rightColumnIndex); + self.setWidth($leftColumn[0], widths.left + difference, leftColumnIndex); + }); + $(document).one('mouseup touchend', function() { + $(document).off('mousemove.rshandle touchmove.rshandle'); + grid.$grid.removeClass('bbGrid-table-resizing'); + self.refreshHandles(); + }); + }, + setWidth: function (node, width, index) { + width = "" + width.toFixed(2) + "%"; + if (index) { + index = index + (this.view.multiselect ? -1 : 0) + (this.view.subgrid && this.view.subgridControl ? -1 : 0); + if (this.cols[index]) { + this.cols[index].width = width; + } + } + node.style.width = width; } }); @@ -504,11 +626,11 @@ <<% if (col.filterType === "input") \ {%>input<%}else{%>select<%\ }%> class="<%if (col.filterColName) {%><%=col.filterColName%><%}else{%><%=col.name %><%}%>" \ - name="filter" type="text" value="<%=filterOptions[col.name]%>">\ + name="filter" type="text" <% if (filterOptions[col.name]) { %>value="<%=filterOptions[col.name].value%>"<% } %>>\ <% if (col.filterType !== "input") {%>\ \ <% _.each(options[col.name], function (option) {%>\ - \ <%})%>\ @@ -524,9 +646,13 @@ onFilter: function () { var text, self = this, collection; _.each($('*[name=filter]', this.$el), function (el) { - text = $.trim($(el).val()); + var type = el.tagName.toLowerCase(); + text = (type === 'select') ? $(el).val() : $.trim($(el).val()); if (text) { - self.view.filterOptions[el.className] = text; + self.view.filterOptions[el.className] = { + value: text, + filterType: type + }; } else { delete self.view.filterOptions[el.className]; } @@ -535,7 +661,7 @@ collection = new Backbone.Collection(this.view._collection.models); this.view.setCollection(collection); if (_.keys(this.view.filterOptions).length) { - self.filter(collection, _.clone(this.view.filterOptions)); + self.filter(collection, $.extend(true, {}, this.view.filterOptions)); } } this.view.trigger('filter', {silent: !this.view.loadDynamic}); @@ -543,7 +669,8 @@ filter: function (collection, options) { var keys = _.keys(options), option, key = _.first(keys), - text = options[key]; + text = options[key] ? options[key].value : '', + filterType = options[key] ? options[key].filterType : ''; if (!keys.length) { return collection; } @@ -552,7 +679,11 @@ collection.reset(_.filter(collection.models, function (model) { option = model.get(key); if (option) { - return ("" + option).toLowerCase().indexOf(text.toLowerCase()) >= 0; + if (filterType === 'select') { + return ("" + option).toLowerCase() === text.toLowerCase(); + } else { + return ("" + option).toLowerCase().indexOf(text.toLowerCase()) >= 0; + } } else { return false; } @@ -628,6 +759,8 @@ _.extend(bbGrid.View.prototype, Backbone.View.prototype, { selectionEnabled: true, subgridControl: true, + resizable: false, + resizeFromBody: true, lang: bbGrid.lang, tagName: 'div', className: 'bbGrid-container', @@ -865,7 +998,7 @@ } }, setRowSelected: function (options) { - var event = {}, className; + var event = {}, className = ''; options || (options = {}); if (!this.selectionEnabled) { return false; @@ -932,6 +1065,9 @@ addModelsHandler: function (model, collection, options) { var index = this.collection.indexOf(model); if ((index + 1) === this.collection.length) { + if (this.sortSequence && this.sortSequence.length) { + this.sortBy(this.sortSequence); + } this.renderPage(); } }, @@ -1031,7 +1167,7 @@ this.rsortBy(col); } } - this.thead.render(); + this.thead.render({silent: true}); this.renderPage({silent: !this.loadDynamic}); }, onDblClick: function (model, $el) { From 820677ef7652b1cf35148bf54c22d89fdedd180f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Fri, 16 May 2014 09:21:56 +0400 Subject: [PATCH 02/13] minor bug fixes --- bbGrid.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bbGrid.css b/bbGrid.css index 5b63fb7..6064ff6 100644 --- a/bbGrid.css +++ b/bbGrid.css @@ -21,7 +21,7 @@ .bbGrid-container .bbGrid-loading .bbGrid-loading-progress{ width: 100%; line-height: 20px} .bbGrid-container .table{ margin: 0 } .bbGrid-container .bbGrid-pager-container{height: 26px; width: 380px; left: 50%; margin-left:-190px; position: absolute} -.bbGrid-container .bbGrid-pager-container-norowslist{width: 200px; margin-left:-100px} +.bbGrid-container .bbGrid-pager-container-norowslist{width: 205px; margin-left:-100px} .bbGrid-container .bbGrid-grid-nav{ margin: 0; background-color: #F5F5F5; background-repeat: repeat-x; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) #A2A2A2; border-image: none; border-radius: 4px 4px 4px 4px; border-style: solid; border-width: 1px; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); color: #333333; padding: 0 0 1px 1px; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; position: relative; height: 28px} .bbGrid-container .bbGrid-subgrid-row:hover{ background-color: transparent} .bbGrid-container .bbGrid-pager{ margin: 2px 0 0; cursor: pointer} @@ -35,7 +35,7 @@ .bbGrid-container .bbGrid-page-counter {font-size: 11px; padding: 2px 0px 0px 4px} .bbGrid-container .bbGrid-page-counter-right {font-size: 11px; padding: 2px 2px 0px 4px} .bbGrid-container .bbGrid-grid-nav .bbGrid-page-input{float: left; padding: 1px; margin: 0 0 0 1px; width: 30px; font-size: 11px;text-align: center } -.bbGrid-navBar-buttonsContainer {margin: 2px 0 0} +.bbGrid-grid-nav .bbGrid-navBar-buttonsContainer {margin: 2px 0 0} .bbGrid-navBar-buttonsContainer button{margin-right: 1px} .bbGrid-container .bbGrid-grid-nav .bbGrid-pager-rowlist {width: 56px; margin-top: 2px; margin-left:2px; padding: 3px 2px; height: 24px; font-size: 11px} .bbGrid-pager-container .bbGrid-pager ul li {list-style:none;padding-left: 0;} @@ -50,3 +50,7 @@ .bbGrid-search-bar input, .bbGrid-search-bar button{padding: 1px 6px;} .bbGrid-search-bar input{ font-size: 12px} .bbGrid-search-bar li > a{font-size: 12px; padding-top: 1px; padding-bottom: 1px} +.bbGrid-rshandle-container {position: relative;} +.bbGrid-rshandle {position: absolute;width: 7px;cursor: ew-resize;margin-left: -3px;z-index: 2;} +.bbGrid-table-resizing {cursor: ew-resize;} +.bbGrid-table-resizing thead, .bbGrid-table-resizing thead > th, .bbGrid-table-resizing thead > th > a {cursor: ew-resize;} \ No newline at end of file From ff22d04bda4ae8db0baed3c530711ea4296dc417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Fri, 16 May 2014 10:04:48 +0400 Subject: [PATCH 03/13] Added an escape option. --- bbGrid.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bbGrid.js b/bbGrid.js index 089396d..02c991e 100644 --- a/bbGrid.js +++ b/bbGrid.js @@ -53,7 +53,7 @@ viewOptions = ['autofetch', 'buttons', 'actions', 'colModel', 'container', 'enableSearch', 'multiselect', 'rows', 'rowList', 'selectedRows', 'subgrid', 'subgridControl', 'subgridAccordion', 'onRowClick', 'onRowDblClick', 'onReady', - 'onBeforeRender', 'onBeforeCollectionRequest', 'onRowExpanded', + 'onBeforeRender', 'onBeforeCollectionRequest', 'onRowExpanded', 'escape', 'onRowCollapsed', 'events', 'searchList', 'sortSequence', 'resizable', 'resizeFromBody']; bbGrid.RowView = function (options) { @@ -85,7 +85,7 @@ <% _.each(row.attributes, function (value, key) { %>\ <%=key%>="<%=value%>" \ <% }); %>>\ - <%=row.value%>\ + <% if (isEscaped || row.escape) { %><%=_.escape(row.value)%><% } else { %><%=row.value%><% } %>\ \ <%})%>', null, templateSettings ), @@ -186,6 +186,7 @@ isSelected: this.selected || false, isChecked: isChecked, isDisabled: isDisabled, + isEscaped: this.view.escape, values: _.map(cols, function (col) { if (col.actions) { col.name = 'bbGrid-actions-cell'; @@ -196,7 +197,7 @@ col.value = self.view.actions[col.actions].call(self, self.model.id, self.model.attributes, self.view); } } else { - col.attributes = _.omit(col, 'name', 'value', 'className', 'title', 'editable', 'width', 'index', + col.attributes = _.omit(col, 'name', 'value', 'className', 'title', 'editable', 'width', 'index', 'escape', 'hidden', 'sorttype', 'filter', 'filterType', 'sortOrder', 'filterColName', 'resizable', 'attributes'); col.value = self.getPropByStr(self.model.attributes, col.name); } @@ -757,6 +758,7 @@ }; _.extend(bbGrid.View.prototype, Backbone.View.prototype, { + escape: false, selectionEnabled: true, subgridControl: true, resizable: false, From d4743f5668a93fb58c99b7e71d3cb7ae9e76b9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Wed, 21 May 2014 12:27:22 +0400 Subject: [PATCH 04/13] Bug fixes for filter.view --- bbGrid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbGrid.js b/bbGrid.js index 02c991e..f642675 100644 --- a/bbGrid.js +++ b/bbGrid.js @@ -659,7 +659,7 @@ } }); if (!this.view.loadDynamic) { - collection = new Backbone.Collection(this.view._collection.models); + collection = new this.view._collection.constructor(this.view._collection.models); this.view.setCollection(collection); if (_.keys(this.view.filterOptions).length) { self.filter(collection, $.extend(true, {}, this.view.filterOptions)); @@ -679,7 +679,7 @@ if (text.length > 0) { collection.reset(_.filter(collection.models, function (model) { option = model.get(key); - if (option) { + if (option !== undefined) { if (filterType === 'select') { return ("" + option).toLowerCase() === text.toLowerCase(); } else { From 08638fbe790e9924d7a89e77542ce6968662b8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Fri, 31 Oct 2014 13:39:50 +0300 Subject: [PATCH 05/13] Bootstrap 2 > 3 moving --- bbGrid.js | 76 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/bbGrid.js b/bbGrid.js index f642675..04a3175 100644 --- a/bbGrid.js +++ b/bbGrid.js @@ -1,4 +1,4 @@ -// bbGrid.js 0.8.5 +// bbGrid.js 0.9.1 // (c) 2012-2013 Minin Alexey, direct-fuel-injection. // bbGrid may be freely distributed under the MIT license. @@ -78,7 +78,7 @@ disabled="disabled"<% } %><% if (isChecked) {%>checked="checked"<%}%>>\ <%} if (isContainSubgrid) {%>\ \ - \ + \ \ <%} _.each(values, function (row) {%>\ class="<%=row.className%>"<% } %> contenteditable="<%=row.editable || false%>"\ @@ -198,7 +198,7 @@ } } else { col.attributes = _.omit(col, 'name', 'value', 'className', 'title', 'editable', 'width', 'index', 'escape', - 'hidden', 'sorttype', 'filter', 'filterType', 'sortOrder', 'filterColName', 'resizable', 'attributes'); + 'hidden', 'sorttype', 'filter', 'filterType', 'sortOrder', 'filterColName', 'resizable', 'attributes', 'tooltip'); col.value = self.getPropByStr(self.model.attributes, col.name); } return col; @@ -228,32 +228,32 @@ _.extend(bbGrid.PagerView.prototype, Backbone.View.prototype, { tagName: 'div', - className: 'bbGrid-pager-container span offset', + className: 'bbGrid-pager-container span col-md-1 col-md-offset-1', template: _.template( - '
\ + '
\ \
\ <% if (rowlist) {%>\
<%=dict.rowsOnPage%>:
\ - \ <% _.each(rowlist, function (val) {%>\ \ <%})%>\ @@ -335,12 +335,13 @@ '<% if (isMultiselect) {%>\ \ <%} if (isContainSubgrid) {%>\ - \ + \ <%} _.each(cols, function (col) {%>\ - data-noresize <% } %>\ + title="<%=col.tooltip%>" <% } %>\ + <% if (!col.resizable) {%> data-noresize <% } %>\ <% if (col.width) {%>style="width:<%=col.width%>"<%}%>><%=col.title%>class="icon-chevron-up"<%} else \ - if (col.sortOrder === "desc" ) {%>class="icon-chevron-down"<% } %>/>\ + if (col.sortOrder === "asc" ) {%>class="glyphicon glyphicon-chevron-up"<%} else \ + if (col.sortOrder === "desc" ) {%>class="glyphicon glyphicon-chevron-down"<% } %>/>\ <%})%>', null, templateSettings ), onAllCheckbox: function (event) { @@ -463,7 +464,7 @@ }, setWidth: function (node, width, index) { width = "" + width.toFixed(2) + "%"; - if (index) { + if (index >= 0) { index = index + (this.view.multiselect ? -1 : 0) + (this.view.subgrid && this.view.subgridControl ? -1 : 0); if (this.cols[index]) { this.cols[index].width = width; @@ -486,12 +487,12 @@ render: function () { if (this.view.buttons) { var self = this, btn, btnHtml, $button; - this.view.$buttonsContainer = $('
', {'class': 'bbGrid-navBar-buttonsContainer btn-group span'}); + this.view.$buttonsContainer = $('
', {'class': 'bbGrid-navBar-buttonsContainer input-group-btn col-md-1'}); this.view.buttons = _.map(this.view.buttons, function (button) { if (!button) { return undefined; } - btn = _.template('', null, templateSettings); + btn = _.template('', null, templateSettings); btnHtml = button.html || btn({id: button.id, title: button.title}); $button = $(btnHtml).appendTo(self.view.$buttonsContainer); if (button.onClick) { @@ -529,12 +530,12 @@ tagName: 'div', className: 'bbGrid-search-bar pull-right', template: _.template( - '
\ - \ -
\ - \