Reputation: 1954
I have a sortable list.
When the user drags a list item, I'd like to stylize the item's original position until the user stops dragging.
In this case, there is a border around a blank space in the list, indicating Poland Spring's original position.
My original idea was to place a container div directly behind each li
item. However, for that to work, the container div would need to be position:relative
and the li
items would need to be position:absolute
but for the purpose of a list, li
items need to be position:relative
.
Here is my relevant code:
import styled from '@emotion/styled';
const App = styled('div')`
font-family: sans-serif;
font-size: 1.5rem;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
ul {
margin: 0;
padding: 0;
list-style: none; !important
}
ul li {
background-color: #D3D3D3;
padding: 10px 20px;
position: relative;
//the line below does not work
//position: absolute;
display: flex;
line-height: 1;
list-style-type: none;
border-style: solid;
margin-top:10px;
cursor: pointer
}
`;
const Container = styled('div')`
background: red;
border-color: coral;
position: relative;
`;
class DragDropList extends React.Component {
*/ ....*/
onDragStart = (e, index) => {
this.draggedItem = this.state.items[index];
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target.parentNode);
//e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
};
onDragOver = index => {
const draggedOverItem = this.state.items[index];
// if the item is dragged over itself, ignore
if (this.draggedItem === draggedOverItem) {
return;
}
// filter out the currently dragged item
let order = this.state.order.filter(item => item !== this.draggedItem);
// add the dragged item after the dragged over item
order.splice(index, 0, this.draggedItem);
this.setState({ order });
};
onDragEnd = index => {
this.draggedIdx = null;
// filter out the currently dragged item
let items = this.state.order;
this.setState({ items });
};
render() {
return (
<App>
<main>
<ul>
{this.state.items.map((item, idx) => (
<div>
<Container>
<li
key={item + `idx`}
onDragOver={() => this.onDragOver(idx)}
draggable
onDragStart={e =>
this.onDragStart(e, idx)
}
onDragEnd={() => this.onDragEnd(idx)}
>
<div
draggable
onDragStart={e =>
this.onDragStart(e, idx)
}
onDragEnd={() =>
this.onDragEnd(idx)
}
>
<span
className="content"
style={{ cursor: 'pointer' }}
>
{item}
</span>
</div>
</li>
</Container>
</div>
))}
</ul>
</main>
</App>
);
}
}
Here is a sandbox: https://codesandbox.io/s/black-dust-hqm2t?fontsize=14
Upvotes: 0
Views: 71
Reputation: 5763
Not sure if this is the best way to do it, but I changed the CSS of the dragged item by storing the dragged index in the state and adding a simple conditional to change the className
if it matches the dragged index. To achieve the exact effect you posted may require a bit more CSS trickery or other conditional rendering, but this should cover the basic functionality.
Upvotes: 2