I've been building the UI for a chat room app in React. Everything was going fine until suddenly, it refreshes and the UI "broke". Basically, I have 2 components, a Sidebar component and a Chat component. I use flex to position them. Here is my code:
Sidebar.js
<div className="sidebar">
<Card className="sidebar_card left">
<Icon icon="mountain" iconSize={40} />
<div>
<Button icon="search" minimal fill large />
</div>
<div>
<Button icon="plus" minimal fill large />
</div>
</Card>
<Card className="sidebar_card right">
<div>
<h2 className="bp3-heading">Smart Labs</h2>
<Popover content={popupMenu}>
<Button icon="chevron-down" minimal />
</Popover>
</div>
<CreateRoom />
<Menu>
{rooms.map(room => (
<SelectRoom text={room.name} id={room.id} />
))}
</Menu>
<div></div>
<Button text="People" icon="new-person" minimal outlined />
<Menu>
<MenuItem text="Tony Quach" />
</Menu>
</Card>
</div>
Chat.js:
<div className="chat">
<Card className="chat_card">
<div className="chat_header">
<h2 className="bp3-heading">{roomDetails?.name}</h2>
<Divider />
</div>
<div className="chat_body">
{roomMessages.map(({ message, timestamp, user, userImage }) => (
<Message
message={message}
timestamp={timestamp}
user={user}
userImage={userImage}
/>
))}
</div>
<ChatInput roomName={roomDetails?.name} roomId={roomId} workspaceId={workspaceId} />
</Card>
</div>
Sidebar.css:
.sidebar {
display: flex;
flex: 0.2;
height: 100vh;
}
.sidebar_card {
border-radius: 0;
margin: 0;
}
.sidebar_card > div {
display: flex;
justify-content: space-between;
}
.left > div {
margin: 10px 0 0;
}
.right > div {
margin: 0 0 20px;
}
Chat.css:
.chat {
display: flex;
flex: 0.7;
height: 100vh;
}
.chat_card {
width: 100%;
border-radius: 0;
margin: 0;
}
.chat_header {
margin: 0 20px 0;
}
.chat_body {
margin: 20px 20px 0;
}
And in App.js I just do:
<div class="app">
<Sidebar />
<Chat />
</div>
App.css:
app {
display: flex;
}
I use Blueprint.js
I used border: 1px solid red to try to debug it, it seems that the Sidebar component, while only has a flex: 0.3 somehow takes up the whole width of the page and pushes Chat component down (so they stack on each other instead of align next to each other).
Any suggestions? Thank you.
I fixed it by adding position: absolute; left: 0 to Sidebar component and position: absolute; right: 0 to Chat component.
Related
So I am creating an application with a sidebar and topbar where the content on the right side changes and the sidebar and topbar are fixed. Now I want to create a page where I can put a horizontal carousel / slider for cards, but this gets pushed down for some rease. I don't think it has anything to do with the cards, but only with the slider since one card gets displayed correctly.
Page with slider]1 and Page with 1 card, ideally the slider should be on the same space where the card is displayed. I think it has something to do maybe with the react-bootstrap Col but I am not sure.
Page.js
const array = [...Array(50).keys()];
return (
<Container fluid className="justify-content-between align-content-between mt-0 mt-lg-4">
<Row>
<Col lg="9" m="4" className="mt-4 mt-lg-0">
<div>
<Slider items={array} />
</div>
</Col>
</Container>
)
Slider.js
return (
<div className="slider">
{ items.map((item, index) => (
<div key={index} className="slider-card-wrapper">
{/*<div>lol</div>*/}
<ProductCard />
</div>
)) }
</div>
)
Slider.css
.slider {
display : -webkit-box;
overflow-x : scroll;
overflow-y : hidden;
border-radius : 5px;
}
.slider-card-wrapper {
margin-left : 10px;
margin-right : 10px;
}
ProductCard.js
return (
<Card className="product-card">
{ isIMG &&
<div className="product-card-img">
<Card.Img variant="top" className="product-card-img" src="" />
<div className="product-card-img-overlay"/>
<div className="product-card-img-text">
text
</div>
</div>
}
<Card.Body className="product-card-body">
body
body
<br/>
body
</Card.Body>
<div className="product-card-divider" />
<Card.Footer className="product-card-footer">
<Link className="product-card-footer-link" to="/dashboard/product">
VIEW ITEM
</Link>
</Card.Footer>
</Card>
)
ProductCard.css
.product-card {
max-height : 400px;
max-width : 250px;
border-radius : 10px 10px 5px 5px;
border : 0;
-webkit-box-shadow: 0 0 14px #e7e7e7;
box-shadow: 0 0 14px #e7e7e7;
word-wrap : break-word;
}
.product-card-img {
position : relative;
border-radius : inherit;
border-color : transparent;
}
.product-card-img-actual {
display : block;
overflow : hidden;
border-radius : inherit;
}
.product-card-img-overlay {
position : absolute;
text-align : left;
left : 0;
right : 0;
top : 0;
bottom : 0;
border-radius : inherit;
background : #1f1f1fff;
opacity : 0.15;
}
.product-card-img-text {
margin-left : 10px;
margin-bottom : 10px;
}
.product-card-body {
text-align : center;
}
.product-card-footer {
text-align : center;
justify-content : center;
border-top-width : 3px;
border-color : #e0e0e0;
background : transparent;
}
.product-card-footer-link {
color : #008d74;
text-decoration : None;
font-weight : bold;
margin-top : 2px;
margin-bottom : 2px;
}
Sidebar.js
return (
<div className={classNames("sidebar", {"open" : isSidebarOpen})}>
<ChevronDoubleLeftIcon
className={classNames("sidebar-toggle", {"closed" : !isSidebarOpen})}
onClick={onSideBarOpen}
/>
<Nav className="flex-column">
{ routes.map((route, index) => (
route.sub_routes ?
<SidebarMenu
key={index}
data={route}
/>
:
<SidebarItem
key={index}
text={route.name}
icon={route.icon}
path={route.path}
/>
)) }
</Nav>
</div>
)
Sidebar.css
.sidebar {
min-width: 230px;
max-width: 240px;
height : 100vh;
margin-left: -200px;
background: #f7f9fb;
transition: all .5s;
}
.sidebar.open {
margin-left: -10px;
transform: scale(1);
transition: .5s;
}
.sidebar-item {
display: inline-flex;
margin-top: 1em;
color: #000000;
font-weight: bold;
}
.sidebar-item:hover {
background-color: #f2f4f7;
}
.sidebar-menu {
color: #000000;
font-weight: bold;
margin-top: 1em;
}
.sidebar-menu-item {
color: #000000;
font-weight: bold;
margin-top: 0.75em;
margin-left: 1.75em;
}
.sidebar-menu-item:hover {
background-color: #f2f4f7;
}
.sidebar-menu-icon {
color: #000000;
}
.sidebar-toggle {
position: relative;
height: 1.25em;
width: 1.25em;
top: 10px;
left: 0px;
z-index: 999;
float: right;
background-color: #f7f9fb;
color: #000000;
cursor: pointer;
border-radius: 0px .25em .25em 0px;
transition: transform .5s ease-in-out;
}
.sidebar-toggle.closed {
transform-origin: center;
transform: translateX(-0px) rotate(180deg) ;
}
.nav-item {
color: #000000;
font-weight: bold;
}
.nav-link {
color: #000000;
font-weight: 600;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar-icon {
color: #000000;
width: 1.25em;
height: 1.25em;
margin-right: 0.75em;
}
Topbar.js
return (
<Navbar bg="light" expand="lg" sticky="top">
<Container fluid>
<Navbar.Brand href="#">PPDB</Navbar.Brand>
<Navbar.Toggle aria-controls="navbarScroll"/>
<Navbar.Collapse id="navbarScroll">
<Nav
className="me-auto my-2 my-lg-0 justify-content-center"
style={{maxHeight: '100px'}}
navbarScroll
>
<Nav.Link href="Dashboard">Home</Nav.Link>
</Nav>
<Form className="d-flex">
<FormControl
type="search"
placeholder="Search"
className="me-2"
aria-label="Search"
/>
</Form>
</Navbar.Collapse>
</Container>
</Navbar>
)
Layout.js
return (
<div>
<Container fluid className={"h-100"} style={{paddingLeft : "0px", paddingRight : "0px"}}>
<Topbar />
<Row className={"h-100"}>
<Sidebar />
<Col>
<Suspense fallback={<Spinner animation="border" role="status" />}>
<Routes>
<Route path='/' element={<h1> BASE </h1>} />
{ routes.map((route, index) => (
route.sub_routes ?
route.sub_routes.map((sub_route, i) => (
<Route
path={sub_route.path}
key={i}
element={<sub_route.component />}
/>
))
:
<Route
path={route.path}
key={index}
element={<route.component />}
/>
)) }
</Routes>
</Suspense>
</Col>
</Row>
</Container>
</div>
)
You overuse Grid there (like many other people do), as you do not have a real two-dimensional layout. I would strongly recommend to stick to flexbox as much as possible. There is a component for it in react-bootstrap called Stack. Using only Stack you will come very far. In your case I would layout application only with this component:
Look into following sandbox: https://codesandbox.io/s/clever-elion-frb220. I have provided colors, ids, names and some padding, so you can see how this layout is structured:
Upper Stack is vertical, for stacking top appbar/navbar and the rest of your application
Lower Stack is horizontal, for stacking sidebar(probably with fixed width) and the content section
Regarding your problem: In your Layout component you define a Row, but do not define Cols everywhere - the imported Sidebar component is not a Col.
Grid is for example useful when:
You have table like structure (two-dimensional)
You want to define responsive design with different layout on some breakpoints
You want to work with offsets, so things are aligned consistently.
I created one component to be a box for other items and I'm using v-for to have four of these box in my page. In this way, they are only displayed in a column (and I'm not being able to change it), but for desktop I need two boxes per row and for mobile I need them displayed in a column. I already have a footer and a background but they're not using flexbox, so I just want to be able to change these boxes without having to change other componentes.
App.Vue code:
<template>
<div id="app">
<Background />
<div v-for="cliente in clientes" :key="cliente.id">
<Steps :cliente="cliente"/>
</div>
<Footer/>
</div>
</template>
<script>
import Steps from './components/Steps.vue'
import Footer from './components/Footer2.vue'
import Background from './components/Background.vue'
export default {
name: 'App',
components: {
Steps,
Background,
Footer
},
data(){
return{
clientes:[
{
id: 1,
name:"Detalhes da Partida"
},
{
id: 2,
name:"Jogadores"
},
{
id: 3,
name:"EvidĂȘncia da Partida"
},
{
id: 4,
name:"Mensagem: (opcional)"
}
]
}
}
}
</script>
<style>
html, body {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #3B3B3B;
}
</style>
Component code:
<template>
<section class="flex">
<div>
<p class="teste">{{cliente.id}}. {{cliente.name}}</p>
</div>
</section>
</template>
<script>
export default {
name: 'Steps',
props:{
cliente: Object
}
}
</script>
<style>
*{
margin: 0;
padding: 0;
}
.teste{
color: #FFF;
border-bottom: 1px solid #707070;
padding: .5em .5em 0;
}
.flex {
display: flex;
flex-wrap: wrap;
max-width: 32em;
height: 30em;
background-color: #232323;
margin: 1em auto;
}
.flex > div {
flex: 1 1 100px;
}
</style>
You are using flexbox on the wrong container. You need to add class flexbox on the container that wraps the element that has v-for so that all the elements that is generated by v-for becomes flex-items.
CODESANDBOX
App.vue
<section class="flex">
<div v-for="cliente in this.clientes" :key="cliente.id">
<Steps :cliente="cliente" />
</div>
</section>
Steps.vue
<template>
<div>
<p class="teste">{{ cliente.id }}. {{ cliente.name }}</p>
</div>
</template>
You need to add a media query to make 2 columns in medium and above devices and single column in mobile devices
App.vue -> style
.flex {
display: flex;
flex-wrap: wrap;
max-width: 32em;
height: 30em;
background-color: #232323;
margin: 1em auto;
}
.flex > div {
flex: 50%;
}
#media only screen and (max-width: 768px) {
.flex > div {
flex: 100%;
}
}
This is what the site is like when in desktop mode:
Now in mobile mode:
You can see that there is now extra space on the bottom. I make sure to erase margins everywhere but the space still remained there.
Pertinent css code:
.grid-container{
display: grid;
grid-template-areas:
"header"
"main"
"footer";
grid-template-columns: 1fr;
grid-template-rows: 5rem 1fr 3rem;
height: 100%;
}
.main {
grid-area: main;
background-image: url("res/background_min.jpg");
overflow: hidden
}
/*Home Screen*/
.home{
display: flex;
}
.menu-list{
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
height: 100%;
}
.menu-list a {
padding: 0rem;
flex: 0 1 50rem;
margin: 1.5rem;
height: 100%;
width: 100%;
display: flex;
text-decoration: none;
color: white;
}
.menu-list li
{
display: flex;
list-style-type: none;
text-decoration: none;
align-items: center;
justify-content: center;
width: 100%;
height: 10%;
}
.menu-list p {
font-size: 2vw;
}
.section a {
color: #ffffff;
font-weight: bold;
font-size: 2vw;
text-decoration: none;
}
.about {
background-image: linear-gradient(to right, cyan, magenta);
}
.contacts {
background-image: linear-gradient(to right, fuchsia, turquoise);
}
.projects{
background-image: linear-gradient(to right, turquoise, fuchsia);
}
.question-box {
background-image: linear-gradient(to right, magenta, aqua);
}
.home-main-image{
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
max-width: 45%;
}
.home-main-image img{
max-width: 99%;
}
.fade-in {
animation: fadeIn ease 2s;
}
.button{
background-image: linear-gradient(to right, cyan, magenta);
border: none;
text-align: center;
color: white;
text-decoration: none;
font-size: 1vw;
padding: .5rem;
}
Menu list is the vertical column of buttons that should occupy most of the screen on the left, and home-main-image is the giant terminal logo plastered on the right.
Note that the issue happens with a real mobile device too.
Edit: For clarification, this is the extra space:
Edit: added react html:
App.js
function App() {
const [lang, setLang] = useLanguage();
const handleLanguageChange = () => {
if (lang.lang === 'eng'){
setLang('fr')
} else {
setLang('en')
}
}
return (
<BrowserRouter>
<div className="grid-container">
<header className="header">
<div className="brand">
<Link to="/" >
Eduardo Breton
</Link>
</div>
<div className="header-side">
{lang.subtitle}
</div>
<div className="header-right">
<button className="button" onClick={() => handleLanguageChange()}>{lang.traduction} </button>
</div>
<div>
</div>
</header>
<main className="main">
<div className="content">
<Route path="/" exact={true} component={HomeScreen} />
<Route path="/about" component={AboutScreen}/>
<Route path="/contact" component={ContactScreen}/>
<Route path="/project" component={ProjectScreen}/>
<Route path="/question" component={QuestionScreen}/>
</div>
</main>
<footer className="footer">
© 2020 Eduardo Breton
</footer>
</div>
</BrowserRouter>
);
}
HomeScreen.js:
const { Link } = require("react-router-dom");
function HomeScreen() {
const [lang, setLang] = useLanguage();
return <div className="home">
<ul className="menu-list">
<Link to="/about">
<li className="fade-in section about" >
{lang.about}
</li>
</Link>
<Link to="/contact">
<li className="fade-in section about" >
{lang.contact}
</li>
</Link>
<Link to="/project">
<li className="fade-in section about" >
{lang.projects}
</li>
</Link>
<Link to="/question">
<li className="fade-in section about" >
{lang.question}
</li>
</Link>
</ul>
<div className="home-main-image fade-in">
<img src={terminalImage} />
</div>
</div>
}
export default HomeScreen;
Checked answer with the discussion has fixed the issue, changing flex-direction to column on a media screen on main has allowed me to center correctly the contents:
the first thing you need to do is add a meta description for mobile view in the head. These in particular
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
Next you need to make your site responsive by adding media queries so that the home main image and your buttons become responsive. You should increase the size of the menu buttons and set them to
#media screen and (max-width:650px) {
.menu-list li {
flex-direction:column;
}
}
That way your buttons will be bigger, aligned in the center, AND in a column (this will look better on mobile). You should also change the flex direction of the main image. Also, try adding some padding between the buttons in mobile view, this should also help in taking up the space.
I have a div with classname grid in the TransformComponent tag.I am trying to apply css to grid through external css by accessing it as .grid{}.But the css is not getting applied to it..grid is the css i am aplying for the div named as grid in transormcomponent tag
Below is the code of .jsx file followed by scss content.
Help would be appreciated.Thank you
<div className="zoom">
<TransformWrapper
defaultScale={1}
defaultPositionX={200}
defaultPositionY={100}
>
{({ zoomIn, zoomOut, resetTransform, positionX, positionY, ...rest }) => (
<React.Fragment>
<div className="tools">
<button onClick={zoomIn}>+</button>
<button onClick={zoomOut}>-</button>
<button onClick={resetTransform}>x</button>
</div>
<TransformComponent >
<div className="grid">
<input
type="file"
className="file"
id="file-browser-input"
name="file-browser-input"
ref ={input=>this.fileInput=input}
onDragOver={(e)=>{
e.preventDefault();
e.stopPropagation();
}
}
onDrop={this.onFileLoad.bind(this)}
onChange={this.onFileLoad.bind(this)}
onMouseDown={this.catchItem.bind(this)}
//onMouseMove={this.mousemove.bind(this)}
//onMouseUp={this.mouseup.bind(this)}
/>
<div
className="files-preview-container"
onMouseDown={this.catchItem.bind(this)}>
{loadedFiles.map((file,idx) => {
return <div
className="file"
key={idx}
>
<img src = {file.data}/>
</div>
})}
</div>
</div>
</TransformComponent>
</React.Fragment>
)}
</TransformWrapper>
</div>
.zoom{
padding: 0px;
position: absolute;
left:15%;
right:15%;
height: 93%;
background-color: palevioletred;
//background: url("grid.svg");
.grid{
position: absolute;
height: 100%;
width: 100%;
top:30px;
left: 0px;
right: 0px;
background-color: black;
background: url("grid.svg");
}
I want to display one HTML element (in my case it's the swap-button with a paired arrows) over the common border between the other two elements (in my case these are input boxes).
But my swap-button goes to the start of the page.
HTML
<div style={{
display: "flex",
flexFlow: "row",
justifyContent: "center",
alignItems: "center"
}}>
{/* origin input */}
<div
style={{
marginTop: this.state.marginTop,
border: "1px solid rgb(36, 2, 2)"
}}
>
<h5 style={{ color: "white" }}>ORIGIN OF SHIPMENT</h5>
<Dropdown name="origin" placeholder="ORIGIN OF SHIPMENT" />
</div>
{/* swap button */}
<div>
<h5>
<span style={{ display: "inline-block", width: "0px" }}>
</span>
</h5>
<Button
type="default"
size="small"
className={[this.state.css, "swap-button"]}
shape="circle"
onClick={this.onSwapLocation}
>
<Icon type="swap" />
</Button>
</div>
{/* destination input */}
<div style={{ marginTop: this.state.marginTop }}>
<h5 style={{ color: "white" }}>DESTINATION OF SHIPMENT</h5>
<Dropdown
name="destination"
placeholder="DESTINATION OF SHIPMENT"
/>
</div>
</div>
CSS
.swap-button {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
z-index: 100;
background-color: var(--whiteColor);
color: var(--blueColor);
}
Expected swap-button image: [2:
Actual swap-button image: :
I will appreciate your reply. thank you.
Edit01: I just want to show that swap-button over the other two input boxes. I have already written HTML and CSS. but when I use position: absolute the swap-button goes to start. I think I need some help in CSS (i.e. .swap-button class)
You have to add an extra container to the swap button and Destination of Shipment input so you can position the swap button precisely in the middle using position absolute just like you are doing.
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
.container {
display: flex;
flex-direction: row;
}
.swap-container {
position: relative;
}
button {
position: absolute;
background: #0084ff;
top: 10px;
left: -15px;
border: none;
border-radius: 5px;
padding: 3px;
font-size: 8px;
color: #fff;
}
input {
padding: 10px;
}
<div class="container">
<input type="text" value="Origin of Shipment"/>
<div class="swap-container">
<button>Swap</button>
<input type="text" value="Destination of Shipment"/>
</div>
</div>
input#addr_from {
border-right: 1px solid #cad3db;
}