Reputation: 438
I'm trying to create a page with a book layout, so a page with some tabs that use can expand one at a time.
Here a working example: https://codesandbox.io/s/book-layout-l28gh?file=/src/App.js:0-1419
import { useState } from "react";
const dataset = [
{ name: "A section", description: "page A" },
{ name: "B section", description: "page B" },
{ name: "C section with long title", description: "page C" },
{ name: "D section", description: "page D" }
];
export default function App() {
return <Page />;
}
function Page({}) {
const [openSection, setOpenSection] = useState(0);
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh"
}}
>
{dataset.map((datum, i) => {
const { name } = datum;
const isOpen = i === openSection;
return (
<div
key={name}
style={{
height: "100%",
backgroundColor: isOpen ? "white" : "lightgray",
border: `1px solid ${isOpen ? "white" : "black"}`,
padding: 10,
flex: 1,
flexGrow: isOpen ? 1 : 0,
transition: "all 2s ease"
}}
>
<div
style={{
cursor: "pointer",
writingMode: isOpen ? "horizontal-tb" : "vertical-rl",
transition: "all 2s ease"
}}
onClick={() => setOpenSection(i)}
>
{name}
</div>
</div>
);
})}
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
If you test it, you can notice some problems:
Why? What's the problem? It there a better approach to do a layout like this?
Upvotes: 4
Views: 613
Reputation: 337
I fixed all of your problems as you said.
Problem #1 -> solved:
you should use white-space:nowrap
. if your text is large so in end of your box it will break to the next line. white-space:nowrap
don't let that. (I don't recommend that cause this is not good for too long title text.)
Problem #2 -> solved: happens when you click some item and in middle of action (opening box) click on another one. this is because of display: flex
and flexGrow: 1
.
you used flex box and justifyContent: "center"
. so when you click and click on another one your wrapper getting smaller and all the cards seem to get closer to each other. flexGrow: 1
will break them.
so flexGrow: 5
is solution.
Problem #3 -> solved: wrong place to set onClick
. you should set onClick
event on your box not on your text. and a condition for cursor is what you want. (item is selected so cursor must be default
otherwise must be poiner
).
Bonus :) if you set a small width to your wrapper so your rotate box will be prettier. it rotates at start of text and bingo.
import { useState } from "react";
const dataset = [
{ name: "A section" },
{ name: "B section" },
{ name: "C section with long title" },
{ name: "D section" },
{ name: "E section" },
];
function Page() {
const [openSection, setOpenSection] = useState(1);
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
{dataset.map((datum, i) => {
const { name } = datum;
const isOpen = i === openSection;
return (
<div
key={name}
onClick={() => setOpenSection(i)}
style={{
height: "100%",
backgroundColor: isOpen ? "white" : "lightgray",
border: `1px solid ${isOpen ? "white" : "black"}`,
padding: "20px 30px",
flex: 1,
flexGrow: isOpen ? 5 : 0,
transition: "all 1s linear",
boxSizing: "border-box",
cursor: openSection !== i ? 'pointer' : 'default',
"&:first-child": {
left: 0,
},
"&:last-child": {
right: 0,
},
}}
>
<div
style={{
transform: `rotate(${isOpen ? "0" : "90"}deg)`,
transition: "all 1s linear",
width: 1,
whiteSpace: "nowrap",
}}
>
{name}
</div>
</div>
);
})}
</div>
);
}
export default Page;
Upvotes: 5
Reputation: 18418
white-space:nowrap
so that the text wont shrink when size reduces. Make title div's position absolute so that it's width won't mess with layout and transition.import { useState } from "react";
const dataset = [
{ name: "A section" },
{ name: "B section" },
{ name: "C section with long title" },
{ name: "D section" },
{ name: "E section" }
];
export default function App() {
return <Page />;
}
function Page({}) {
const [openSection, setOpenSection] = useState(0);
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh"
}}
>
{dataset.map((datum, i) => {
const { name } = datum;
const isOpen = i === openSection;
return (
<div
key={name}
style={{
height: "100%",
backgroundColor: isOpen ? "white" : "lightgray",
border: `1px solid ${isOpen ? "white" : "black"}`,
flex: 1,
flexGrow: isOpen ? 1 : 0,
transition: "all 2s ease",
//my changes
padding: 0,
flexBasis: "1.2rem",
cursor: !isOpen ? "pointer" : "auto",
position: "relative"
}}
onClick={!isOpen ? () => setOpenSection(i) : null}
>
<div
style={{
transition: "all 2s ease",
//my changes
transform: `rotate(${isOpen ? "0" : "90"}deg)
translateX(${isOpen ? "0" : "50"}%)`,
whiteSpace: "nowrap",
width: isOpen ? "100%" : "1rem",
position: "absolute",
top: isOpen ? "1rem" : "0",
left: isOpen ? "1rem" : "0",
fontWeight: "bold"
}}
>
{name}
</div>
</div>
);
})}
</div>
);
}
Upvotes: 1
Reputation: 441
try this to your code for grey part area clickable and ease in transition
import { useState } from "react";
const dataset = [
{ name: "A section" },
{ name: "B section" },
{ name: "C section with long title" },
{ name: "D section" },
{ name: "E section" }
];
export default function App() {
return <Page />;
}
function Page({}) {
const [openSection, setOpenSection] = useState(0);
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh"
}}
>
{dataset.map((datum, i) => {
const { name } = datum;
const isOpen = i === openSection;
return (
<div
key={name}
style={{
height: "100%",
backgroundColor: isOpen ? "white" : "lightgray",
border: `1px solid ${isOpen ? "white" : "black"}`,
padding: 10,
flex: 1,
flexGrow: isOpen ? 1 : 0,
cursor: "pointer",
writingMode: isOpen ? "horizontal-tb" : "vertical-rl",
transition: "all 2s ease"
}}
>
<div
style={{
transition: "all 2s ease"
}}
onClick={() => setOpenSection(i)}
>
{name}
</div>
</div>
);
})}
</div>
);
}
Upvotes: 0
Reputation: 107
I will follow the same order in which you introduced your problems.
Upvotes: 0