react-grid-layout / react-draggable

React draggable component
MIT License
9.02k stars 1.03k forks source link

Z-index bring to front last dragged window #402

Open lifeofjer opened 5 years ago

lifeofjer commented 5 years ago

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);

}
devinhalladay commented 4 years ago

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>
  )
}
promet99 commented 4 years ago

Dear future developers who are confused...

  1. use onStop instead of onEnd
  2. try position: 'relative' on <div> if it still doesnt work
LucasBoscariole commented 2 years ago

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.

imgopaal commented 1 year ago

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>
)
SaleemTheKing commented 5 months ago

This might not be the best solution but this is what worked for me. A simple state managed z-index draggable component.

Notice how I set position: "absolute" in the style. This is because the Draggable component has position: "static" which doesn't listen to z-index changes.

import { useState } from 'react';
import Draggable from 'react-draggable';

class DragCompIndex {
    static index = 0;
    static updateIndex() { DragCompIndex.index++; }
}

const DraggableComponent = () => {
    const [currentZIndex, setZIndex] = useState<number>(DragCompIndex.index);

    const Prioritise = () => {
        setZIndex(DragCompIndex.index);
    }

    const Deprioritise = () => {
        DragCompIndex.updateIndex();
    }

    return (
        <Draggable onStart={Prioritise} onStop={Deprioritise}>
            <div style={{
                zIndex: currentZIndex,
                position: "absolute"
            }}
            >
                <h1>I am always in on top!</h1>
            </div>
        </Draggable>

    )
}

export default DraggableComponent;