Skip to content

Instantly share code, notes, and snippets.

@oauo
Last active June 20, 2020 22:36
Show Gist options
  • Save oauo/110832d6fbba1fe52af0076082f8ef61 to your computer and use it in GitHub Desktop.
Save oauo/110832d6fbba1fe52af0076082f8ef61 to your computer and use it in GitHub Desktop.
React Draggable
#app
#items
.item
.underlying
.base
.space
.grab
i.far.fa-ellipsis-v
.item
.underlying
.base
.space
.grab
i.far.fa-ellipsis-v
{
"scripts": [
"react",
"react-dom",
"https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.6/index.min.js"
],
"styles": [
"https://pro.fontawesome.com/releases/v5.13.1/css/all.css",
"https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
]
}
const Item = React.forwardRef((props, ref) =>
<div ref={ref} className={classNames("item",{dragging:props.position})} style={props.position ? props.position : {}}>
<div className="underlying"></div>
<div className={classNames("base",{hidden:props.hidden})}>
<div className="space">{props.data.x}</div>
<div className="grab" onMouseDown={props.dragging ? () => {} : () => props.onMouseDown()}>
<i className="far fa-ellipsis-v"/>
</div>
</div>
</div>
)
class Items extends React.Component {
constructor(props) {
super(props)
this.state = {predrag:-1, dragging:-1, items: props.items.map((x,i) => ({ref:null, data:x, id:i, top:0})), position:{left:0, top:0}, offset:{left:0, top:0}}
console.log(this.state.items)
document.addEventListener("mouseup", this.handleMouseUp)
document.addEventListener("mousemove", this.handleMouseMove)
}
handleMouseDown = (e,idx) => {
this.setState({items:(this.state.items.map(x => ({ref:React.createRef(), data:x.data, id:x.id, top:0}))), predrag:idx})
}
handleMouseUp = () => {
this.setState({items:(this.state.items.map(x => ({ref:null, data:x.data, id:x.id, top:0}))),predrag:-1, dragging:-1})
//this.setState({items:(this.state.items.map(x => ({ref:React.createRef(), data:x.data, id:x.id, top:0})))})
console.log(this.state.items.map(x => x.id))
}
handleMouseMove = e => {
if (this.state.predrag == -1) return;
//let offset = this.state.offset
let {pageX, pageY} = e
let pos = this.state.items[this.state.predrag].ref.current.getBoundingClientRect()
if(this.state.predrag != this.state.dragging) {
let offset = {left:pageX-pos.left+32,top:pageY-pos.top+32}
console.log(pos)
this.setState({dragging:this.state.predrag, position:{left:pageX-offset.left, top:pageY-offset.top}, offset})
} else {
let items = this.state.items.sort((a,b) => a.top - b.top).map(x => {
let itm = x
x.top = x.id == this.state.dragging ? pageY: x.ref.current.getBoundingClientRect().top + x.ref.current.getBoundingClientRect().height/2
return x
})
this.setState({items, position:{left:pageX-this.state.offset.left, top:pageY-this.state.offset.top}})
}
console.log(this.state.dragging)
//console.log({pageX, pageY}, pos)
//this.state.items.forEach(a => console.log(a.id,this.state.predrag, (a.id == this.state.predrag ? this.state.offset.top : a.ref.current.getBoundingClientRect().top)));
//console.log(this.state.items.sort((a,b) => (a.id == this.state.predrag ? this.state.offset.top : a.ref.current.getBoundingClientRect().top) - (b.id == this.state.predrag ? this.state.offset.top : b.ref.current.getBoundingClientRect().top)).map(x => x.ref.current.getBoundingClientRect().top))
}
render() {
return (
<div id="items">
{this.state.dragging != -1 &&
<Item data={this.state.items.find(x => x.id == this.state.dragging).data} hidden={false} position={this.state.position} />
}
{this.state.items.map(x =>
<Item data={x.data} ref={x.ref} hidden={this.state.dragging == x.id} onMouseDown={e => this.handleMouseDown(e,x.id)}/>
)}
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<>
<Items items={[...new Array(5).keys()].map(x => ({x}))}/>
</>
)
}
}
ReactDOM.render(<App />, document.getElementById("app"));
:root {
--background:#23282E;
--text:white;
--text-dim:#CCD1D9;
--card-background:#2C3339;
--card-text:var(--text);
--card-text-dim:var(--text-dim);
--card-background-grab:#262C32;
--card-text-grab:#3C4349;
--input-background:#15191C;
--input-text:var(--text);
--button-red-hover:#BF263C;
--welcome-point:#FFCE54;
--button-text-disabled:#656D78;
}
body {
display: flex;
flex-direction: column;
align-items: center;
background:var(--background);
color:var(--text);
font-family: "Segoe UI", Tahoma, Verdana, sans-serif;
overflow-x:hidden;
#app {
display:flex;
flex-direction: column;
width:calc(100% - 4rem);
max-width: 60rem;
#items {
position: relative;
display: grid;
margin:2rem 0;
grid-gap: 1rem;
width:100%;
.item {
position: relative;
&.dragging {
z-index:10;
position: absolute;
top:0;
left:0;
width:100%;
animation:box-shadow-in .25s ease-out forwards;
&, * {
cursor:grabbing !important;
}
@keyframes box-shadow-in {
to {
box-shadow:0 0 2rem -.5rem rgba(0,0,0,.75);
}
}
}
.underlying {
position: absolute;
top:.25rem;
left:.25rem;
bottom:.25rem;
right:.25rem;
border:.25rem dotted var(--card-background);
border-radius: 1rem;
}
.base {
position: relative;
display: flex;
min-height: 5rem;
&.hidden {
opacity:0;
pointer-events:none;
}
.space {
flex-grow: 1;
background:var(--card-background);
}
.grab {
display: grid;
place-items: center;
width:2rem;
background:var(--card-background-grab);
color:var(--card-text-grab);
font-size:2rem;
cursor:grab;
user-select:none;
&:active {
cursor: grabbing;
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment