What i want to accomplish is when i click on a box, the previous box to be behind the one that is on top, for better reference please check the next code.
https://codesandbox.io/s/optimistic-payne-4644yf?file=/src/styles.css
Desired behavior:
- click on red box
- click on blue box
and the sequence from bottom to top would be: green,red,blue
I tried a lot of ways but im keep messing up the code, so any help will be welcomed.
CodePudding user response:
do you mean something like this?
const { useState, useEffect } = React
const Test = () => {
const [data, setData] = useState([
{ id: 1, label: "box 1", class: "box1", z: 0 },
{ id: 2, label: "box 2", class: "box2", z: 1 },
{ id: 3, label: "box 3", class: "box3", z: 2 }
]);
const handleClickBox = id => {
setData(p => {
let tmpArr = [...p];
tmpArr = tmpArr.sort((a) => a.id - (id 1)).reverse().map((ta, i) => ({ ...ta, z: i })).sort((a, b) => a.id - b.id);
return tmpArr;
})
}
return <div className="box-wrapper">
{data.map((d, i) => {
return (
<div
className={d.class}
key={d.id}
style={{ left: i * 100, zIndex: d.z }}
onClick={() => handleClickBox(d.id)}
>
{d.label}
</div>
);
})}
</div>
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Test />
);
.box-wrapper {
position: relative;
}
.box1,
.box2,
.box3 {
position: absolute;
width: 300px;
height: 150px;
color: white;
top: 0;
}
.box1 {
background: green;
}
.box2 {
background: red;
}
.box3 {
background: blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
CodePudding user response:
When I was typing my response Layhout answered. That solution works, but mine is slightly different, you need to know the greatest value of zIndex.
import "./styles.css";
import { useState } from "react";
export default function App() {
const [data, setData] = useState([
{ id: 1, label: "box 1", class: "box1", index: 0 }, // red
{ id: 2, label: "box 2", class: "box2", index: 1 }, // blue
{ id: 3, label: "box 3", class: "box3", index: 2 } // green
]);
const handlePosition = (index, selectedIndex) =>
index === selectedIndex ? 2 : index > selectedIndex ? index - 1 : index;
const handleClick = (selectedIndex) => {
// nothing happens if we click on the first item
if (selectedIndex === 2) return;
setData(
data.map((i) => ({
...i,
index: handlePosition(i.index, selectedIndex)
}))
);
};
return (
<div className="App">
<div className="box-wrapper">
{data.map((b) => {
return (
<div
className={b.class}
key={b.id}
style={{ zIndex: b.index }}
onClick={() => handleClick(b.index)}
>
{b.label}
</div>
);
})}
</div>
</div>
);
}
Here is the full implementation on CodeSandbox.
The most important fact is zIndex of each object is also a UI state, so it needs to be in useState to change with user clicks. After this, you need to implement an algorithm to reorder items based on the clicked item. That is this function:
const handlePosition = (index, selectedIndex) =>
index === selectedIndex ? 2 : index > selectedIndex ? index - 1 : index;
CodePudding user response:
It seems that the desired result may actually be a solution that handles z-index
independently, without adding to the given data
, and is capable of handling more than 3 div
items if needed.
Here is a basic example that uses a state array activeList
to handle the changes of z-index
, so it is independent to data
and can still work if data
scales.
It uses the index
of the state array to calculate z-index
for each item. On click event, it pushes an item to the end of array (so it will have the highest z-index), as a lightweight approach to handle the re-order of z-index
.
Forked live demo on: codesandbox
import "./styles.css";
import { useState } from "react";
export default function App() {
const [activeList, setActiveList] = useState([]);
const handleClick = (id) =>
setActiveList((prev) => {
const current = prev.filter((item) => item !== id);
return [...current, id];
});
const data = [
{ id: 1, label: "box 1", class: "box1" },
{ id: 2, label: "box 2", class: "box2" },
{ id: 3, label: "box 3", class: "box3" }
];
return (
<div className="App">
<div className="box-wrapper">
{data.map((b) => {
const activeIndex = activeList.findIndex((id) => id === b.id);
const zIndex = activeIndex >= 0 ? activeIndex 1 : 0;
return (
<div
className={b.class}
key={b.id}
style={{ zIndex }}
onClick={() => handleClick(b.id)}
>
{b.label}
</div>
);
})}
</div>
</div>
);
}
Hope this could help.