Fix sidebar interaction
- Clicking a file row or selecting it will open the sidebar. - When sidebar is open, its contents update with the last selection. - Dragging doesn't open the sidebar but does update its contents if it was open already. - Switching folders closes the sidebar. - Close sidebar when highlighted file got deleted/removed from list
This commit is contained in:
parent
d68079f932
commit
f9acf4627e
|
@ -239,13 +239,6 @@
|
||||||
|
|
||||||
this.updateSearch();
|
this.updateSearch();
|
||||||
|
|
||||||
this.$el.on('click', function(event) {
|
|
||||||
var $target = $(event.target);
|
|
||||||
// click outside file row ?
|
|
||||||
if (!$target.closest('tbody').length && !$target.closest('#app-sidebar').length) {
|
|
||||||
self._updateDetailsView(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this));
|
this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this));
|
||||||
this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
|
this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
|
||||||
this.$el.on('urlChanged', _.bind(this._onUrlChanged, this));
|
this.$el.on('urlChanged', _.bind(this._onUrlChanged, this));
|
||||||
|
@ -278,6 +271,9 @@
|
||||||
if (this._newButton) {
|
if (this._newButton) {
|
||||||
this._newButton.remove();
|
this._newButton.remove();
|
||||||
}
|
}
|
||||||
|
if (this._detailsView) {
|
||||||
|
this._detailsView.remove();
|
||||||
|
}
|
||||||
// TODO: also unregister other event handlers
|
// TODO: also unregister other event handlers
|
||||||
this.fileActions.off('registerAction', this._onFileActionsUpdated);
|
this.fileActions.off('registerAction', this._onFileActionsUpdated);
|
||||||
this.fileActions.off('setDefault', this._onFileActionsUpdated);
|
this.fileActions.off('setDefault', this._onFileActionsUpdated);
|
||||||
|
@ -307,7 +303,6 @@
|
||||||
permissions: OC.PERMISSION_READ,
|
permissions: OC.PERMISSION_READ,
|
||||||
actionHandler: function(fileName, context) {
|
actionHandler: function(fileName, context) {
|
||||||
self._updateDetailsView(fileName);
|
self._updateDetailsView(fileName);
|
||||||
OC.Apps.showAppSidebar(self._detailsView.$el);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -408,9 +403,14 @@
|
||||||
this._currentFileModel.off();
|
this._currentFileModel.off();
|
||||||
}
|
}
|
||||||
this._currentFileModel = null;
|
this._currentFileModel = null;
|
||||||
|
OC.Apps.hideAppSidebar(this._detailsView.$el);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._detailsView.$el.hasClass('disappear')) {
|
||||||
|
OC.Apps.showAppSidebar(this._detailsView.$el);
|
||||||
|
}
|
||||||
|
|
||||||
var $tr = this.findFileEl(fileName);
|
var $tr = this.findFileEl(fileName);
|
||||||
var model = this.getModelForFile($tr);
|
var model = this.getModelForFile($tr);
|
||||||
|
|
||||||
|
@ -453,10 +453,10 @@
|
||||||
* Selected/deselects the given file element and updated
|
* Selected/deselects the given file element and updated
|
||||||
* the internal selection cache.
|
* the internal selection cache.
|
||||||
*
|
*
|
||||||
* @param $tr single file row element
|
* @param {Object} $tr single file row element
|
||||||
* @param state true to select, false to deselect
|
* @param {bool} state true to select, false to deselect
|
||||||
*/
|
*/
|
||||||
_selectFileEl: function($tr, state) {
|
_selectFileEl: function($tr, state, showDetailsView) {
|
||||||
var $checkbox = $tr.find('td.filename>.selectCheckBox');
|
var $checkbox = $tr.find('td.filename>.selectCheckBox');
|
||||||
var oldData = !!this._selectedFiles[$tr.data('id')];
|
var oldData = !!this._selectedFiles[$tr.data('id')];
|
||||||
var data;
|
var data;
|
||||||
|
@ -475,11 +475,8 @@
|
||||||
delete this._selectedFiles[$tr.data('id')];
|
delete this._selectedFiles[$tr.data('id')];
|
||||||
this._selectionSummary.remove(data);
|
this._selectionSummary.remove(data);
|
||||||
}
|
}
|
||||||
if (this._selectionSummary.getTotal() === 1) {
|
if (this._detailsView && this._selectionSummary.getTotal() === 1 && !this._detailsView.$el.hasClass('disappear')) {
|
||||||
this._updateDetailsView(_.values(this._selectedFiles)[0].name);
|
this._updateDetailsView(_.values(this._selectedFiles)[0].name);
|
||||||
} else {
|
|
||||||
// show nothing when multiple files are selected
|
|
||||||
this._updateDetailsView(null);
|
|
||||||
}
|
}
|
||||||
this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length);
|
this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length);
|
||||||
},
|
},
|
||||||
|
@ -489,6 +486,9 @@
|
||||||
*/
|
*/
|
||||||
_onClickFile: function(event) {
|
_onClickFile: function(event) {
|
||||||
var $tr = $(event.target).closest('tr');
|
var $tr = $(event.target).closest('tr');
|
||||||
|
if ($tr.hasClass('dragging')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._allowSelection && (event.ctrlKey || event.shiftKey)) {
|
if (this._allowSelection && (event.ctrlKey || event.shiftKey)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
|
@ -552,9 +552,13 @@
|
||||||
*/
|
*/
|
||||||
_onClickFileCheckbox: function(e) {
|
_onClickFileCheckbox: function(e) {
|
||||||
var $tr = $(e.target).closest('tr');
|
var $tr = $(e.target).closest('tr');
|
||||||
this._selectFileEl($tr, !$tr.hasClass('selected'));
|
var state = !$tr.hasClass('selected');
|
||||||
|
this._selectFileEl($tr, state);
|
||||||
this._lastChecked = $tr;
|
this._lastChecked = $tr;
|
||||||
this.updateSelectionSummary();
|
this.updateSelectionSummary();
|
||||||
|
if (state) {
|
||||||
|
this._updateDetailsView($tr.attr('data-file'));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1305,8 +1309,10 @@
|
||||||
sortdirection: this._sortDirection
|
sortdirection: this._sortDirection
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// close sidebar
|
if (this._detailsView) {
|
||||||
this._updateDetailsView(null);
|
// close sidebar
|
||||||
|
this._updateDetailsView(null);
|
||||||
|
}
|
||||||
var callBack = this.reloadCallback.bind(this);
|
var callBack = this.reloadCallback.bind(this);
|
||||||
return this._reloadCall.then(callBack, callBack);
|
return this._reloadCall.then(callBack, callBack);
|
||||||
},
|
},
|
||||||
|
@ -1513,11 +1519,12 @@
|
||||||
remove: function(name, options){
|
remove: function(name, options){
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var fileEl = this.findFileEl(name);
|
var fileEl = this.findFileEl(name);
|
||||||
|
var fileId = fileEl.data('id');
|
||||||
var index = fileEl.index();
|
var index = fileEl.index();
|
||||||
if (!fileEl.length) {
|
if (!fileEl.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (this._selectedFiles[fileEl.data('id')]) {
|
if (this._selectedFiles[fileId]) {
|
||||||
// remove from selection first
|
// remove from selection first
|
||||||
this._selectFileEl(fileEl, false);
|
this._selectFileEl(fileEl, false);
|
||||||
this.updateSelectionSummary();
|
this.updateSelectionSummary();
|
||||||
|
@ -1527,6 +1534,14 @@
|
||||||
fileEl.find('td.filename').draggable('destroy');
|
fileEl.find('td.filename').draggable('destroy');
|
||||||
}
|
}
|
||||||
this.files.splice(index, 1);
|
this.files.splice(index, 1);
|
||||||
|
if (this._currentFileModel && this._currentFileModel.get('id') === fileId) {
|
||||||
|
// Note: in the future we should call destroy() directly on the model
|
||||||
|
// and the model will take care of the deletion.
|
||||||
|
// Here we only trigger the event to notify listeners that
|
||||||
|
// the file was removed.
|
||||||
|
this._currentFileModel.trigger('destroy');
|
||||||
|
this._updateDetailsView(null);
|
||||||
|
}
|
||||||
fileEl.remove();
|
fileEl.remove();
|
||||||
// TODO: improve performance on batch update
|
// TODO: improve performance on batch update
|
||||||
this.isEmpty = !this.files.length;
|
this.isEmpty = !this.files.length;
|
||||||
|
|
|
@ -356,7 +356,7 @@ var createDragShadow = function(event) {
|
||||||
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
|
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
|
||||||
if (!isDragSelected) {
|
if (!isDragSelected) {
|
||||||
//select dragged file
|
//select dragged file
|
||||||
FileList._selectFileEl($(event.target).parents('tr:first'), true);
|
FileList._selectFileEl($(event.target).parents('tr:first'), true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not show drag shadow for too many files
|
// do not show drag shadow for too many files
|
||||||
|
@ -365,7 +365,7 @@ var createDragShadow = function(event) {
|
||||||
|
|
||||||
if (!isDragSelected && selectedFiles.length === 1) {
|
if (!isDragSelected && selectedFiles.length === 1) {
|
||||||
//revert the selection
|
//revert the selection
|
||||||
FileList._selectFileEl($(event.target).parents('tr:first'), false);
|
FileList._selectFileEl($(event.target).parents('tr:first'), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// build dragshadow
|
// build dragshadow
|
||||||
|
@ -413,22 +413,17 @@ var dragOptions={
|
||||||
cursor: 'move',
|
cursor: 'move',
|
||||||
start: function(event, ui){
|
start: function(event, ui){
|
||||||
var $selectedFiles = $('td.filename input:checkbox:checked');
|
var $selectedFiles = $('td.filename input:checkbox:checked');
|
||||||
if($selectedFiles.length > 1){
|
if (!$selectedFiles.length) {
|
||||||
$selectedFiles.parents('tr').fadeTo(250, 0.2);
|
$selectedFiles = $(this);
|
||||||
}
|
|
||||||
else{
|
|
||||||
$(this).fadeTo(250, 0.2);
|
|
||||||
}
|
}
|
||||||
|
$selectedFiles.closest('tr').fadeTo(250, 0.2).addClass('dragging');
|
||||||
},
|
},
|
||||||
stop: function(event, ui) {
|
stop: function(event, ui) {
|
||||||
var $selectedFiles = $('td.filename input:checkbox:checked');
|
var $selectedFiles = $('td.filename input:checkbox:checked');
|
||||||
if($selectedFiles.length > 1){
|
if (!$selectedFiles.length) {
|
||||||
$selectedFiles.parents('tr').fadeTo(250, 1);
|
$selectedFiles = $(this);
|
||||||
}
|
}
|
||||||
else{
|
$selectedFiles.closest('tr').fadeTo(250, 1).removeClass('dragging');
|
||||||
$(this).fadeTo(250, 1);
|
|
||||||
}
|
|
||||||
$('#fileList tr td.filename').addClass('ui-draggable');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// sane browsers support using the distance option
|
// sane browsers support using the distance option
|
||||||
|
|
|
@ -135,6 +135,9 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
testFiles = undefined;
|
testFiles = undefined;
|
||||||
|
if (fileList) {
|
||||||
|
fileList.destroy();
|
||||||
|
}
|
||||||
fileList = undefined;
|
fileList = undefined;
|
||||||
|
|
||||||
notificationStub.restore();
|
notificationStub.restore();
|
||||||
|
@ -1881,8 +1884,9 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
describe('Details sidebar', function() {
|
describe('Details sidebar', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
fileList.setFiles(testFiles);
|
fileList.setFiles(testFiles);
|
||||||
|
fileList.showDetailsView('Two.jpg');
|
||||||
});
|
});
|
||||||
it('Clicking on a file row will trigger file action if no details view configured', function() {
|
it('triggers file action when clicking on row if no details view configured', function() {
|
||||||
fileList._detailsView = null;
|
fileList._detailsView = null;
|
||||||
var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView');
|
var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView');
|
||||||
var actionStub = sinon.stub();
|
var actionStub = sinon.stub();
|
||||||
|
@ -1904,7 +1908,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
expect(updateDetailsViewStub.notCalled).toEqual(true);
|
expect(updateDetailsViewStub.notCalled).toEqual(true);
|
||||||
updateDetailsViewStub.restore();
|
updateDetailsViewStub.restore();
|
||||||
});
|
});
|
||||||
it('Clicking on a file row will trigger details sidebar', function() {
|
it('highlights current file when clicked and updates sidebar', function() {
|
||||||
fileList.fileActions.setDefault('text/plain', 'Test');
|
fileList.fileActions.setDefault('text/plain', 'Test');
|
||||||
var $tr = fileList.findFileEl('One.txt');
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
$tr.find('td.filename>a.name').click();
|
$tr.find('td.filename>a.name').click();
|
||||||
|
@ -1912,14 +1916,34 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
|
|
||||||
expect(fileList._detailsView.getFileInfo().id).toEqual(1);
|
expect(fileList._detailsView.getFileInfo().id).toEqual(1);
|
||||||
});
|
});
|
||||||
it('Clicking outside to deselect a file row will trigger details sidebar', function() {
|
it('keeps the last highlighted file when clicking outside', function() {
|
||||||
var $tr = fileList.findFileEl('One.txt');
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
$tr.find('td.filename>a.name').click();
|
$tr.find('td.filename>a.name').click();
|
||||||
|
|
||||||
fileList.$el.find('tfoot').click();
|
fileList.$el.find('tfoot').click();
|
||||||
|
|
||||||
expect($tr.hasClass('highlighted')).toEqual(false);
|
expect($tr.hasClass('highlighted')).toEqual(true);
|
||||||
expect(fileList._detailsView.getFileInfo()).toEqual(null);
|
expect(fileList._detailsView.getFileInfo().id).toEqual(1);
|
||||||
|
});
|
||||||
|
it('keeps the last highlighted file when unselecting file using checkbox', function() {
|
||||||
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
|
$tr.find('input:checkbox').click();
|
||||||
|
expect($tr.hasClass('highlighted')).toEqual(true);
|
||||||
|
$tr.find('input:checkbox').click();
|
||||||
|
|
||||||
|
expect($tr.hasClass('highlighted')).toEqual(true);
|
||||||
|
expect(fileList._detailsView.getFileInfo().id).toEqual(1);
|
||||||
|
});
|
||||||
|
it('closes sidebar whenever the currently highlighted file was removed from the list', function() {
|
||||||
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
|
$tr.find('td.filename>a.name').click();
|
||||||
|
expect($tr.hasClass('highlighted')).toEqual(true);
|
||||||
|
|
||||||
|
expect(fileList._detailsView.getFileInfo().id).toEqual(1);
|
||||||
|
|
||||||
|
expect($('#app-sidebar').hasClass('disappear')).toEqual(false);
|
||||||
|
fileList.remove('One.txt');
|
||||||
|
expect($('#app-sidebar').hasClass('disappear')).toEqual(true);
|
||||||
});
|
});
|
||||||
it('returns the currently selected model instance when calling getModelForFile', function() {
|
it('returns the currently selected model instance when calling getModelForFile', function() {
|
||||||
var $tr = fileList.findFileEl('One.txt');
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
|
@ -1935,6 +1959,14 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
var model3 = fileList.getModelForFile($tr);
|
var model3 = fileList.getModelForFile($tr);
|
||||||
expect(model3).toEqual(model1);
|
expect(model3).toEqual(model1);
|
||||||
});
|
});
|
||||||
|
it('closes the sidebar when switching folders', function() {
|
||||||
|
var $tr = fileList.findFileEl('One.txt');
|
||||||
|
$tr.find('td.filename>a.name').click();
|
||||||
|
|
||||||
|
expect($('#app-sidebar').hasClass('disappear')).toEqual(false);
|
||||||
|
fileList.changeDirectory('/another');
|
||||||
|
expect($('#app-sidebar').hasClass('disappear')).toEqual(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('File actions', function() {
|
describe('File actions', function() {
|
||||||
it('Clicking on a file name will trigger default action', function() {
|
it('Clicking on a file name will trigger default action', function() {
|
||||||
|
|
Loading…
Reference in New Issue