Use proper Vue components and methods for selection

This commit is contained in:
An Phan 2016-01-17 17:59:58 +08:00
parent f3355746cd
commit adc77e882f
3 changed files with 67 additions and 46 deletions

View file

@ -26,6 +26,7 @@
data() { data() {
return { return {
playbackState: 'stopped', playbackState: 'stopped',
selected: false,
}; };
}, },
@ -57,6 +58,21 @@
break; break;
} }
}, },
/**
* Toggle the "selected" state of the current component.
*/
toggleSelectedState() {
this.selected = !this.selected;
},
select() {
this.selected = true;
},
deselect() {
this.selected = false;
}
}, },
events: { events: {

View file

@ -39,14 +39,15 @@
| limitBy numOfItems" | limitBy numOfItems"
is="song-item" is="song-item"
data-song-id="{{ item.id }}" data-song-id="{{ item.id }}"
track-by="$index" track-by="id"
:song="item" :song="item"
@click="rowClick" v-ref:rows
@click="rowClick(item.id, $event)"
draggable="true" draggable="true"
@dragstart="dragStart" @dragstart="dragStart(item.id, $event)"
@dragleave="removeDroppableState" @dragleave="removeDroppableState(item.id, $event)"
@dragover.prevent="allowDrop" @dragover.prevent="allowDrop(item.id, $event)"
@drop.stop.prevent="handleDrop" @drop.stop.prevent="handleDrop(item.id, $event)"
> >
</tr> </tr>
</tbody> </tbody>
@ -79,6 +80,7 @@
q: '', // The filter query q: '', // The filter query
sortKey: 'title', sortKey: 'title',
order: 1, order: 1,
componentCache: {},
}; };
}, },
@ -203,6 +205,22 @@
this.clearSelection(); this.clearSelection();
}, },
/**
* Get the song-item component that's associated with a song ID.
*
* @param {string} id The song ID.
*
* @return {Object} The Vue compoenent
*/
getComponentBySongId(id) {
// A Vue component can be removed (as a result of filter for example), so we check for its $el as well.
if (!this.componentCache[id] || !this.componentCache[id].$el) {
this.componentCache[id] = _.find(this.$refs.rows, { song: { id } });
}
return this.componentCache[id];
},
/** /**
* Capture A keydown event and select all if applicable. * Capture A keydown event and select all if applicable.
* *
@ -213,7 +231,7 @@
return; return;
} }
$(this.$els.wrapper).find('.song-item').addClass('selected'); _.invoke(this.$refs.rows, 'select');
this.gatherSelected(); this.gatherSelected();
}, },
@ -223,7 +241,8 @@
* @return {Array} An array of Song objects * @return {Array} An array of Song objects
*/ */
gatherSelected() { gatherSelected() {
var ids = _.map($(this.$els.wrapper).find('.song-item.selected'), row => $(row).data('song-id')); var selectedRows = _.where(this.$refs.rows, { selected: true });
var ids = _.map(selectedRows, row => row.song.id);
this.selectedSongs = songStore.byIds(ids); this.selectedSongs = songStore.byIds(ids);
}, },
@ -238,15 +257,14 @@
* ----------------------------------------------------------- * -----------------------------------------------------------
*/ */
rowClick(e) { rowClick(songId, e) {
var $target = $(e.target); var row = this.getComponentBySongId(songId);
var row = $target.is('tr') ? $target[0] : $target.parents('tr')[0];
// If we're on a touch device, or if Ctrl/Cmd key is pressed, just toggle selection.
// If we're on a touch device, just toggle selection.
if (isMobile.any) { if (isMobile.any) {
this.toggleRow(row); this.toggleRow(row);
this.gatherSelected(); this.gatherSelected();
return; return;
} }
@ -260,8 +278,8 @@
this.toggleRow(row); this.toggleRow(row);
} }
if (e.shiftKey && this.lastSelectedRow) { if (e.shiftKey && this.lastSelectedRow && this.lastSelectedRow.$el) {
this.selectRowsBetweenIndexes([this.lastSelectedRow.rowIndex, row.rowIndex]) this.selectRowsBetweenIndexes([this.lastSelectedRow.$el.rowIndex, row.$el.rowIndex]);
} }
} }
@ -271,26 +289,26 @@
/** /**
* Toggle select/unslect a row. * Toggle select/unslect a row.
* *
* @param {Object} row The row DOM. * @param {Object} row The song-item component
*/ */
toggleRow(row) { toggleRow(row) {
$(row).toggleClass('selected'); row.toggleSelectedState();
this.lastSelectedRow = row; this.lastSelectedRow = row;
}, },
selectRowsBetweenIndexes(indexes) { selectRowsBetweenIndexes(indexes) {
indexes.sort((a, b) => a - b); indexes.sort((a, b) => a - b);
var rows = $(this.lastSelectedRow).parents('tbody').find('tr'); var rows = $(this.$els.wrapper).find('tbody tr');
for (var i = indexes[0]; i <= indexes[1]; ++i) { for (var i = indexes[0]; i <= indexes[1]; ++i) {
$(rows[i-1]).addClass('selected'); this.getComponentBySongId($(rows[i-1]).data('song-id')).select();
} }
}, },
clearSelection() { clearSelection() {
this.selectedSongs = []; _.invoke(this.$refs.rows, 'deselect');
$(this.$els.wrapper).find('.song-item.selected').removeClass('selected'); this.gatherSelected();
}, },
/** /**
@ -300,10 +318,9 @@
* *
* @param {Object} e The event. * @param {Object} e The event.
*/ */
dragStart(e) { dragStart(songId, e) {
// Select the current target as well. // Select the current target as well.
$(e.target).addClass('selected'); this.getComponentBySongId(songId).select();
this.gatherSelected(); this.gatherSelected();
// We can opt for something like application/x-koel.text+plain here to sound fancy, // We can opt for something like application/x-koel.text+plain here to sound fancy,
@ -322,7 +339,7 @@
* *
* @param {Object} e The dragover event. * @param {Object} e The dragover event.
*/ */
allowDrop(e) { allowDrop(songId, e) {
if (this.type !== 'queue') { if (this.type !== 'queue') {
return; return;
} }
@ -333,7 +350,7 @@
return false; return false;
}, },
handleDrop(e) { handleDrop(songId, e) {
if (this.type !== 'queue') { if (this.type !== 'queue') {
return; return;
} }
@ -350,34 +367,17 @@
var $row = this.removeDroppableState(e); var $row = this.removeDroppableState(e);
queueStore.move(songs, songStore.byId($row.data('song-id'))); queueStore.move(songs, songStore.byId(songId));
return false; return false;
}, },
removeDroppableState(e) { removeDroppableState(songId, e) {
return $(e.target).parents('tr').removeClass('droppable'); return $(e.target).parents('tr').removeClass('droppable');
}, },
}, },
events: { events: {
/**
* Listen to queue:select-rows event and mark a range of song-item rows as selected
*
* @param {Array} range An array in the format of [startIndex, stopIndex]
*/
'queue:select-rows': function (range) {
if (this.type != 'queue') {
return;
}
var rows = $(this.$els.wrapper).find('.song-item');
for (i = range[0]; i <= range[1]; ++i) {
$(rows[i]).addClass('selected');
}
},
/** /**
* Listen to song:played event to do some logic. * Listen to song:played event to do some logic.
* *

View file

@ -79,8 +79,13 @@ export default {
* @param {?Function} cb * @param {?Function} cb
*/ */
addSongs(playlist, songs, cb = null) { addSongs(playlist, songs, cb = null) {
var count = playlist.songs.length;
playlist.songs = _.union(playlist.songs, songs); playlist.songs = _.union(playlist.songs, songs);
if (count === playlist.songs.length) {
return;
}
http.put(`playlist/${playlist.id}/sync`, { songs: _.pluck(playlist.songs, 'id') }, () => { http.put(`playlist/${playlist.id}/sync`, { songs: _.pluck(playlist.songs, 'id') }, () => {
if (cb) { if (cb) {
cb(); cb();