Button sit at bottom of page centered - css

I would like a scroll to the top button to sit at the bottom, much like a footer. Current Behavior: I have an array I'm filtering through and displaying different lengths of data. When there is only one item in a certain category the button will move all the way to the top of the page under the item. Wanted Behavior: I would like the button to stay at the bottom and not move, but not sticky. my button styling is as follows:
import React, { useState, useContext } from "react"
// components
import SelectStatus from "../components/SelectStatus"
import RepoCard from "../components/RepoCard"
// Context
import { GithubContext } from "../context/GithubContext"
// Material UI Stuff
import TextField from "#material-ui/core/TextField"
import CardContent from "#material-ui/core/CardContent"
import Button from "#material-ui/core/Button"
import Card from "#material-ui/core/Card"
import Grid from "#material-ui/core/Grid"
import { makeStyles } from "#material-ui/core/styles"
import Container from "#material-ui/core/Container"
// context
const useStyles = makeStyles((theme) => ({
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
card: {
display: "flex",
marginBottom: 10,
minHeight: 90,
},
form: {
display: "flex",
alignItems: "center",
width: "100%",
},
content: {
display: "flex",
alignItems: "center",
width: "100%",
justifyContent: "space-between",
},
jobField: {
margin: 0,
padding: 0,
},
grid: {
padding: 0,
},
dashboardContainer: {
marginTop: 70,
padding: 10,
},
loading: {
textAlign: "center",
},
}))
const INITIAL_STATE = {
language: "All",
search: "",
}
const Profile = () => {
const [formData, setFormData] = useState(INITIAL_STATE)
const [updated, setUpdated] = useState(false)
const [created, setCreated] = useState(false)
const { data } = useContext(GithubContext)
const handleUpdated = () => {
setUpdated(!updated)
data &&
data.sort((a, b) => {
if (updated) return a.updated_at > b.updated_at ? -1 : 1
return a.updated_at > b.updated_at ? 1 : -1
})
}
const handleCreated = () => {
setCreated(!created)
data &&
data.sort((a, b) => {
if (created) return a.created_at > b.created_at ? -1 : 1
return a.created_at > b.created_at ? 1 : -1
})
}
const handleInputChange = (field) => (e) => {
setFormData({ ...formData, [field]: e.target.value })
}
const classes = useStyles()
return (
<>
<div style={{ marginTop: 85, marginBottom: 85 }}>
<Container className={classes.dashboardContainer}>
<Card className={classes.card} style={{ width: "100%" }}>
<CardContent className={classes.content}>
<div className={classes.form}>
<Grid
container
spacing={2}
alignItems='center'
justify='space-between'
>
<Grid item sm={4} xs={12} className={classes.grid}>
<SelectStatus
language={formData.language}
handleInputChange={handleInputChange}
/>
</Grid>
<Grid item sm={4} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin='normal'
fullWidth
id='search'
name='search'
label='Search by Title'
placeholder='Search by Title'
onChange={handleInputChange("search")}
value={formData.search}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleUpdated}
>
Updated {updated ? "(oldest)" : "(newest)"}
</Button>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleCreated}
>
Created {created ? "(oldest)" : "(newest)"}
</Button>
</Grid>
</Grid>
</div>
</CardContent>
</Card>
</Container>
{!data ? (
<h1 className={classes.loading}>Initializing Repos...</h1>
) : (
<Container style={{ padding: 10 }}>
{!data ? (
<div style={{ placeItems: "center" }}>Loading...</div>
) : (
<Grid container alignItems='center' spacing={4}>
{data &&
data
.filter((data) => {
if (formData.language === "All") return true
return data.language === formData.language
})
.filter((data) => {
if (formData.search === "") return true
return (data.name + data.language)
.toLowerCase()
.includes(formData.search.toLowerCase())
})
.map((user) => <RepoCard key={user.id} user={user} />)}
</Grid>
)}
</Container>
)}
<Button
variant='contained'
color='primary'
disableElevation
style={{
borderRadius: 0,
display: "block",
marginLeft: "auto",
marginRight: "auto",
position: "relative",
marginTop: "80px"
}}
>
Back to Top
</Button>
</div>
</>
)
}
export default Profile

You can use flex and space-around feature. like below:
<div class="container">
<div>
your items
</div>
<Button
variant='contained'
color='primary'
disableElevation
style={{
borderRadius: 0,
elevation: "disabled",
display: "block",
marginLeft: "auto",
marginRight: "auto",
marginTop: "80px",
}}
>
Back to Top
</Button>
</div>
Style:
.container{
display: flex;
flex-direction:column;
justify-content: space-between;
}
Using this you split your content to two part.
First part which will be your array items will be shown at the top.
The second part which is your button will be shown at the bottom.
This is because your making a space between first part and second part by using space-around of justify-content.

without sticky or fixed the only thing i can think of is using position: absolute; and bottom: 0; or placing them in a container with some combination of the same, then positioning it as needed. same as Joeri in the comments, would need a bit more context to help :) good luck!

Related

Two components overlap each other when screen size is reduced

My site has a component (NavPanel) that consists of two components (a back button (BackToButton ) and a search field (SearchTextField )). With a standard screen size, they are positioned as they should be, but if the screen sizes are reduced, then these two components overlap each other.
The most optimal for me would be if, with a compressed screen size, the second component (SearchTextField ) will be located under the first (BackToButton ). Tell me how you can solve this problem?
const Style = {
paddingLeft: '8%',
paddingRight: '8%',
minHeight: '70px',
alignItems: 'center',
flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}
export default function NavPanel() {
return (
<Grid container sx={Style}>
<BackToButton />
<SearchTextField />
</Grid>
);
}
Here you go...
You didn't use the grid correctly.
See the forked snippet here.
EDIT 1
A:
B:
EDIT 2
You can change the breakpoint from sm to md. It means that PageNameBackToButton and FilterButtonSearchTextField will be stacked earlier, but A will not happen.
See the forked snippet here.
PageNameBackToButton.jsx
import React from "react";
import { Tooltip } from "#mui/material";
import BackToButton from "./BackToButton";
const PageName = {
color: "#000000",
fontSize: "20px",
fontWeight: "700",
letterSpacing: "0.2px",
paddingRight: "20px"
};
const PageNameBackToButtonContainerStyle = {
width: "50%",
justifyContent: "start",
alignContent: "center"
};
export default function PageNameBackToButton(props) {
return (
<div sx={PageNameBackToButtonContainerStyle}>
<BackToButton />
<div style={{ marginTop: "5px", display: "inline-block" }}>
<span style={PageName}>Some Text</span>
<span>
<Tooltip title={`Here long long text`} placement="right">
<span
style={{ fontSize: "18px", color: "#ef1400", userSelect: "none" }}
>
Live
</span>
</Tooltip>
</span>
</div>
</div>
);
}
FilterButtonSearchTextField.jsx
import { TextField } from "#mui/material";
const FilterButtonSearchTextFieldContainerStyle = {};
const SearchTextField = {};
export default function FilterButtonSearchTextField() {
return (
<div sx={FilterButtonSearchTextFieldContainerStyle}>
<TextField
required
label="Search Search"
size="small"
style={SearchTextField}
/>
</div>
);
}
Filter.jsx
import React from "react";
import { Grid } from "#mui/material";
import FilterButtonSearchTextField from "./FilterButtonSearchTextField";
import PageNameBackToButton from "./PageNameBackToButton";
import { styled } from "#mui/material/styles";
const FilterContainerStyle = {};
const Root = styled("div")(({ theme }) => ({
padding: theme.spacing(1),
[theme.breakpoints.up("md")]: {
display: "flex",
justifyContent: "flex-end"
}
}));
export default function Filter(props) {
const pageName = props.pageName !== undefined ? props.pageName : "";
const showBackToButton =
props.showBackToButton !== undefined ? props.showBackToButton : false;
return (
<Grid container spacing={2} sx={FilterContainerStyle}>
<Grid item md={6} xs={12}>
<PageNameBackToButton
showBackToButton={showBackToButton}
pageName={pageName}
/>
</Grid>
<Grid item md={6} xs={12}>
<Root>
<FilterButtonSearchTextField table={props.table} />
</Root>
</Grid>
</Grid>
);
}
<Grid container> is meaningless without some <Grid item>s inside it.
Just like the Bootstrap,
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design's responsive UI is based on a 12-column grid layout.
Your code would be like this:
<Grid container spacing={1} sx={Style}>
<Grid item xs={12} sm={6}>
<BackToButton />
</Grid>
<Grid item xs={12} sm={6}>
<SearchTextField />
</Grid>
</Grid>
I strongly recommend reading the MUI Grid Docs.

custom accordion in Mui Nextjs and mui

there is quite a new component introduced in #mui/lab which is Masonary which deals with accordions on the sidebar but not the one I posted above. so far I have achieved
import React from 'react';
import IconButton from '#mui/material/IconButton';
import FavoriteIcon from '#mui/icons-material/Favorite';
import Devider from '#mui/material/Divider';
import ExpandMoreIcon from '#mui/icons-material/ExpandMore';
import { styled } from '#mui/material/styles';
import { css } from '#emotion/react';
import { Rating, Paper, Card, Box, CardHeader, CardContent, CardMedia, CardActions, Toolbar, Typography, Collapse } from '#mui/material';
const ExpandMore = styled((props) => {
const { expand, ...other } = props;
return <IconButton {...other} />;
})(({ theme, expand }) => ({
transform: !expand ? 'rotate(0deg)' : 'rotate(180deg)',
marginLeft: 'auto',
transition: theme.transitions.create('transform', {
duration: theme.transitions.duration.shortest,
}),
}));
const DropDownPaperSection = ({ Pages, error }) => {
const [expanded, setExpanded] = React.useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
if (error) {
return <div className={style.Container} >An error occured: {error.message}</div>;
}
return (
<>
<Toolbar sx={{ bgcolor : '#f4f4f8'}}>
{Pages.data.map(Pages => (
//<div classname={style.main}>
<Box key={`$Pages.id`} variant='outlined' spacing={2 } elevation={1} style={{ margin: "16px 0px", border: "1px solid black" }}
css={css`
color: #20b2aa;
:hover {
color: #2e8b57;
}
`}>
{/* {Pages.attributes.Title}
{Pages.attributes.createdAt}
*/}
<Box>
<Typography variant="body2" color="text.secondary" className="hover:bg-violet-300">
{Pages.attributes.Title}</Typography>
</Box>
<Box enableSpacing>
<ExpandMore
expand={expanded}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon />
</ExpandMore>
</Box>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<Typography paragraph padding="10px">
<Box sx={{ my: 8, spacing: 10, margin: 2 }} key={`$Pages.id`}> {Pages.attributes.Description} </Box></Typography>
<Devider />
<Box sx={{ my: 8, spacing: 10, margin: 2 }} key={`$Pages.id`}> {Pages.attributes.Content} </Box>
</Collapse>
</Box>
))} </Toolbar>
</>
)
}
export default DropDownPaperSection
But I am stuck with detaching those two elements as seen in the picture.
if you need you may find the rest of the code in github is there any possible way to achieve the closest one? I am not very sure how can i proceed next to detach this accordion. Maybe I am missing very evil details to detach them.
EDIT :
if we somehow use the portal and update the component it comes out as a combined section of multiple rows of data But not the desired output that I want.
```..........
<Collapse in={expanded} timeout="auto" unmountOnExit>
<Portal container={container.current} key={`$Pages.id`}>
<Typography paragraph padding="10px">
<Box sx={{ my: 8, spacing: 10, margin: 2 }} key={`$Pages.id`}> {Pages.attributes.Description} </Box></Typography>
<Devider />
<Box sx={{ my: 8, spacing: 10, margin: 2 }} key={`$Pages.id`}> {Pages.attributes.Content} </Box>
</Portal>
</Collapse>
<Box sx={{ p: 1, my: 1, border: '1px solid' }} key={`$Pages.id`} ref={container} />
</Box>
))} </Toolbar>
</React.Fragment> ```

Circular Loader with Percentage and Texts in React and Material-UI

I have here a CircularLoader using Material UI.
My only problem is that if the variant is indeterminate then show the loader as is with no percentage while if it is determinate show the percentage inside the loader and the text as is.
Codesandbox: CLICK HERE
const CircularLoader = (props) => {
const {
height = "auto",
color = "primary",
text = "",
value = 0,
variant = "indeterminate"
} = props;
return (
<Grid
component="div"
container
justifyContent="center"
alignItems="center"
flexDirection="column"
sx={{ height }}
>
<CircularProgress
// variant={variant}
// value={value}
disableShrink
color={color}
thickness={4}
sx={{ marginBottom: "0.5rem" }}
/>
<Typography variant="body1" component="div" color={color}>
{text}
</Typography>
</Grid>
);
};
export default CircularLoader;
I think it's probably best to split these components up (since they have different props). But if you want them in the same component, you need to provide a dynamic value from 0-100 to the determinate variant. See the docs source code example.
In your case you could have a ternary choose between which variant to render, as a quick demo the following should work (note i'm ignoring value prop and simulating it with progress for demo purposes):
import React from "react";
import Grid from "#mui/material/Grid";
import Stack from "#mui/material/Stack";
import Typography from "#mui/material/Typography";
import CircularProgress from "#mui/material/CircularProgress";
import Box from '#mui/material/Box';
const CircularLoader = ({
height = "auto",
color = "primary",
text = "",
value = 0,
variant = "indeterminate"
}) => {
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) =>
prevProgress >= 100 ? 0 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return (
<Grid
component="div"
container
justifyContent="center"
alignItems="center"
flexDirection="column"
sx={{ height }}
>
{variant === "indeterminate" ? (
<CircularProgress />
) : (
<Stack spacing={2} direction="row">
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress variant="determinate" value={progress}/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Typography variant="caption" component="div" color="text.secondary">
{`${Math.round(progress)}%`}
</Typography>
</Box>
</Box>
</Stack>
)}
<Typography variant="body1" component="div" color={color}>
{text}
</Typography>
</Grid>
);
};
export default CircularLoader;
progress is just simulated in this case for demo purposes, whereas you need to pass down progress as value via props and use that in the CircularProgress component (it should be some dynamic value linked to actual loading progress state).
Link to Sandbox

React JSX background position not placing image to center right in Material UI Grid item

I've just created a one page website portfolio and it is pretty much complete, but having issues with setting the background position of the home screen to center right. Here is my site:
https://berkley-portfolio.netlify.app/
My repo: https://github.com/Bolmstead/Portfolio
I just want the image of me to appear when resizing the window to mobile. I believe background-position would fix this right? The background size is cover, and I believe I am doing it correctly, but seem to be missing something. Below is my home component:
import React, { useEffect } from "react";
import Grid from "#material-ui/core/Grid";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core/styles";
import Container from "#material-ui/core/Container";
const useStyles = makeStyles((theme) => ({
homeContainer: {
backgroundImage: `url(/images/home2.jpg)`,
height: "103vh",
backgroundSize: "cover",
backgoundPosition: "center right"
},
overlay: {
zIndex: 1,
height: "100%",
width: "100%",
backgroundSize: "cover",
background: "rgba(0, 0, 0, 0.5)",
},
firstName: {
fontWeight: "bold",
color: "white"
},
lastName: {
fontWeight: "bold",
color: "#FFC220"
},
caption: {
fontWeight: "bold",
color: "white"
},
}));
export default function Home() {
const classes = useStyles();
const [fadedIn, setFadedIn] = React.useState(false);
useEffect(() => {
async function fadeInHomeScreen() {
setFadedIn((prev) => !prev);
}
fadeInHomeScreen();
}, []);
return (
<Grid item xs={12} className={classes.homeContainer}>
<a id="Home">
<Grid
container
alignItems="center"
justify="center"
direction="row"
className={classes.overlay}
>
<Grid item xs={12} align="center" justify="center">
<Container maxWidth="md" align="center" justify="center" className={classes.container}>
<Typography
m={12}
variant="h2"
component="h2"
className={classes.firstName}
display="inline"
>
Berkley{" "}
</Typography>
<Typography
m={12}
variant="h2"
component="h2"
className={classes.lastName}
display="inline"
>
Olmstead
</Typography>
<Typography
m={12}
variant="h6"
component="h6"
className={classes.caption}
>
I'm a Full-Stack Developer
</Typography>
</Container>
</Grid>
</Grid>
</a>
</Grid>
);
}
You have a typo, backgoundPosition should be backgroundPosition
homeContainer: {
backgroundImage: `url(/images/home2.jpg)`,
height: "103vh",
backgroundSize: "cover",
backgroundPosition: "center right"
},

Material-ui Grid: Grid item's width too big and going outside of Grid container

I am trying to set up a simple layout for a SPA using Material-ui and React. My idea is to have a left hand column as a sidebar and a right-hand main area to render information etc. However, in my current set-up I have two issues:
The <Grid item> and its container <Button> elements extend beyond the left sidebard <Grid container item xs={3} className={classes.sideBarGrid}> into the right hand column. I am not sure what I am doing wrong and any help would be greatly appreciated!
Code Sandbox
Also, I cannot get the right hand grid column <Grid container item xs={9} className={classes.labelGrid}> get to work to be full width, even though I set it to width: "100%".
Code:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
mainContainer: {
width: "100vw",
height: "100vh"
},
labelGrid: {
flexGrow: 1,
flexDirection: "column",
backgroundColor: "#EBEDF0",
alignItems: "center",
justifyContent: "center",
width: "100%"
},
sideBarGrid: {
maxWidth: 300,
flexDirection: "column",
justifyContent: "space-between"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
labelarea: {
margin: theme.spacing(1)
},
imagearea: {
minHeight: 200
},
classButton: {
margin: theme.spacing(1)
},
submit: {
margin: theme.spacing(1)
},
commentField: {
margin: theme.spacing(2, 2, 3)
}
}));
export default function Labelscreen(props) {
const classes = useStyles();
// history for react router
// array with potential classes for image
const buttonText = ["one", "two"];
// function to filter list of labels by property and see if object property is null
return (
<Grid container className={classes.mainContainer}>
<CssBaseline />
<Grid container item xs={3} className={classes.sideBarGrid}>
<Grid item>
{buttonText.map((item, key) => (
<Button
className={classes.classButton}
variant="outlined"
color="primary"
fullWidth
>
{item} ({key + 1})
</Button>
))}
<TextField
id="imageComment"
label="Comment"
placeholder="please put comments here"
multiline
fullWidth
variant="outlined"
value="adfljdaf"
/>
</Grid>
<Grid item>
<Button
type="submit"
fullWidth
variant="contained"
color="secondary"
className={classes.submit}
>
Go back
</Button>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Next
</Button>
</Grid>
</Grid>
<Grid container item xs={9} className={classes.labelGrid}>
<Typography component="h1" variant="h5">
Something
</Typography>
</Grid>
</Grid>
);
}
EDIT
The gray area on the right hand does not fill the whole screen when the screen size is large, even though the width is set to 100% in the labelGrid
Your buttons have margin (right & left), so they move beyond your left sidebar.
You can fix this using:
classButton: {
margin: theme.spacing(1, 0)
},
submit: {
margin: theme.spacing(1, 0)
},
To add back the space on the left&right side you can add padding on the container:
sideBarGrid: {
maxWidth: 300,
flexDirection: "column",
justifyContent: "space-between",
padding: theme.spacing(0, 1)
},

Resources