implement most of drag & drop.
This commit is contained in:
parent
7fe7af6026
commit
61e55b3ca3
3 changed files with 202 additions and 7 deletions
|
@ -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));
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
Loading…
Add table
Reference in a new issue