Move TagTile to separate file, and make it dragable
parent
82a95f0793
commit
a8a650c24a
|
@ -77,6 +77,8 @@
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-addons-css-transition-group": "15.3.2",
|
"react-addons-css-transition-group": "15.3.2",
|
||||||
|
"react-dnd": "^2.1.4",
|
||||||
|
"react-dnd-html5-backend": "^2.1.2",
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||||
"sanitize-html": "^1.14.1",
|
"sanitize-html": "^1.14.1",
|
||||||
|
|
|
@ -15,82 +15,17 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { DragDropContext } from 'react-dnd';
|
||||||
|
import HTML5Backend from 'react-dnd-html5-backend';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import classNames from 'classnames';
|
|
||||||
import FilterStore from '../../stores/FilterStore';
|
import FilterStore from '../../stores/FilterStore';
|
||||||
import FlairStore from '../../stores/FlairStore';
|
import FlairStore from '../../stores/FlairStore';
|
||||||
import TagOrderStore from '../../stores/TagOrderStore';
|
import TagOrderStore from '../../stores/TagOrderStore';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import { isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
|
||||||
|
|
||||||
const TagTile = React.createClass({
|
const TagPanel = React.createClass({
|
||||||
displayName: 'TagTile',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupProfile: PropTypes.object,
|
|
||||||
},
|
|
||||||
|
|
||||||
contextTypes: {
|
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
hover: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onClick: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'select_tag',
|
|
||||||
tag: this.props.groupProfile.groupId,
|
|
||||||
ctrlOrCmdKey: isOnlyCtrlOrCmdKeyEvent(e),
|
|
||||||
shiftKey: e.shiftKey,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseOver: function() {
|
|
||||||
this.setState({hover: true});
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseOut: function() {
|
|
||||||
this.setState({hover: false});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
||||||
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
|
|
||||||
const profile = this.props.groupProfile || {};
|
|
||||||
const name = profile.name || profile.groupId;
|
|
||||||
const avatarHeight = 35;
|
|
||||||
|
|
||||||
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
|
|
||||||
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
const className = classNames({
|
|
||||||
mx_TagTile: true,
|
|
||||||
mx_TagTile_selected: this.props.selected,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tip = this.state.hover ?
|
|
||||||
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
|
|
||||||
<div />;
|
|
||||||
return <AccessibleButton className={className} onClick={this.onClick}>
|
|
||||||
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
|
||||||
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
|
||||||
{ tip }
|
|
||||||
</div>
|
|
||||||
</AccessibleButton>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default React.createClass({
|
|
||||||
displayName: 'TagPanel',
|
displayName: 'TagPanel',
|
||||||
|
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
|
@ -166,14 +101,16 @@ export default React.createClass({
|
||||||
render() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
|
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
||||||
|
|
||||||
const orderedGroupProfiles = this.state.orderedTags ?
|
const orderedGroupProfiles = this.state.orderedTags ?
|
||||||
this.state.joinedGroupProfiles.sort((a, b) =>
|
this.state.joinedGroupProfiles.sort((a, b) =>
|
||||||
this.state.orderedTags.indexOf(a.groupId) -
|
this.state.orderedTags.indexOf(a.groupId) -
|
||||||
this.state.orderedTags.indexOf(b.groupId),
|
this.state.orderedTags.indexOf(b.groupId),
|
||||||
) : this.state.joinedGroupProfiles;
|
) : this.state.joinedGroupProfiles;
|
||||||
|
|
||||||
const tags = orderedGroupProfiles.map((groupProfile, index) => {
|
const tags = orderedGroupProfiles.map((groupProfile, index) => {
|
||||||
return <TagTile
|
return <DNDTagTile
|
||||||
key={groupProfile.groupId + '_' + index}
|
key={groupProfile.groupId + '_' + index}
|
||||||
groupProfile={groupProfile}
|
groupProfile={groupProfile}
|
||||||
selected={this.state.selectedTags.includes(groupProfile.groupId)}
|
selected={this.state.selectedTags.includes(groupProfile.groupId)}
|
||||||
|
@ -189,3 +126,4 @@ export default React.createClass({
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
export default DragDropContext(HTML5Backend)(TagPanel);
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector Ltd.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DragSource, DropTarget } from 'react-dnd';
|
||||||
|
|
||||||
|
import TagTile from './TagTile';
|
||||||
|
import dis from '../../../dispatcher';
|
||||||
|
import { findDOMNode } from 'react-dom';
|
||||||
|
|
||||||
|
const tagTileSource = {
|
||||||
|
canDrag: function(props, monitor) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
beginDrag: function(props) {
|
||||||
|
// Return the data describing the dragged item
|
||||||
|
return {
|
||||||
|
tag: props.groupProfile.groupId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrag: function(props, monitor, component) {
|
||||||
|
const dropResult = monitor.getDropResult();
|
||||||
|
if (!monitor.didDrop() || !dropResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'commit_tags',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagTileTarget = {
|
||||||
|
canDrop(props, monitor) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
hover(props, monitor, component) {
|
||||||
|
if (!monitor.canDrop()) return;
|
||||||
|
const draggedY = monitor.getClientOffset().y;
|
||||||
|
const {top, bottom} = findDOMNode(component).getBoundingClientRect();
|
||||||
|
const targetY = (top + bottom) / 2;
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'order_tag',
|
||||||
|
tag: monitor.getItem().tag,
|
||||||
|
targetTag: props.groupProfile.groupId,
|
||||||
|
// Note: we indicate that the tag should be after the target when
|
||||||
|
// it's being dragged over the top half of the target.
|
||||||
|
after: draggedY < targetY,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
drop(props) {
|
||||||
|
// Return the data to be returned by getDropResult
|
||||||
|
return {
|
||||||
|
tag: props.groupProfile.groupId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default
|
||||||
|
DropTarget('TagTile', tagTileTarget, (connect, monitor) => ({
|
||||||
|
connectDropTarget: connect.dropTarget(),
|
||||||
|
}))(DragSource('TagTile', tagTileSource, (connect, monitor) => ({
|
||||||
|
connectDragSource: connect.dragSource(),
|
||||||
|
}))((props) => {
|
||||||
|
const { connectDropTarget, connectDragSource, ...otherProps } = props;
|
||||||
|
return connectDropTarget(connectDragSource(
|
||||||
|
<div>
|
||||||
|
<TagTile {...otherProps} />
|
||||||
|
</div>,
|
||||||
|
));
|
||||||
|
}));
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector Ltd.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import dis from '../../../dispatcher';
|
||||||
|
import { isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard';
|
||||||
|
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'TagTile',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
groupProfile: PropTypes.object,
|
||||||
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
hover: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'select_tag',
|
||||||
|
tag: this.props.groupProfile.groupId,
|
||||||
|
ctrlOrCmdKey: isOnlyCtrlOrCmdKeyEvent(e),
|
||||||
|
shiftKey: e.shiftKey,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseOver: function() {
|
||||||
|
this.setState({hover: true});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseOut: function() {
|
||||||
|
this.setState({hover: false});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
|
||||||
|
const profile = this.props.groupProfile || {};
|
||||||
|
const name = profile.name || profile.groupId;
|
||||||
|
const avatarHeight = 35;
|
||||||
|
|
||||||
|
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
|
||||||
|
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const className = classNames({
|
||||||
|
mx_TagTile: true,
|
||||||
|
mx_TagTile_selected: this.props.selected,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tip = this.state.hover ?
|
||||||
|
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
|
||||||
|
<div />;
|
||||||
|
return <AccessibleButton className={className} onClick={this.onClick}>
|
||||||
|
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
||||||
|
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
||||||
|
{ tip }
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>;
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue