I have a project underway with an interesting dilemma. I have created a component (from Recommended way of making React component/div draggable) and modified it so that the onMouseDown is only on the element not on document. In the same page I have an which is not registering the click event, although it receives focus on the right click. I am enclosing my entire code here of both the element and the input layout if anybody can spot why I cannot click to focus on the text inputs.
Draggable.js
import React from 'react';
import PropTypes from 'prop-types';
class Draggable extends React.Component {
constructor(props) {
super(props);
this.state = {
relX: 0,
relY: 0,
x: parseInt(props.left),
y: parseInt(props.top),
dragged: false
};
this.gridX = props.gridX || 2;
this.gridY = props.gridY || 2;
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onContext = this.onContext.bind(this);
this.clickRef = React.createRef();
}
static propTypes = {
onMove: PropTypes.func,
onStop: PropTypes.func,
onContext: PropTypes.func,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
gridX: PropTypes.number,
gridY: PropTypes.number
};
onStart(e) {
if (this.props.enabled) {
this.setState({
relX: e.pageX - this.props.left,
relY: e.pageY - this.props.top
})
}
}
onMove(e, current) {
if (this.props.enabled) {
const x = Math.trunc((e.pageX - this.state.relX) / this.gridX) * this.gridX;
const y = Math.trunc((e.pageY - this.state.relY) / this.gridY) * this.gridY;
if (x !== this.state.x || y !== this.state.y) {
this.setState({
x,
y,
dragged: true
});
this.props.onMove && this.props.onMove({ left: this.state.x, top: this.state.y });
}
}
}
onMouseDown(e) {
if (e.button !== 0) return;
this.onStart(e);
this.clickRef.current.addEventListener('mousemove', this.onMouseMove);
this.clickRef.current.addEventListener('mouseup', this.onMouseUp);
e.preventDefault();
}
onMouseUp(e) {
this.clickRef.current.removeEventListener('mousemove', this.onMouseMove);
this.clickRef.current.removeEventListener('mouseup', this.onMouseUp);
this.props.onStop && this.props.onStop(e, this.props.index ? this.props.index : null, { left: this.state.x, top: this.state.y, dragged: this.state.dragged });
this.setState({ dragged: false });
if (this.props.onStop) e.preventDefault();
}
onMouseMove(e) {
this.onMove(e, this.props.index ? this.props.index : null, { left: this.state.x, top: this.state.y });
e.preventDefault();
}
onContext(e) {
if (this.props.onRightClick) {
e.preventDefault();
this.props.onRightClick(e, this.props.index ? this.props.index : null);
}
}
render() {
let style = {}
if (this.props.style) {
style = { ...this.props.style }
}
if (!(style.position && style.position === 'fixed')) {
style.position = 'absolute';
}
style.left = this.state.x + 'px';
style.top = this.state.y + 'px'
return <div
onMouseDown={this.onMouseDown}
onContextMenu={this.onContext}
onMouseMove={this.onMouseMove}
onMouseUp={this.onMouseUp}
style={style}
ref={this.clickRef}
>
{this.props.children}
</div>;
}
}
export default Draggable;
And the function creating the draggable components:
buildDesks = () => {
const newScale = this.getScale();
const layout = this.props.layout;
let container = document.querySelector("#Layout_Map_Container");
const desks = layout.desks;//.filter(desk => parseInt(desk.SiteId) === parseInt(map.id));
let ret = desks.map((desk, index) => {
let deskImg = null;
try {
let dImg = layout.deskTypes.find(d => parseInt(d.deskType) === parseInt(desk.deskType));
deskImg = dImg.deskImage;
}
catch (ex) {
console.log(ex);
}
const userName = desk.UserLogon !== (null || '') ? desk.UserLogon : "Unassigned";
const top = Math.trunc(parseInt(parseInt(desk.y) * newScale));
const left = Math.trunc(parseInt(parseInt(desk.x) * newScale));
const imgStyle = {
width: `${parseInt(parseInt(desk.width) * newScale)}px`,
height: `${parseInt((parseInt(desk.height) * newScale))}px`,
transform: `rotate(${parseInt(desk.rotation)}deg)`
}
const url = `data:image/jpeg;base64,${deskImg}`;
try {
return (
<Draggable key={desk.DeskID}
index={desk.DeskID}
enabled={this.state.edit}
left={left}
top={top}
onMove={this.updateProperties}
onStop={this.endMove}
onRightClick={this.rightClick}
>
<img style={imgStyle} alt={userName} src={url} />
</Draggable>
);
}
catch (ex) {
console.log(ex);
return null;
}
});//desks.map
return ret;
}//buildDesks
Next the creation of the text boxes:
showStatus = () => {
//screen width - object width - right coordinate
let left = parseInt(window.innerWidth - 225);
let top = 106;
// return (<div></div>);
return (
<Draggable enabled={true}
top={top}
left={left}
>
<div style={{ width: '200px' }} className='editData'
style={{ position: 'fixed', top: top + 'px', left: left + 'px' }}>
<Row className='statusRow'>
<Col sm={12}>
<div id="Layout_UserImg" title="Click to upload new user image">
<div id="Layout_UserImgLabel">
Click to
upload new
user image</div>
</div>
</Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Manager</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_Manager" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>User</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_User" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Emp. Id</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_EmpId" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Extension</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_Extension" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Department</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_Department" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>DB Id</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_DBRowId" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Desk ID</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_DeskID" /></Col>
</Row>
<Row className='statusRow'>
<Col sm={5} className='statusDivHeading'>Assets</Col>
<Col sm={7} className='statusDivData'><input type='text' id="Layout_Assets" /></Col>
</Row>
<Row>
<Col>
<button onClick={this.updateStats}>Save Changes</button>
</Col>
<Col>
<button onClick={this.clearUserStats}>Clear all fields</button>
</Col>
</Row >
</div >
</Draggable >
)
}
and finally my render:
render() {
if (this.props.layout.isLoading) {
return (<Loading title="Site Layout" />);
}
else if (this.props.layout.isLoadingMap) {
const map = this.props.layout.maps[this.props.layout.currentMap];
const siteName = map.SiteName;
return (
<Row>
<Col sm={1}></Col>
<Col sm={10} id="Layout_Map_Container">
<Loading title={"map '" + siteName + "'"} />
</Col>
</Row>
);
}
else if (this.props.layout.mapLoaded) {
return (
<div>
<Row>
<Col sm={1}>
{this.showAdmin()}
</Col>
<Col sm={10}>
{this.state.details}
</Col>
</Row>
<Row>
<Col sm={1}>
<select onChange={(e) => this.changeMap(e.target)}>
{this.buildMapOptions()}
</select>
</Col>
<Col sm={10} id="Layout_Map_Container">
{this.buildMap()}
{this.buildDesks()}
</Col>
</Row >
{this.showStatus()}
</div>
);
}
else {
return (
<Row>
<Col sm={1}>
<select onChange={(e) => this.changeMap(e.target)}>
{this.buildMapOptions()}
</select>
</Col>
<Col sm={10} id="Layout_Map_Container">
</Col>
</Row>
);
}
}
A lot of this code is still unrefined so if you see any changes that should be made please point them out also. In the meantime, why do the text boxes in showStatus() not work?
Thank you.