implement most of drag & drop.

pull/342/head
Matthew Hodgson 2015-11-04 02:25:08 +00:00
parent 7fe7af6026
commit 61e55b3ca3
3 changed files with 202 additions and 7 deletions

View File

@ -17,6 +17,8 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var DragSource = require('react-dnd').DragSource;
var DropTarget = require('react-dnd').DropTarget;
var classNames = require('classnames'); var classNames = require('classnames');
var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile') var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile')
@ -25,10 +27,89 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
module.exports = React.createClass({ /**
* Specifies the drag source contract.
* Only `beginDrag` function is required.
*/
var roomTileSource = {
beginDrag: function (props) {
// Return the data describing the dragged item
var item = {
room: props.room,
originalList: props.roomSubList,
originalIndex: props.roomSubList.findRoomTile(props.room).index,
targetList: props.roomSubList, // at first target is same as original
};
console.log("roomTile beginDrag for " + item.room.roomId);
return item;
},
endDrag: function (props, monitor, component) {
var item = monitor.getItem();
var dropResult = monitor.getDropResult();
console.log("roomTile endDrag for " + item.room.roomId + " with didDrop=" + monitor.didDrop());
if (!monitor.didDrop() || !item.targetList.props.editable) {
props.roomSubList.moveRoomTile(item.room, item.originalIndex);
if (item.targetList && item.targetList !== item.originalList) {
item.targetList.removeRoomTile(item.room);
}
return;
}
else {
// if it's not manual ordering, we'll need to position the tile correctly here according to the right ordering
// When dropped on a compatible target, actually set the right tags for the new ordering
// persistNewOrder(item.room, dropResult.listId);
}
}
};
var roomTileTarget = {
canDrop: function() {
return false;
},
hover: function(props, monitor) {
var item = monitor.getItem();
console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver());
//console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList);
if (item.targetList !== props.roomSubList) {
// we've switched target, so remove the tile from the previous target.
// n.b. the previous target might actually be the source list.
item.targetList.removeRoomTile(item.room);
item.targetList = props.roomSubList;
}
if (item.targetList.props.order === 'manual' && item.room.roomId !== props.room.roomId) {
var roomTile = props.roomSubList.findRoomTile(props.room);
props.roomSubList.moveRoomTile(item.room, roomTile.index);
}
},
};
var RoomTile = React.createClass({
displayName: 'RoomTile', displayName: 'RoomTile',
mixins: [RoomTileController], mixins: [RoomTileController],
propTypes: {
connectDragSource: React.PropTypes.func.isRequired,
connectDropTarget: React.PropTypes.func.isRequired,
isDragging: React.PropTypes.bool.isRequired,
room: React.PropTypes.object.isRequired,
collapsed: React.PropTypes.bool.isRequired,
selected: React.PropTypes.bool.isRequired,
unread: React.PropTypes.bool.isRequired,
highlight: React.PropTypes.bool.isRequired,
isInvite: React.PropTypes.bool.isRequired,
roomSubList: React.PropTypes.object.isRequired,
},
getInitialState: function() { getInitialState: function() {
return( { hover : false }); return( { hover : false });
}, },
@ -92,7 +173,14 @@ module.exports = React.createClass({
} }
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar'); var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
return (
// These props are injected by React DnD,
// as defined by your `collect` function above:
var isDragging = this.props.isDragging;
var connectDragSource = this.props.connectDragSource;
var connectDropTarget = this.props.connectDropTarget;
return connectDragSource(connectDropTarget(
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> <div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<div className="mx_RoomTile_avatar"> <div className="mx_RoomTile_avatar">
<RoomAvatar room={this.props.room} width="24" height="24" /> <RoomAvatar room={this.props.room} width="24" height="24" />
@ -100,6 +188,26 @@ module.exports = React.createClass({
</div> </div>
{ label } { label }
</div> </div>
); ));
} }
}); });
// Export the wrapped version, inlining the 'collect' functions
// to more closely resemble the ES7
module.exports =
DropTarget('RoomTile', roomTileTarget, function(connect) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDropTarget: connect.dropTarget(),
}
})(
DragSource('RoomTile', roomTileSource, function(connect, monitor) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource(),
// You can ask the monitor about the current drag state:
isDragging: monitor.isDragging()
};
})(RoomTile));

View File

@ -17,10 +17,32 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var DropTarget = require('react-dnd').DropTarget;
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
module.exports = React.createClass({ var roomListTarget = {
canDrop: function() {
return true;
},
hover: function(props, monitor, component) {
var item = monitor.getItem();
if (component.state.sortedList.length == 0 && props.editable) {
console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver());
if (item.targetList !== component) {
item.targetList.removeRoomTile(item.room);
item.targetList = component;
}
component.moveRoomTile(item.room, 0);
}
},
};
var RoomSubList = React.createClass({
displayName: 'RoomSubList', displayName: 'RoomSubList',
propTypes: { propTypes: {
@ -80,14 +102,64 @@ module.exports = React.createClass({
this.setState({ sortedList: list.sort(comparator) }); this.setState({ sortedList: list.sort(comparator) });
}, },
moveRoomTile: function(room, atIndex) {
console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
//console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
var found = this.findRoomTile(room);
var rooms = this.state.sortedList;
if (found.room) {
console.log("removing at index " + found.index + " and adding at index " + atIndex);
rooms.splice(found.index, 1);
rooms.splice(atIndex, 0, found.room);
}
else {
console.log("Adding at index " + atIndex);
rooms.splice(atIndex, 0, room);
}
this.setState({ sortedList: rooms });
// console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
},
// XXX: this isn't invoked via a property method but indirectly via
// the roomList property method. Unsure how evil this is.
removeRoomTile: function(room) {
console.log("remove room " + room.roomId);
var found = this.findRoomTile(room);
var rooms = this.state.sortedList;
if (found.room) {
rooms.splice(found.index, 1);
}
else {
console.log*("Can't remove room " + room.roomId + " - can't find it");
}
this.setState({ sortedList: rooms });
},
findRoomTile: function(room) {
var index = this.state.sortedList.indexOf(room);
if (index >= 0) {
console.log("found: room: " + room + " with id " + room.roomId);
}
else {
console.log("didn't find room");
room = null;
}
return ({
room: room,
index: index,
});
},
makeRoomTiles: function() { makeRoomTiles: function() {
var self = this; var self = this;
var RoomTile = sdk.getComponent("molecules.RoomTile"); var RoomTile = sdk.getComponent("molecules.RoomTile");
return this.state.sortedList.map(function(room) { return this.state.sortedList.map(function(room) {
var selected = room.roomId == self.props.selectedRoom; var selected = room.roomId == self.props.selectedRoom;
// XXX: is it evil to pass in self as a prop to RoomTile?
return ( return (
<RoomTile <RoomTile
room={room} room={room}
roomSubList={self}
key={room.roomId} key={room.roomId}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
selected={selected} selected={selected}
@ -99,14 +171,17 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var connectDropTarget = this.props.connectDropTarget;
var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget'); var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
var label = this.props.collapsed ? null : this.props.label; var label = this.props.collapsed ? null : this.props.label;
//console.log("render: " + JSON.stringify(this.state.sortedList));
if (this.state.sortedList.length > 0 || this.props.editable) { if (this.state.sortedList.length > 0 || this.props.editable) {
return ( return connectDropTarget(
<div> <div>
<h2 className="mx_RoomSubList_label">{ this.props.label }</h2> <h2 className="mx_RoomSubList_label">{ this.props.collapsed ? '' : this.props.label }</h2>
<div className="mx_RoomSubList"> <div className="mx_RoomSubList">
{ this.makeRoomTiles() } { this.makeRoomTiles() }
</div> </div>
@ -122,3 +197,11 @@ module.exports = React.createClass({
} }
}); });
// Export the wrapped version, inlining the 'collect' functions
// to more closely resemble the ES7
module.exports =
DropTarget('RoomTile', roomListTarget, function(connect) {
return {
connectDropTarget: connect.dropTarget(),
}
})(RoomSubList);

View File

@ -17,6 +17,8 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var DragDropContext = require('react-dnd').DragDropContext;
var HTML5Backend = require('react-dnd/modules/backends/HTML5');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat') var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat')
@ -28,7 +30,7 @@ var dis = require('matrix-react-sdk/lib/dispatcher');
var Matrix = require("matrix-js-sdk"); var Matrix = require("matrix-js-sdk");
var ContextualMenu = require("../../../../ContextualMenu"); var ContextualMenu = require("../../../../ContextualMenu");
module.exports = React.createClass({ var MatrixChat = React.createClass({
displayName: 'MatrixChat', displayName: 'MatrixChat',
mixins: [MatrixChatController], mixins: [MatrixChatController],
@ -172,3 +174,5 @@ module.exports = React.createClass({
} }
} }
}); });
module.exports = DragDropContext(HTML5Backend)(MatrixChat);