react-draggable
react-draggable copied to clipboard
Z-index bring to front last dragged window
Wanted to share this snippet of code I am using to bring the latest dragged window to the front. Any feedback or improvements are welcome for others looking for the same functionality.
Note: This is definitely a clunky implementation and involves a flash due to timing on when the onStop is triggered
<Draggable
onStop={onDraggableStop}
/>
export function onDraggableStop(element) {
var prev_window = document.getElementsByClassName('react-draggable-last');
var new_window = document.getElementById(element.path[1].id);
setTimeout(function(){
var i;
for (i = 0; i < prev_window.length; i++) {
prev_window[i].classList.remove('react-draggable-last');
}
new_window.classList.add('react-draggable-last');
}, 0);
}
I'm not sure if I'm solving the same problem as you, here's my approach for bringing forward the window currently being dragged (and leaving it at the top of the z-index — which I think accomplishes the sam thing):
(edit: this is actually buggy, the z-index values on elements can become out of sync, but the approach with a little tweaking seems clean to me)
const DraggableBlock = (props) => {
const [isDragging, setIsDragging] = useState({status: false, zIndex: 1000})
return (
<Draggable
handle=".draggable-block-container"
onStart={() => setIsDragging({...isDragging, status: true, zIndex: isDragging.zIndex + 1})}
onEnd={() => setIsDragging({...isDragging, status: false})}
>
<div
className={`draggable-block-container`}
style={{
zIndex: isDragging.zIndex
}}
>
...
</div>
</Draggable>
)
}
Dear future developers who are confused...
- use
onStopinstead ofonEnd - try
position: 'relative'on<div>if it still doesnt work
I was able to find a solution. I was not happy with any approach that I was having, I tried dinamic zindex with useState but it was not being left in order after drag, I tried with classnames, timeout and I was not happy.
The code that I find best is.
const DraggableBlock = (props) => {
const [currentZIndex, setCurrentZIndex] = useState(31);
const handleZindex = (element) => {
element.style.zIndex = currentZIndex;
setCurrentZIndex((state) => state + 1);
};
return (
<Draggable
handle=".draggable-block-container"
onStart={() => handleZindex(blockRef.current)}
>
<div
className={`draggable-block-container`}
ref={blockRef}
>
...
</div>
</Draggable>
)
}
This way each component will remain in order of dragable.
Bonus:
I also needed to made sure that some components would not be able to be dragged on. For example: if we drag a component and the current z index is bigger than the component, it will be on top of the component that we don't want to. In my case, it was a cart slide panel, I handle by passing the zIndex: currentZIndex + 10 on inline style to it.
This worked for me.
"DraggableContext.tsx"
import { createContext } from "react";
export interface DraggableContextInterface {
index: number;
setIndex: (index: number) => void;
}
const initialState: DraggableContextInterface = {
index: 3,
setIndex: (index: number) => {},
};
export const DraggableContext = createContext(initialState);
"App.tsx"
const [index, setIndex] = useState(3);
return(
<DraggableContext.Provider value={{ index, setIndex }}>
<App/>
</DraggableContext.Provider>
)
"Draggable Component"
const { index, setIndex }: DraggableContextInterface = useContext(DraggableContext);
const [currentZIndex, setCurrentZIndex] = useState(1);
const onStart: DraggableEventHandler | undefined = () => {
if (index != currentZIndex + 1) {
setCurrentZIndex(index);
setIndex(index + 1);
}
};
return (
<Draggable onStart={onStart}>
<div zIndex={currentZIndex}>I'm dragable</div>
</Draggable>
)