Permalink
Browse files

HtmlTable.Select: add support for multiple tables on the same page (k…

…eyboard focus)
  • Loading branch information...
1 parent 0d937eb commit fcc9477fb560f6cc347a518fa32565debc9dc9d4 @anutron anutron committed Jan 5, 2011
Showing with 127 additions and 70 deletions.
  1. +65 −62 Source/Interface/HtmlTable.Select.js
  2. +60 −7 Specs/1.3/Interface/HtmlTable.Select.js
  3. +2 −1 Specs/Configuration.js
@@ -44,11 +44,14 @@ HtmlTable = Class.refactor(HtmlTable, {
this.previous.apply(this, arguments);
if (this.occluded) return this.occluded;
- this._selectedRows = new Elements();
-
- this._bound = {
- mouseleave: this._mouseleave.bind(this),
- clickRow: this._clickRow.bind(this)
+ this.selectedRows = new Elements();
+
+ this.bound = {
@fat

fat May 2, 2011

Contributor

this is a breaking change.

@seanmonstar

seanmonstar May 2, 2011

It looks like its private properties, there's no contract when it comes to private stuff.

@fat

fat May 2, 2011

Contributor

with this commit, both HtmlTable.select and HtmlTable.sort now both define a property bound. Because these classes use refactor to extend htmltable, they are effectively overwriting eachothers values. Therefore it is a breaking change internal to mootools.

+ mouseleave: this.mouseleave.bind(this),
+ clickRow: this.clickRow.bind(this),
+ activateKeyboard: function() {
+ if (this.keyboard && this.selectEnabled) this.keyboard.activate();
+ }.bind(this)
};
if (this.options.selectable) this.enableSelect();
@@ -60,27 +63,27 @@ HtmlTable = Class.refactor(HtmlTable, {
},
enableSelect: function(){
- this._selectEnabled = true;
- this._attachSelects();
+ this.selectEnabled = true;
+ this.attachSelects();
this.element.addClass(this.options.classSelectable);
return this;
},
disableSelect: function(){
- this._selectEnabled = false;
- this._attachSelects(false);
+ this.selectEnabled = false;
+ this.attachSelects(false);
this.element.removeClass(this.options.classSelectable);
return this;
},
push: function(){
var ret = this.previous.apply(this, arguments);
- this._updateSelects();
+ this.updateSelects();
return ret;
},
isSelected: function(row){
- return this._selectedRows.contains(row);
+ return this.selectedRows.contains(row);
},
toggleRow: function(row){
@@ -94,26 +97,26 @@ HtmlTable = Class.refactor(HtmlTable, {
if (!this.options.allowMultiSelect) this.selectNone();
if (!this.isSelected(row)){
- this._selectedRows.push(row);
+ this.selectedRows.push(row);
row.addClass(this.options.classRowSelected);
- this.fireEvent('rowFocus', [row, this._selectedRows]);
+ this.fireEvent('rowFocus', [row, this.selectedRows]);
this.fireEvent('stateChanged');
}
- this._focused = row;
+ this.focused = row;
document.clearSelection();
return this;
},
getSelected: function(){
- return this._selectedRows;
+ return this.selectedRows;
},
serialize: function() {
var previousSerialization = this.previous.apply(this, arguments) || {};
if (this.options.selectable) {
- previousSerialization.selectedRows = this._selectedRows.map(function(row) {
+ previousSerialization.selectedRows = this.selectedRows.map(function(row) {
return Array.indexOf(this.body.rows, row);
}.bind(this));
}
@@ -132,9 +135,9 @@ HtmlTable = Class.refactor(HtmlTable, {
deselectRow: function(row, _nocheck){
if (!this.isSelected(row) || (!_nocheck && !this.body.getChildren().contains(row))) return;
- this._selectedRows = new Elements(Array.from(this._selectedRows).erase(row));
+ this.selectedRows = new Elements(Array.from(this.selectedRows).erase(row));
row.removeClass(this.options.classRowSelected);
- this.fireEvent('rowUnfocus', [row, this._selectedRows]);
+ this.fireEvent('rowUnfocus', [row, this.selectedRows]);
this.fireEvent('stateChanged');
return this;
},
@@ -174,101 +177,104 @@ HtmlTable = Class.refactor(HtmlTable, {
},
getSelected: function(){
- return this._selectedRows;
+ return this.selectedRows;
},
/*
Private methods:
*/
- _enterRow: function(row){
- if (this._hovered) this._hovered = this._leaveRow(this._hovered);
- this._hovered = row.addClass(this.options.classRowHovered);
+ enterRow: function(row){
+ if (this.hovered) this.hovered = this.leaveRow(this.hovered);
+ this.hovered = row.addClass(this.options.classRowHovered);
},
- _leaveRow: function(row){
+ leaveRow: function(row){
row.removeClass(this.options.classRowHovered);
},
- _updateSelects: function(){
+ updateSelects: function(){
Array.each(this.body.rows, function(row){
var binders = row.retrieve('binders');
- if (!binders && !this._selectEnabled) return;
+ if (!binders && !this.selectEnabled) return;
if (!binders){
binders = {
- mouseenter: this._enterRow.pass([row], this),
- mouseleave: this._leaveRow.pass([row], this)
+ mouseenter: this.enterRow.pass([row], this),
+ mouseleave: this.leaveRow.pass([row], this)
};
row.store('binders', binders);
}
- if (this._selectEnabled) row.addEvents(binders);
+ if (this.selectEnabled) row.addEvents(binders);
else row.removeEvents(binders);
}, this);
},
- _shiftFocus: function(offset, event){
- if (!this._focused) return this.selectRow(this.body.rows[0], event);
- var to = this._getRowByOffset(offset);
- if (to === null || this._focused == this.body.rows[to]) return this;
+ shiftFocus: function(offset, event){
+ if (!this.focused) return this.selectRow(this.body.rows[0], event);
+ var to = this.getRowByOffset(offset);
+ if (to === null || this.focused == this.body.rows[to]) return this;
this.toggleRow(this.body.rows[to], event);
},
- _clickRow: function(event, row){
+ clickRow: function(event, row){
var selecting = (event.shift || event.meta || event.control) && this.options.shiftForMultiSelect;
if (!selecting && !(event.rightClick && this.isSelected(row) && this.options.allowMultiSelect)) this.selectNone();
if (event.rightClick) this.selectRow(row);
else this.toggleRow(row);
if (event.shift){
- this.selectRange(this._rangeStart || this.body.rows[0], row, this._rangeStart ? !this.isSelected(row) : true);
- this._focused = row;
+ this.selectRange(this.rangeStart || this.body.rows[0], row, this.rangeStart ? !this.isSelected(row) : true);
+ this.focused = row;
}
- this._rangeStart = row;
+ this.rangeStart = row;
},
- _getRowByOffset: function(offset){
- if (!this._focused) return 0;
+ getRowByOffset: function(offset){
+ if (!this.focused) return 0;
var rows = Array.clone(this.body.rows),
- index = rows.indexOf(this._focused) + offset;
+ index = rows.indexOf(this.focused) + offset;
if (index < 0) index = null;
if (index >= rows.length) index = null;
return index;
},
- _attachSelects: function(attach){
+ attachSelects: function(attach){
attach = attach != null ? attach : true;
var method = attach ? 'addEvents' : 'removeEvents';
this.element[method]({
- mouseleave: this._bound.mouseleave
+ mouseleave: this.bound.mouseleave,
+ click: this.bound.activateKeyboard
});
this.body[method]({
- 'click:relay(tr)': this._bound.clickRow,
- 'contextmenu:relay(tr)': this._bound.clickRow
+ 'click:relay(tr)': this.bound.clickRow,
+ 'contextmenu:relay(tr)': this.bound.clickRow
});
if (this.options.useKeyboard || this.keyboard){
- if (!this.keyboard){
+ if (!this.keyboard) this.keyboard = new Keyboard();
+ if (!this.selectKeysDefined) {
+ this.selectKeysDefined = true;
var timer, held;
var move = function(offset){
var mover = function(e){
clearTimeout(timer);
e.preventDefault();
- var to = this.body.rows[this._getRowByOffset(offset)];
+ var to = this.body.rows[this.getRowByOffset(offset)];
if (e.shift && to && this.isSelected(to)){
- this.deselectRow(this._focused);
- this._focused = to;
+ this.deselectRow(this.focused);
+ this.focused = to;
} else {
if (to && (!this.options.allowMultiSelect || !e.shift)){
this.selectNone();
}
- this._shiftFocus(offset, e);
+ this.shiftFocus(offset, e);
}
if (held){
@@ -287,17 +293,14 @@ HtmlTable = Class.refactor(HtmlTable, {
clearTimeout(timer);
held = false;
};
-
- this.keyboard = new Keyboard({
- events: {
- 'keydown:shift+up': move(-1),
- 'keydown:shift+down': move(1),
- 'keyup:shift+up': clear,
- 'keyup:shift+down': clear,
- 'keyup:up': clear,
- 'keyup:down': clear
- },
- active: true
+
+ this.keyboard.addEvents({
+ 'keydown:shift+up': move(-1),
+ 'keydown:shift+down': move(1),
+ 'keyup:shift+up': clear,
+ 'keyup:shift+down': clear,
+ 'keyup:up': clear,
+ 'keyup:down': clear
});
var shiftHint = '';
@@ -323,11 +326,11 @@ HtmlTable = Class.refactor(HtmlTable, {
}
this.keyboard[attach ? 'activate' : 'deactivate']();
}
- this._updateSelects();
+ this.updateSelects();
},
- _mouseleave: function(){
- if (this._hovered) this._leaveRow(this._hovered);
+ mouseleave: function(){
+ if (this.hovered) this.leaveRow(this.hovered);
}
});
@@ -1,16 +1,20 @@
describe('HtmlTable.Select', function(){
- it('should clear selections when emptying a table', function(){
- var SelectableTable = new HtmlTable({
+ var getTable = function(){
+ return new HtmlTable({
selectable: true,
- useKeyboard: false,
+ useKeyboard: true,
rows: [[0],[1],[2]]
});
+ };
- var row = SelectableTable.body.getChildren()[0];
- SelectableTable.selectRow(row);
- SelectableTable.empty();
- expect(SelectableTable.isSelected(row)).toEqual(false);
+ it('should clear selections when emptying a table', function(){
+ var table = getTable();
+
+ var row = table.body.getChildren()[0];
+ table.selectRow(row);
+ table.empty();
+ expect(table.isSelected(row)).toEqual(false);
});
it('should serialize the state of the table', function(){
@@ -37,4 +41,53 @@ describe('HtmlTable.Select', function(){
});
+ it('should allow keyboard events to change selection', function(){
+ var table = getTable().inject(document.body);
+
+ var rows = table.body.getChildren();
+
+ Syn.type('[down]', table.toElement());
+
+ expect(table.isSelected(rows[0])).toEqual(true);
+ Syn.type('[down]', table.toElement());
+ expect(table.isSelected(rows[0])).toEqual(false);
+ expect(table.isSelected(rows[1])).toEqual(true);
+ Syn.type('[shift][up]', table.toElement());
+ expect(table.isSelected(rows[0])).toEqual(true);
+ expect(table.isSelected(rows[1])).toEqual(true);
+ table.dispose();
+ });
+
+ it('should enable a table\'s keyboard', function(){
+ var table1 = getTable().inject(document.body);
+ var table2 = getTable().inject(document.body);
+ table1.toElement().id = 'one';
+ table2.toElement().id = 'two';
+
+ var t1rows = table1.body.getChildren();
+ var t2rows = table2.body.getChildren();
+
+ Syn.type('[down]', table2.toElement());
+
+ expect(table2.isSelected(t2rows[0])).toEqual(true);
+
+ //can't get the click to work for some reason...
+ //Syn.click({}, table1.toElement());
+ //works fine if I activate it manually
+ table1.keyboard.activate();
+
+ Syn.type('[down]', table1.toElement());
+ expect(table1.isSelected(t1rows[0])).toEqual(true);
+
+ //and this should be deactivated, so no change
+ Syn.type('[down]', table2.toElement());
+ expect(table2.isSelected(t2rows[0])).toEqual(true);
+
+ table1.dispose();
+ table2.dispose();
+
+
+ });
+
+
});
View
@@ -55,7 +55,7 @@ Configuration.sets = {
'Element/Element.Forms', 'Element/Element.Measure', 'Element/Elements.From', 'Element/Element.Shortcuts',
'Element/Element.Event.Pseudos', 'Element/Element.Event.Pseudos.Keys', 'Element/Element.Delegation', 'Element/Element.Pin', 'Element/Element.Position',
'Types/URI', 'Types/URI.Relative', 'Types/Object.Extras_client',
- 'Interface/Keyboard', 'Interface/HtmlTable', 'Interface/HtmlTable.Sort', 'Interface/HtmlTable.Select',
+ 'Interface/Keyboard', 'Interface/Keyboard.Extras', 'Interface/HtmlTable', 'Interface/HtmlTable.Sort', 'Interface/HtmlTable.Select',
'Forms/Form.Validator',
'Fx/Fx.Reveal',
'Request/Request.JSONP',
@@ -182,6 +182,7 @@ Configuration.source = {
'Types/URI.Relative',
'Interface/Keyboard',
+ 'Interface/Keyboard.Extras',
'Interface/HtmlTable',
'Interface/HtmlTable.Sort',
'Interface/HtmlTable.Select',

0 comments on commit fcc9477

Please sign in to comment.